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: principal-based account #102

Open
wants to merge 1 commit into
base: feat/continuous-index
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
221 changes: 140 additions & 81 deletions src/WrappedMToken.sol

Large diffs are not rendered by default.

29 changes: 22 additions & 7 deletions src/interfaces/IWrappedMToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ interface IWrappedMToken is IMigratable, IERC20Extended {

/* ============ Custom Errors ============ */

/// @notice Emitted when performing an operation that is not allowed on an un-migrated account.
error AccountNotMigrated();

/// @notice Emitted when performing an operation that is not allowed when earning is disabled.
error EarningIsDisabled();

Expand Down Expand Up @@ -190,6 +193,18 @@ interface IWrappedMToken is IMigratable, IERC20Extended {
*/
function stopEarningFor(address account) external;

/**
* @notice Migrates the account struct for `account` from v1 to v2.
* @param account The account to migrate.
*/
function migrateAccount(address account) external;

/**
* @notice Migrates the account structs for `accounts` from v1 to v2.
* @param accounts The accounts to migrate.
*/
function migrateAccounts(address[] calldata accounts) external;

/* ============ Temporary Admin Migration ============ */

/**
Expand Down Expand Up @@ -227,11 +242,11 @@ interface IWrappedMToken is IMigratable, IERC20Extended {
function balanceWithYieldOf(address account) external view returns (uint256 balance);
Copy link
Contributor

Choose a reason for hiding this comment

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

balanceWithUnclaimedYieldOf or even delete this function

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I will handle fucntion renames later on as:

  • it is outside the scope of this PR
  • they would be applicable and useful even if this pr is not merged
  • the rebase(s) would potentially be too tedious at this point


/**
* @notice Returns the last index of `account`.
* @param account The address of some account.
* @return lastIndex The last index of `account`, 0 if the account is not earning.
* @notice Returns the earning principal of `account`.
* @param account The address of some account.
* @return earningPrincipal The earning principal of `account`.
*/
function lastIndexOf(address account) external view returns (uint128 lastIndex);
function earningPrincipalOf(address account) external view returns (uint112 earningPrincipal);

/**
* @notice Returns the recipient to override as the destination for an account's claim of yield.
Expand All @@ -255,7 +270,7 @@ interface IWrappedMToken is IMigratable, IERC20Extended {
/**
* @notice Returns whether `account` is a wM earner.
* @param account The account being queried.
* @return isEarning true if the account has started earning.
* @return isEarning Whether the account is a wM earner.
*/
function isEarning(address account) external view returns (bool isEarning);

Expand All @@ -280,8 +295,8 @@ interface IWrappedMToken is IMigratable, IERC20Extended {
/// @notice The portion of total supply that is earning yield.
function totalEarningSupply() external view returns (uint240 totalSupply);

/// @notice The principal of totalEarningSupply to help compute totalAccruedYield(), and thus excess().
function principalOfTotalEarningSupply() external view returns (uint112 principalOfTotalEarningSupply);
/// @notice The total earning principal to help compute totalAccruedYield(), and thus excess().
function totalEarningPrincipal() external view returns (uint112 totalEarningPrincipal);

/// @notice The address of the destination where excess is claimed to.
function excessDestination() external view returns (address excessDestination);
Expand Down
8 changes: 5 additions & 3 deletions test/integration/MorphoBlue.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ contract MorphoBlueTests is MorphoTestBase {
_deployV2Components();
_migrate();

_wrappedMToken.migrateAccount(_MORPHO);

_oracle = _createOracle();

_morphoBalanceOfUSDC = IERC20(_USDC).balanceOf(_MORPHO);
Expand Down Expand Up @@ -95,7 +97,7 @@ contract MorphoBlueTests is MorphoTestBase {
vm.warp(vm.getBlockTimestamp() + 365 days);

assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM);
assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 49_292101);
assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 49_292100);

// USDC balance is unchanged.
assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC);
Expand Down Expand Up @@ -188,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_994258);
assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 4_994256);

// USDC balance is unchanged.
assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC);
Expand Down Expand Up @@ -222,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 += 77193);
assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 77192);

// 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 @@ -97,8 +97,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 99_999999);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield);
assertEq(_wrappedMToken.excess(), _excess);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 1);
assertEq(_wrappedMToken.excess(), _excess += 1);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand Down Expand Up @@ -164,8 +164,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 199_999999);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield);
assertEq(_wrappedMToken.excess(), _excess);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 1);
assertEq(_wrappedMToken.excess(), _excess += 1);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand Down Expand Up @@ -221,7 +221,7 @@ contract ProtocolIntegrationTests is TestBase {

// Assert Alice (Earner)
assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance);
assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 2_423880);
assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 2_423881);

// Assert Bob (Earner)
assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance);
Expand Down Expand Up @@ -264,8 +264,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += _aliceBalance);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += 100_000000);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield);
assertEq(_wrappedMToken.excess(), _excess);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 1);
assertEq(_wrappedMToken.excess(), _excess += 1);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand Down Expand Up @@ -319,8 +319,8 @@ contract ProtocolIntegrationTests is TestBase {
// 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.excess(), _excess -= 1);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 2_395361 + 1);
assertEq(_wrappedMToken.excess(), _excess += 2);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand All @@ -337,8 +337,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 50_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply -= 50_000000);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield += 1);
assertEq(_wrappedMToken.excess(), _excess -= 1);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield);
assertEq(_wrappedMToken.excess(), _excess);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand Down Expand Up @@ -390,8 +390,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 99_999999);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += 100_000000);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield);
assertEq(_wrappedMToken.excess(), _excess);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 1);
assertEq(_wrappedMToken.excess(), _excess += 1);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand Down Expand Up @@ -419,8 +419,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += 100_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += 100_000000);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield += 1);
assertEq(_wrappedMToken.excess(), _excess -= 1);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield);
assertEq(_wrappedMToken.excess(), _excess);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand Down Expand Up @@ -448,8 +448,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= 99_999999);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply += _aliceBalance);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 3_614473);
assertEq(_wrappedMToken.excess(), _excess);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 3_614473 + 1);
assertEq(_wrappedMToken.excess(), _excess += 1);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand All @@ -465,8 +465,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply += _carolBalance);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply -= _carolBalance);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield += 1);
assertEq(_wrappedMToken.excess(), _excess -= 1);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield);
assertEq(_wrappedMToken.excess(), _excess);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand Down Expand Up @@ -525,8 +525,8 @@ contract ProtocolIntegrationTests is TestBase {
// Assert Globals
assertEq(_wrappedMToken.totalEarningSupply(), _totalEarningSupply -= 100_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), _totalNonEarningSupply);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 2_395361);
assertEq(_wrappedMToken.excess(), _excess);
assertEq(_wrappedMToken.totalAccruedYield(), _totalAccruedYield -= 2_395361 + 1);
assertEq(_wrappedMToken.excess(), _excess += 1);

assertGe(_wrapperBalanceOfM, _totalEarningSupply + _totalNonEarningSupply + _totalAccruedYield + _excess);

Expand Down
4 changes: 3 additions & 1 deletion test/integration/UniswapV3.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ contract UniswapV3IntegrationTests is TestBase {
_deployV2Components();
_migrate();

_wrappedMToken.migrateAccount(_pool);

_poolClaimRecipient = _wrappedMToken.claimOverrideRecipientFor(_pool);

_wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken));
Expand Down Expand Up @@ -338,7 +340,7 @@ contract UniswapV3IntegrationTests is TestBase {
// Move 5 days forward and check that yield has accrued.
vm.warp(vm.getBlockTimestamp() + 5 days);

assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 11_753_024234);
assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 11_753_024235);

/* ============ Eric (Earner) Swaps Exact wM for USDC ============ */

Expand Down
32 changes: 16 additions & 16 deletions test/unit/Stories.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ contract StoryTests is Test {
assertEq(_wrappedMToken.totalEarningSupply(), 300_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), 300_000000);
assertEq(_wrappedMToken.totalSupply(), 600_000000);
assertEq(_wrappedMToken.totalAccruedYield(), 50_000001);
assertEq(_wrappedMToken.excess(), 249_999999);
assertEq(_wrappedMToken.totalAccruedYield(), 49_999998);
assertEq(_wrappedMToken.excess(), 250_000002);

vm.prank(_dave);
_wrappedMToken.transfer(_bob, 50_000000);
Expand All @@ -212,8 +212,8 @@ contract StoryTests is Test {
assertEq(_wrappedMToken.totalEarningSupply(), 400_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), 250_000000);
assertEq(_wrappedMToken.totalSupply(), 650_000000);
assertEq(_wrappedMToken.totalAccruedYield(), 2);
assertEq(_wrappedMToken.excess(), 249_999996);
assertEq(_wrappedMToken.totalAccruedYield(), 0);
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
Expand All @@ -238,8 +238,8 @@ contract StoryTests is Test {
assertEq(_wrappedMToken.totalEarningSupply(), 400_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), 250_000000);
assertEq(_wrappedMToken.totalSupply(), 650_000000);
assertEq(_wrappedMToken.totalAccruedYield(), 133_333336);
assertEq(_wrappedMToken.excess(), 416_666664);
assertEq(_wrappedMToken.totalAccruedYield(), 133_333328);
assertEq(_wrappedMToken.excess(), 416_666672);

_registrar.setListContains(_EARNERS_LIST_NAME, _alice, false);

Expand All @@ -253,8 +253,8 @@ contract StoryTests is Test {
assertEq(_wrappedMToken.totalEarningSupply(), 200_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), 516_666664);
assertEq(_wrappedMToken.totalSupply(), 716_666664);
assertEq(_wrappedMToken.totalAccruedYield(), 66_666672);
assertEq(_wrappedMToken.excess(), 416_666664);
assertEq(_wrappedMToken.totalAccruedYield(), 66_666664);
assertEq(_wrappedMToken.excess(), 416_666672);

_registrar.setListContains(_EARNERS_LIST_NAME, _carol, true);

Expand All @@ -268,8 +268,8 @@ contract StoryTests is Test {
assertEq(_wrappedMToken.totalEarningSupply(), 400_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), 316_666664);
assertEq(_wrappedMToken.totalSupply(), 716_666664);
assertEq(_wrappedMToken.totalAccruedYield(), 66_666672);
assertEq(_wrappedMToken.excess(), 416_666664);
assertEq(_wrappedMToken.totalAccruedYield(), 66_666664);
assertEq(_wrappedMToken.excess(), 416_666672);

_mToken.setCurrentIndex(5 * _EXP_SCALED_ONE);
_mToken.setBalanceOf(address(_wrappedMToken), 1_500_000000); // was 1200 @ 4.0, so 1500 @ 5.0
Expand All @@ -294,8 +294,8 @@ contract StoryTests is Test {
assertEq(_wrappedMToken.totalEarningSupply(), 400_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), 316_666664);
assertEq(_wrappedMToken.totalSupply(), 716_666664);
assertEq(_wrappedMToken.totalAccruedYield(), 183_333340);
assertEq(_wrappedMToken.excess(), 599_999995);
assertEq(_wrappedMToken.totalAccruedYield(), 183_333330);
assertEq(_wrappedMToken.excess(), 600_000005);

vm.prank(_alice);
_wrappedMToken.unwrap(_alice, 266_666664);
Expand All @@ -308,8 +308,8 @@ contract StoryTests is Test {
assertEq(_wrappedMToken.totalEarningSupply(), 400_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000);
assertEq(_wrappedMToken.totalSupply(), 450_000000);
assertEq(_wrappedMToken.totalAccruedYield(), 183_333340);
assertEq(_wrappedMToken.excess(), 600_000000);
assertEq(_wrappedMToken.totalAccruedYield(), 183_333330);
assertEq(_wrappedMToken.excess(), 600_000010);

vm.prank(_bob);
_wrappedMToken.unwrap(_bob, 333_333330);
Expand All @@ -322,8 +322,8 @@ contract StoryTests is Test {
assertEq(_wrappedMToken.totalEarningSupply(), 200_000000);
assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000);
assertEq(_wrappedMToken.totalSupply(), 250_000000);
assertEq(_wrappedMToken.totalAccruedYield(), 50_000010);
assertEq(_wrappedMToken.excess(), 600_000000);
assertEq(_wrappedMToken.totalAccruedYield(), 50_000000);
assertEq(_wrappedMToken.excess(), 600_000010);

vm.prank(_carol);
_wrappedMToken.unwrap(_carol, 250_000000);
Expand Down
Loading
Loading