@@ -9,8 +9,6 @@ import {MToken} from "../MToken.sol";
99import {EIP20Interface} from "../EIP20Interface.sol " ;
1010import {IChainlinkOracle} from "../interfaces/IChainlinkOracle.sol " ;
1111
12- import {console} from "forge-std/console.sol " ;
13-
1412/**
1513 * @title ChainlinkOEVWrapper
1614 * @notice A wrapper for Chainlink price feeds that allows early updates for liquidation
@@ -20,6 +18,12 @@ contract ChainlinkOEVWrapper is Ownable, AggregatorV3Interface {
2018 /// @notice The maximum basis points for the fee multiplier
2119 uint16 public constant MAX_BPS = 10000 ;
2220
21+ /// @notice Chainlink feed decimals (USD feeds use 8 decimals)
22+ uint8 private constant CHAINLINK_FEED_DECIMALS = 8 ;
23+
24+ /// @notice Price mantissa decimals (used by ChainlinkOracle)
25+ uint8 private constant PRICE_MANTISSA_DECIMALS = 18 ;
26+
2327 /// @notice The ChainlinkOracle contract
2428 IChainlinkOracle public immutable chainlinkOracle;
2529
@@ -368,7 +372,7 @@ contract ChainlinkOEVWrapper is Ownable, AggregatorV3Interface {
368372 }
369373
370374 // execute liquidation and redeem collateral
371- uint256 collateralReceived = _executeLiquidationAndRedeem (
375+ uint256 collateralSeized = _executeLiquidationAndRedeem (
372376 borrower,
373377 repayAmount,
374378 mTokenCollateral,
@@ -377,16 +381,14 @@ contract ChainlinkOEVWrapper is Ownable, AggregatorV3Interface {
377381 underlyingCollateral
378382 );
379383
380- console.log ("collateralReceived " , collateralReceived);
381-
382384 // Calculate the split of collateral between liquidator and protocol
383385 (
384386 uint256 liquidatorFee ,
385387 uint256 protocolFee
386388 ) = _calculateCollateralSplit (
387389 repayAmount,
388390 collateralAnswer,
389- collateralReceived ,
391+ collateralSeized ,
390392 mTokenLoan,
391393 underlyingCollateral
392394 );
@@ -441,7 +443,6 @@ contract ChainlinkOEVWrapper is Ownable, AggregatorV3Interface {
441443 (10 ** decimalDelta);
442444 }
443445
444- // Adjust for token decimals (same logic as ChainlinkOracle)
445446 uint256 collateralDecimalDelta = uint256 (18 ) -
446447 uint256 (underlyingCollateral.decimals ());
447448 if (collateralDecimalDelta > 0 ) {
@@ -457,24 +458,22 @@ contract ChainlinkOEVWrapper is Ownable, AggregatorV3Interface {
457458 /// @param mTokenLoan The mToken market for the loan token
458459 /// @param underlyingLoan The underlying loan token interface
459460 /// @param underlyingCollateral The underlying collateral token interface
460- /// @return collateralReceived The amount of underlying collateral received
461+ /// @return collateralSeized The amount of underlying collateral received
461462 function _executeLiquidationAndRedeem (
462463 address borrower ,
463464 uint256 repayAmount ,
464465 address mTokenCollateral ,
465466 address mTokenLoan ,
466467 EIP20Interface underlyingLoan ,
467468 EIP20Interface underlyingCollateral
468- ) internal returns (uint256 collateralReceived ) {
469+ ) internal returns (uint256 collateralSeized ) {
469470 uint256 collateralBefore = underlyingCollateral.balanceOf (
470471 address (this )
471472 );
472473 uint256 nativeBalanceBefore = address (this ).balance;
473474
474- // approve the mToken loan market to spend the loan tokens for liquidation
475475 underlyingLoan.approve (mTokenLoan, repayAmount);
476476
477- // liquidate the borrower's position: repay their loan and seize their collateral
478477 uint256 mTokenCollateralBalanceBefore = MTokenInterface (
479478 mTokenCollateral
480479 ).balanceOf (address (this ));
@@ -487,7 +486,6 @@ contract ChainlinkOEVWrapper is Ownable, AggregatorV3Interface {
487486 "ChainlinkOEVWrapper: liquidation failed "
488487 );
489488
490- // get the amount of mToken collateral received from liquidation
491489 uint256 mTokenBalanceDelta = MTokenInterface (mTokenCollateral)
492490 .balanceOf (address (this )) - mTokenCollateralBalanceBefore;
493491
@@ -507,56 +505,53 @@ contract ChainlinkOEVWrapper is Ownable, AggregatorV3Interface {
507505 require (success, "ChainlinkOEVWrapper: WETH deposit failed " );
508506 }
509507
510- console.log (
511- "underlyingCollateral.balanceOf(address(this)) " ,
512- underlyingCollateral.balanceOf (address (this ))
513- );
514- console.log ("collateralBefore " , collateralBefore);
515-
516- collateralReceived =
508+ collateralSeized =
517509 underlyingCollateral.balanceOf (address (this )) -
518510 collateralBefore;
519511 }
520512
521513 /// @notice Calculate the split of seized collateral between liquidator and fee recipient
522514 /// @param repayAmount The amount of loan tokens being repaid
523- /// @param collateralReceived The amount of collateral tokens seized
515+ /// @param collateralSeized The amount of collateral tokens seized
524516 /// @param mTokenLoan The mToken for the loan being repaid
525517 /// @param underlyingCollateral The underlying collateral token interface
526518 /// @return liquidatorFee The amount of collateral to send to the liquidator (repayment + bonus)
527519 /// @return protocolFee The amount of collateral to send to the fee recipient (remainder)
528520 function _calculateCollateralSplit (
529521 uint256 repayAmount ,
530522 int256 collateralAnswer ,
531- uint256 collateralReceived ,
523+ uint256 collateralSeized ,
532524 address mTokenLoan ,
533525 EIP20Interface underlyingCollateral
534526 ) internal view returns (uint256 liquidatorFee , uint256 protocolFee ) {
535- uint256 loanTokenPrice = chainlinkOracle.getUnderlyingPrice (
527+ uint256 loanPrice = chainlinkOracle.getUnderlyingPrice (
536528 MToken (mTokenLoan)
537529 );
538-
539- // Get the fully adjusted collateral token price
540- uint256 collateralTokenPrice = _getCollateralTokenPrice (
530+ uint256 collateralPrice = _getCollateralTokenPrice (
541531 collateralAnswer,
542532 underlyingCollateral
543533 );
544534
545- // Calculate USD value of the repay amount
546- uint256 repayValueUSD = (repayAmount * loanTokenPrice);
547- uint256 collateralValueUSD = (collateralReceived *
548- collateralTokenPrice);
535+ uint256 usdNormalizer = 10 ** PRICE_MANTISSA_DECIMALS; // 1e18
536+ uint256 repayUSD = (repayAmount * loanPrice) / usdNormalizer;
537+ uint256 collateralUSD = (collateralSeized * collateralPrice) /
538+ usdNormalizer;
539+
540+ // If collateral is worth less than repayment, liquidator gets all collateral
541+ if (collateralUSD <= repayUSD) {
542+ liquidatorFee = collateralSeized;
543+ protocolFee = 0 ;
544+ return (liquidatorFee, protocolFee);
545+ }
549546
550- // Liquidator receives: collateral worth repay amount + bonus (remainder * feeMultiplier)
551- uint256 liquidatorPaymentUSD = repayValueUSD +
552- ((collateralValueUSD - repayValueUSD ) * uint256 (feeMultiplier)) /
547+ // Liquidator gets the repayment amount + bonus (remainder * feeMultiplier)
548+ uint256 liquidatorUSD = repayUSD +
549+ ((collateralUSD - repayUSD ) * uint256 (feeMultiplier)) /
553550 MAX_BPS;
554551
555- // Convert USD value back to collateral token amount
556- // Both prices from oracle are already scaled for token decimals, so simple division works
557- liquidatorFee = liquidatorPaymentUSD / collateralTokenPrice;
552+ // Convert back to collateral token amount
553+ liquidatorFee = (liquidatorUSD * usdNormalizer) / collateralPrice;
558554
559- // Protocol gets the remainder
560- protocolFee = collateralReceived - liquidatorFee;
555+ protocolFee = collateralSeized - liquidatorFee;
561556 }
562557}
0 commit comments