Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: No claim on wrap/unwrap/transfer #103

Open
wants to merge 1 commit into
base: feat/principal-based-accounts
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 8 additions & 14 deletions src/WrappedMToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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_);
}
Expand Down Expand Up @@ -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_);
}
Expand All @@ -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_);
}
Expand Down Expand Up @@ -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_);

Expand Down
24 changes: 12 additions & 12 deletions test/integration/MorphoBlue.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
42 changes: 21 additions & 21 deletions test/integration/Protocol.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be 100_000000 now that we always receive 1 wM for 1 M?

Copy link
Collaborator Author

@deluca-mike deluca-mike Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is not built on top of the wrap/unwrap fix PR. For the time being, they are separate features that are not mutually exclusive.


// 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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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);

Expand Down
Loading
Loading