diff --git a/lib/common b/lib/common index 3692db1..7679630 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 3692db150ad90b21d7c213ea535f34792ad8873f +Subproject commit 767963087f9ee5a487a1ef0bb39ad23edde678f4 diff --git a/src/WrappedMToken.sol b/src/WrappedMToken.sol index 096231c..52c3105 100644 --- a/src/WrappedMToken.sol +++ b/src/WrappedMToken.sol @@ -542,16 +542,13 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { * @return wrapped_ The amount of wM minted. */ function _wrap(address account_, address recipient_, uint240 amount_) internal returns (uint240 wrapped_) { - uint240 startingBalance_ = _mBalanceOf(address(this)); - // NOTE: The behavior of `IMTokenLike.transferFrom` is known, so its return can be ignored. IMTokenLike(mToken).transferFrom(account_, address(this), amount_); // NOTE: When this WrappedMToken contract is earning, any amount of M sent to it is converted to a principal // amount at the MToken contract, which when represented as a present amount, may be a rounding error - // amount less than `amount_`. In order to capture the real increase in M, the difference between the - // starting and ending M balance is minted as WrappedM token. - _mint(recipient_, wrapped_ = _mBalanceOf(address(this)) - startingBalance_); + // amount less than `amount_`. This will reduce excess by the rounding error. + _mint(recipient_, wrapped_ = amount_); } /** @@ -562,18 +559,13 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { * @return unwrapped_ The amount of M withdrawn. */ function _unwrap(address account_, address recipient_, uint240 amount_) internal returns (uint240 unwrapped_) { - _burn(account_, amount_); - - uint240 startingBalance_ = _mBalanceOf(address(this)); - - // NOTE: The behavior of `IMTokenLike.transfer` is known, so its return can be ignored. - IMTokenLike(mToken).transfer(recipient_, _getSafeTransferableM(amount_, currentIndex())); - // NOTE: When this WrappedMToken contract is earning, any amount of M sent from it is converted to a principal // amount at the MToken contract, which when represented as a present amount, may be a rounding error - // amount more than `amount_`. In order to capture the real decrease in M, the difference between the - // ending and starting M balance is returned. - return startingBalance_ - _mBalanceOf(address(this)); + // amount more than `amount_`. The real decrease in M may be larger than `amount_`. + _burn(account_, unwrapped_ = amount_); + + // NOTE: The behavior of `IMTokenLike.transfer` is known, so its return can be ignored. + IMTokenLike(mToken).transfer(recipient_, _getSufficientTransferableM(recipient_, amount_)); } /** @@ -673,6 +665,15 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { return IRegistrarLike(registrar).get(key_); } + /** + * @dev Returns whether `account_` is earning M Token. + * @param account_ The account being queried. + * @return isEarning_ Whether the account is earning M Token. + */ + function _isEarningM(address account_) internal view returns (bool isEarning_) { + return IMTokenLike(mToken).isEarning(account_); + } + /** * @dev Compute the adjusted amount of M that can safely be transferred out given the current index. * @param amount_ Some amount to be transferred out of this contract. @@ -682,7 +683,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { function _getSafeTransferableM(uint240 amount_, uint128 currentIndex_) internal view returns (uint240 safeAmount_) { // If this contract is earning, adjust `amount_` to ensure it's M balance decrement is limited to `amount_`. return - IMTokenLike(mToken).isEarning(address(this)) + _isEarningM(address(this)) ? IndexingMath.getPresentAmountRoundedDown( IndexingMath.getPrincipalAmountRoundedDown(amount_, currentIndex_), currentIndex_ @@ -690,6 +691,34 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { : amount_; } + /** + * @dev Compute the adjusted amount of M that must be transferred so the recipient receives at least that amount. + * @param recipient_ The address of some recipient. + * @param amount_ Some amount to be transferred out of the wrapper. + * @return sufficientAmount_ The adjusted amount that must be transferred. + */ + function _getSufficientTransferableM( + address recipient_, + uint240 amount_ + ) internal view returns (uint240 sufficientAmount_) { + // If the recipient is not earning or if the wrapper is earning, not need to adjust `amount_`. + // See: https://github.com/m0-foundation/protocol/blob/main/src/MToken.sol#L385 + if (!_isEarningM(recipient_) || _isEarningM(address(this))) return amount_; + + uint128 currentMIndex_ = _currentMIndex(); + uint112 principal = uint112(IMTokenLike(mToken).principalBalanceOf(recipient_)); + uint240 balance = IndexingMath.getPresentAmountRoundedDown(principal, currentMIndex_); + + // Adjust `amount_` to ensure the recipient's M balance increments by at least `amount_`. + unchecked { + return + IndexingMath.getPresentAmountRoundedUp( + IndexingMath.getPrincipalAmountRoundedUp(balance + amount_, currentMIndex_) - principal, + currentMIndex_ + ); + } + } + /// @dev Returns the address of the contract to use as a migrator, if any. function _getMigrator() internal view override returns (address migrator_) { return diff --git a/src/interfaces/IMTokenLike.sol b/src/interfaces/IMTokenLike.sol index 3d902bc..6881566 100644 --- a/src/interfaces/IMTokenLike.sol +++ b/src/interfaces/IMTokenLike.sol @@ -50,4 +50,11 @@ interface IMTokenLike { /// @notice The current index that would be written to storage if `updateIndex` is called. function currentIndex() external view returns (uint128 currentIndex); + + /** + * @notice The principal of an earner M token balance. + * @param account The account to get the principal balance of. + * @return principal The principal balance of the account. + */ + function principalBalanceOf(address account) external view returns (uint240 principal); } diff --git a/test/integration/Protocol.t.sol b/test/integration/Protocol.t.sol index dffe6c6..0536581 100644 --- a/test/integration/Protocol.t.sol +++ b/test/integration/Protocol.t.sol @@ -75,14 +75,14 @@ contract ProtocolIntegrationTests is TestBase { assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 99_999999); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance = 99_999999); + assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance = 100_000000); assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 99_999999); + assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 100_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply); assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_wrappedMToken.excess(), _excess -= 1); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); @@ -142,14 +142,14 @@ contract ProtocolIntegrationTests is TestBase { assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 199_999999); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance = 199_999999); + assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance = 200_000000); assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 199_999999); + assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 200_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply); assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_wrappedMToken.excess(), _excess -= 1); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); @@ -164,14 +164,14 @@ contract ProtocolIntegrationTests is TestBase { assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 149_999999); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance = 149_999999); + assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance = 150_000000); assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); // Assert Globals assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply); - assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += 149_999999); + assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += 150_000000); assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_wrappedMToken.excess(), _excess -= 1); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); @@ -233,7 +233,7 @@ contract ProtocolIntegrationTests is TestBase { assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 99_999999); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance = 99_999999); + assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance = 100_000000); assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); _giveM(_carol, 100_000000); @@ -246,10 +246,10 @@ contract ProtocolIntegrationTests is TestBase { assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += _aliceBalance); + assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 100_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += 100_000000); assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_wrappedMToken.excess(), _excess -= 1); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); @@ -338,7 +338,7 @@ contract ProtocolIntegrationTests is TestBase { // Assert Alice (Earner) assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 57376); + assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 57377); // Assert Bob (Earner) assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance); @@ -366,16 +366,16 @@ contract ProtocolIntegrationTests is TestBase { _giveM(_carol, 100_000000); _wrap(_carol, _carol, 100_000000); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance += 99_999999); + assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance += 100_000000); assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance += 100_000000); assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 199_999999); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 99_999999); + assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 100_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += 100_000000); assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_wrappedMToken.excess(), _excess -= 1); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); @@ -430,7 +430,7 @@ contract ProtocolIntegrationTests is TestBase { assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield -= 3_614473); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= 99_999999); + assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= 100_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += _aliceBalance); assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 3_614473); assertEq(_wrappedMToken.excess(), _excess); @@ -469,64 +469,64 @@ contract ProtocolIntegrationTests is TestBase { _unwrap(_alice, _alice, _aliceBalance); - // Assert Alice (Non-Earner) - assertEq(_mToken.balanceOf(_alice), 103_614471); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance -= 103_614472); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); - // Assert Globals assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply); - assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply -= 103_614472); + assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply -= _aliceBalance); assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield); - assertEq(_wrappedMToken.excess(), _excess += 1); + assertEq(_wrappedMToken.excess(), _excess); + + // Assert Alice (Non-Earner) + assertEq(_mToken.balanceOf(_alice), _aliceBalance); + assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance -= _aliceBalance); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); // Accrued yield of Bob is claimed when unwrapping _unwrap(_bob, _bob, _bobBalance + _bobAccruedYield); - // Assert Bob (Earner) - assertEq(_mToken.balanceOf(_bob), 100_000000 + 3_614474 - 1); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance -= 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield -= 3_614474); - // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= 100_000000); + assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= _bobBalance); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply); - assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 3_614474); - assertEq(_wrappedMToken.excess(), _excess += 1); + assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= _bobAccruedYield); + assertEq(_wrappedMToken.excess(), _excess -= 1); + + // Assert Bob (Earner) + assertEq(_mToken.balanceOf(_bob), _bobBalance + _bobAccruedYield); + assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance -= _bobBalance); + assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield -= _bobAccruedYield); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); // Accrued yield of Carol is claimed when unwrapping _unwrap(_carol, _carol, _carolBalance + _carolAccruedYield); - // Assert Carol (Earner) - assertEq(_mToken.balanceOf(_carol), 100_000000 + 2_395361 - 1); - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance -= 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), _carolAccruedYield -= 2_395361); - // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= 100_000000); + assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= _carolBalance); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply); - assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 2_395361); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= _carolAccruedYield); + assertEq(_wrappedMToken.excess(), _excess -= 1); + + // Assert Carol (Earner) + assertEq(_mToken.balanceOf(_carol), _carolBalance + _carolAccruedYield); + assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance -= _carolBalance); + assertEq(_wrappedMToken.accruedYieldOf(_carol), _carolAccruedYield -= _carolAccruedYield); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); _unwrap(_dave, _dave, _daveBalance); - // Assert Dave (Non-Earner) - assertEq(_mToken.balanceOf(_dave), 100_000000 - 1); - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance -= 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); - // Assert Globals assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply); - assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply -= 100_000000); + assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply -= _daveBalance); assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield); assertEq(_wrappedMToken.excess(), _excess); + // Assert Dave (Non-Earner) + assertEq(_mToken.balanceOf(_dave), _daveBalance); + assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance -= _daveBalance); + assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); uint256 vaultStartingBalance_ = _mToken.balanceOf(_excessDestination); diff --git a/test/unit/Stories.t.sol b/test/unit/Stories.t.sol index 906a3a0..ceeb315 100644 --- a/test/unit/Stories.t.sol +++ b/test/unit/Stories.t.sol @@ -309,7 +309,7 @@ contract StoryTests is Test { assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); assertEq(_wrappedMToken.totalSupply(), 450_000000); assertEq(_wrappedMToken.totalAccruedYield(), 183_333340); - assertEq(_wrappedMToken.excess(), 600_000000); + assertEq(_wrappedMToken.excess(), 599_999995); vm.prank(_bob); _wrappedMToken.unwrap(_bob, 333_333330); @@ -323,7 +323,7 @@ contract StoryTests is Test { assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); assertEq(_wrappedMToken.totalSupply(), 250_000000); assertEq(_wrappedMToken.totalAccruedYield(), 50_000010); - assertEq(_wrappedMToken.excess(), 600_000000); + assertEq(_wrappedMToken.excess(), 599_999995); vm.prank(_carol); _wrappedMToken.unwrap(_carol, 250_000000); @@ -337,7 +337,7 @@ contract StoryTests is Test { assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); assertEq(_wrappedMToken.totalSupply(), 50_000000); assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 600_000010); + assertEq(_wrappedMToken.excess(), 600_000005); vm.prank(_dave); _wrappedMToken.unwrap(_dave, 50_000000); @@ -351,7 +351,7 @@ contract StoryTests is Test { assertEq(_wrappedMToken.totalNonEarningSupply(), 0); assertEq(_wrappedMToken.totalSupply(), 0); assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 600_000010); + assertEq(_wrappedMToken.excess(), 600_000005); _wrappedMToken.claimExcess(); diff --git a/test/unit/WrappedMToken.t.sol b/test/unit/WrappedMToken.t.sol index 53823ab..916371b 100644 --- a/test/unit/WrappedMToken.t.sol +++ b/test/unit/WrappedMToken.t.sol @@ -11,6 +11,7 @@ import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.s import { Proxy } from "../../lib/common/src/Proxy.sol"; import { Test } from "../../lib/forge-std/src/Test.sol"; +import { IMTokenLike } from "../../src/interfaces/IMTokenLike.sol"; import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; import { MockM, MockRegistrar } from "../utils/Mocks.sol"; @@ -19,6 +20,7 @@ import { WrappedMTokenHarness } from "../utils/WrappedMTokenHarness.sol"; // TODO: All operations involving earners should include demonstration of accrued yield being added to their balance. // TODO: Add relevant unit tests while earning enabled/disabled. // TODO: Remove unneeded _wrappedMToken.enableEarning. +// TODO: Expect call for IMTokenLike(mToken).transfer contract WrappedMTokenTests is Test { uint56 internal constant _EXP_SCALED_ONE = IndexingMath.EXP_SCALED_ONE; @@ -332,6 +334,8 @@ contract WrappedMTokenTests is Test { _mToken.setBalanceOf(address(_wrappedMToken), 1_000); + vm.expectCall(address(_mToken), abi.encodeWithSelector(IMTokenLike.transfer.selector, _alice, 500)); + vm.prank(_alice); assertEq(_wrappedMToken.unwrap(_alice, 500), 500); @@ -349,6 +353,27 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 0); } + function test_unwrap_fromNonEarner_toMEarnerWhileWrapperNotEarning_() external { + _wrappedMToken.setTotalNonEarningSupply(500); + + _wrappedMToken.setAccountOf(_alice, 500); + + _mToken.setBalanceOf(address(_wrappedMToken), 501); + + _mToken.setIsEarning(_bob, true); + _mToken.setPrincipalBalanceOf(_bob, 905); + + vm.expectCall(address(_mToken), abi.encodeWithSelector(IMTokenLike.transfer.selector, _bob, 501)); + + vm.prank(_alice); + assertEq(_wrappedMToken.unwrap(_bob, 500), 500); + + assertEq(_wrappedMToken.balanceOf(_alice), 0); + assertEq(_wrappedMToken.totalNonEarningSupply(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 0); + assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 0); + } + function test_unwrap_fromEarner() external { _registrar.setListContains(_EARNERS_LIST_NAME, address(_wrappedMToken), true); @@ -362,7 +387,7 @@ contract WrappedMTokenTests is Test { _mToken.setBalanceOf(address(_wrappedMToken), 1_000); vm.prank(_alice); - assertEq(_wrappedMToken.unwrap(_alice, 1), 0); + assertEq(_wrappedMToken.unwrap(_alice, 1), 1); // Change due to principal round up on unwrap. assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); @@ -371,7 +396,7 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.totalEarningSupply(), 999); vm.prank(_alice); - assertEq(_wrappedMToken.unwrap(_alice, 999), 998); + assertEq(_wrappedMToken.unwrap(_alice, 999), 999); assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); assertEq(_wrappedMToken.balanceOf(_alice), 0); diff --git a/test/utils/Mocks.sol b/test/utils/Mocks.sol index 805eb7b..3b7643c 100644 --- a/test/utils/Mocks.sol +++ b/test/utils/Mocks.sol @@ -7,6 +7,7 @@ contract MockM { mapping(address account => uint256 balance) public balanceOf; mapping(address account => bool isEarning) public isEarning; + mapping(address account => uint240 principal) public principalBalanceOf; function transfer(address recipient_, uint256 amount_) external returns (bool success_) { balanceOf[msg.sender] -= amount_; @@ -26,6 +27,14 @@ contract MockM { balanceOf[account_] = balance_; } + function setIsEarning(address account_, bool isEarning_) external { + isEarning[account_] = isEarning_; + } + + function setPrincipalBalanceOf(address account_, uint112 principal_) external { + principalBalanceOf[account_] = principal_; + } + function setCurrentIndex(uint128 currentIndex_) external { currentIndex = currentIndex_; }