Skip to content

Commit bfb338d

Browse files
committed
fix morpho tests
1 parent 76e41ab commit bfb338d

File tree

3 files changed

+81
-12
lines changed

3 files changed

+81
-12
lines changed

proposals/ChainlinkOracleConfigs.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,13 @@ abstract contract ChainlinkOracleConfigs is Test {
8686
"MOONWELL_MAMO"
8787
)
8888
);
89+
90+
/// NOTE: stkWELL does not have an equivalent MToken to add reserves to, so use TEMPORAL_GOVERNOR as the fee recipient
8991
_MorphoOracleConfigs[BASE_CHAIN_ID].push(
9092
MorphoOracleConfig(
9193
"CHAINLINK_stkWELL_USD",
9294
"CHAINLINK_WELL_USD",
93-
"MOONWELL_WELL"
95+
"TEMPORAL_GOVERNOR"
9496
)
9597
);
9698
}

src/oracles/ChainlinkOEVMorphoWrapper.sol

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -452,12 +452,27 @@ contract ChainlinkOEVMorphoWrapper is
452452
liquidatorFee
453453
);
454454

455-
// transfer the remainder to the protocol's core market for this collateral
456-
EIP20Interface(marketParams.collateralToken).approve(
457-
feeRecipient,
458-
protocolFee
455+
// transfer the remainder to the fee recipient
456+
// if the fee recipient is an MToken, add as reserves
457+
// otherwise, just transfer the tokens directly
458+
EIP20Interface collateralToken = EIP20Interface(
459+
marketParams.collateralToken
459460
);
460-
MErc20(feeRecipient)._addReserves(protocolFee);
461+
462+
// Check if fee recipient is an MToken by checking if it has isMToken constant
463+
try MErc20(feeRecipient).isMToken() returns (bool isMToken) {
464+
if (isMToken) {
465+
// It's an MToken, add as reserves
466+
collateralToken.approve(feeRecipient, protocolFee);
467+
MErc20(feeRecipient)._addReserves(protocolFee);
468+
} else {
469+
// Not an MToken, just transfer
470+
collateralToken.transfer(feeRecipient, protocolFee);
471+
}
472+
} catch {
473+
// If the call fails, it's not an MToken, just transfer
474+
collateralToken.transfer(feeRecipient, protocolFee);
475+
}
461476

462477
emit PriceUpdatedEarlyAndLiquidated(
463478
borrower,

test/integration/oracle/ChainlinkOEVMorphoWrapperIntegration.t.sol

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ contract ChainlinkOEVMorphoWrapperIntegrationTest is
190190
}
191191

192192
function testUpdatePriceEarlyAndLiquidate_stkWELL() public {
193+
// Note: stkWELL has no market, so fee recipient should be set to treasury
194+
// The wrapper will automatically detect this and transfer instead of calling _addReserves
193195
_testLiquidation(
194196
addresses.getAddress("CHAINLINK_stkWELL_USD_ORACLE_PROXY"),
195197
addresses.getAddress("STK_GOVTOKEN_PROXY"),
@@ -206,8 +208,8 @@ contract ChainlinkOEVMorphoWrapperIntegrationTest is
206208
addresses.getAddress("MAMO"),
207209
addresses.getAddress("MORPHO_CHAINLINK_MAMO_USD_ORACLE"),
208210
0.385e18,
209-
50_000e18, // More MAMO tokens
210-
500e18
211+
250_000e18, // Scale up collateral (5x from 50k to match WELL's economic value)
212+
2_500e18 // Scale up seized amount proportionally (MAMO is 4x WELL price, so 10k/4 = 2.5k)
211213
);
212214
}
213215

@@ -266,14 +268,37 @@ contract ChainlinkOEVMorphoWrapperIntegrationTest is
266268
)
267269
);
268270

269-
// Execute liquidation and validate
271+
// Execute liquidation
272+
_executeLiquidation(
273+
wrapper,
274+
params,
275+
loanToken,
276+
borrowAmount,
277+
seized,
278+
collToken
279+
);
280+
}
281+
282+
function _executeLiquidation(
283+
ChainlinkOEVMorphoWrapper wrapper,
284+
MarketParams memory params,
285+
address loanToken,
286+
uint256 borrowAmount,
287+
uint256 seized,
288+
address collToken
289+
) internal {
270290
deal(loanToken, LIQUIDATOR, borrowAmount);
271291
vm.startPrank(LIQUIDATOR);
272292
IERC20(loanToken).approve(address(wrapper), borrowAmount);
273293

274294
uint256 liqLoanBefore = IERC20(loanToken).balanceOf(LIQUIDATOR);
275295
uint256 liqCollBefore = IERC20(collToken).balanceOf(LIQUIDATOR);
276296

297+
// Capture fee recipient state before
298+
address feeRecipient = wrapper.feeRecipient();
299+
uint256 feeStateBefore = _getFeeRecipientState(feeRecipient, collToken);
300+
bool isMToken = _isFeeRecipientMToken(feeRecipient);
301+
277302
wrapper.updatePriceEarlyAndLiquidate(
278303
params,
279304
BORROWER,
@@ -282,6 +307,7 @@ contract ChainlinkOEVMorphoWrapperIntegrationTest is
282307
);
283308
vm.stopPrank();
284309

310+
// Assertions
285311
assertEq(wrapper.cachedRoundId(), 777);
286312
assertGt(
287313
liqLoanBefore - IERC20(loanToken).balanceOf(LIQUIDATOR),
@@ -293,13 +319,39 @@ contract ChainlinkOEVMorphoWrapperIntegrationTest is
293319
0,
294320
"no collateral received"
295321
);
322+
323+
// Verify protocol fee was collected
324+
uint256 feeStateAfter = _getFeeRecipientState(feeRecipient, collToken);
296325
assertGt(
297-
IERC20(collToken).balanceOf(wrapper.feeRecipient()),
298-
0,
299-
"no fee collected"
326+
feeStateAfter,
327+
feeStateBefore,
328+
isMToken
329+
? "no fee collected (reserves)"
330+
: "no fee collected (balance)"
300331
);
301332
}
302333

334+
function _isFeeRecipientMToken(
335+
address feeRecipient
336+
) internal view returns (bool) {
337+
try MErc20(feeRecipient).isMToken() returns (bool _isMToken) {
338+
return _isMToken;
339+
} catch {
340+
return false;
341+
}
342+
}
343+
344+
function _getFeeRecipientState(
345+
address feeRecipient,
346+
address collToken
347+
) internal view returns (uint256) {
348+
if (_isFeeRecipientMToken(feeRecipient)) {
349+
return MErc20(feeRecipient).totalReserves();
350+
} else {
351+
return IERC20(collToken).balanceOf(feeRecipient);
352+
}
353+
}
354+
303355
function testUpdatePriceEarlyAndLiquidate_RevertArgsZero() public {
304356
MarketParams memory params;
305357
address mUSDC = addresses.getAddress("MOONWELL_USDC");

0 commit comments

Comments
 (0)