From b31c25c7118fb10dc2a1f336f8914801e8b95d7d Mon Sep 17 00:00:00 2001 From: Michael De Luca Date: Mon, 13 Jan 2025 12:36:42 -0500 Subject: [PATCH] feat: No claim on wrap/unwrap/transfer --- src/WrappedMToken.sol | 22 +-- test/integration/MorphoBlue.t.sol | 24 +-- test/integration/Protocol.t.sol | 42 +++--- test/integration/UniswapV3.t.sol | 96 ++++++------ test/unit/Stories.t.sol | 78 +++++----- test/unit/WrappedMToken.t.sol | 236 +++++++++++------------------- 6 files changed, 217 insertions(+), 281 deletions(-) diff --git a/src/WrappedMToken.sol b/src/WrappedMToken.sol index d3de485..6532cb2 100644 --- a/src/WrappedMToken.sol +++ b/src/WrappedMToken.sol @@ -177,9 +177,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { /// @inheritdoc IWrappedMToken function unwrap(address recipient_) external returns (uint240 unwrapped_) { - _migrateEarner(msg.sender); // NOTE: Need to migrate before calling `balanceWithYieldOf`. - - return _unwrap(msg.sender, recipient_, uint240(balanceWithYieldOf(msg.sender))); + return _unwrap(msg.sender, recipient_, uint240(balanceOf(msg.sender))); } /// @inheritdoc IWrappedMToken @@ -270,7 +268,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { } /// @inheritdoc IWrappedMToken - function balanceWithYieldOf(address account_) public view returns (uint256 balance_) { + function balanceWithYieldOf(address account_) external view returns (uint256 balance_) { unchecked { return balanceOf(account_) + accruedYieldOf(account_); } @@ -349,12 +347,10 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { _revertIfInvalidRecipient(recipient_); if (_isEarning(_accounts[recipient_])) { - uint128 currentIndex_ = currentIndex(); - - _claim(recipient_, currentIndex_); + _migrateEarner(recipient_); // NOTE: Additional principal may end up being rounded to 0 and this will not `_revertIfInsufficientAmount`. - _addEarningAmount(recipient_, amount_, currentIndex_); + _addEarningAmount(recipient_, amount_, currentIndex()); } else { _addNonEarningAmount(recipient_, amount_); } @@ -371,12 +367,10 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { _revertIfInsufficientAmount(amount_); if (_isEarning(_accounts[account_])) { - uint128 currentIndex_ = currentIndex(); - - _claim(account_, currentIndex_); + _migrateEarner(account_); // NOTE: Subtracted principal may end up being rounded to 0 and this will not `_revertIfInsufficientAmount`. - _subtractEarningAmount(account_, amount_, currentIndex_); + _subtractEarningAmount(account_, amount_, currentIndex()); } else { _subtractNonEarningAmount(account_, amount_); } @@ -511,8 +505,8 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { function _transfer(address sender_, address recipient_, uint240 amount_, uint128 currentIndex_) internal { _revertIfInvalidRecipient(recipient_); - _claim(sender_, currentIndex_); - _claim(recipient_, currentIndex_); + _migrateEarner(sender_); + _migrateEarner(recipient_); emit Transfer(sender_, recipient_, amount_); diff --git a/test/integration/MorphoBlue.t.sol b/test/integration/MorphoBlue.t.sol index 0e1dd2c..5e3b4bf 100644 --- a/test/integration/MorphoBlue.t.sol +++ b/test/integration/MorphoBlue.t.sol @@ -59,8 +59,8 @@ contract MorphoBlueTests is MorphoTestBase { // borrowing 0.90 USDC. _createMarket(_alice, _USDC); - // The market creation has triggered a wM transfer and the yield has been claimed for morpho. - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= _morphoAccruedYield); + // The market creation has triggered a wM transfer but the yield has not been claimed for morpho. + assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= 1); assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1e6); assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1e6); @@ -97,7 +97,7 @@ contract MorphoBlueTests is MorphoTestBase { vm.warp(vm.getBlockTimestamp() + 365 days); assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 49_292100); + assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 49_292110); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); @@ -111,8 +111,8 @@ contract MorphoBlueTests is MorphoTestBase { _withdrawCollateral(_bob, address(_wrappedMToken), 1_000e6, _bob, _USDC); - // The collateral withdrawal has triggered a wM transfer and the yield has been claimed for morpho. - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= _morphoAccruedYield); + // The collateral withdrawal has triggered a wM transfer but the yield has not been claimed for morpho. + assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= 1); assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000e6); assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000e6); @@ -130,7 +130,7 @@ contract MorphoBlueTests is MorphoTestBase { vm.warp(vm.getBlockTimestamp() + 365 days); assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 121446); + assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 2_545180); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); @@ -151,8 +151,8 @@ contract MorphoBlueTests is MorphoTestBase { // borrowing 0.90 wM. _createMarket(_alice, address(_wrappedMToken)); - // The market creation has triggered a wM transfer and the yield has been claimed for morpho. - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= _morphoAccruedYield); + // The market creation has triggered a wM transfer but the yield has not been claimed for morpho. + assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= 2); assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 100000); assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 100000); @@ -190,7 +190,7 @@ contract MorphoBlueTests is MorphoTestBase { // `startEarningFor` has been called so wM yield has accrued in the pool. assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 4_994256); + assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 4_994266); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); @@ -199,8 +199,8 @@ contract MorphoBlueTests is MorphoTestBase { _repay(_bob, address(_wrappedMToken), 900e6, _USDC); - // The repay has triggered a wM transfer and the yield has been claimed for morpho. - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= _morphoAccruedYield); + // The repay has triggered a wM transfer but the yield has not been claimed for morpho. + assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM -= 900e6); assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 900e6); @@ -224,7 +224,7 @@ contract MorphoBlueTests is MorphoTestBase { // `startEarningFor` has been called so wM yield has accrued in the pool. assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 77192); + assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 322772); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); diff --git a/test/integration/Protocol.t.sol b/test/integration/Protocol.t.sol index fd7edb8..45b6f0e 100644 --- a/test/integration/Protocol.t.sol +++ b/test/integration/Protocol.t.sol @@ -306,20 +306,20 @@ contract ProtocolIntegrationTests is TestBase { assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); // Alice transfers all her tokens and only keeps her accrued yield. - _transferWM(_alice, _carol, 100_000000); + _transferWM(_alice, _carol, 99_999999); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance = _aliceBalance + 2_395361 - 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield -= 2_395361); + assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance -= 99_999999); + assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield -= 1); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance += 100_000000); + assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance += 99_999999); assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += _bobBalance + 2_395361 - 100_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += _daveBalance + 100_000000); - assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 2_395361 + 1); + assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += _bobBalance - 99_999999); + assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += _daveBalance + 99_999999); + assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 1); assertEq(_wrappedMToken.excess(), _excess += 2); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); @@ -354,7 +354,7 @@ contract ProtocolIntegrationTests is TestBase { // Assert Alice (Earner) assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 57376); + assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 57378); // Assert Bob (Earner) assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance); @@ -498,35 +498,35 @@ contract ProtocolIntegrationTests is TestBase { assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); - // Accrued yield of Bob is claimed when unwrapping - _unwrap(_bob, _bob, _bobBalance + _bobAccruedYield); + // Accrued yield of Bob is not claimed when unwrapping + _unwrap(_bob, _bob, _bobBalance); // Assert Bob (Earner) - assertEq(_mToken.balanceOf(_bob), 100_000000 + 3_614474 - 1); + assertEq(_mToken.balanceOf(_bob), 100_000000 - 1); assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance -= 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield -= 3_614474); + assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield -= 1); // Assert Globals assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= 100_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply); - assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 3_614474); - assertEq(_wrappedMToken.excess(), _excess += 1); + assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield); + assertEq(_wrappedMToken.excess(), _excess); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); - // Accrued yield of Carol is claimed when unwrapping - _unwrap(_carol, _carol, _carolBalance + _carolAccruedYield); + // Accrued yield of Carol is not claimed when unwrapping + _unwrap(_carol, _carol, _carolBalance); // Assert Carol (Earner) - assertEq(_mToken.balanceOf(_carol), 100_000000 + 2_395361 - 1); + assertEq(_mToken.balanceOf(_carol), 100_000000 - 1); assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance -= 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), _carolAccruedYield -= 2_395361); + assertEq(_wrappedMToken.accruedYieldOf(_carol), _carolAccruedYield); // Assert Globals assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= 100_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply); - assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 2_395361 + 1); - assertEq(_wrappedMToken.excess(), _excess += 1); + assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 1); + assertEq(_wrappedMToken.excess(), _excess += 2); assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess); @@ -541,7 +541,7 @@ contract ProtocolIntegrationTests is TestBase { assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply); 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); diff --git a/test/integration/UniswapV3.t.sol b/test/integration/UniswapV3.t.sol index 7364fdd..8823b5d 100644 --- a/test/integration/UniswapV3.t.sol +++ b/test/integration/UniswapV3.t.sol @@ -90,10 +90,10 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 999_930937); assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 999_930937); - // The mint has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The mint has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= 1); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 1_000e6); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 1_000e6); @@ -105,7 +105,7 @@ contract UniswapV3IntegrationTests is TestBase { // `startEarningFor` has been called so wM yield has accrued in the pool. assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 878_557_430309); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 878_576_252249); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC); @@ -123,11 +123,11 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 1_000e6); assertEq(_wrappedMToken.balanceOf(_bob), swapAmountOut_); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM -= swapAmountOut_); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); /* ============ Second 1-Year Time Warp ============ */ @@ -136,7 +136,7 @@ contract UniswapV3IntegrationTests is TestBase { // `startEarningFor` has been called so wM yield has accrued in the pool. assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 878_508_264721); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 921_727_256737); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC); @@ -156,11 +156,11 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(IERC20(_USDC).balanceOf(_dave), _daveBalanceOfUSDC += swapAmountOut_); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC -= swapAmountOut_); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000e6); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); } function testFuzz_uniswapV3_earning(uint256 aliceAmount_, uint256 bobUsdc_, uint256 daveWrappedM_) public { @@ -179,10 +179,8 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= amount0_); assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += amount0_); - // The mint has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); - - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + // The mint has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= amount1_); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += amount1_); @@ -200,11 +198,13 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); assertApproxEqAbs( - _poolAccruedYield += _wrappedMToken.accruedYieldOf(_pool), - (_poolBalanceOfWM * newIndex_) / index_ - _poolBalanceOfWM, + _wrappedMToken.accruedYieldOf(_pool), + ((_poolBalanceOfWM + _poolAccruedYield) * newIndex_) / index_ - _poolBalanceOfWM, 10 ); + _poolAccruedYield = _wrappedMToken.accruedYieldOf(_pool); + // _USDC balance is unchanged since no swap has been performed. assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC); @@ -219,11 +219,10 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += bobUsdc_); assertEq(_wrappedMToken.balanceOf(_bob), swapOutWM_); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM -= swapOutWM_); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); /* ============ Second 1-Year Time Warp ============ */ @@ -238,11 +237,13 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); assertApproxEqAbs( - _poolAccruedYield += _wrappedMToken.accruedYieldOf(_pool), - (_poolBalanceOfWM * newIndex_) / index_ - _poolBalanceOfWM, + _wrappedMToken.accruedYieldOf(_pool), + ((_poolBalanceOfWM + _poolAccruedYield) * newIndex_) / index_ - _poolBalanceOfWM, 10 ); + _poolAccruedYield = _wrappedMToken.accruedYieldOf(_pool); + // USDC balance is unchanged since no swap has been performed. assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC); @@ -259,11 +260,10 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(IERC20(_USDC).balanceOf(_dave), swapOutUSDC_); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC -= swapOutUSDC_); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += daveWrappedM_); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); } function test_uniswapV3_exactInputOrOutputForEarnersAndNonEarners() public { @@ -282,10 +282,10 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 999_930937); assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 999_930937); - // The mint has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The mint has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= 1); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 1_000e6); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 1_000e6); @@ -295,7 +295,7 @@ contract UniswapV3IntegrationTests is TestBase { // Move 10 days forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 10 days); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 23_512_463128); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 23_512_966853); /* ============ 2 Non-Earners and 2 Earners are Initialized ============ */ @@ -316,17 +316,17 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000e6); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); /* ============ 1-Day Time Warp ============ */ // Move 1 day forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 1 days); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 2_349_986661); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 2_353_129323); // Claim yield for the pool and check that carol received yield. _wrappedMToken.claimFor(_pool); @@ -348,17 +348,17 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000e6); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= 1); /* ============ 3-Day Time Warp ============ */ // Move 3 days forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 3 days); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 7_051_281772); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 7_055_919498); /* ============ Dave (Non-Earner) Swaps wM for Exact USDC ============ */ @@ -367,17 +367,17 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += daveOutput_); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); /* ============ 7-Day Time Warp ============ */ // Move 7 day forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 7 days); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 16_458_240233); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 16_475_562741); /* ============ Frank (Earner) Swaps wM for Exact USDC ============ */ @@ -385,10 +385,10 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += frankOutput_); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= 1); } function test_uniswapV3_increaseDecreaseLiquidity() public { @@ -439,7 +439,7 @@ contract UniswapV3IntegrationTests is TestBase { vm.warp(vm.getBlockTimestamp() + 10 days); assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield += 1_317339); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 23_130_990918); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 23_514_282694); /* ============ Dave (Non-Earner) Swaps Exact wM for USDC ============ */ @@ -453,10 +453,10 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000e6); - // The swap has triggered a wM transfer and the yield has been claimed for the pool. - assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM += _poolAccruedYield); + // The swap has triggered a wM transfer but the yield has not been claimed for the pool. + assertEq(_wrappedMToken.balanceOf(_poolClaimRecipient), _poolClaimRecipientBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); /* ============ Alice (Non-Earner) Decreases Liquidity And Bob (Earner) Increases Liquidity ============ */ diff --git a/test/unit/Stories.t.sol b/test/unit/Stories.t.sol index 145d080..7b3c916 100644 --- a/test/unit/Stories.t.sol +++ b/test/unit/Stories.t.sol @@ -183,48 +183,48 @@ contract StoryTests is Test { _wrappedMToken.transfer(_carol, 100_000000); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.balanceOf(_alice), 100_000000); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 99_999998); // Assert Carol (Non-Earner) assertEq(_wrappedMToken.balanceOf(_carol), 200_000000); assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 300_000000); + assertEq(_wrappedMToken.totalEarningSupply(), 200_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), 300_000000); - assertEq(_wrappedMToken.totalSupply(), 600_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 49_999998); + assertEq(_wrappedMToken.totalSupply(), 500_000000); + assertEq(_wrappedMToken.totalAccruedYield(), 149_999998); assertEq(_wrappedMToken.excess(), 250_000002); vm.prank(_dave); _wrappedMToken.transfer(_bob, 50_000000); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_wrappedMToken.balanceOf(_bob), 150_000000); + assertEq(_wrappedMToken.accruedYieldOf(_bob), 49_999998); // Assert Dave (Non-Earner) assertEq(_wrappedMToken.balanceOf(_dave), 50_000000); assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 400_000000); + assertEq(_wrappedMToken.totalEarningSupply(), 250_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), 250_000000); - assertEq(_wrappedMToken.totalSupply(), 650_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalSupply(), 500_000000); + assertEq(_wrappedMToken.totalAccruedYield(), 149_999996); assertEq(_wrappedMToken.excess(), 250_000002); _mToken.setCurrentIndex(4 * _EXP_SCALED_ONE); _mToken.setBalanceOf(address(_wrappedMToken), 1_200_000000); // was 900 @ 3.0, so 1200 @ 4.0 // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 66_666664); + assertEq(_wrappedMToken.balanceOf(_alice), 100_000000); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 166_666664); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 66_666664); + assertEq(_wrappedMToken.balanceOf(_bob), 150_000000); + assertEq(_wrappedMToken.accruedYieldOf(_bob), 116_666664); // Assert Carol (Non-Earner) assertEq(_wrappedMToken.balanceOf(_carol), 200_000000); @@ -235,10 +235,10 @@ contract StoryTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 400_000000); + assertEq(_wrappedMToken.totalEarningSupply(), 250_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), 250_000000); - assertEq(_wrappedMToken.totalSupply(), 650_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 133_333328); + assertEq(_wrappedMToken.totalSupply(), 500_000000); + assertEq(_wrappedMToken.totalAccruedYield(), 283_333328); assertEq(_wrappedMToken.excess(), 416_666672); _registrar.setListContains(_EARNERS_LIST_NAME, _alice, false); @@ -250,10 +250,10 @@ contract StoryTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 200_000000); + assertEq(_wrappedMToken.totalEarningSupply(), 150_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), 516_666664); - assertEq(_wrappedMToken.totalSupply(), 716_666664); - assertEq(_wrappedMToken.totalAccruedYield(), 66_666664); + assertEq(_wrappedMToken.totalSupply(), 666_666664); + assertEq(_wrappedMToken.totalAccruedYield(), 116_666664); assertEq(_wrappedMToken.excess(), 416_666672); _registrar.setListContains(_EARNERS_LIST_NAME, _carol, true); @@ -265,10 +265,10 @@ contract StoryTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 400_000000); + assertEq(_wrappedMToken.totalEarningSupply(), 350_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), 316_666664); - assertEq(_wrappedMToken.totalSupply(), 716_666664); - assertEq(_wrappedMToken.totalAccruedYield(), 66_666664); + assertEq(_wrappedMToken.totalSupply(), 666_666664); + assertEq(_wrappedMToken.totalAccruedYield(), 116_666664); assertEq(_wrappedMToken.excess(), 416_666672); _mToken.setCurrentIndex(5 * _EXP_SCALED_ONE); @@ -279,8 +279,8 @@ contract StoryTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 133_333330); + assertEq(_wrappedMToken.balanceOf(_bob), 150_000000); + assertEq(_wrappedMToken.accruedYieldOf(_bob), 183_333330); // Assert Carol (Earner) assertEq(_wrappedMToken.balanceOf(_carol), 200_000000); @@ -291,10 +291,10 @@ contract StoryTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 400_000000); + assertEq(_wrappedMToken.totalEarningSupply(), 350_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), 316_666664); - assertEq(_wrappedMToken.totalSupply(), 716_666664); - assertEq(_wrappedMToken.totalAccruedYield(), 183_333330); + assertEq(_wrappedMToken.totalSupply(), 666_666664); + assertEq(_wrappedMToken.totalAccruedYield(), 233_333330); assertEq(_wrappedMToken.excess(), 600_000005); vm.prank(_alice); @@ -305,38 +305,38 @@ contract StoryTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 400_000000); + assertEq(_wrappedMToken.totalEarningSupply(), 350_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); - assertEq(_wrappedMToken.totalSupply(), 450_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 183_333330); + assertEq(_wrappedMToken.totalSupply(), 400_000000); + assertEq(_wrappedMToken.totalAccruedYield(), 233_333330); assertEq(_wrappedMToken.excess(), 600_000010); vm.prank(_bob); - _wrappedMToken.unwrap(_bob, 333_333330); + _wrappedMToken.unwrap(_bob, 150_000000); // Assert Bob (Earner) assertEq(_wrappedMToken.balanceOf(_bob), 0); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_wrappedMToken.accruedYieldOf(_bob), 183_333330); // Assert Globals assertEq(_wrappedMToken.totalEarningSupply(), 200_000000); assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); assertEq(_wrappedMToken.totalSupply(), 250_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 50_000000); + assertEq(_wrappedMToken.totalAccruedYield(), 233_333330); assertEq(_wrappedMToken.excess(), 600_000010); vm.prank(_carol); - _wrappedMToken.unwrap(_carol, 250_000000); + _wrappedMToken.unwrap(_carol, 200_000000); // Assert Carol (Earner) assertEq(_wrappedMToken.balanceOf(_carol), 0); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_wrappedMToken.accruedYieldOf(_carol), 50_000000); // Assert Globals assertEq(_wrappedMToken.totalEarningSupply(), 0); assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); assertEq(_wrappedMToken.totalSupply(), 50_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalAccruedYield(), 233_333330); assertEq(_wrappedMToken.excess(), 600_000010); vm.prank(_dave); @@ -350,7 +350,7 @@ contract StoryTests is Test { assertEq(_wrappedMToken.totalEarningSupply(), 0); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); assertEq(_wrappedMToken.totalSupply(), 0); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalAccruedYield(), 233_333330); assertEq(_wrappedMToken.excess(), 600_000010); _wrappedMToken.claimExcess(); @@ -359,7 +359,7 @@ contract StoryTests is Test { assertEq(_wrappedMToken.totalEarningSupply(), 0); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); assertEq(_wrappedMToken.totalSupply(), 0); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalAccruedYield(), 233_333330); assertEq(_wrappedMToken.excess(), 0); } diff --git a/test/unit/WrappedMToken.t.sol b/test/unit/WrappedMToken.t.sol index 00ff22c..a7c3b34 100644 --- a/test/unit/WrappedMToken.t.sol +++ b/test/unit/WrappedMToken.t.sol @@ -173,12 +173,12 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.internalWrap(_alice, _alice, 999), 999); assertEq(_wrappedMToken.earningPrincipalOf(_alice), 1_000 + 908); - assertEq(_wrappedMToken.balanceOf(_alice), 1_000 + 100 + 999); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.balanceOf(_alice), 1_000 + 999); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 99); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); assertEq(_wrappedMToken.totalEarningPrincipal(), 1_000 + 908); - assertEq(_wrappedMToken.totalEarningSupply(), 1_000 + 100 + 999); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 1_000 + 999); + assertEq(_wrappedMToken.totalAccruedYield(), 99); vm.expectEmit(); emit IERC20.Transfer(address(0), _alice, 1); @@ -187,12 +187,12 @@ contract WrappedMTokenTests is Test { // No change due to principal round down on wrap. assertEq(_wrappedMToken.earningPrincipalOf(_alice), 1_000 + 908 + 0); - assertEq(_wrappedMToken.balanceOf(_alice), 1_000 + 100 + 999 + 1); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.balanceOf(_alice), 1_000 + 999 + 1); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 98); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); assertEq(_wrappedMToken.totalEarningPrincipal(), 1_000 + 908 + 0); - assertEq(_wrappedMToken.totalEarningSupply(), 1_000 + 100 + 999 + 1); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 1_000 + 999 + 1); + assertEq(_wrappedMToken.totalAccruedYield(), 98); vm.expectEmit(); emit IERC20.Transfer(address(0), _alice, 2); @@ -200,12 +200,12 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.internalWrap(_alice, _alice, 2), 2); assertEq(_wrappedMToken.earningPrincipalOf(_alice), 1_000 + 908 + 0 + 1); - assertEq(_wrappedMToken.balanceOf(_alice), 1_000 + 100 + 999 + 1 + 2); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.balanceOf(_alice), 1_000 + 999 + 1 + 2); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 97); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); assertEq(_wrappedMToken.totalEarningPrincipal(), 1_000 + 908 + 0 + 1); - assertEq(_wrappedMToken.totalEarningSupply(), 1_000 + 100 + 999 + 1 + 2); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 1_000 + 999 + 1 + 2); + assertEq(_wrappedMToken.totalAccruedYield(), 97); } /* ============ wrap ============ */ @@ -246,8 +246,6 @@ contract WrappedMTokenTests is Test { _mToken.setBalanceOf(_alice, wrapAmount_); - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - if (wrapAmount_ == 0) { vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); } else { @@ -260,7 +258,7 @@ contract WrappedMTokenTests is Test { if (wrapAmount_ == 0) return; - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); + assertEq(_wrappedMToken.balanceOf(_alice), balance_ + wrapAmount_); assertEq( accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), @@ -308,8 +306,6 @@ contract WrappedMTokenTests is Test { _mToken.setBalanceOf(_alice, wrapAmount_); - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - if (wrapAmount_ == 0) { vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); } else { @@ -322,7 +318,7 @@ contract WrappedMTokenTests is Test { if (wrapAmount_ == 0) return; - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); + assertEq(_wrappedMToken.balanceOf(_alice), balance_ + wrapAmount_); assertEq( accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), @@ -368,8 +364,6 @@ contract WrappedMTokenTests is Test { _mToken.setBalanceOf(_alice, wrapAmount_); - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - if (wrapAmount_ == 0) { vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); } else { @@ -382,7 +376,7 @@ contract WrappedMTokenTests is Test { if (wrapAmount_ == 0) return; - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); + assertEq(_wrappedMToken.balanceOf(_alice), balance_ + wrapAmount_); assertEq( accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), @@ -428,8 +422,6 @@ contract WrappedMTokenTests is Test { _mToken.setBalanceOf(_alice, wrapAmount_); - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - if (wrapAmount_ == 0) { vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); } else { @@ -442,7 +434,7 @@ contract WrappedMTokenTests is Test { if (wrapAmount_ == 0) return; - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); + assertEq(_wrappedMToken.balanceOf(_alice), balance_ + wrapAmount_); assertEq( accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), @@ -540,18 +532,18 @@ contract WrappedMTokenTests is Test { _mToken.setBalanceOf(address(_wrappedMToken), 1_000); - _wrappedMToken.setTotalEarningPrincipal(909); - _wrappedMToken.setTotalEarningSupply(909); + _wrappedMToken.setTotalEarningPrincipal(1_000); + _wrappedMToken.setTotalEarningSupply(1_000); - _wrappedMToken.setAccountOf(_alice, 909, 909); // 999 balance with yield. + _wrappedMToken.setAccountOf(_alice, 1_000, 1_000); // 1_100 balance with yield. - assertEq(_wrappedMToken.earningPrincipalOf(_alice), 909); - assertEq(_wrappedMToken.balanceOf(_alice), 909); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 90); + assertEq(_wrappedMToken.earningPrincipalOf(_alice), 1_000); + assertEq(_wrappedMToken.balanceOf(_alice), 1_000); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 100); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningPrincipal(), 909); - assertEq(_wrappedMToken.totalEarningSupply(), 909); - assertEq(_wrappedMToken.totalAccruedYield(), 90); + assertEq(_wrappedMToken.totalEarningPrincipal(), 1_000); + assertEq(_wrappedMToken.totalEarningSupply(), 1_000); + assertEq(_wrappedMToken.totalAccruedYield(), 100); vm.expectEmit(); emit IERC20.Transfer(_alice, address(0), 1); @@ -559,39 +551,39 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.internalUnwrap(_alice, _alice, 1), 0); // Change due to principal round up on unwrap. - assertEq(_wrappedMToken.earningPrincipalOf(_alice), 909 - 1); - assertEq(_wrappedMToken.balanceOf(_alice), 999 - 1); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.earningPrincipalOf(_alice), 1_000 - 1); + assertEq(_wrappedMToken.balanceOf(_alice), 1_000 - 1); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 99); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningPrincipal(), 909 - 1); - assertEq(_wrappedMToken.totalEarningSupply(), 999 - 1); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningPrincipal(), 1_000 - 1); + assertEq(_wrappedMToken.totalEarningSupply(), 1_000 - 1); + assertEq(_wrappedMToken.totalAccruedYield(), 99); vm.expectEmit(); - emit IERC20.Transfer(_alice, address(0), 498); + emit IERC20.Transfer(_alice, address(0), 499); - assertEq(_wrappedMToken.internalUnwrap(_alice, _alice, 498), 497); + assertEq(_wrappedMToken.internalUnwrap(_alice, _alice, 499), 498); - assertEq(_wrappedMToken.earningPrincipalOf(_alice), 909 - 1 - 453); - assertEq(_wrappedMToken.balanceOf(_alice), 999 - 1 - 498); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.earningPrincipalOf(_alice), 1_000 - 1 - 454); + assertEq(_wrappedMToken.balanceOf(_alice), 1_000 - 1 - 499); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 99); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningPrincipal(), 909 - 1 - 453); - assertEq(_wrappedMToken.totalEarningSupply(), 999 - 1 - 498); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningPrincipal(), 1_000 - 1 - 454); + assertEq(_wrappedMToken.totalEarningSupply(), 1_000 - 1 - 499); + assertEq(_wrappedMToken.totalAccruedYield(), 99); vm.expectEmit(); emit IERC20.Transfer(_alice, address(0), 500); assertEq(_wrappedMToken.internalUnwrap(_alice, _alice, 500), 499); - assertEq(_wrappedMToken.earningPrincipalOf(_alice), 909 - 1 - 453 - 455); // 0 - assertEq(_wrappedMToken.balanceOf(_alice), 999 - 1 - 498 - 500); // 0 - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.earningPrincipalOf(_alice), 1_000 - 1 - 454 - 455); // 0 + assertEq(_wrappedMToken.balanceOf(_alice), 1_000 - 1 - 499 - 500); // 0 + assertEq(_wrappedMToken.accruedYieldOf(_alice), 99); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningPrincipal(), 909 - 1 - 453 - 455); // 0 - assertEq(_wrappedMToken.totalEarningSupply(), 999 - 1 - 498 - 500); // 0 - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningPrincipal(), 1_000 - 1 - 454 - 455); // 0 + assertEq(_wrappedMToken.totalEarningSupply(), 1_000 - 1 - 499 - 500); // 0 + assertEq(_wrappedMToken.totalAccruedYield(), 99); } /* ============ unwrap ============ */ @@ -628,22 +620,15 @@ contract WrappedMTokenTests is Test { _setupAccount(_alice, accountEarning_, balanceWithYield_, balance_); - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - _mToken.setBalanceOf(address(_wrappedMToken), balance_ + accruedYield_); + _mToken.setBalanceOf(address(_wrappedMToken), balance_); - unwrapAmount_ = uint240(bound(unwrapAmount_, 0, (11 * (balance_ + accruedYield_)) / 10)); + unwrapAmount_ = uint240(bound(unwrapAmount_, 0, (11 * balance_) / 10)); if (unwrapAmount_ == 0) { vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); - } else if (unwrapAmount_ > balance_ + accruedYield_) { + } else if (unwrapAmount_ > balance_) { vm.expectRevert( - abi.encodeWithSelector( - IWrappedMToken.InsufficientBalance.selector, - _alice, - balance_ + accruedYield_, - unwrapAmount_ - ) + abi.encodeWithSelector(IWrappedMToken.InsufficientBalance.selector, _alice, balance_, unwrapAmount_) ); } else { vm.expectEmit(); @@ -653,9 +638,9 @@ contract WrappedMTokenTests is Test { vm.startPrank(_alice); _wrappedMToken.unwrap(_alice, unwrapAmount_); - if ((unwrapAmount_ == 0) || (unwrapAmount_ > balance_ + accruedYield_)) return; + if ((unwrapAmount_ == 0) || (unwrapAmount_ > balance_)) return; - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ - unwrapAmount_); + assertEq(_wrappedMToken.balanceOf(_alice), balance_ - unwrapAmount_); assertEq( accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), @@ -689,21 +674,19 @@ contract WrappedMTokenTests is Test { _setupAccount(_alice, accountEarning_, balanceWithYield_, balance_); - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - _mToken.setBalanceOf(address(_wrappedMToken), balance_ + accruedYield_); + _mToken.setBalanceOf(address(_wrappedMToken), balance_); - if (balance_ + accruedYield_ == 0) { + if (balance_ == 0) { vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); } else { vm.expectEmit(); - emit IERC20.Transfer(_alice, address(0), balance_ + accruedYield_); + emit IERC20.Transfer(_alice, address(0), balance_); } vm.startPrank(_alice); _wrappedMToken.unwrap(_alice); - if (balance_ + accruedYield_ == 0) return; + if (balance_ == 0) return; assertEq(_wrappedMToken.balanceOf(_alice), 0); @@ -918,11 +901,11 @@ contract WrappedMTokenTests is Test { _mToken.setCurrentIndex(1_210000000000); _wrappedMToken.setEnableMIndex(1_100000000000); - _wrappedMToken.setAccountOf(_alice, 909, 909); + _wrappedMToken.setAccountOf(_alice, 1_000, 1_000); // 1_100 balance with yield. - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.InsufficientBalance.selector, _alice, 999, 1_000)); + vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.InsufficientBalance.selector, _alice, 1_000, 1_001)); vm.prank(_alice); - _wrappedMToken.transfer(_bob, 1_000); + _wrappedMToken.transfer(_bob, 1_001); } function test_transfer_fromNonEarner_toNonEarner() external { @@ -989,12 +972,6 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_alice), 100); - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 100); - vm.expectEmit(); emit IERC20.Transfer(_alice, _bob, 500); @@ -1002,15 +979,15 @@ contract WrappedMTokenTests is Test { _wrappedMToken.transfer(_bob, 500); assertEq(_wrappedMToken.earningPrincipalOf(_alice), 545); - assertEq(_wrappedMToken.balanceOf(_alice), 600); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.balanceOf(_alice), 500); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 99); assertEq(_wrappedMToken.balanceOf(_bob), 1_000); assertEq(_wrappedMToken.totalNonEarningSupply(), 1_000); assertEq(_wrappedMToken.totalEarningPrincipal(), 545); - assertEq(_wrappedMToken.totalEarningSupply(), 600); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 500); + assertEq(_wrappedMToken.totalAccruedYield(), 99); vm.expectEmit(); emit IERC20.Transfer(_alice, _bob, 1); @@ -1019,15 +996,15 @@ contract WrappedMTokenTests is Test { _wrappedMToken.transfer(_bob, 1); assertEq(_wrappedMToken.earningPrincipalOf(_alice), 544); - assertEq(_wrappedMToken.balanceOf(_alice), 599); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.balanceOf(_alice), 499); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 99); assertEq(_wrappedMToken.balanceOf(_bob), 1_001); assertEq(_wrappedMToken.totalNonEarningSupply(), 1_001); assertEq(_wrappedMToken.totalEarningPrincipal(), 544); - assertEq(_wrappedMToken.totalEarningSupply(), 599); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 499); + assertEq(_wrappedMToken.totalAccruedYield(), 99); } function test_transfer_fromNonEarner_toEarner() external { @@ -1044,12 +1021,6 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_bob), 50); - vm.expectEmit(); - emit IWrappedMToken.Claimed(_bob, _bob, 50); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _bob, 50); - vm.expectEmit(); emit IERC20.Transfer(_alice, _bob, 500); @@ -1059,13 +1030,13 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.balanceOf(_alice), 500); assertEq(_wrappedMToken.earningPrincipalOf(_bob), 954); - assertEq(_wrappedMToken.balanceOf(_bob), 1_050); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_wrappedMToken.balanceOf(_bob), 1_000); + assertEq(_wrappedMToken.accruedYieldOf(_bob), 49); assertEq(_wrappedMToken.totalNonEarningSupply(), 500); assertEq(_wrappedMToken.totalEarningPrincipal(), 954); - assertEq(_wrappedMToken.totalEarningSupply(), 1_050); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 1_000); + assertEq(_wrappedMToken.totalAccruedYield(), 49); } function test_transfer_fromEarner_toEarner() external { @@ -1081,18 +1052,6 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.accruedYieldOf(_alice), 100); assertEq(_wrappedMToken.accruedYieldOf(_bob), 50); - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 100); - - vm.expectEmit(); - emit IWrappedMToken.Claimed(_bob, _bob, 50); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _bob, 50); - vm.expectEmit(); emit IERC20.Transfer(_alice, _bob, 500); @@ -1100,17 +1059,17 @@ contract WrappedMTokenTests is Test { _wrappedMToken.transfer(_bob, 500); assertEq(_wrappedMToken.earningPrincipalOf(_alice), 545); - assertEq(_wrappedMToken.balanceOf(_alice), 600); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.balanceOf(_alice), 500); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 99); assertEq(_wrappedMToken.earningPrincipalOf(_bob), 954); - assertEq(_wrappedMToken.balanceOf(_bob), 1_050); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_wrappedMToken.balanceOf(_bob), 1_000); + assertEq(_wrappedMToken.accruedYieldOf(_bob), 49); assertEq(_wrappedMToken.totalNonEarningSupply(), 0); assertEq(_wrappedMToken.totalEarningPrincipal(), 1_499); - assertEq(_wrappedMToken.totalEarningSupply(), 1_650); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 1_500); + assertEq(_wrappedMToken.totalAccruedYield(), 148); } function test_transfer_nonEarnerToSelf() external { @@ -1141,12 +1100,6 @@ contract WrappedMTokenTests is Test { assertEq(_wrappedMToken.balanceOf(_alice), 1_000); assertEq(_wrappedMToken.accruedYieldOf(_alice), 100); - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 100); - vm.expectEmit(); emit IERC20.Transfer(_alice, _alice, 500); @@ -1154,12 +1107,12 @@ contract WrappedMTokenTests is Test { _wrappedMToken.transfer(_alice, 500); assertEq(_wrappedMToken.earningPrincipalOf(_alice), 1_000); - assertEq(_wrappedMToken.balanceOf(_alice), 1_100); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_wrappedMToken.balanceOf(_alice), 1_000); + assertEq(_wrappedMToken.accruedYieldOf(_alice), 100); assertEq(_wrappedMToken.totalEarningPrincipal(), 1_000); - assertEq(_wrappedMToken.totalEarningSupply(), 1_100); - assertEq(_wrappedMToken.totalAccruedYield(), 0); + assertEq(_wrappedMToken.totalEarningSupply(), 1_000); + assertEq(_wrappedMToken.totalAccruedYield(), 100); } function testFuzz_transfer( @@ -1199,19 +1152,11 @@ contract WrappedMTokenTests is Test { _setupAccount(_bob, bobEarning_, bobBalanceWithYield_, bobBalance_); - uint240 aliceAccruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - uint240 bobAccruedYield_ = _wrappedMToken.accruedYieldOf(_bob); + amount_ = uint240(bound(amount_, 0, (11 * aliceBalance_) / 10)); - amount_ = uint240(bound(amount_, 0, (11 * (aliceBalance_ + aliceAccruedYield_)) / 10)); - - if (amount_ > aliceBalance_ + aliceAccruedYield_) { + if (amount_ > aliceBalance_) { vm.expectRevert( - abi.encodeWithSelector( - IWrappedMToken.InsufficientBalance.selector, - _alice, - aliceBalance_ + aliceAccruedYield_, - amount_ - ) + abi.encodeWithSelector(IWrappedMToken.InsufficientBalance.selector, _alice, aliceBalance_, amount_) ); } else { vm.expectEmit(); @@ -1221,22 +1166,19 @@ contract WrappedMTokenTests is Test { vm.prank(_alice); _wrappedMToken.transfer(_bob, amount_); - if (amount_ > aliceBalance_ + aliceAccruedYield_) return; + if (amount_ > aliceBalance_) return; - assertEq(_wrappedMToken.balanceOf(_alice), aliceBalance_ + aliceAccruedYield_ - amount_); - assertEq(_wrappedMToken.balanceOf(_bob), bobBalance_ + bobAccruedYield_ + amount_); + assertEq(_wrappedMToken.balanceOf(_alice), aliceBalance_ - amount_); + assertEq(_wrappedMToken.balanceOf(_bob), bobBalance_ + amount_); if (aliceEarning_ && bobEarning_) { - assertEq( - _wrappedMToken.totalEarningSupply(), - aliceBalance_ + aliceAccruedYield_ + bobBalance_ + bobAccruedYield_ - ); + assertEq(_wrappedMToken.totalEarningSupply(), aliceBalance_ + bobBalance_); } else if (aliceEarning_) { - assertEq(_wrappedMToken.totalEarningSupply(), aliceBalance_ + aliceAccruedYield_ - amount_); + assertEq(_wrappedMToken.totalEarningSupply(), aliceBalance_ - amount_); assertEq(_wrappedMToken.totalNonEarningSupply(), bobBalance_ + amount_); } else if (bobEarning_) { assertEq(_wrappedMToken.totalNonEarningSupply(), aliceBalance_ - amount_); - assertEq(_wrappedMToken.totalEarningSupply(), bobBalance_ + bobAccruedYield_ + amount_); + assertEq(_wrappedMToken.totalEarningSupply(), bobBalance_ + amount_); } else { assertEq(_wrappedMToken.totalNonEarningSupply(), aliceBalance_ + bobBalance_); }