From aac5bf45a0834677a3421c227b249815e9c59b09 Mon Sep 17 00:00:00 2001 From: Michael De Luca Date: Mon, 28 Oct 2024 13:53:49 -0400 Subject: [PATCH] feat: Smart M Rename --- README.md | 26 +- package.json | 11 +- script/DeployBase.sol | 38 +- script/DeployProduction.s.sol | 22 +- src/{WrappedMToken.sol => SmartMToken.sol} | 92 +- ...gratorV1.sol => SmartMTokenMigratorV1.sol} | 4 +- src/interfaces/IEarnerManager.sol | 2 +- .../{IWrappedMToken.sol => ISmartMToken.sol} | 24 +- test/integration/Deploy.t.sol | 38 +- test/integration/MorphoBlue.t.sol | 266 +-- test/integration/Protocol.t.sol | 446 ++-- test/integration/TestBase.sol | 50 +- test/integration/UniswapV3.t.sol | 272 +-- test/unit/Migrations.t.sol | 30 +- test/unit/SmartMToken.t.sol | 1783 ++++++++++++++++ test/unit/Stories.t.sol | 392 ++-- test/unit/WrappedMToken.t.sol | 1789 ----------------- test/utils/Invariants.sol | 58 +- ...okenHarness.sol => SmartMTokenHarness.sol} | 6 +- 19 files changed, 2664 insertions(+), 2685 deletions(-) rename src/{WrappedMToken.sol => SmartMToken.sol} (94%) rename src/{WrappedMTokenMigratorV1.sol => SmartMTokenMigratorV1.sol} (84%) rename src/interfaces/{IWrappedMToken.sol => ISmartMToken.sol} (93%) create mode 100644 test/unit/SmartMToken.t.sol delete mode 100644 test/unit/WrappedMToken.t.sol rename test/utils/{WrappedMTokenHarness.sol => SmartMTokenHarness.sol} (92%) diff --git a/README.md b/README.md index d3e0b72..c0c7986 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Wrapped M Token - Smart Wrapper contract +# Smart M Token - Smart Wrapper contract ## Overview @@ -35,7 +35,13 @@ cp .env.example .env Run the following command to compile the contracts: ```bash -forge compile +npm run build +``` + +or + +```bash +make build ``` ### Coverage @@ -46,6 +52,12 @@ Forge is used for coverage, run it with: npm run coverage ``` +or + +```bash +make coverage +``` + You can then consult the report by opening `coverage/index.html`: ```bash @@ -57,19 +69,13 @@ open coverage/index.html To run all tests: ```bash -forge test -``` - -Run test that matches a test contract: - -```bash -forge test --mc +make tests ``` Test a specific test case: ```bash -forge test --mt +./test.sh -v -t ``` To run slither: diff --git a/package.json b/package.json index 7c09ae5..ceaf8d5 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,22 @@ { - "name": "@mzero-labs/wrapped-m-token", + "name": "@mzero-labs/smart-m-token", "version": "2.0.0", "description": "Wrapped M Token", "author": "M^0 Labs ", "repository": { "type": "git", - "url": "git+https://github.com/m0-foundation/wrapped-m-token.git" + "url": "git+https://github.com/m0-foundation/smart-m-token.git" }, "bugs": { - "url": "https://github.com/m0-foundation/wrapped-m-token/issues" + "url": "https://github.com/m0-foundation/smart-m-token/issues" }, - "homepage": "https://github.com/m0-foundation/wrapped-m-token#readme", + "homepage": "https://github.com/m0-foundation/smart-m-token#readme", "scripts": { "build": "make -B build", "clean": "make -B clean", - "compile": "forge compile", "coverage": "make -B coverage", + "deploy": "make -B deploy", "deploy-local": "make -B deploy-local", - "deploy-sepolia": "make -B deploy-sepolia", "doc": "forge doc --serve --port 4000", "lint-staged": "lint-staged", "prepack": "npm run clean && npm run build", diff --git a/script/DeployBase.sol b/script/DeployBase.sol index fb6d014..79f0d10 100644 --- a/script/DeployBase.sol +++ b/script/DeployBase.sol @@ -4,21 +4,21 @@ pragma solidity 0.8.26; import { ContractHelper } from "../lib/common/src/libs/ContractHelper.sol"; -import { WrappedMToken } from "../src/WrappedMToken.sol"; +import { SmartMToken } from "../src/SmartMToken.sol"; import { EarnerManager } from "../src/EarnerManager.sol"; import { Proxy } from "../src/Proxy.sol"; contract DeployBase { /** - * @dev Deploys Wrapped M Token. + * @dev Deploys Smart M Token. * @param mToken_ The address of the M Token contract. * @param registrar_ The address of the Registrar contract. * @param excessDestination_ The address of the excess destination. * @param migrationAdmin_ The address of the Migration Admin. * @return earnerManagerImplementation_ The address of the deployed Earner Manager implementation. * @return earnerManagerProxy_ The address of the deployed Earner Manager proxy. - * @return wrappedMTokenImplementation_ The address of the deployed Wrapped M Token implementation. - * @return wrappedMTokenProxy_ The address of the deployed Wrapped M Token proxy. + * @return smartMTokenImplementation_ The address of the deployed Smart M Token implementation. + * @return smartMTokenProxy_ The address of the deployed Smart M Token proxy. */ function deploy( address mToken_, @@ -31,24 +31,24 @@ contract DeployBase { returns ( address earnerManagerImplementation_, address earnerManagerProxy_, - address wrappedMTokenImplementation_, - address wrappedMTokenProxy_ + address smartMTokenImplementation_, + address smartMTokenProxy_ ) { // Earner Manager Proxy constructor needs only known values. // Earner Manager Implementation constructor needs `earnerManagerImplementation_`. - // Wrapped M Token Implementation constructor needs `earnerManagerProxy_`. - // Wrapped M Token Proxy constructor needs `wrappedMTokenImplementation_`. + // Smart M Token Implementation constructor needs `earnerManagerProxy_`. + // Smart M Token Proxy constructor needs `smartMTokenImplementation_`. earnerManagerImplementation_ = address(new EarnerManager(registrar_, migrationAdmin_)); earnerManagerProxy_ = address(new Proxy(earnerManagerImplementation_)); - wrappedMTokenImplementation_ = address( - new WrappedMToken(mToken_, registrar_, earnerManagerProxy_, excessDestination_, migrationAdmin_) + smartMTokenImplementation_ = address( + new SmartMToken(mToken_, registrar_, earnerManagerProxy_, excessDestination_, migrationAdmin_) ); - wrappedMTokenProxy_ = address(new Proxy(wrappedMTokenImplementation_)); + smartMTokenProxy_ = address(new Proxy(smartMTokenImplementation_)); } function _getExpectedEarnerManager(address deployer_, uint256 deployerNonce_) internal pure returns (address) { @@ -70,34 +70,32 @@ contract DeployBase { return _getExpectedEarnerManagerProxy(deployer_, deployerNonce_); } - function _getExpectedWrappedMTokenImplementation( + function _getExpectedSmartMTokenImplementation( address deployer_, uint256 deployerNonce_ ) internal pure returns (address) { return ContractHelper.getContractFrom(deployer_, deployerNonce_ + 2); } - function getExpectedWrappedMTokenImplementation( + function getExpectedSmartMTokenImplementation( address deployer_, uint256 deployerNonce_ ) public pure virtual returns (address) { - return _getExpectedWrappedMTokenImplementation(deployer_, deployerNonce_); + return _getExpectedSmartMTokenImplementation(deployer_, deployerNonce_); } - function _getExpectedWrappedMTokenProxy(address deployer_, uint256 deployerNonce_) internal pure returns (address) { + function _getExpectedSmartMTokenProxy(address deployer_, uint256 deployerNonce_) internal pure returns (address) { return ContractHelper.getContractFrom(deployer_, deployerNonce_ + 3); } - function getExpectedWrappedMTokenProxy( + function getExpectedSmartMTokenProxy( address deployer_, uint256 deployerNonce_ ) public pure virtual returns (address) { - return _getExpectedWrappedMTokenProxy(deployer_, deployerNonce_); + return _getExpectedSmartMTokenProxy(deployer_, deployerNonce_); } - function getDeployerNonceAfterWrappedMTokenDeployment( - uint256 deployerNonce_ - ) public pure virtual returns (uint256) { + function getDeployerNonceAfterSmartMTokenDeployment(uint256 deployerNonce_) public pure virtual returns (uint256) { return deployerNonce_ + 4; } } diff --git a/script/DeployProduction.s.sol b/script/DeployProduction.s.sol index 2dfb74e..e5fd170 100644 --- a/script/DeployProduction.s.sol +++ b/script/DeployProduction.s.sol @@ -37,8 +37,8 @@ contract DeployProduction is Script, DeployBase { // NOTE: Ensure this is the correct nonce to use to deploy the Proxy on testnet/mainnet. uint256 internal constant _DEPLOYER_PROXY_NONCE = 40; - // NOTE: Ensure this is the correct expected testnet/mainnet address for the Wrapped M Token Proxy. - address internal constant _EXPECTED_WRAPPED_M_TOKEN_PROXY = address(0); + // NOTE: Ensure this is the correct expected testnet/mainnet address for the Smart M Token Proxy. + address internal constant _EXPECTED_SMART_M_TOKEN_PROXY = address(0); // NOTE: Ensure this is the correct expected testnet/mainnet address for the Earner Manager Proxy. address internal constant _EXPECTED_EARNER_MANAGER_PROXY = address(0); @@ -54,10 +54,10 @@ contract DeployProduction is Script, DeployBase { if (currentNonce_ >= _DEPLOYER_PROXY_NONCE - 1) revert DeployerNonceTooHigh(); - address expectedProxy_ = getExpectedWrappedMTokenProxy(deployer_, _DEPLOYER_PROXY_NONCE); + address expectedProxy_ = getExpectedSmartMTokenProxy(deployer_, _DEPLOYER_PROXY_NONCE); - if (expectedProxy_ != _EXPECTED_WRAPPED_M_TOKEN_PROXY) - revert ExpectedProxyMismatch(_EXPECTED_WRAPPED_M_TOKEN_PROXY, expectedProxy_); + if (expectedProxy_ != _EXPECTED_SMART_M_TOKEN_PROXY) + revert ExpectedProxyMismatch(_EXPECTED_SMART_M_TOKEN_PROXY, expectedProxy_); vm.startBroadcast(deployer_); @@ -74,19 +74,19 @@ contract DeployProduction is Script, DeployBase { ( address earnerManagerProxy_, address earnerManagerImplementation_, - address wrappedMTokenImplementation_, - address wrappedMTokenProxy_ + address smartMTokenImplementation_, + address smartMTokenProxy_ ) = deploy(_M_TOKEN, _REGISTRAR, _EXCESS_DESTINATION, _MIGRATION_ADMIN); vm.stopBroadcast(); console2.log("Earner Manager Proxy address:", earnerManagerProxy_); console2.log("Earner Manager Implementation address:", earnerManagerImplementation_); - console2.log("Wrapped M Implementation address:", wrappedMTokenImplementation_); - console2.log("Wrapped M Proxy address:", wrappedMTokenProxy_); + console2.log("Smart M Implementation address:", smartMTokenImplementation_); + console2.log("Smart M Proxy address:", smartMTokenProxy_); - if (wrappedMTokenProxy_ != _EXPECTED_WRAPPED_M_TOKEN_PROXY) { - revert ResultingProxyMismatch(_EXPECTED_WRAPPED_M_TOKEN_PROXY, wrappedMTokenProxy_); + if (smartMTokenProxy_ != _EXPECTED_SMART_M_TOKEN_PROXY) { + revert ResultingProxyMismatch(_EXPECTED_SMART_M_TOKEN_PROXY, smartMTokenProxy_); } if (earnerManagerProxy_ != _EXPECTED_EARNER_MANAGER_PROXY) { diff --git a/src/WrappedMToken.sol b/src/SmartMToken.sol similarity index 94% rename from src/WrappedMToken.sol rename to src/SmartMToken.sol index 5bad451..0448e22 100644 --- a/src/WrappedMToken.sol +++ b/src/SmartMToken.sol @@ -12,7 +12,7 @@ import { IndexingMath } from "./libs/IndexingMath.sol"; import { IEarnerManager } from "./interfaces/IEarnerManager.sol"; import { IMTokenLike } from "./interfaces/IMTokenLike.sol"; import { IRegistrarLike } from "./interfaces/IRegistrarLike.sol"; -import { IWrappedMToken } from "./interfaces/IWrappedMToken.sol"; +import { ISmartMToken } from "./interfaces/ISmartMToken.sol"; import { Migratable } from "./Migratable.sol"; @@ -31,7 +31,7 @@ import { Migratable } from "./Migratable.sol"; * @title ERC20 Token contract for wrapping M into a non-rebasing token with claimable yields. * @author M^0 Labs */ -contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { +contract SmartMToken is ISmartMToken, Migratable, ERC20Extended { /* ============ Structs ============ */ /** @@ -53,43 +53,43 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { /* ============ Variables ============ */ - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken uint16 public constant HUNDRED_PERCENT = 10_000; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken bytes32 public constant EARNERS_LIST_IGNORED_KEY = "earners_list_ignored"; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken bytes32 public constant EARNERS_LIST_NAME = "earners"; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken bytes32 public constant CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX = "wm_claim_override_recipient"; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken bytes32 public constant MIGRATOR_KEY_PREFIX = "wm_migrator_v2"; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken address public immutable earnerManager; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken address public immutable migrationAdmin; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken address public immutable mToken; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken address public immutable registrar; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken address public immutable excessDestination; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken uint112 public principalOfTotalEarningSupply; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken uint240 public totalEarningSupply; - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken uint240 public totalNonEarningSupply; /// @dev Mapping of accounts to their respective `AccountInfo` structs. @@ -127,17 +127,17 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { /* ============ Interactive Functions ============ */ - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function wrap(address recipient_, uint256 amount_) external returns (uint240 wrapped_) { return _wrap(msg.sender, recipient_, UIntMath.safe240(amount_)); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function wrap(address recipient_) external returns (uint240 wrapped_) { return _wrap(msg.sender, recipient_, UIntMath.safe240(IMTokenLike(mToken).balanceOf(msg.sender))); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function wrapWithPermit( address recipient_, uint256 amount_, @@ -151,7 +151,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { return _wrap(msg.sender, recipient_, UIntMath.safe240(amount_)); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function wrapWithPermit( address recipient_, uint256 amount_, @@ -163,29 +163,29 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { return _wrap(msg.sender, recipient_, UIntMath.safe240(amount_)); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function unwrap(address recipient_, uint256 amount_) external returns (uint240 unwrapped_) { return _unwrap(msg.sender, recipient_, UIntMath.safe240(amount_)); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function unwrap(address recipient_) external returns (uint240 unwrapped_) { return _unwrap(msg.sender, recipient_, uint240(balanceWithYieldOf(msg.sender))); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function claimFor(address account_) external returns (uint240 yield_) { return _claim(account_, currentIndex()); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function claimExcess() external returns (uint240 excess_) { emit ExcessClaimed(excess_ = excess()); IMTokenLike(mToken).transfer(excessDestination, excess_); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function enableEarning() external { if (!_isThisApprovedEarner()) revert NotApprovedEarner(address(this)); if (isEarningEnabled()) revert EarningIsEnabled(); @@ -203,7 +203,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { emit EarningEnabled(currentMIndex_); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function disableEarning() external { if (_isThisApprovedEarner()) revert IsApprovedEarner(address(this)); if (!isEarningEnabled()) revert EarningIsDisabled(); @@ -217,7 +217,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { emit EarningDisabled(currentMIndex_); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function startEarningFor(address account_) external { if (!isEarningEnabled()) revert EarningIsDisabled(); @@ -225,7 +225,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { _startEarningFor(account_, _currentMIndex()); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function startEarningFor(address[] calldata accounts_) external { if (!isEarningEnabled()) revert EarningIsDisabled(); @@ -236,12 +236,12 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { } } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function stopEarningFor(address account_) external { _stopEarningFor(account_, currentIndex()); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function stopEarningFor(address[] calldata accounts_) external { uint128 currentIndex_ = currentIndex(); @@ -250,7 +250,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { } } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function setClaimRecipient(address claimRecipient_) external { _accounts[msg.sender].hasClaimRecipient = (_claimRecipients[msg.sender] = claimRecipient_) != address(0); @@ -259,7 +259,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { /* ============ Temporary Admin Migration ============ */ - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function migrate(address migrator_) external { if (msg.sender != migrationAdmin) revert UnauthorizedMigration(); @@ -268,7 +268,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { /* ============ View/Pure Functions ============ */ - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function accruedYieldOf(address account_) public view returns (uint240 yield_) { Account storage accountInfo_ = _accounts[account_]; @@ -281,17 +281,17 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { return _accounts[account_].balance; } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function balanceWithYieldOf(address account_) public view returns (uint256 balance_) { return balanceOf(account_) + accruedYieldOf(account_); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function lastIndexOf(address account_) public view returns (uint128 lastIndex_) { return _accounts[account_].lastIndex; } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function claimRecipientFor(address account_) public view returns (address recipient_) { return (_accounts[account_].hasClaimRecipient) @@ -307,27 +307,27 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { ); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function currentIndex() public view returns (uint128 index_) { return isEarningEnabled() ? _currentMIndex() : _lastDisableEarningIndex(); } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function isEarning(address account_) external view returns (bool isEarning_) { return _accounts[account_].isEarning; } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function isEarningEnabled() public view returns (bool isEnabled_) { return _enableDisableEarningIndices.length % 2 == 1; } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function wasEarningEnabled() public view returns (bool wasEarning_) { return _enableDisableEarningIndices.length != 0; } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function excess() public view returns (uint240 excess_) { unchecked { uint128 currentIndex_ = currentIndex(); @@ -338,7 +338,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { } } - /// @inheritdoc IWrappedMToken + /// @inheritdoc ISmartMToken function totalAccruedYield() external view returns (uint240 yield_) { unchecked { uint240 projectedEarningSupply_ = _projectedEarningSupply(currentIndex()); @@ -650,10 +650,10 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { // 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 + // NOTE: When this SmartMToken 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. + // starting and ending M balance is minted as SmartM. _mint(recipient_, wrapped_ = UIntMath.safe240(IMTokenLike(mToken).balanceOf(address(this)) - startingBalance_)); } @@ -672,7 +672,7 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { // 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 + // NOTE: When this SmartMToken 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. @@ -782,12 +782,12 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended { /** * @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 the wrapper. + * @param amount_ Some amount to be transferred out of this contract. * @param currentIndex_ The current index. * @return safeAmount_ The adjusted amount that can safely be transferred out. */ function _getSafeTransferableM(uint240 amount_, uint128 currentIndex_) internal view returns (uint240 safeAmount_) { - // If the wrapper is earning, adjust `amount_` to ensure it's M balance decrement is limited to `amount_`. + // If this contract is earning, adjust `amount_` to ensure it's M balance decrement is limited to `amount_`. return IMTokenLike(mToken).isEarning(address(this)) ? IndexingMath.getPresentAmountRoundedDown( diff --git a/src/WrappedMTokenMigratorV1.sol b/src/SmartMTokenMigratorV1.sol similarity index 84% rename from src/WrappedMTokenMigratorV1.sol rename to src/SmartMTokenMigratorV1.sol index eb0cf22..adec720 100644 --- a/src/WrappedMTokenMigratorV1.sol +++ b/src/SmartMTokenMigratorV1.sol @@ -3,10 +3,10 @@ pragma solidity 0.8.26; /** - * @title Migrator contract for migrating a WrappedMToken contract from V1 to V2. + * @title Migrator contract for migrating a SmartMToken contract from V1 to V2. * @author M^0 Labs */ -contract WrappedMTokenMigratorV1 { +contract SmartMTokenMigratorV1 { /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`. uint256 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; diff --git a/src/interfaces/IEarnerManager.sol b/src/interfaces/IEarnerManager.sol index ab5189d..2e1839c 100644 --- a/src/interfaces/IEarnerManager.sol +++ b/src/interfaces/IEarnerManager.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.26; import { IMigratable } from "./IMigratable.sol"; /** - * @title Earner Status Manager interface for setting and returning earner status for Wrapped M Token accounts. + * @title Earner Status Manager interface for setting and returning earner status for Smart M Token accounts. * @author M^0 Labs */ interface IEarnerManager is IMigratable { diff --git a/src/interfaces/IWrappedMToken.sol b/src/interfaces/ISmartMToken.sol similarity index 93% rename from src/interfaces/IWrappedMToken.sol rename to src/interfaces/ISmartMToken.sol index 579f1c7..6ccb7cf 100644 --- a/src/interfaces/IWrappedMToken.sol +++ b/src/interfaces/ISmartMToken.sol @@ -7,10 +7,10 @@ import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.s import { IMigratable } from "./IMigratable.sol"; /** - * @title Wrapped M Token interface extending Extended ERC20. + * @title Smart M Token interface extending Extended ERC20. * @author M^0 Labs */ -interface IWrappedMToken is IMigratable, IERC20Extended { +interface ISmartMToken is IMigratable, IERC20Extended { /* ============ Events ============ */ /** @@ -29,19 +29,19 @@ interface IWrappedMToken is IMigratable, IERC20Extended { event ClaimRecipientSet(address indexed account, address indexed claimRecipient); /** - * @notice Emitted when earning is enabled for the entire wrapper. + * @notice Emitted when Smart M earning is enabled. * @param index The index at the moment earning is enabled. */ event EarningEnabled(uint128 index); /** - * @notice Emitted when earning is disabled for the entire wrapper. + * @notice Emitted when Smart M earning is disabled. * @param index The index at the moment earning is disabled. */ event EarningDisabled(uint128 index); /** - * @notice Emitted when the wrapper's excess M is claimed. + * @notice Emitted when this contract's excess M is claimed. * @param excess The amount of excess M claimed. */ event ExcessClaimed(uint240 excess); @@ -184,15 +184,15 @@ interface IWrappedMToken is IMigratable, IERC20Extended { function claimFor(address account) external returns (uint240 yield); /** - * @notice Claims any excess M of the wrapper. + * @notice Claims any excess M of this contract. * @return excess The amount of excess claimed. */ function claimExcess() external returns (uint240 excess); - /// @notice Enables earning for the wrapper if allowed by the Registrar and if it has never been done. + /// @notice Enables earning of Smart M if allowed by the Registrar and if it has never been done. function enableEarning() external; - /// @notice Disables earning for the wrapper if disallowed by the Registrar and if it has never been done. + /// @notice Disables earning of Smart M if disallowed by the Registrar and if it has never been done. function disableEarning() external; /** @@ -278,10 +278,10 @@ interface IWrappedMToken is IMigratable, IERC20Extended { */ function claimRecipientFor(address account) external view returns (address recipient); - /// @notice The current index of the wrapper's earning mechanism. + /// @notice The current index of Smart M's earning mechanism. function currentIndex() external view returns (uint128 index); - /// @notice The current excess M of the wrapper that is not earmarked for account balances or accrued yield. + /// @notice This contract's current excess M that is not earmarked for account balances or accrued yield. function excess() external view returns (uint240 excess); /** @@ -291,10 +291,10 @@ interface IWrappedMToken is IMigratable, IERC20Extended { */ function isEarning(address account) external view returns (bool isEarning); - /// @notice Whether earning is enabled for the entire wrapper. + /// @notice Whether Smart M earning is enabled. function isEarningEnabled() external view returns (bool isEnabled); - /// @notice Whether earning has been enabled at least once or not. + /// @notice Whether Smart M earning has been enabled at least once. function wasEarningEnabled() external view returns (bool wasEnabled); /// @notice The account that can bypass the Registrar and call the `migrate(address migrator)` function. diff --git a/test/integration/Deploy.t.sol b/test/integration/Deploy.t.sol index 335b2a4..902c731 100644 --- a/test/integration/Deploy.t.sol +++ b/test/integration/Deploy.t.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.26; import { Test } from "../../lib/forge-std/src/Test.sol"; import { IEarnerManager } from "../../src/interfaces/IEarnerManager.sol"; -import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; +import { ISmartMToken } from "../../src/interfaces/ISmartMToken.sol"; import { DeployBase } from "../../script/DeployBase.sol"; @@ -24,8 +24,8 @@ contract Deploy is Test, DeployBase { ( address earnerManagerImplementation_, address earnerManagerProxy_, - address wrappedMTokenImplementation_, - address wrappedMTokenProxy_ + address smartMTokenImplementation_, + address smartMTokenProxy_ ) = deploy(_M_TOKEN, _REGISTRAR, _EXCESS_DESTINATION, _MIGRATION_ADMIN); vm.stopPrank(); @@ -36,21 +36,21 @@ contract Deploy is Test, DeployBase { assertEq(IEarnerManager(earnerManagerProxy_).registrar(), _REGISTRAR); assertEq(IEarnerManager(earnerManagerProxy_).implementation(), earnerManagerImplementation_); - // Wrapped M Token Implementation assertions - assertEq(wrappedMTokenImplementation_, getExpectedWrappedMTokenImplementation(_DEPLOYER, _DEPLOYER_NONCE)); - assertEq(IWrappedMToken(wrappedMTokenImplementation_).earnerManager(), earnerManagerProxy_); - assertEq(IWrappedMToken(wrappedMTokenImplementation_).migrationAdmin(), _MIGRATION_ADMIN); - assertEq(IWrappedMToken(wrappedMTokenImplementation_).mToken(), _M_TOKEN); - assertEq(IWrappedMToken(wrappedMTokenImplementation_).registrar(), _REGISTRAR); - assertEq(IWrappedMToken(wrappedMTokenImplementation_).excessDestination(), _EXCESS_DESTINATION); - - // Wrapped M Token Proxy assertions - assertEq(wrappedMTokenProxy_, getExpectedWrappedMTokenProxy(_DEPLOYER, _DEPLOYER_NONCE)); - assertEq(IWrappedMToken(wrappedMTokenProxy_).earnerManager(), earnerManagerProxy_); - assertEq(IWrappedMToken(wrappedMTokenProxy_).migrationAdmin(), _MIGRATION_ADMIN); - assertEq(IWrappedMToken(wrappedMTokenProxy_).mToken(), _M_TOKEN); - assertEq(IWrappedMToken(wrappedMTokenProxy_).registrar(), _REGISTRAR); - assertEq(IWrappedMToken(wrappedMTokenProxy_).excessDestination(), _EXCESS_DESTINATION); - assertEq(IWrappedMToken(wrappedMTokenProxy_).implementation(), wrappedMTokenImplementation_); + // Smart M Token Implementation assertions + assertEq(smartMTokenImplementation_, getExpectedSmartMTokenImplementation(_DEPLOYER, _DEPLOYER_NONCE)); + assertEq(ISmartMToken(smartMTokenImplementation_).earnerManager(), earnerManagerProxy_); + assertEq(ISmartMToken(smartMTokenImplementation_).migrationAdmin(), _MIGRATION_ADMIN); + assertEq(ISmartMToken(smartMTokenImplementation_).mToken(), _M_TOKEN); + assertEq(ISmartMToken(smartMTokenImplementation_).registrar(), _REGISTRAR); + assertEq(ISmartMToken(smartMTokenImplementation_).excessDestination(), _EXCESS_DESTINATION); + + // Smart M Token Proxy assertions + assertEq(smartMTokenProxy_, getExpectedSmartMTokenProxy(_DEPLOYER, _DEPLOYER_NONCE)); + assertEq(ISmartMToken(smartMTokenProxy_).earnerManager(), earnerManagerProxy_); + assertEq(ISmartMToken(smartMTokenProxy_).migrationAdmin(), _MIGRATION_ADMIN); + assertEq(ISmartMToken(smartMTokenProxy_).mToken(), _M_TOKEN); + assertEq(ISmartMToken(smartMTokenProxy_).registrar(), _REGISTRAR); + assertEq(ISmartMToken(smartMTokenProxy_).excessDestination(), _EXCESS_DESTINATION); + assertEq(ISmartMToken(smartMTokenProxy_).implementation(), smartMTokenImplementation_); } } diff --git a/test/integration/MorphoBlue.t.sol b/test/integration/MorphoBlue.t.sol index cfe9ff6..ee4e51e 100644 --- a/test/integration/MorphoBlue.t.sol +++ b/test/integration/MorphoBlue.t.sol @@ -28,21 +28,21 @@ contract MorphoBlueTests is MorphoTestBase { function setUp() external { _oracle = _createOracle(); - _wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)); + _wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)); _morphoBalanceOfUSDC = IERC20(_USDC).balanceOf(_MORPHO); - _morphoBalanceOfWM = _wrappedMToken.balanceOf(_MORPHO); + _morphoBalanceOfWM = _smartMToken.balanceOf(_MORPHO); - _excess = _wrappedMToken.excess(); + _excess = _smartMToken.excess(); _deployV2Components(); _migrate(); } function test_state() external view { - assertTrue(_mToken.isEarning(address(_wrappedMToken))); - assertTrue(_wrappedMToken.isEarningEnabled()); - assertFalse(_wrappedMToken.isEarning(_MORPHO)); + assertTrue(_mToken.isEarning(address(_smartMToken))); + assertTrue(_smartMToken.isEarningEnabled()); + assertFalse(_smartMToken.isEarning(_MORPHO)); } function test_morphoBlue_nonEarning_wM_as_collateralToken() external { @@ -50,7 +50,7 @@ contract MorphoBlueTests is MorphoTestBase { _giveWM(_alice, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); _give(_USDC, _alice, 1_000_100e6); @@ -60,15 +60,15 @@ contract MorphoBlueTests is MorphoTestBase { // borrowing 0.90 USDC. _createMarket(_alice, _USDC); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1e6); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 100000); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 100000); /* ============ Alice Supplies Seed USDC For Loans ============ */ - _supply(_alice, _USDC, 1_000_000e6, address(_wrappedMToken)); + _supply(_alice, _USDC, 1_000_000e6, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 1_000_000e6); @@ -77,14 +77,14 @@ contract MorphoBlueTests is MorphoTestBase { _giveWM(_bob, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000_100e6); - _supplyCollateral(_bob, address(_wrappedMToken), 1_000_000e6, _USDC); + _supplyCollateral(_bob, address(_smartMToken), 1_000_000e6, _USDC); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1_000_000e6); - _borrow(_bob, _USDC, 900_000e6, _bob, address(_wrappedMToken)); + _borrow(_bob, _USDC, 900_000e6, _bob, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC += 900_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC -= 900_000e6); @@ -94,34 +94,34 @@ contract MorphoBlueTests is MorphoTestBase { // Move 1 year forward and check that no yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 383_154_367021); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 383_154_367021); // `startEarningFor` hasn't been called so no wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 383_154_367021); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 383_154_367021); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); /* ============ Bob Repays USDC Loan And Withdraws wM Collateral ============ */ - _repay(_bob, _USDC, 900_000e6, address(_wrappedMToken)); + _repay(_bob, _USDC, 900_000e6, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC -= 900_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 900_000e6); - _withdrawCollateral(_bob, address(_wrappedMToken), 1_000_000e6, _bob, _USDC); + _withdrawCollateral(_bob, address(_smartMToken), 1_000_000e6, _bob, _USDC); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000_000e6); /* ============ Alice Withdraws Seed USDC For Loans ============ */ - _withdraw(_alice, _USDC, 1_000_000e6, _alice, address(_wrappedMToken)); + _withdraw(_alice, _USDC, 1_000_000e6, _alice, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC += 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC -= 1_000_000e6); @@ -131,15 +131,15 @@ contract MorphoBlueTests is MorphoTestBase { // Move 1 year forward and check that no yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 391_011_884644); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 391_011_884644); // `startEarningFor` hasn't been called so no wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 391_011_884644); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 391_011_884644); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); @@ -150,7 +150,7 @@ contract MorphoBlueTests is MorphoTestBase { _giveWM(_alice, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); _give(_USDC, _alice, 1_000_100e6); @@ -158,20 +158,20 @@ contract MorphoBlueTests is MorphoTestBase { // NOTE: Creating a market also result in `_alice` supplying 1.00 wM as supply, 1.00 USDC as collateral, and // borrowing 0.90 wM. - _createMarket(_alice, address(_wrappedMToken)); + _createMarket(_alice, address(_smartMToken)); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 100000); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 100000); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 100000); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 100000); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 1e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 1e6); /* ============ Alice Supplies Seed wM For Loans ============ */ - _supply(_alice, address(_wrappedMToken), 1_000_000e6, _USDC); + _supply(_alice, address(_smartMToken), 1_000_000e6, _USDC); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1_000_000e6); /* ============ Bob Takes Out wM Loan Against USDC Collateral ============ */ @@ -179,67 +179,67 @@ contract MorphoBlueTests is MorphoTestBase { assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC += 1_000_000e6); - _supplyCollateral(_bob, _USDC, 1_000_000e6, address(_wrappedMToken)); + _supplyCollateral(_bob, _USDC, 1_000_000e6, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC -= 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 1_000_000e6); - _borrow(_bob, address(_wrappedMToken), 900_000e6, _bob, _USDC); + _borrow(_bob, address(_smartMToken), 900_000e6, _bob, _USDC); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += 900_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 900_000e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM += 900_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 900_000e6); /* ============ First 1-Year Time Warp ============ */ // Move 1 year forward and check that no yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 383_154_367021); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 383_154_367021); // `startEarningFor` hasn't been called so no wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 383_154_367021); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 383_154_367021); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); /* ============ Bob Repays wM Loan And Withdraws USDC Collateral ============ */ - _repay(_bob, address(_wrappedMToken), 900_000e6, _USDC); + _repay(_bob, address(_smartMToken), 900_000e6, _USDC); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM -= 900_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 900_000e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM -= 900_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 900_000e6); - _withdrawCollateral(_bob, _USDC, 1_000_000e6, _bob, address(_wrappedMToken)); + _withdrawCollateral(_bob, _USDC, 1_000_000e6, _bob, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC += 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC -= 1_000_000e6); /* ============ Alice Withdraws Seed wM For Loans ============ */ - _withdraw(_alice, address(_wrappedMToken), 1_000_000e6, _alice, _USDC); + _withdraw(_alice, address(_smartMToken), 1_000_000e6, _alice, _USDC); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000_000e6); /* ============ Second 1-Year Time Warp ============ */ // Move 1 year forward and check that no yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 391_011_884644); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 391_011_884644); // `startEarningFor` hasn't been called so no wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 391_011_884644); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 391_011_884644); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); @@ -250,7 +250,7 @@ contract MorphoBlueTests is MorphoTestBase { _giveWM(_alice, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); _give(_USDC, _alice, 1_000_100e6); @@ -260,15 +260,15 @@ contract MorphoBlueTests is MorphoTestBase { // borrowing 0.90 USDC. _createMarket(_alice, _USDC); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1e6); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 100000); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 100000); /* ============ Alice Supplies Seed USDC For Loans ============ */ - _supply(_alice, _USDC, 1_000_000e6, address(_wrappedMToken)); + _supply(_alice, _USDC, 1_000_000e6, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 1_000_000e6); @@ -277,14 +277,14 @@ contract MorphoBlueTests is MorphoTestBase { _giveWM(_bob, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000_100e6); - _supplyCollateral(_bob, address(_wrappedMToken), 1_000_000e6, _USDC); + _supplyCollateral(_bob, address(_smartMToken), 1_000_000e6, _USDC); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1_000_000e6); - _borrow(_bob, _USDC, 900_000e6, _bob, address(_wrappedMToken)); + _borrow(_bob, _USDC, 900_000e6, _bob, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC += 900_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC -= 900_000e6); @@ -294,53 +294,53 @@ contract MorphoBlueTests is MorphoTestBase { _setClaimOverrideRecipient(_MORPHO, _carol); _addToList(_EARNERS_LIST, _MORPHO); - _wrappedMToken.startEarningFor(_MORPHO); + _smartMToken.startEarningFor(_MORPHO); // Check that the pool is earning wM. - assertTrue(_wrappedMToken.isEarning(_MORPHO)); + assertTrue(_smartMToken.isEarning(_MORPHO)); - assertEq(_wrappedMToken.claimRecipientFor(_MORPHO), _carol); + assertEq(_smartMToken.claimRecipientFor(_MORPHO), _carol); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); /* ============ First 1-Year Time Warp ============ */ // Move 1 year forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 383_154_367021); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 383_154_367021); // `startEarningFor` has been called so wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 20_507_491868); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 20_507_491868); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 362_646_875152); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 362_646_875152); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); /* ============ Bob Repays USDC Loan And Withdraws wM Collateral ============ */ - _repay(_bob, _USDC, 900_000e6, address(_wrappedMToken)); + _repay(_bob, _USDC, 900_000e6, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC -= 900_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 900_000e6); - _withdrawCollateral(_bob, address(_wrappedMToken), 1_000_000e6, _bob, _USDC); + _withdrawCollateral(_bob, address(_smartMToken), 1_000_000e6, _bob, _USDC); // The collateral withdrawal has triggered a wM transfer and the yield has been claimed to carol for the pool. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _morphoAccruedYield); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= _morphoAccruedYield); /* ============ Alice Withdraws Seed USDC For Loans ============ */ - _withdraw(_alice, _USDC, 1_000_000e6, _alice, address(_wrappedMToken)); + _withdraw(_alice, _USDC, 1_000_000e6, _alice, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC += 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC -= 1_000_000e6); @@ -350,15 +350,15 @@ contract MorphoBlueTests is MorphoTestBase { // Move 1 year forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 391_011_884644); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 391_011_884644); // `startEarningFor` has been called so wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 45526); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 45526); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 391_011_839116); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 391_011_839116); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); @@ -369,7 +369,7 @@ contract MorphoBlueTests is MorphoTestBase { _giveWM(_alice, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); _give(_USDC, _alice, 1_000_100e6); @@ -377,20 +377,20 @@ contract MorphoBlueTests is MorphoTestBase { // NOTE: Creating a market also result in `_alice` supplying 1.00 wM as supply, 1.00 USDC as collateral, and // borrowing 0.90 wM. - _createMarket(_alice, address(_wrappedMToken)); + _createMarket(_alice, address(_smartMToken)); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 100000); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 100000); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 100000); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 100000); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 1e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 1e6); /* ============ Alice Supplies Seed wM For Loans ============ */ - _supply(_alice, address(_wrappedMToken), 1_000_000e6, _USDC); + _supply(_alice, address(_smartMToken), 1_000_000e6, _USDC); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 1_000_000e6); /* ============ Bob Takes Out wM Loan Against USDC Collateral ============ */ @@ -398,93 +398,93 @@ contract MorphoBlueTests is MorphoTestBase { assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC += 1_000_000e6); - _supplyCollateral(_bob, _USDC, 1_000_000e6, address(_wrappedMToken)); + _supplyCollateral(_bob, _USDC, 1_000_000e6, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC -= 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC += 1_000_000e6); - _borrow(_bob, address(_wrappedMToken), 900_000e6, _bob, _USDC); + _borrow(_bob, address(_smartMToken), 900_000e6, _bob, _USDC); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += 900_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 900_000e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM += 900_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 900_000e6); /* ============ Morpho Becomes An Earner ============ */ _setClaimOverrideRecipient(_MORPHO, _carol); _addToList(_EARNERS_LIST, _MORPHO); - _wrappedMToken.startEarningFor(_MORPHO); + _smartMToken.startEarningFor(_MORPHO); // Check that the pool is earning wM. - assertTrue(_wrappedMToken.isEarning(_MORPHO)); + assertTrue(_smartMToken.isEarning(_MORPHO)); - assertEq(_wrappedMToken.claimRecipientFor(_MORPHO), _carol); + assertEq(_smartMToken.claimRecipientFor(_MORPHO), _carol); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield); /* ============ First 1-Year Time Warp ============ */ // Move 1 year forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 383_154_367021); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 383_154_367021); // `startEarningFor` has been called so wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 2_050_771703); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 2_050_771703); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 381_103_595317); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 381_103_595317); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); /* ============ Bob Repays wM Loan And Withdraws USDC Collateral ============ */ - _repay(_bob, address(_wrappedMToken), 900_000e6, _USDC); + _repay(_bob, address(_smartMToken), 900_000e6, _USDC); // The repay has triggered a wM transfer and the yield has been claimed to carol for the pool. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _morphoAccruedYield); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM -= 900_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 900_000e6); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= _morphoAccruedYield); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM -= 900_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM += 900_000e6); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield -= _morphoAccruedYield); - _withdrawCollateral(_bob, _USDC, 1_000_000e6, _bob, address(_wrappedMToken)); + _withdrawCollateral(_bob, _USDC, 1_000_000e6, _bob, address(_smartMToken)); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC += 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC -= 1_000_000e6); /* ============ Alice Withdraws Seed wM For Loans ============ */ - _withdraw(_alice, address(_wrappedMToken), 1_000_000e6, _alice, _USDC); + _withdraw(_alice, address(_smartMToken), 1_000_000e6, _alice, _USDC); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM -= 1_000_000e6); /* ============ Second 1-Year Time Warp ============ */ // Move 1 year forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 391_011_884644); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 391_011_884644); // `startEarningFor` has been called so wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 27069); + assertEq(_smartMToken.balanceOf(_MORPHO), _morphoBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_MORPHO), _morphoAccruedYield += 27069); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 391_011_857572); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 391_011_857572); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_MORPHO), _morphoBalanceOfUSDC); } function _createMarket(address account_, address loanToken_) internal { - address collateralToken_ = loanToken_ == address(_wrappedMToken) ? _USDC : address(_wrappedMToken); + address collateralToken_ = loanToken_ == address(_smartMToken) ? _USDC : address(_smartMToken); _createMarket(account_, loanToken_, collateralToken_, _oracle, _LLTV); diff --git a/test/integration/Protocol.t.sol b/test/integration/Protocol.t.sol index cd9b191..50d24dd 100644 --- a/test/integration/Protocol.t.sol +++ b/test/integration/Protocol.t.sol @@ -28,8 +28,8 @@ contract ProtocolIntegrationTests is TestBase { _addToList(_EARNERS_LIST, _alice); _addToList(_EARNERS_LIST, _bob); - _wrappedMToken.startEarningFor(_alice); - _wrappedMToken.startEarningFor(_bob); + _smartMToken.startEarningFor(_alice); + _smartMToken.startEarningFor(_bob); _totalEarningSupplyOfM = _mToken.totalEarningSupply(); @@ -38,22 +38,22 @@ contract ProtocolIntegrationTests is TestBase { } function test_constants() external view { - assertEq(_wrappedMToken.EARNERS_LIST_IGNORED_KEY(), "earners_list_ignored"); - assertEq(_wrappedMToken.EARNERS_LIST_NAME(), "earners"); - assertEq(_wrappedMToken.CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX(), "wm_claim_override_recipient"); - assertEq(_wrappedMToken.MIGRATOR_KEY_PREFIX(), "wm_migrator_v2"); - assertEq(_wrappedMToken.name(), "Smart M by M^0"); - assertEq(_wrappedMToken.symbol(), "MSMART"); - assertEq(_wrappedMToken.decimals(), 6); + assertEq(_smartMToken.EARNERS_LIST_IGNORED_KEY(), "earners_list_ignored"); + assertEq(_smartMToken.EARNERS_LIST_NAME(), "earners"); + assertEq(_smartMToken.CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX(), "wm_claim_override_recipient"); + assertEq(_smartMToken.MIGRATOR_KEY_PREFIX(), "wm_migrator_v2"); + assertEq(_smartMToken.name(), "Smart M by M^0"); + assertEq(_smartMToken.symbol(), "MSMART"); + assertEq(_smartMToken.decimals(), 6); } function test_initialState() external { // TODO: Reinstate to test post-migration for new version. vm.skip(true); - assertEq(_mToken.currentIndex(), _wrappedMToken.currentIndex()); - assertEq(_mToken.balanceOf(address(_wrappedMToken)), 0); - assertTrue(_mToken.isEarning(address(_wrappedMToken))); + assertEq(_mToken.currentIndex(), _smartMToken.currentIndex()); + assertEq(_mToken.balanceOf(address(_smartMToken)), 0); + assertTrue(_mToken.isEarning(address(_smartMToken))); } function test_wrapWithPermits() external { @@ -64,12 +64,12 @@ contract ProtocolIntegrationTests is TestBase { _wrapWithPermitVRS(_alice, _aliceKey, _alice, 100_000000, 0, block.timestamp); assertEq(_mToken.balanceOf(_alice), 100_000000); - assertEq(_wrappedMToken.balanceOf(_alice), 100_000000); + assertEq(_smartMToken.balanceOf(_alice), 100_000000); _wrapWithPermitSignature(_alice, _aliceKey, _alice, 100_000000, 1, block.timestamp); assertEq(_mToken.balanceOf(_alice), 0); - assertEq(_wrappedMToken.balanceOf(_alice), 199_999999); + assertEq(_smartMToken.balanceOf(_alice), 199_999999); } function test_integration_yieldAccumulation() external { @@ -83,19 +83,19 @@ contract ProtocolIntegrationTests is TestBase { _wrap(_alice, _alice, 100_000000); // Assert M Token - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM = 99_999999); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM = 99_999999); assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 99_999999); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance = 99_999999); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance = 99_999999); + assertEq(_smartMToken.accruedYieldOf(_alice), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 99_999999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalSupply(), 99_999999); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 0); + assertEq(_smartMToken.totalEarningSupply(), 99_999999); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalSupply(), 99_999999); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), 0); assertGe( _wrapperBalanceOfM, @@ -108,19 +108,19 @@ contract ProtocolIntegrationTests is TestBase { _wrap(_carol, _carol, 50_000000); - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 50_000000); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 50_000000); assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 50_000000); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance = 50_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance = 50_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 99_999999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); - assertEq(_wrappedMToken.totalSupply(), 149_999999); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 0); + assertEq(_smartMToken.totalEarningSupply(), 99_999999); + assertEq(_smartMToken.totalNonEarningSupply(), 50_000000); + assertEq(_smartMToken.totalSupply(), 149_999999); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), 0); assertGe( _wrapperBalanceOfM, @@ -130,26 +130,26 @@ contract ProtocolIntegrationTests is TestBase { // Fast forward 90 days in the future to generate yield vm.warp(vm.getBlockTimestamp() + 90 days); - assertEq(_mToken.currentIndex(), _wrappedMToken.currentIndex()); + assertEq(_mToken.currentIndex(), _smartMToken.currentIndex()); // Assert M Token - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 1_860762); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 1_860762); assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 1_860762); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield = 1_240507); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield = 1_240507); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 99_999999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); - assertEq(_wrappedMToken.totalSupply(), 149_999999); - assertEq(_wrappedMToken.totalAccruedYield(), 1_240508); - assertEq(_wrappedMToken.excess(), _excess = 62_0253); + assertEq(_smartMToken.totalEarningSupply(), 99_999999); + assertEq(_smartMToken.totalNonEarningSupply(), 50_000000); + assertEq(_smartMToken.totalSupply(), 149_999999); + assertEq(_smartMToken.totalAccruedYield(), 1_240508); + assertEq(_smartMToken.excess(), _excess = 62_0253); assertGe( _wrapperBalanceOfM, @@ -163,19 +163,19 @@ contract ProtocolIntegrationTests is TestBase { _wrap(_bob, _bob, 200_000000); // Assert M Token - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 199_999999); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 199_999999); assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 199_999999); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance = 199_999999); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_smartMToken.balanceOf(_bob), _bobBalance = 199_999999); + assertEq(_smartMToken.accruedYieldOf(_bob), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 299_999998); - assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); - assertEq(_wrappedMToken.totalSupply(), 349_999998); - assertEq(_wrappedMToken.totalAccruedYield(), 1_240509); - assertEq(_wrappedMToken.excess(), _excess -= 1); + assertEq(_smartMToken.totalEarningSupply(), 299_999998); + assertEq(_smartMToken.totalNonEarningSupply(), 50_000000); + assertEq(_smartMToken.totalSupply(), 349_999998); + assertEq(_smartMToken.totalAccruedYield(), 1_240509); + assertEq(_smartMToken.excess(), _excess -= 1); assertGe( _wrapperBalanceOfM, @@ -189,41 +189,41 @@ contract ProtocolIntegrationTests is TestBase { _wrap(_dave, _dave, 150_000000); // Assert M Token - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 150_000000); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 150_000000); assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 150_000000); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance = 150_000000); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), _daveBalance = 150_000000); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 299_999998); - assertEq(_wrappedMToken.totalNonEarningSupply(), 200_000000); - assertEq(_wrappedMToken.totalSupply(), 499_999998); - assertEq(_wrappedMToken.totalAccruedYield(), 1_240509); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_smartMToken.totalEarningSupply(), 299_999998); + assertEq(_smartMToken.totalNonEarningSupply(), 200_000000); + assertEq(_smartMToken.totalSupply(), 499_999998); + assertEq(_smartMToken.totalAccruedYield(), 1_240509); + assertEq(_smartMToken.excess(), _excess); assertGe( _wrapperBalanceOfM, _aliceBalance + _aliceAccruedYield + _bobBalance + _bobAccruedYield + _carolBalance + _daveBalance + _excess ); - assertEq(_wrappedMToken.claimFor(_alice), _aliceAccruedYield); + assertEq(_smartMToken.claimFor(_alice), _aliceAccruedYield); // Assert M Token - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM); assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance += _aliceAccruedYield); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield -= 1_240507); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance += _aliceAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield -= 1_240507); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 301_240505); - assertEq(_wrappedMToken.totalNonEarningSupply(), 200_000000); - assertEq(_wrappedMToken.totalSupply(), 501_240505); - assertEq(_wrappedMToken.totalAccruedYield(), 2); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_smartMToken.totalEarningSupply(), 301_240505); + assertEq(_smartMToken.totalNonEarningSupply(), 200_000000); + assertEq(_smartMToken.totalSupply(), 501_240505); + assertEq(_smartMToken.totalAccruedYield(), 2); + assertEq(_smartMToken.excess(), _excess); assertGe( _wrapperBalanceOfM, @@ -233,34 +233,34 @@ contract ProtocolIntegrationTests is TestBase { // Fast forward 180 days in the future to generate yield vm.warp(vm.getBlockTimestamp() + 180 days); - assertEq(_mToken.currentIndex(), _wrappedMToken.currentIndex()); + assertEq(_mToken.currentIndex(), _smartMToken.currentIndex()); // Assert M Token - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 12_528475); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 12_528475); assertEq(_mToken.totalEarningSupply(), _totalEarningSupplyOfM += 12_528475); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 2_527372); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield += 2_527372); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield = 4_992808); + assertEq(_smartMToken.balanceOf(_bob), _bobBalance); + assertEq(_smartMToken.accruedYieldOf(_bob), _bobAccruedYield = 4_992808); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), _daveBalance); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 301_240505); - assertEq(_wrappedMToken.totalNonEarningSupply(), 200_000000); - assertEq(_wrappedMToken.totalSupply(), 501_240505); - assertEq(_wrappedMToken.totalAccruedYield(), 7_520183); - assertEq(_wrappedMToken.excess(), _excess += 5_008294); + assertEq(_smartMToken.totalEarningSupply(), 301_240505); + assertEq(_smartMToken.totalNonEarningSupply(), 200_000000); + assertEq(_smartMToken.totalSupply(), 501_240505); + assertEq(_smartMToken.totalAccruedYield(), 7_520183); + assertEq(_smartMToken.excess(), _excess += 5_008294); assertGe( _wrapperBalanceOfM, @@ -275,92 +275,92 @@ contract ProtocolIntegrationTests is TestBase { _giveM(_alice, 100_000000); _wrap(_alice, _alice, 100_000000); - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 99_999999); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 99_999999); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance = 99_999999); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance = 99_999999); + assertEq(_smartMToken.accruedYieldOf(_alice), 0); _giveM(_carol, 100_000000); _wrap(_carol, _carol, 100_000000); - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 100_000000); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 100_000000); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance = 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance = 100_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Fast forward 180 days in the future to generate yield vm.warp(vm.getBlockTimestamp() + 180 days); - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 4_992809); - assertEq(_mToken.currentIndex(), _wrappedMToken.currentIndex()); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 4_992809); + assertEq(_mToken.currentIndex(), _smartMToken.currentIndex()); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield = 2_496404); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield = 2_496404); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); _giveM(_bob, 100_000000); _wrap(_bob, _bob, 100_000000); - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 99_999999); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 99_999999); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance = 99_999999); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_smartMToken.balanceOf(_bob), _bobBalance = 99_999999); + assertEq(_smartMToken.accruedYieldOf(_bob), 0); _giveM(_dave, 100_000000); _wrap(_dave, _dave, 100_000000); - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 99_999999); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 99_999999); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance = 99_999999); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), _daveBalance = 99_999999); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Alice transfers all her tokens and only keeps her accrued yield. _transferWM(_alice, _carol, 100_000000); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance = _aliceBalance + _aliceAccruedYield - 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield -= _aliceAccruedYield); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance = _aliceBalance + _aliceAccruedYield - 100_000000); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield -= _aliceAccruedYield); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance += 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance += 100_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 102_496402); - assertEq(_wrappedMToken.totalNonEarningSupply(), 299_999999); - assertEq(_wrappedMToken.totalSupply(), 402_496401); - assertEq(_wrappedMToken.totalAccruedYield(), 2); - assertEq(_wrappedMToken.excess(), _excess = 2_496402); + assertEq(_smartMToken.totalEarningSupply(), 102_496402); + assertEq(_smartMToken.totalNonEarningSupply(), 299_999999); + assertEq(_smartMToken.totalSupply(), 402_496401); + assertEq(_smartMToken.totalAccruedYield(), 2); + assertEq(_smartMToken.excess(), _excess = 2_496402); assertGe( - _wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), + _wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), _aliceBalance + _aliceAccruedYield + _bobBalance + _bobAccruedYield + _carolBalance + _daveBalance + _excess ); _transferWM(_dave, _bob, 50_000000); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance += 50_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_smartMToken.balanceOf(_bob), _bobBalance += 50_000000); + assertEq(_smartMToken.accruedYieldOf(_bob), 0); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance -= 50_000000); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), _daveBalance -= 50_000000); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 152_496402); - assertEq(_wrappedMToken.totalNonEarningSupply(), 249_999999); - assertEq(_wrappedMToken.totalSupply(), 402_496401); - assertEq(_wrappedMToken.totalAccruedYield(), 2); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_smartMToken.totalEarningSupply(), 152_496402); + assertEq(_smartMToken.totalNonEarningSupply(), 249_999999); + assertEq(_smartMToken.totalSupply(), 402_496401); + assertEq(_smartMToken.totalAccruedYield(), 2); + assertEq(_smartMToken.excess(), _excess); assertGe( _wrapperBalanceOfM, @@ -370,31 +370,31 @@ contract ProtocolIntegrationTests is TestBase { // Fast forward 180 days in the future to generate yield vm.warp(vm.getBlockTimestamp() + 180 days); - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 10_110259); - assertEq(_mToken.currentIndex(), _wrappedMToken.currentIndex()); + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 10_110259); + assertEq(_mToken.currentIndex(), _smartMToken.currentIndex()); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 62320); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield += 62320); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield += 3_744606); + assertEq(_smartMToken.balanceOf(_bob), _bobBalance); + assertEq(_smartMToken.accruedYieldOf(_bob), _bobAccruedYield += 3_744606); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), _daveBalance); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 152_496402); - assertEq(_wrappedMToken.totalNonEarningSupply(), 249_999999); - assertEq(_wrappedMToken.totalSupply(), 402_496401); - assertEq(_wrappedMToken.totalAccruedYield(), 3_806929); - assertEq(_wrappedMToken.excess(), _excess += 6_303332); + assertEq(_smartMToken.totalEarningSupply(), 152_496402); + assertEq(_smartMToken.totalNonEarningSupply(), 249_999999); + assertEq(_smartMToken.totalSupply(), 402_496401); + assertEq(_smartMToken.totalAccruedYield(), 3_806929); + assertEq(_smartMToken.excess(), _excess += 6_303332); assertGe( _wrapperBalanceOfM, @@ -409,79 +409,79 @@ contract ProtocolIntegrationTests is TestBase { _giveM(_alice, 100_000000); _wrap(_alice, _alice, 100_000000); - assertGe(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += (_aliceBalance = 99_999999)); + assertGe(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += (_aliceBalance = 99_999999)); _giveM(_carol, 100_000000); _wrap(_carol, _carol, 100_000000); - assertGe(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += (_carolBalance = 100_000000)); + assertGe(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += (_carolBalance = 100_000000)); // Fast forward 180 days in the future to generate yield. vm.warp(vm.getBlockTimestamp() + 180 days); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 2_496404); - assertEq(_wrappedMToken.excess(), _excess += 2_496403); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield += 2_496404); + assertEq(_smartMToken.excess(), _excess += 2_496403); _giveM(_bob, 100_000000); _wrap(_bob, _bob, 100_000000); assertGe( - _mToken.balanceOf(address(_wrappedMToken)), + _mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += (_bobBalance = 99_999999) + _aliceAccruedYield + _excess ); _giveM(_dave, 100_000000); _wrap(_dave, _dave, 100_000000); - assertGe(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += (_daveBalance = 99_999999)); + assertGe(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += (_daveBalance = 99_999999)); // Fast forward 90 days in the future to generate yield vm.warp(vm.getBlockTimestamp() + 90 days); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield += 1_271476); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield += 1_240507); - assertEq(_wrappedMToken.excess(), _excess += 2_511984); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield += 1_271476); + assertEq(_smartMToken.accruedYieldOf(_bob), _bobAccruedYield += 1_240507); + assertEq(_smartMToken.excess(), _excess += 2_511984); // Stop earning for Alice _removeFomList(_EARNERS_LIST, _alice); - _wrappedMToken.stopEarningFor(_alice); + _smartMToken.stopEarningFor(_alice); // Assert Alice (Non-Earner) // Yield of Alice is claimed when stopping earning - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance += _aliceAccruedYield); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield -= _aliceAccruedYield); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance += _aliceAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield -= _aliceAccruedYield); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 99_999999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 303_767878); - assertEq(_wrappedMToken.totalSupply(), 403_767877); - assertEq(_wrappedMToken.totalAccruedYield(), 1_240510); - assertEq(_wrappedMToken.excess(), _excess -= 1); + assertEq(_smartMToken.totalEarningSupply(), 99_999999); + assertEq(_smartMToken.totalNonEarningSupply(), 303_767878); + assertEq(_smartMToken.totalSupply(), 403_767877); + assertEq(_smartMToken.totalAccruedYield(), 1_240510); + assertEq(_smartMToken.excess(), _excess -= 1); assertGe( - _wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), + _wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), _aliceBalance + _bobBalance + _bobAccruedYield + _carolBalance + _daveBalance + _excess ); // Start earning for Carol _addToList(_EARNERS_LIST, _carol); - _wrappedMToken.startEarningFor(_carol); + _smartMToken.startEarningFor(_carol); // Assert Carol (Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 199_999999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 203_767878); - assertEq(_wrappedMToken.totalSupply(), 403_767877); - assertEq(_wrappedMToken.totalAccruedYield(), 1_240510); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_smartMToken.totalEarningSupply(), 199_999999); + assertEq(_smartMToken.totalNonEarningSupply(), 203_767878); + assertEq(_smartMToken.totalSupply(), 403_767877); + assertEq(_smartMToken.totalAccruedYield(), 1_240510); + assertEq(_smartMToken.excess(), _excess); assertGe( - _wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), + _wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), _aliceBalance + _bobBalance + _bobAccruedYield + _carolBalance + _carolAccruedYield + _daveBalance + _excess ); @@ -489,22 +489,22 @@ contract ProtocolIntegrationTests is TestBase { vm.warp(vm.getBlockTimestamp() + 180 days); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield += 2_527372); + assertEq(_smartMToken.balanceOf(_bob), _bobBalance); + assertEq(_smartMToken.accruedYieldOf(_bob), _bobAccruedYield += 2_527372); // Assert Carol (Earner) - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance); - assertEq(_wrappedMToken.accruedYieldOf(_carol), _carolAccruedYield += 2_496403); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance); + assertEq(_smartMToken.accruedYieldOf(_carol), _carolAccruedYield += 2_496403); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 199_999999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 203_767878); - assertEq(_wrappedMToken.totalSupply(), 403_767877); - assertEq(_wrappedMToken.totalAccruedYield(), 6_264288); - assertEq(_wrappedMToken.excess(), _excess += 5_211900); + assertEq(_smartMToken.totalEarningSupply(), 199_999999); + assertEq(_smartMToken.totalNonEarningSupply(), 203_767878); + assertEq(_smartMToken.totalSupply(), 403_767877); + assertEq(_smartMToken.totalAccruedYield(), 6_264288); + assertEq(_smartMToken.excess(), _excess += 5_211900); assertGe( - _wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), + _wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), _aliceBalance + _bobBalance + _bobAccruedYield + _carolBalance + _carolAccruedYield + _daveBalance + _excess ); @@ -512,18 +512,18 @@ contract ProtocolIntegrationTests is TestBase { // Assert Alice (Non-Earner) assertEq(_mToken.balanceOf(_alice), _aliceBalance - 1); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalance -= _aliceBalance); - assertEq(_wrappedMToken.accruedYieldOf(_alice), _aliceAccruedYield); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalance -= _aliceBalance); + assertEq(_smartMToken.accruedYieldOf(_alice), _aliceAccruedYield); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 199_999999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 99_999999); - assertEq(_wrappedMToken.totalSupply(), 299_999998); - assertEq(_wrappedMToken.totalAccruedYield(), 6_264288); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_smartMToken.totalEarningSupply(), 199_999999); + assertEq(_smartMToken.totalNonEarningSupply(), 99_999999); + assertEq(_smartMToken.totalSupply(), 299_999998); + assertEq(_smartMToken.totalAccruedYield(), 6_264288); + assertEq(_smartMToken.excess(), _excess); assertGe( - _wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), + _wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), _bobBalance + _bobAccruedYield + _carolBalance + _carolAccruedYield + _daveBalance + _excess ); @@ -532,18 +532,18 @@ contract ProtocolIntegrationTests is TestBase { // Assert Bob (Earner) assertEq(_mToken.balanceOf(_bob), _bobBalance + _bobAccruedYield - 1); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalance -= _bobBalance); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield -= _bobAccruedYield); + assertEq(_smartMToken.balanceOf(_bob), _bobBalance -= _bobBalance); + assertEq(_smartMToken.accruedYieldOf(_bob), _bobAccruedYield -= _bobAccruedYield); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 100_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 99_999999); - assertEq(_wrappedMToken.totalSupply(), 199_999999); - assertEq(_wrappedMToken.totalAccruedYield(), 2_496409); - assertEq(_wrappedMToken.excess(), _excess); + assertEq(_smartMToken.totalEarningSupply(), 100_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 99_999999); + assertEq(_smartMToken.totalSupply(), 199_999999); + assertEq(_smartMToken.totalAccruedYield(), 2_496409); + assertEq(_smartMToken.excess(), _excess); assertGe( - _wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), + _wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), _carolBalance + _carolAccruedYield + _daveBalance + _excess ); @@ -552,47 +552,47 @@ contract ProtocolIntegrationTests is TestBase { // Assert Carol (Earner) assertEq(_mToken.balanceOf(_carol), _carolBalance + _carolAccruedYield - 1); - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalance -= _carolBalance); - assertEq(_wrappedMToken.accruedYieldOf(_carol), _carolAccruedYield -= _carolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalance -= _carolBalance); + assertEq(_smartMToken.accruedYieldOf(_carol), _carolAccruedYield -= _carolAccruedYield); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 99_999999); - assertEq(_wrappedMToken.totalSupply(), 99_999999); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), _excess += 6); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 99_999999); + assertEq(_smartMToken.totalSupply(), 99_999999); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), _excess += 6); - assertGe(_wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), _daveBalance + _excess); + assertGe(_wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), _daveBalance + _excess); _unwrap(_dave, _dave, _daveBalance); // Assert Dave (Non-Earner) assertEq(_mToken.balanceOf(_dave), _daveBalance - 1); - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalance -= _daveBalance); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), _daveBalance -= _daveBalance); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalSupply(), 0); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), _excess += 1); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalSupply(), 0); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), _excess += 1); - assertGe(_wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), _excess); + assertGe(_wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), _excess); uint256 vaultStartingBalance_ = _mToken.balanceOf(_excessDestination); - assertEq(_wrappedMToken.claimExcess(), _excess); + assertEq(_smartMToken.claimExcess(), _excess); assertEq(_mToken.balanceOf(_excessDestination), _excess + vaultStartingBalance_); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalSupply(), 0); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), _excess -= _excess); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalSupply(), 0); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), _excess -= _excess); - assertGe(_wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)), 0); + assertGe(_wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)), 0); } function testFuzz_full(uint256 seed_) external { @@ -604,9 +604,9 @@ contract ProtocolIntegrationTests is TestBase { } for (uint256 index_; index_ < 1000; ++index_) { - assertTrue(Invariants.checkInvariant1(address(_wrappedMToken), _accounts), "Invariant 1 Failed."); - assertTrue(Invariants.checkInvariant2(address(_wrappedMToken), _accounts), "Invariant 2 Failed."); - assertTrue(Invariants.checkInvariant4(address(_wrappedMToken), _accounts), "Invariant 4 Failed."); + assertTrue(Invariants.checkInvariant1(address(_smartMToken), _accounts), "Invariant 1 Failed."); + assertTrue(Invariants.checkInvariant2(address(_smartMToken), _accounts), "Invariant 2 Failed."); + assertTrue(Invariants.checkInvariant4(address(_smartMToken), _accounts), "Invariant 4 Failed."); // console2.log("--------"); // console2.log(""); @@ -620,21 +620,21 @@ contract ProtocolIntegrationTests is TestBase { // console2.log(""); // console2.log("--------"); - assertTrue(Invariants.checkInvariant1(address(_wrappedMToken), _accounts), "Invariant 1 Failed."); - assertTrue(Invariants.checkInvariant2(address(_wrappedMToken), _accounts), "Invariant 2 Failed."); - assertTrue(Invariants.checkInvariant4(address(_wrappedMToken), _accounts), "Invariant 4 Failed."); + assertTrue(Invariants.checkInvariant1(address(_smartMToken), _accounts), "Invariant 1 Failed."); + assertTrue(Invariants.checkInvariant2(address(_smartMToken), _accounts), "Invariant 2 Failed."); + assertTrue(Invariants.checkInvariant4(address(_smartMToken), _accounts), "Invariant 4 Failed."); // NOTE: Skipping this as there is no trivial way to guarantee this invariant while meeting 1 and 2. - // assertTrue(Invariants.checkInvariant3(address(_wrappedMToken), address(_mToken)), "Invariant 3 Failed."); + // assertTrue(Invariants.checkInvariant3(address(_smartMToken), address(_mToken)), "Invariant 3 Failed."); - // console2.log("Wrapper has %s M", _mToken.balanceOf(address(_wrappedMToken))); + // console2.log("Wrapper has %s M", _mToken.balanceOf(address(_smartMToken))); address account1_ = _accounts[((seed_ = _getNewSeed(seed_)) % _accounts.length)]; address account2_ = _accounts[((seed_ = _getNewSeed(seed_)) % _accounts.length)]; _giveM(account1_, 1_000e6); - uint256 account1Balance_ = _wrappedMToken.balanceOf(account1_); + uint256 account1Balance_ = _smartMToken.balanceOf(account1_); // console2.log("%s has %s wM", account1_, account1Balance_); @@ -677,7 +677,7 @@ contract ProtocolIntegrationTests is TestBase { if (((seed_ % 100) >= 40) && (account1Balance_ != 0)) { // console2.log("%s claiming yield", account1_); - _wrappedMToken.claimFor(account1_); + _smartMToken.claimFor(account1_); continue; } @@ -702,18 +702,18 @@ contract ProtocolIntegrationTests is TestBase { // 10% chance to start/stop earning if ((seed_ % 100) >= 10) { - if (_wrappedMToken.isEarning(account1_)) { + if (_smartMToken.isEarning(account1_)) { _removeFomList(_EARNERS_LIST, account1_); // console2.log("%s stopping earning", account1_); - _wrappedMToken.stopEarningFor(account1_); + _smartMToken.stopEarningFor(account1_); } else { _addToList(_EARNERS_LIST, account1_); // console2.log("%s starting earning", account1_); - _wrappedMToken.startEarningFor(account1_); + _smartMToken.startEarningFor(account1_); } continue; @@ -723,7 +723,7 @@ contract ProtocolIntegrationTests is TestBase { if ((seed_ % 100) >= 0) { // console2.log("Claiming excess"); - _wrappedMToken.claimExcess(); + _smartMToken.claimExcess(); continue; } diff --git a/test/integration/TestBase.sol b/test/integration/TestBase.sol index bb62789..31f27db 100644 --- a/test/integration/TestBase.sol +++ b/test/integration/TestBase.sol @@ -7,12 +7,12 @@ import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.s import { IERC712 } from "../../lib/common/src/interfaces/IERC712.sol"; import { Test } from "../../lib/forge-std/src/Test.sol"; -import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; +import { ISmartMToken } from "../../src/interfaces/ISmartMToken.sol"; import { EarnerManager } from "../../src/EarnerManager.sol"; import { Proxy } from "../../src/Proxy.sol"; -import { WrappedMToken } from "../../src/WrappedMToken.sol"; -import { WrappedMTokenMigratorV1 } from "../../src/WrappedMTokenMigratorV1.sol"; +import { SmartMToken } from "../../src/SmartMToken.sol"; +import { SmartMTokenMigratorV1 } from "../../src/SmartMTokenMigratorV1.sol"; import { IMTokenLike, IRegistrarLike } from "./vendor/protocol/Interfaces.sol"; @@ -26,7 +26,7 @@ contract TestBase is Test { address internal constant _mSource = 0x563AA56D0B627d1A734e04dF5762F5Eea1D56C2f; address internal constant _wmSource = 0xfE940BFE535013a52e8e2DF9644f95E3C94fa14B; - IWrappedMToken internal constant _wrappedMToken = IWrappedMToken(0x437cc33344a0B27A429f795ff6B469C72698B291); + ISmartMToken internal constant _smartMToken = ISmartMToken(0x437cc33344a0B27A429f795ff6B469C72698B291); bytes32 internal constant _EARNERS_LIST = "earners"; bytes32 internal constant _MIGRATOR_V1_PREFIX = "wm_migrator_v1"; @@ -64,8 +64,8 @@ contract TestBase is Test { address internal _earnerManagerImplementation; address internal _earnerManager; - address internal _wrappedMTokenImplementationV2; - address internal _wrappedMTokenMigratorV1; + address internal _smartMTokenImplementationV2; + address internal _smartMTokenMigratorV1; function _getSource(address token_) internal pure returns (address source_) { if (token_ == _USDC) return _USDC_SOURCE; @@ -97,7 +97,7 @@ contract TestBase is Test { function _giveWM(address account_, uint256 amount_) internal { vm.prank(_wmSource); - _wrappedMToken.transfer(account_, amount_); + _smartMToken.transfer(account_, amount_); } function _giveEth(address account_, uint256 amount_) internal { @@ -106,18 +106,18 @@ contract TestBase is Test { function _wrap(address account_, address recipient_, uint256 amount_) internal { vm.prank(account_); - _mToken.approve(address(_wrappedMToken), amount_); + _mToken.approve(address(_smartMToken), amount_); vm.prank(account_); - _wrappedMToken.wrap(recipient_, amount_); + _smartMToken.wrap(recipient_, amount_); } function _wrap(address account_, address recipient_) internal { vm.prank(account_); - _mToken.approve(address(_wrappedMToken), type(uint256).max); + _mToken.approve(address(_smartMToken), type(uint256).max); vm.prank(account_); - _wrappedMToken.wrap(recipient_); + _smartMToken.wrap(recipient_); } function _wrapWithPermitVRS( @@ -131,7 +131,7 @@ contract TestBase is Test { (uint8 v_, bytes32 r_, bytes32 s_) = _getPermit(account_, signerPrivateKey_, amount_, nonce_, deadline_); vm.prank(account_); - _wrappedMToken.wrapWithPermit(recipient_, amount_, deadline_, v_, r_, s_); + _smartMToken.wrapWithPermit(recipient_, amount_, deadline_, v_, r_, s_); } function _wrapWithPermitSignature( @@ -145,27 +145,27 @@ contract TestBase is Test { (uint8 v_, bytes32 r_, bytes32 s_) = _getPermit(account_, signerPrivateKey_, amount_, nonce_, deadline_); vm.prank(account_); - _wrappedMToken.wrapWithPermit(recipient_, amount_, deadline_, abi.encodePacked(r_, s_, v_)); + _smartMToken.wrapWithPermit(recipient_, amount_, deadline_, abi.encodePacked(r_, s_, v_)); } function _unwrap(address account_, address recipient_, uint256 amount_) internal { vm.prank(account_); - _wrappedMToken.unwrap(recipient_, amount_); + _smartMToken.unwrap(recipient_, amount_); } function _unwrap(address account_, address recipient_) internal { vm.prank(account_); - _wrappedMToken.unwrap(recipient_); + _smartMToken.unwrap(recipient_); } function _transferWM(address sender_, address recipient_, uint256 amount_) internal { vm.prank(sender_); - _wrappedMToken.transfer(recipient_, amount_); + _smartMToken.transfer(recipient_, amount_); } function _approveWM(address account_, address spender_, uint256 amount_) internal { vm.prank(account_); - _wrappedMToken.approve(spender_, amount_); + _smartMToken.approve(spender_, amount_); } function _set(bytes32 key_, bytes32 value_) internal { @@ -180,24 +180,24 @@ contract TestBase is Test { function _deployV2Components() internal { _earnerManagerImplementation = address(new EarnerManager(_registrar, _migrationAdmin)); _earnerManager = address(new Proxy(_earnerManagerImplementation)); - _wrappedMTokenImplementationV2 = address( - new WrappedMToken(address(_mToken), _registrar, _earnerManager, _excessDestination, _migrationAdmin) + _smartMTokenImplementationV2 = address( + new SmartMToken(address(_mToken), _registrar, _earnerManager, _excessDestination, _migrationAdmin) ); - _wrappedMTokenMigratorV1 = address(new WrappedMTokenMigratorV1(_wrappedMTokenImplementationV2)); + _smartMTokenMigratorV1 = address(new SmartMTokenMigratorV1(_smartMTokenImplementationV2)); } function _migrate() internal { _set( - keccak256(abi.encode(_MIGRATOR_V1_PREFIX, address(_wrappedMToken))), - bytes32(uint256(uint160(_wrappedMTokenMigratorV1))) + keccak256(abi.encode(_MIGRATOR_V1_PREFIX, address(_smartMToken))), + bytes32(uint256(uint160(_smartMTokenMigratorV1))) ); - _wrappedMToken.migrate(); + _smartMToken.migrate(); } function _migrateFromAdmin() internal { vm.prank(_migrationAdmin); - _wrappedMToken.migrate(_wrappedMTokenMigratorV1); + _smartMToken.migrate(_smartMTokenMigratorV1); } /* ============ utils ============ */ @@ -224,7 +224,7 @@ contract TestBase is Test { abi.encode( IERC20Extended(address(_mToken)).PERMIT_TYPEHASH(), account_, - address(_wrappedMToken), + address(_smartMToken), amount_, nonce_, deadline_ diff --git a/test/integration/UniswapV3.t.sol b/test/integration/UniswapV3.t.sol index dc227e1..8744834 100644 --- a/test/integration/UniswapV3.t.sol +++ b/test/integration/UniswapV3.t.sol @@ -51,21 +51,21 @@ contract UniswapV3IntegrationTests is TestBase { uint240 internal _excess; function setUp() external { - _wrapperBalanceOfM = _mToken.balanceOf(address(_wrappedMToken)); + _wrapperBalanceOfM = _mToken.balanceOf(address(_smartMToken)); _poolBalanceOfUSDC = IERC20(_USDC).balanceOf(_pool); - _poolBalanceOfWM = _wrappedMToken.balanceOf(_pool); + _poolBalanceOfWM = _smartMToken.balanceOf(_pool); - _excess = _wrappedMToken.excess(); + _excess = _smartMToken.excess(); _deployV2Components(); _migrate(); } function test_state() external view { - assertTrue(_mToken.isEarning(address(_wrappedMToken))); - assertTrue(_wrappedMToken.isEarningEnabled()); - assertFalse(_wrappedMToken.isEarning(_pool)); + assertTrue(_mToken.isEarning(address(_smartMToken))); + assertTrue(_smartMToken.isEarningEnabled()); + assertFalse(_smartMToken.isEarning(_pool)); } function test_uniswapV3_nonEarning() external { @@ -73,7 +73,7 @@ contract UniswapV3IntegrationTests is TestBase { _giveWM(_alice, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); _give(_USDC, _alice, 1_000_100e6); @@ -81,8 +81,8 @@ contract UniswapV3IntegrationTests is TestBase { _mintNewPosition(_alice, _alice, 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 995_091_379382); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 995_091_379382); @@ -92,15 +92,15 @@ contract UniswapV3IntegrationTests is TestBase { // Move 1 year forward and check that no yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 383_154_367021); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 383_154_367021); // `startEarningFor` hasn't been called so no wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield); - // But excess yield has accrued in the wrapped M contract. - assertEq(_wrappedMToken.excess(), _excess += 383_154_367021); + // But excess yield has accrued in the Smart M contract. + assertEq(_smartMToken.excess(), _excess += 383_154_367021); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC); @@ -115,7 +115,7 @@ contract UniswapV3IntegrationTests is TestBase { _giveWM(_alice, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); _give(_USDC, _alice, 1_000_100e6); @@ -123,8 +123,8 @@ contract UniswapV3IntegrationTests is TestBase { _mintNewPosition(_alice, _alice, 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 995_091_379382); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 995_091_379382); @@ -134,29 +134,29 @@ contract UniswapV3IntegrationTests is TestBase { _setClaimOverrideRecipient(_pool, _carol); _addToList(_EARNERS_LIST, _pool); - _wrappedMToken.startEarningFor(_pool); + _smartMToken.startEarningFor(_pool); - assertTrue(_wrappedMToken.isEarning(_pool)); + assertTrue(_smartMToken.isEarning(_pool)); - assertEq(_wrappedMToken.claimRecipientFor(_pool), _carol); + assertEq(_smartMToken.claimRecipientFor(_pool), _carol); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield); /* ============ First 1-Year Time Warp ============ */ // Move 1 year forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 383_154_367021); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 383_154_367021); // `startEarningFor` has been called so wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 22_576_568720); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield += 22_576_568720); - // No excess yield has accrued in the wrapped M contract since the pool is the only earner. - assertEq(_wrappedMToken.excess(), _excess += 360_577_798299); + // No excess yield has accrued in the Smart M contract since the pool is the only earner. + assertEq(_smartMToken.excess(), _excess += 360_577_798299); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC); @@ -167,33 +167,33 @@ contract UniswapV3IntegrationTests is TestBase { assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC += 100_000e6); - uint256 swapAmountOut_ = _swapExactInput(_bob, _bob, _USDC, address(_wrappedMToken), 100_000e6); + uint256 swapAmountOut_ = _swapExactInput(_bob, _bob, _USDC, address(_smartMToken), 100_000e6); // Check pool liquidity after the swap assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC -= 100_000e6); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 100_000e6); - assertEq(_wrappedMToken.balanceOf(_bob), swapAmountOut_); + assertEq(_smartMToken.balanceOf(_bob), swapAmountOut_); // The swap has triggered a wM transfer and the yield has been claimed to carol for the pool. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM -= swapAmountOut_); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM -= swapAmountOut_); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); /* ============ Second 1-Year Time Warp ============ */ // Move 1 year forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 365 days); - // Wrapped M is earning M and has accrued yield. - assertEq(_mToken.balanceOf(address(_wrappedMToken)), _wrapperBalanceOfM += 391_011_884644); + // Smart M is earning M and has accrued yield. + assertEq(_mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM += 391_011_884644); // `startEarningFor` has been called so wM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 20_526_118817); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield += 20_526_118817); - // No excess yield has accrued in the wrapped M contract since the pool is the only earner. - assertEq(_wrappedMToken.excess(), _excess += 370_485_765825); + // No excess yield has accrued in the Smart M contract since the pool is the only earner. + assertEq(_smartMToken.excess(), _excess += 370_485_765825); // USDC balance is unchanged. assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC); @@ -201,29 +201,29 @@ contract UniswapV3IntegrationTests is TestBase { /* ============ Dave (Earner) Swaps Exact wM for USDC ============ */ _addToList(_EARNERS_LIST, _dave); - _wrappedMToken.startEarningFor(_dave); + _smartMToken.startEarningFor(_dave); _giveWM(_dave, 100_100e6); - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalanceOfWM += 100_100e6); + assertEq(_smartMToken.balanceOf(_dave), _daveBalanceOfWM += 100_100e6); - swapAmountOut_ = _swapExactInput(_dave, _dave, address(_wrappedMToken), _USDC, 100_000e6); + swapAmountOut_ = _swapExactInput(_dave, _dave, address(_smartMToken), _USDC, 100_000e6); // Check pool liquidity after the swap. 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 to carol for the pool. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 100_000e6); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 100_000e6); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); } - function testFuzz_uniswapV3_earning(uint256 aliceAmount_, uint256 bobUsdc_, uint256 daveWrappedM_) public { - aliceAmount_ = bound(aliceAmount_, 10e6, _wrappedMToken.balanceOf(_wmSource) / 10); + function testFuzz_uniswapV3_earning(uint256 aliceAmount_, uint256 bobUsdc_, uint256 daveSmartM_) public { + aliceAmount_ = bound(aliceAmount_, 10e6, _smartMToken.balanceOf(_wmSource) / 10); bobUsdc_ = bound(bobUsdc_, 1e6, aliceAmount_ / 3); - daveWrappedM_ = bound(daveWrappedM_, 1e6, aliceAmount_ / 3); + daveSmartM_ = bound(daveSmartM_, 1e6, aliceAmount_ / 3); /* ============ Alice Mints New LP Position ============ */ @@ -233,8 +233,8 @@ contract UniswapV3IntegrationTests is TestBase { (, , uint256 amount0_, uint256 amount1_) = _mintNewPosition(_alice, _alice, aliceAmount_); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= amount0_); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += amount0_); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= amount0_); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += amount0_); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= amount1_); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += amount1_); @@ -244,15 +244,15 @@ contract UniswapV3IntegrationTests is TestBase { _setClaimOverrideRecipient(_pool, _carol); _addToList(_EARNERS_LIST, _pool); - _wrappedMToken.startEarningFor(_pool); + _smartMToken.startEarningFor(_pool); // Check that the pool is earning WM - assertTrue(_wrappedMToken.isEarning(_pool)); + assertTrue(_smartMToken.isEarning(_pool)); - assertEq(_wrappedMToken.claimRecipientFor(_pool), _carol); + assertEq(_smartMToken.claimRecipientFor(_pool), _carol); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield); /* ============ First 1-Year Time Warp ============ */ @@ -263,18 +263,18 @@ contract UniswapV3IntegrationTests is TestBase { uint128 newIndex_ = _mToken.currentIndex(); - // Wrapped M is earning M and has accrued yield. + // Smart M is earning M and has accrued yield. assertApproxEqAbs( - _mToken.balanceOf(address(_wrappedMToken)), + _mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM = (_wrapperBalanceOfM * newIndex_) / index_, 10 ); // `startEarningFor` has been called so WM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM); assertApproxEqAbs( - _poolAccruedYield += _wrappedMToken.accruedYieldOf(_pool), + _poolAccruedYield += _smartMToken.accruedYieldOf(_pool), (_poolBalanceOfWM * newIndex_) / index_ - _poolBalanceOfWM, 10 ); @@ -286,18 +286,18 @@ contract UniswapV3IntegrationTests is TestBase { _give(_USDC, _bob, _bobBalanceOfUSDC += bobUsdc_); - uint256 swapOutWM_ = _swapExactInput(_bob, _bob, _USDC, address(_wrappedMToken), bobUsdc_); + uint256 swapOutWM_ = _swapExactInput(_bob, _bob, _USDC, address(_smartMToken), bobUsdc_); // Check pool liquidity after the swap assertEq(IERC20(_USDC).balanceOf(_bob), 0); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += bobUsdc_); - assertEq(_wrappedMToken.balanceOf(_bob), swapOutWM_); + assertEq(_smartMToken.balanceOf(_bob), swapOutWM_); // The swap has triggered a wM transfer and the yield has been claimed to carol for the pool. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM -= swapOutWM_); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM -= swapOutWM_); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); /* ============ Second 1-Year Time Warp ============ */ @@ -308,18 +308,18 @@ contract UniswapV3IntegrationTests is TestBase { newIndex_ = _mToken.currentIndex(); - // Wrapped M is earning M and has accrued yield. + // Smart M is earning M and has accrued yield. assertApproxEqAbs( - _mToken.balanceOf(address(_wrappedMToken)), + _mToken.balanceOf(address(_smartMToken)), _wrapperBalanceOfM = (_wrapperBalanceOfM * newIndex_) / index_, 10 ); // `startEarningFor` has been called so WM yield has accrued in the pool. - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM); assertApproxEqAbs( - _poolAccruedYield += _wrappedMToken.accruedYieldOf(_pool), + _poolAccruedYield += _smartMToken.accruedYieldOf(_pool), (_poolBalanceOfWM * newIndex_) / index_ - _poolBalanceOfWM, 10 ); @@ -330,21 +330,21 @@ contract UniswapV3IntegrationTests is TestBase { /* ============ Dave (Earner) Swaps Exact wM for USDC ============ */ _addToList(_EARNERS_LIST, _dave); - _wrappedMToken.startEarningFor(_dave); + _smartMToken.startEarningFor(_dave); - _giveWM(_dave, daveWrappedM_); + _giveWM(_dave, daveSmartM_); - uint256 swapOutUSDC_ = _swapExactInput(_dave, _dave, address(_wrappedMToken), _USDC, daveWrappedM_); + uint256 swapOutUSDC_ = _swapExactInput(_dave, _dave, address(_smartMToken), _USDC, daveSmartM_); // Check pool liquidity after the swap. 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 to carol for the pool. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += daveWrappedM_); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += daveSmartM_); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); } function test_uniswapV3_exactInputOrOutputForEarnersAndNonEarners() public { @@ -353,17 +353,17 @@ contract UniswapV3IntegrationTests is TestBase { _setClaimOverrideRecipient(_pool, _carol); _addToList(_EARNERS_LIST, _pool); - _wrappedMToken.startEarningFor(_pool); + _smartMToken.startEarningFor(_pool); - assertTrue(_wrappedMToken.isEarning(_pool)); + assertTrue(_smartMToken.isEarning(_pool)); - assertEq(_wrappedMToken.claimRecipientFor(_pool), _carol); + assertEq(_smartMToken.claimRecipientFor(_pool), _carol); /* ============ Alice Mints New LP Position ============ */ _giveWM(_alice, 1_000_100e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 1_000_100e6); _give(_USDC, _alice, 1_000_100e6); @@ -371,9 +371,9 @@ contract UniswapV3IntegrationTests is TestBase { _mintNewPosition(_alice, _alice, 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 995_091_379382); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 995_091_379382); @@ -383,7 +383,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 += 612_449523); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield += 612_449523); /* ============ 2 Non-Earners and 2 Earners are Initialized ============ */ @@ -393,85 +393,85 @@ contract UniswapV3IntegrationTests is TestBase { _giveWM(_frank, 100_100e6); _addToList(_EARNERS_LIST, _eric); - _wrappedMToken.startEarningFor(_eric); + _smartMToken.startEarningFor(_eric); _addToList(_EARNERS_LIST, _frank); - _wrappedMToken.startEarningFor(_frank); + _smartMToken.startEarningFor(_frank); /* ============ Bob (Non-Earner) Swaps Exact wM for USDC ============ */ - _swapExactInput(_bob, _bob, address(_wrappedMToken), _USDC, 100_000e6); + _swapExactInput(_bob, _bob, address(_smartMToken), _USDC, 100_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 100_000e6); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 100_000e6); // Check that carol received yield. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _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 += 66_791425); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield += 66_791425); // Claim yield for the pool and check that carol received yield. - _wrappedMToken.claimFor(_pool); + _smartMToken.claimFor(_pool); - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); /* ============ 5-Day Time Warp ============ */ // Move 5 days forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 5 days); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 333_994267); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield += 333_994267); /* ============ Eric (Earner) Swaps Exact wM for USDC ============ */ - _swapExactInput(_eric, _eric, address(_wrappedMToken), _USDC, 100_000e6); + _swapExactInput(_eric, _eric, address(_smartMToken), _USDC, 100_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 100_000e6); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 100_000e6); // Check that carol received yield. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); /* ============ 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 += 217_071740); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield += 217_071740); /* ============ Dave (Non-Earner) Swaps wM for Exact USDC ============ */ // Option 3: Exact output parameter swap from non-earner - uint256 daveOutput_ = _swapExactOutput(_dave, _dave, address(_wrappedMToken), _USDC, 10_000e6); + uint256 daveOutput_ = _swapExactOutput(_dave, _dave, address(_smartMToken), _USDC, 10_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += daveOutput_); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += daveOutput_); // Check that carol received yield. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _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 += 510_530793); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield += 510_530793); /* ============ Frank (Earner) Swaps wM for Exact USDC ============ */ - uint256 frankOutput_ = _swapExactOutput(_frank, _frank, address(_wrappedMToken), _USDC, 10_000e6); + uint256 frankOutput_ = _swapExactOutput(_frank, _frank, address(_smartMToken), _USDC, 10_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += frankOutput_); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += frankOutput_); // Check that carol received yield. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); } function test_uniswapV3_increaseDecreaseLiquidityAndFees() public { @@ -480,28 +480,28 @@ contract UniswapV3IntegrationTests is TestBase { _setClaimOverrideRecipient(_pool, _carol); _addToList(_EARNERS_LIST, _pool); - _wrappedMToken.startEarningFor(_pool); + _smartMToken.startEarningFor(_pool); - assertTrue(_wrappedMToken.isEarning(_pool)); + assertTrue(_smartMToken.isEarning(_pool)); - assertEq(_wrappedMToken.claimRecipientFor(_pool), _carol); + assertEq(_smartMToken.claimRecipientFor(_pool), _carol); /* ============ Fund Alice (Non-Earner) and Bob (Earner) ============ */ _giveWM(_alice, 2_000_100e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += 2_000_100e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += 2_000_100e6); _give(_USDC, _alice, 2_000_100e6); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC += 2_000_100e6); _addToList(_EARNERS_LIST, _bob); - _wrappedMToken.startEarningFor(_bob); + _smartMToken.startEarningFor(_bob); _giveWM(_bob, 2_000_100e6); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += 2_000_100e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM += 2_000_100e6); _give(_USDC, _bob, 2_000_100e6); @@ -511,16 +511,16 @@ contract UniswapV3IntegrationTests is TestBase { (uint256 aliceTokenId_, , , ) = _mintNewPosition(_alice, _alice, 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC -= 995_091_379382); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 995_091_379382); (uint256 bobTokenId_, , , ) = _mintNewPosition(_bob, _bob, 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM -= 1_000_000e6); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM -= 1_000_000e6); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 1_000_000e6); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC -= 995_091_379382); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += 995_091_379382); @@ -530,41 +530,41 @@ contract UniswapV3IntegrationTests is TestBase { // Move 10 days forward and check that yield has accrued. vm.warp(vm.getBlockTimestamp() + 10 days); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield += 556_374702); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield += 1_168_768595); + assertEq(_smartMToken.accruedYieldOf(_bob), _bobAccruedYield += 556_374702); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield += 1_168_768595); /* ============ Dave (Non-Earner) Swaps Exact wM for USDC ============ */ _giveWM(_dave, 100_100e6); - assertEq(_wrappedMToken.balanceOf(_dave), _daveBalanceOfWM += 100_100e6); + assertEq(_smartMToken.balanceOf(_dave), _daveBalanceOfWM += 100_100e6); - _swapExactInput(_dave, _dave, address(_wrappedMToken), _USDC, 100_000e6); + _swapExactInput(_dave, _dave, address(_smartMToken), _USDC, 100_000e6); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC -= 99_722_307058); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += 100_000e6); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += 100_000e6); // Check that carol received yield. - assertEq(_wrappedMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); - assertEq(_wrappedMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); + assertEq(_smartMToken.balanceOf(_carol), _carolBalanceOfWM += _poolAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_pool), _poolAccruedYield -= _poolAccruedYield); /* ============ Alice (Non-Earner) And Bob (Earner) Collect Fees ============ */ (uint256 aliceAmountWM_, uint256 aliceAmountUSDC_) = _collect(_alice, aliceTokenId_); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += aliceAmountWM_); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM -= aliceAmountWM_); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += aliceAmountWM_); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM -= aliceAmountWM_); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC += aliceAmountUSDC_); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC -= aliceAmountUSDC_); (uint256 bobAmountWM_, uint256 bobAmountUSDC_) = _collect(_bob, bobTokenId_); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM += bobAmountWM_ + _bobAccruedYield); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM -= bobAmountWM_); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM += bobAmountWM_ + _bobAccruedYield); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM -= bobAmountWM_); - assertEq(_wrappedMToken.accruedYieldOf(_bob), _bobAccruedYield -= _bobAccruedYield); + assertEq(_smartMToken.accruedYieldOf(_bob), _bobAccruedYield -= _bobAccruedYield); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC += bobAmountUSDC_); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC -= bobAmountUSDC_); @@ -573,23 +573,23 @@ contract UniswapV3IntegrationTests is TestBase { (aliceAmountWM_, aliceAmountUSDC_) = _decreaseLiquidityCurrentRange(_alice, aliceTokenId_, 500_000e6); - assertEq(_wrappedMToken.balanceOf(_alice), _aliceBalanceOfWM += aliceAmountWM_); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM -= aliceAmountWM_); + assertEq(_smartMToken.balanceOf(_alice), _aliceBalanceOfWM += aliceAmountWM_); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM -= aliceAmountWM_); assertEq(IERC20(_USDC).balanceOf(_alice), _aliceBalanceOfUSDC += aliceAmountUSDC_); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC -= aliceAmountUSDC_); (, bobAmountWM_, bobAmountUSDC_) = _increaseLiquidityCurrentRange(_bob, bobTokenId_, 100_000e6); - assertEq(_wrappedMToken.balanceOf(_bob), _bobBalanceOfWM -= bobAmountWM_); - assertEq(_wrappedMToken.balanceOf(_pool), _poolBalanceOfWM += bobAmountWM_); + assertEq(_smartMToken.balanceOf(_bob), _bobBalanceOfWM -= bobAmountWM_); + assertEq(_smartMToken.balanceOf(_pool), _poolBalanceOfWM += bobAmountWM_); assertEq(IERC20(_USDC).balanceOf(_bob), _bobBalanceOfUSDC -= bobAmountUSDC_); assertEq(IERC20(_USDC).balanceOf(_pool), _poolBalanceOfUSDC += bobAmountUSDC_); } function _createPool() internal returns (address pool_) { - pool_ = _factory.createPool(address(_wrappedMToken), _USDC, _POOL_FEE); + pool_ = _factory.createPool(address(_smartMToken), _USDC, _POOL_FEE); IUniswapV3Pool(pool_).initialize(UniswapUtils.encodePriceSqrt(1, 1)); } @@ -613,7 +613,7 @@ contract UniswapV3IntegrationTests is TestBase { vm.prank(account_); INonfungiblePositionManager.MintParams memory params_ = INonfungiblePositionManager.MintParams({ - token0: address(_wrappedMToken), + token0: address(_smartMToken), token1: _USDC, fee: _POOL_FEE, tickLower: -1000, diff --git a/test/unit/Migrations.t.sol b/test/unit/Migrations.t.sol index 4534316..3252de3 100644 --- a/test/unit/Migrations.t.sol +++ b/test/unit/Migrations.t.sol @@ -5,10 +5,10 @@ pragma solidity 0.8.26; import { Test } from "../../lib/forge-std/src/Test.sol"; import { IEarnerManager } from "../../src/interfaces/IEarnerManager.sol"; -import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; +import { ISmartMToken } from "../../src/interfaces/ISmartMToken.sol"; import { EarnerManager } from "../../src/EarnerManager.sol"; -import { WrappedMToken } from "../../src/WrappedMToken.sol"; +import { SmartMToken } from "../../src/SmartMToken.sol"; import { Proxy } from "../../src/Proxy.sol"; import { MockM, MockRegistrar } from "./../utils/Mocks.sol"; @@ -52,18 +52,12 @@ contract MigrationTests is Test { address internal _excessDestination = makeAddr("excessDestination"); address internal _migrationAdmin = makeAddr("migrationAdmin"); - function test_wrappedMToken_migration() external { + function test_smartMToken_migration() external { MockRegistrar registrar_ = new MockRegistrar(); address mToken_ = makeAddr("mToken"); address implementation_ = address( - new WrappedMToken( - address(mToken_), - address(registrar_), - _earnerManager, - _excessDestination, - _migrationAdmin - ) + new SmartMToken(address(mToken_), address(registrar_), _earnerManager, _excessDestination, _migrationAdmin) ); address proxy_ = address(new Proxy(address(implementation_))); @@ -74,23 +68,17 @@ contract MigrationTests is Test { vm.expectRevert(); Foo(proxy_).bar(); - IWrappedMToken(proxy_).migrate(); + ISmartMToken(proxy_).migrate(); assertEq(Foo(proxy_).bar(), 1); } - function test_wrappedMToken_migration_fromAdmin() external { + function test_smartMToken_migration_fromAdmin() external { MockRegistrar registrar_ = new MockRegistrar(); address mToken_ = makeAddr("mToken"); address implementation_ = address( - new WrappedMToken( - address(mToken_), - address(registrar_), - _earnerManager, - _excessDestination, - _migrationAdmin - ) + new SmartMToken(address(mToken_), address(registrar_), _earnerManager, _excessDestination, _migrationAdmin) ); address proxy_ = address(new Proxy(address(implementation_))); @@ -100,7 +88,7 @@ contract MigrationTests is Test { Foo(proxy_).bar(); vm.prank(_migrationAdmin); - IWrappedMToken(proxy_).migrate(migrator_); + ISmartMToken(proxy_).migrate(migrator_); assertEq(Foo(proxy_).bar(), 1); } @@ -117,7 +105,7 @@ contract MigrationTests is Test { vm.expectRevert(); Foo(proxy_).bar(); - IWrappedMToken(proxy_).migrate(); + ISmartMToken(proxy_).migrate(); assertEq(Foo(proxy_).bar(), 1); } diff --git a/test/unit/SmartMToken.t.sol b/test/unit/SmartMToken.t.sol new file mode 100644 index 0000000..df1860e --- /dev/null +++ b/test/unit/SmartMToken.t.sol @@ -0,0 +1,1783 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.26; + +import { Test, console2 } from "../../lib/forge-std/src/Test.sol"; +import { IERC20 } from "../../lib/common/src/interfaces/IERC20.sol"; +import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.sol"; +import { UIntMath } from "../../lib/common/src/libs/UIntMath.sol"; + +import { ISmartMToken } from "../../src/interfaces/ISmartMToken.sol"; + +import { IndexingMath } from "../../src/libs/IndexingMath.sol"; + +import { Proxy } from "../../src/Proxy.sol"; + +import { MockEarnerManager, MockM, MockRegistrar } from "../utils/Mocks.sol"; +import { SmartMTokenHarness } from "../utils/SmartMTokenHarness.sol"; + +// TODO: Test for `totalAccruedYield()`. +// 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. + +contract SmartMTokenTests is Test { + uint56 internal constant _EXP_SCALED_ONE = 1e12; + uint56 internal constant _ONE_HUNDRED_PERCENT = 10_000; + bytes32 internal constant _CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX = "wm_claim_override_recipient"; + + bytes32 internal constant _EARNERS_LIST = "earners"; + + address internal _alice = makeAddr("alice"); + address internal _bob = makeAddr("bob"); + address internal _charlie = makeAddr("charlie"); + address internal _david = makeAddr("david"); + + address internal _excessDestination = makeAddr("excessDestination"); + address internal _migrationAdmin = makeAddr("migrationAdmin"); + + address[] internal _accounts = [_alice, _bob, _charlie, _david]; + + uint128 internal _currentIndex; + + MockEarnerManager internal _earnerManager; + MockM internal _mToken; + MockRegistrar internal _registrar; + SmartMTokenHarness internal _implementation; + SmartMTokenHarness internal _smartMToken; + + function setUp() external { + _registrar = new MockRegistrar(); + + _mToken = new MockM(); + _mToken.setCurrentIndex(_EXP_SCALED_ONE); + + _earnerManager = new MockEarnerManager(); + + _implementation = new SmartMTokenHarness( + address(_mToken), + address(_registrar), + address(_earnerManager), + _excessDestination, + _migrationAdmin + ); + + _smartMToken = SmartMTokenHarness(address(new Proxy(address(_implementation)))); + + _mToken.setCurrentIndex(_currentIndex = 1_100000068703); + } + + /* ============ constructor ============ */ + function test_constructor() external view { + assertEq(_smartMToken.migrationAdmin(), _migrationAdmin); + assertEq(_smartMToken.mToken(), address(_mToken)); + assertEq(_smartMToken.registrar(), address(_registrar)); + assertEq(_smartMToken.excessDestination(), _excessDestination); + assertEq(_smartMToken.name(), "Smart M by M^0"); + assertEq(_smartMToken.symbol(), "MSMART"); + assertEq(_smartMToken.decimals(), 6); + assertEq(_smartMToken.implementation(), address(_implementation)); + } + + function test_constructor_zeroMToken() external { + vm.expectRevert(ISmartMToken.ZeroMToken.selector); + new SmartMTokenHarness(address(0), address(0), address(0), address(0), address(0)); + } + + function test_constructor_zeroRegistrar() external { + vm.expectRevert(ISmartMToken.ZeroRegistrar.selector); + new SmartMTokenHarness(address(_mToken), address(0), address(0), address(0), address(0)); + } + + function test_constructor_zeroEarnerManager() external { + vm.expectRevert(ISmartMToken.ZeroEarnerManager.selector); + new SmartMTokenHarness(address(_mToken), address(_registrar), address(0), address(0), address(0)); + } + + function test_constructor_zeroExcessDestination() external { + vm.expectRevert(ISmartMToken.ZeroExcessDestination.selector); + new SmartMTokenHarness(address(_mToken), address(_registrar), address(_earnerManager), address(0), address(0)); + } + + function test_constructor_zeroMigrationAdmin() external { + vm.expectRevert(ISmartMToken.ZeroMigrationAdmin.selector); + new SmartMTokenHarness( + address(_mToken), + address(_registrar), + address(_earnerManager), + _excessDestination, + address(0) + ); + } + + function test_constructor_zeroImplementation() external { + vm.expectRevert(); + SmartMTokenHarness(address(new Proxy(address(0)))); + } + + /* ============ _wrap ============ */ + function test_internalWrap_insufficientAmount() external { + vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, 0)); + + _smartMToken.internalWrap(_alice, _alice, 0); + } + + function test_internalWrap_invalidRecipient() external { + _mToken.setBalanceOf(_alice, 1_000); + + vm.expectRevert(ISmartMToken.ZeroAccount.selector); + + _smartMToken.internalWrap(_alice, address(0), 1_000); + } + + function test_internalWrap_toNonEarner() external { + _mToken.setBalanceOf(_alice, 1_000); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 1_000); + + assertEq(_smartMToken.internalWrap(_alice, _alice, 1_000), 1_000); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + assertEq(_smartMToken.totalNonEarningSupply(), 1_000); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 0); + } + + function test_internalWrap_toEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 0, _EXP_SCALED_ONE, false, false); + + _mToken.setBalanceOf(_alice, 1_002); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 999); + + assertEq(_smartMToken.internalWrap(_alice, _alice, 999), 999); + + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 999); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 909); + assertEq(_smartMToken.totalEarningSupply(), 999); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 1); + + assertEq(_smartMToken.internalWrap(_alice, _alice, 1), 1); + + // No change due to principal round down on wrap. + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 1_000); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 910); + assertEq(_smartMToken.totalEarningSupply(), 1_000); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 2); + + assertEq(_smartMToken.internalWrap(_alice, _alice, 2), 2); + + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 1_002); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 912); + assertEq(_smartMToken.totalEarningSupply(), 1_002); + } + + /* ============ wrap ============ */ + function test_wrap_invalidAmount() external { + vm.expectRevert(UIntMath.InvalidUInt240.selector); + + vm.prank(_alice); + _smartMToken.wrap(_alice, uint256(type(uint240).max) + 1); + } + + function testFuzz_wrap( + bool earningEnabled_, + bool accountEarning_, + uint240 balance_, + uint240 wrapAmount_, + uint128 accountIndex_, + uint128 currentIndex_ + ) external { + accountEarning_ = earningEnabled_ && accountEarning_; + + if (earningEnabled_) { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _smartMToken.enableEarning(); + } + + accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); + + if (accountEarning_) { + _smartMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); + _smartMToken.setTotalEarningSupply(balance_); + + _smartMToken.setPrincipalOfTotalEarningSupply( + IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) + ); + } else { + _smartMToken.setAccountOf(_alice, balance_); + _smartMToken.setTotalNonEarningSupply(balance_); + } + + currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); + wrapAmount_ = uint240(bound(wrapAmount_, 0, _getMaxAmount(currentIndex_) - balance_)); + + _mToken.setCurrentIndex(_currentIndex = currentIndex_); + _mToken.setBalanceOf(_alice, wrapAmount_); + + uint240 accruedYield_ = _smartMToken.accruedYieldOf(_alice); + + if (wrapAmount_ == 0) { + vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); + } else { + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, wrapAmount_); + } + + vm.startPrank(_alice); + _smartMToken.wrap(_alice, wrapAmount_); + + if (wrapAmount_ == 0) return; + + assertEq(_smartMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); + + assertEq( + accountEarning_ ? _smartMToken.totalEarningSupply() : _smartMToken.totalNonEarningSupply(), + _smartMToken.balanceOf(_alice) + ); + } + + /* ============ wrap entire balance ============ */ + function test_wrap_entireBalance_invalidAmount() external { + _mToken.setBalanceOf(_alice, uint256(type(uint240).max) + 1); + + vm.expectRevert(UIntMath.InvalidUInt240.selector); + + vm.prank(_alice); + _smartMToken.wrap(_alice, uint256(type(uint240).max) + 1); + } + + function testFuzz_wrap_entireBalance( + bool earningEnabled_, + bool accountEarning_, + uint240 balance_, + uint240 wrapAmount_, + uint128 accountIndex_, + uint128 currentIndex_ + ) external { + accountEarning_ = earningEnabled_ && accountEarning_; + + if (earningEnabled_) { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _smartMToken.enableEarning(); + } + + accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); + + if (accountEarning_) { + _smartMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); + _smartMToken.setTotalEarningSupply(balance_); + + _smartMToken.setPrincipalOfTotalEarningSupply( + IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) + ); + } else { + _smartMToken.setAccountOf(_alice, balance_); + _smartMToken.setTotalNonEarningSupply(balance_); + } + + currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); + wrapAmount_ = uint240(bound(wrapAmount_, 0, _getMaxAmount(currentIndex_) - balance_)); + + _mToken.setCurrentIndex(_currentIndex = currentIndex_); + _mToken.setBalanceOf(_alice, wrapAmount_); + + uint240 accruedYield_ = _smartMToken.accruedYieldOf(_alice); + + if (wrapAmount_ == 0) { + vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); + } else { + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, wrapAmount_); + } + + vm.startPrank(_alice); + _smartMToken.wrap(_alice); + + if (wrapAmount_ == 0) return; + + assertEq(_smartMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); + + assertEq( + accountEarning_ ? _smartMToken.totalEarningSupply() : _smartMToken.totalNonEarningSupply(), + _smartMToken.balanceOf(_alice) + ); + } + + /* ============ wrapWithPermit vrs ============ */ + function test_wrapWithPermit_vrs_invalidAmount() external { + vm.expectRevert(UIntMath.InvalidUInt240.selector); + + vm.prank(_alice); + _smartMToken.wrapWithPermit(_alice, uint256(type(uint240).max) + 1, 0, 0, bytes32(0), bytes32(0)); + } + + function testFuzz_wrapWithPermit_vrs( + bool earningEnabled_, + bool accountEarning_, + uint240 balance_, + uint240 wrapAmount_, + uint128 accountIndex_, + uint128 currentIndex_ + ) external { + accountEarning_ = earningEnabled_ && accountEarning_; + + if (earningEnabled_) { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _smartMToken.enableEarning(); + } + + accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); + + if (accountEarning_) { + _smartMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); + _smartMToken.setTotalEarningSupply(balance_); + + _smartMToken.setPrincipalOfTotalEarningSupply( + IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) + ); + } else { + _smartMToken.setAccountOf(_alice, balance_); + _smartMToken.setTotalNonEarningSupply(balance_); + } + + currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); + wrapAmount_ = uint240(bound(wrapAmount_, 0, _getMaxAmount(currentIndex_) - balance_)); + + _mToken.setCurrentIndex(_currentIndex = currentIndex_); + _mToken.setBalanceOf(_alice, wrapAmount_); + + uint240 accruedYield_ = _smartMToken.accruedYieldOf(_alice); + + if (wrapAmount_ == 0) { + vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); + } else { + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, wrapAmount_); + } + + vm.startPrank(_alice); + _smartMToken.wrapWithPermit(_alice, wrapAmount_, 0, 0, bytes32(0), bytes32(0)); + + if (wrapAmount_ == 0) return; + + assertEq(_smartMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); + + assertEq( + accountEarning_ ? _smartMToken.totalEarningSupply() : _smartMToken.totalNonEarningSupply(), + _smartMToken.balanceOf(_alice) + ); + } + + /* ============ wrapWithPermit signature ============ */ + function test_wrapWithPermit_signature_invalidAmount() external { + vm.expectRevert(UIntMath.InvalidUInt240.selector); + + vm.prank(_alice); + _smartMToken.wrapWithPermit(_alice, uint256(type(uint240).max) + 1, 0, hex""); + } + + function testFuzz_wrapWithPermit_signature( + bool earningEnabled_, + bool accountEarning_, + uint240 balance_, + uint240 wrapAmount_, + uint128 accountIndex_, + uint128 currentIndex_ + ) external { + accountEarning_ = earningEnabled_ && accountEarning_; + + if (earningEnabled_) { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _smartMToken.enableEarning(); + } + + accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); + + if (accountEarning_) { + _smartMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); + _smartMToken.setTotalEarningSupply(balance_); + + _smartMToken.setPrincipalOfTotalEarningSupply( + IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) + ); + } else { + _smartMToken.setAccountOf(_alice, balance_); + _smartMToken.setTotalNonEarningSupply(balance_); + } + + currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); + wrapAmount_ = uint240(bound(wrapAmount_, 0, _getMaxAmount(currentIndex_) - balance_)); + + _mToken.setCurrentIndex(_currentIndex = currentIndex_); + _mToken.setBalanceOf(_alice, wrapAmount_); + + uint240 accruedYield_ = _smartMToken.accruedYieldOf(_alice); + + if (wrapAmount_ == 0) { + vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); + } else { + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, wrapAmount_); + } + + vm.startPrank(_alice); + _smartMToken.wrapWithPermit(_alice, wrapAmount_, 0, hex""); + + if (wrapAmount_ == 0) return; + + assertEq(_smartMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); + + assertEq( + accountEarning_ ? _smartMToken.totalEarningSupply() : _smartMToken.totalNonEarningSupply(), + _smartMToken.balanceOf(_alice) + ); + } + + /* ============ _unwrap ============ */ + function test_internalUnwrap_insufficientAmount() external { + vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, 0)); + + _smartMToken.internalUnwrap(_alice, _alice, 0); + } + + function test_internalUnwrap_insufficientBalance_fromNonEarner() external { + _smartMToken.setAccountOf(_alice, 999); + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.InsufficientBalance.selector, _alice, 999, 1_000)); + _smartMToken.internalUnwrap(_alice, _alice, 1_000); + } + + function test_internalUnwrap_insufficientBalance_fromEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 999, _currentIndex, false, false); + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.InsufficientBalance.selector, _alice, 999, 1_000)); + _smartMToken.internalUnwrap(_alice, _alice, 1_000); + } + + function test_internalUnwrap_fromNonEarner() external { + _smartMToken.setTotalNonEarningSupply(1_000); + + _smartMToken.setAccountOf(_alice, 1_000); + + _mToken.setBalanceOf(address(_smartMToken), 1_000); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, address(0), 500); + + assertEq(_smartMToken.internalUnwrap(_alice, _alice, 500), 500); + + assertEq(_smartMToken.balanceOf(_alice), 500); + assertEq(_smartMToken.totalNonEarningSupply(), 500); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 0); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, address(0), 500); + + assertEq(_smartMToken.internalUnwrap(_alice, _alice, 500), 500); + + assertEq(_smartMToken.balanceOf(_alice), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 0); + } + + function test_internalUnwrap_fromEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setPrincipalOfTotalEarningSupply(909); + _smartMToken.setTotalEarningSupply(1_000); + + _smartMToken.setAccountOf(_alice, 1_000, _currentIndex, false, false); + + _mToken.setBalanceOf(address(_smartMToken), 1_000); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, address(0), 1); + + assertEq(_smartMToken.internalUnwrap(_alice, _alice, 1), 0); + + // Change due to principal round up on unwrap. + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 999); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalEarningSupply(), 999); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, address(0), 999); + + assertEq(_smartMToken.internalUnwrap(_alice, _alice, 999), 998); + + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalEarningSupply(), 0); + } + + /* ============ unwrap ============ */ + function test_unwrap_invalidAmount() external { + vm.expectRevert(UIntMath.InvalidUInt240.selector); + + vm.prank(_alice); + _smartMToken.unwrap(_alice, uint256(type(uint240).max) + 1); + } + + function testFuzz_unwrap( + bool earningEnabled_, + bool accountEarning_, + uint240 balance_, + uint240 unwrapAmount_, + uint128 accountIndex_, + uint128 currentIndex_ + ) external { + accountEarning_ = earningEnabled_ && accountEarning_; + + if (earningEnabled_) { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _smartMToken.enableEarning(); + } + + accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); + + if (accountEarning_) { + _smartMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); + _smartMToken.setTotalEarningSupply(balance_); + + _smartMToken.setPrincipalOfTotalEarningSupply( + IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) + ); + } else { + _smartMToken.setAccountOf(_alice, balance_); + _smartMToken.setTotalNonEarningSupply(balance_); + } + + currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); + unwrapAmount_ = uint240(bound(unwrapAmount_, 0, 2 * balance_)); + + _mToken.setCurrentIndex(_currentIndex = currentIndex_); + + uint240 accruedYield_ = _smartMToken.accruedYieldOf(_alice); + + _mToken.setBalanceOf(address(_smartMToken), balance_ + accruedYield_); + + if (unwrapAmount_ == 0) { + vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); + } else if (unwrapAmount_ > balance_ + accruedYield_) { + vm.expectRevert( + abi.encodeWithSelector( + ISmartMToken.InsufficientBalance.selector, + _alice, + balance_ + accruedYield_, + unwrapAmount_ + ) + ); + } else { + vm.expectEmit(); + emit IERC20.Transfer(_alice, address(0), unwrapAmount_); + } + + vm.startPrank(_alice); + _smartMToken.unwrap(_alice, unwrapAmount_); + + if ((unwrapAmount_ == 0) || (unwrapAmount_ > balance_ + accruedYield_)) return; + + assertEq(_smartMToken.balanceOf(_alice), balance_ + accruedYield_ - unwrapAmount_); + + assertEq( + accountEarning_ ? _smartMToken.totalEarningSupply() : _smartMToken.totalNonEarningSupply(), + _smartMToken.balanceOf(_alice) + ); + } + + /* ============ unwrap entire balance ============ */ + function testFuzz_unwrap_entireBalance( + bool earningEnabled_, + bool accountEarning_, + uint240 balance_, + uint128 accountIndex_, + uint128 currentIndex_ + ) external { + accountEarning_ = earningEnabled_ && accountEarning_; + + if (earningEnabled_) { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _smartMToken.enableEarning(); + } + + accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); + + if (accountEarning_) { + _smartMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); + _smartMToken.setTotalEarningSupply(balance_); + + _smartMToken.setPrincipalOfTotalEarningSupply( + IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) + ); + } else { + _smartMToken.setAccountOf(_alice, balance_); + _smartMToken.setTotalNonEarningSupply(balance_); + } + + currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); + + _mToken.setCurrentIndex(_currentIndex = currentIndex_); + + uint240 accruedYield_ = _smartMToken.accruedYieldOf(_alice); + + _mToken.setBalanceOf(address(_smartMToken), balance_ + accruedYield_); + + if (balance_ + accruedYield_ == 0) { + vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); + } else { + vm.expectEmit(); + emit IERC20.Transfer(_alice, address(0), balance_ + accruedYield_); + } + + vm.startPrank(_alice); + _smartMToken.unwrap(_alice); + + if (balance_ + accruedYield_ == 0) return; + + assertEq(_smartMToken.balanceOf(_alice), 0); + + assertEq(accountEarning_ ? _smartMToken.totalEarningSupply() : _smartMToken.totalNonEarningSupply(), 0); + } + + /* ============ claimFor ============ */ + function test_claimFor_nonEarner() external { + _smartMToken.setAccountOf(_alice, 1_000); + + vm.prank(_alice); + assertEq(_smartMToken.claimFor(_alice), 0); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + } + + function test_claimFor_earner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, false, false); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + + vm.expectEmit(); + emit ISmartMToken.Claimed(_alice, _alice, 100); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 100); + + assertEq(_smartMToken.claimFor(_alice), 100); + + assertEq(_smartMToken.balanceOf(_alice), 1_100); + } + + function test_claimFor_earner_withOverrideRecipient() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _registrar.set( + keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), + bytes32(uint256(uint160(_bob))) + ); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, false, false); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + + vm.expectEmit(); + emit ISmartMToken.Claimed(_alice, _bob, 100); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 100); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 100); + + assertEq(_smartMToken.claimFor(_alice), 100); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + assertEq(_smartMToken.balanceOf(_bob), 100); + } + + function test_claimFor_earner_withFee() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, true, false); + + _earnerManager.setEarnerDetails(_alice, true, 1_500, _bob); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + + vm.expectEmit(); + emit ISmartMToken.Claimed(_alice, _alice, 100); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 100); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 15); + + assertEq(_smartMToken.claimFor(_alice), 100); + + assertEq(_smartMToken.balanceOf(_alice), 1_085); + assertEq(_smartMToken.balanceOf(_bob), 15); + } + + function test_claimFor_earner_withFeeAboveOneHundredPercent() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, true, false); + + _earnerManager.setEarnerDetails(_alice, true, type(uint16).max, _bob); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + + vm.expectEmit(); + emit ISmartMToken.Claimed(_alice, _alice, 100); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 100); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 100); + + assertEq(_smartMToken.claimFor(_alice), 100); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + assertEq(_smartMToken.balanceOf(_bob), 100); + } + + function test_claimFor_earner_withOverrideRecipientAndFee() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _registrar.set( + keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), + bytes32(uint256(uint160(_charlie))) + ); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, true, false); + + _earnerManager.setEarnerDetails(_alice, true, 1_500, _bob); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + + vm.expectEmit(); + emit ISmartMToken.Claimed(_alice, _charlie, 100); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, 100); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 15); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _charlie, 85); + + assertEq(_smartMToken.claimFor(_alice), 100); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + assertEq(_smartMToken.balanceOf(_bob), 15); + assertEq(_smartMToken.balanceOf(_charlie), 85); + } + + function testFuzz_claimFor( + uint240 balance_, + uint128 accountIndex_, + uint128 index_, + bool claimOverride_, + uint16 feeRate_ + ) external { + accountIndex_ = uint128(bound(index_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); + index_ = uint128(bound(index_, accountIndex_, 10 * _EXP_SCALED_ONE)); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + if (claimOverride_) { + _registrar.set( + keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), + bytes32(uint256(uint160(_charlie))) + ); + } + + _smartMToken.enableEarning(); + + _smartMToken.setTotalEarningSupply(balance_); + + _smartMToken.setAccountOf(_alice, balance_, accountIndex_, feeRate_ != 0, false); + + if (feeRate_ != 0) { + _earnerManager.setEarnerDetails(_alice, true, feeRate_, _bob); + } + + _mToken.setCurrentIndex(index_); + + uint240 accruedYield_ = _smartMToken.accruedYieldOf(_alice); + + if (accruedYield_ != 0) { + vm.expectEmit(); + emit ISmartMToken.Claimed(_alice, claimOverride_ ? _charlie : _alice, accruedYield_); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), _alice, accruedYield_); + } + + uint240 fee_ = (accruedYield_ * (feeRate_ > _ONE_HUNDRED_PERCENT ? _ONE_HUNDRED_PERCENT : feeRate_)) / + _ONE_HUNDRED_PERCENT; + + if (fee_ != 0) { + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, fee_); + } + + if (claimOverride_ && (accruedYield_ - fee_ != 0)) { + vm.expectEmit(); + emit IERC20.Transfer(_alice, _charlie, accruedYield_ - fee_); + } + + assertEq(_smartMToken.claimFor(_alice), accruedYield_); + + assertEq( + _smartMToken.totalSupply(), + _smartMToken.balanceOf(_alice) + _smartMToken.balanceOf(_bob) + _smartMToken.balanceOf(_charlie) + ); + } + + /* ============ claimExcess ============ */ + function testFuzz_claimExcess( + uint128 index_, + uint240 totalNonEarningSupply_, + uint112 principalOfTotalEarningSupply_, + uint240 mBalance_ + ) external { + index_ = uint128(bound(index_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + + totalNonEarningSupply_ = uint240(bound(totalNonEarningSupply_, 0, _getMaxAmount(index_))); + + uint240 totalEarningSupply_ = uint112(bound(principalOfTotalEarningSupply_, 0, _getMaxAmount(index_))); + + principalOfTotalEarningSupply_ = uint112(totalEarningSupply_ / index_); + + mBalance_ = uint240(bound(mBalance_, totalNonEarningSupply_ + totalEarningSupply_, type(uint240).max)); + + _mToken.setBalanceOf(address(_smartMToken), mBalance_); + _smartMToken.setTotalNonEarningSupply(totalNonEarningSupply_); + _smartMToken.setPrincipalOfTotalEarningSupply(principalOfTotalEarningSupply_); + + _mToken.setCurrentIndex(index_); + + uint240 expectedExcess_ = _smartMToken.excess(); + + vm.expectCall( + address(_mToken), + abi.encodeCall(_mToken.transfer, (_smartMToken.excessDestination(), expectedExcess_)) + ); + + vm.expectEmit(); + emit ISmartMToken.ExcessClaimed(expectedExcess_); + + assertEq(_smartMToken.claimExcess(), expectedExcess_); + assertEq(_smartMToken.excess(), 0); + } + + /* ============ transfer ============ */ + function test_transfer_invalidRecipient() external { + _smartMToken.setAccountOf(_alice, 1_000); + + vm.expectRevert(ISmartMToken.ZeroAccount.selector); + + vm.prank(_alice); + _smartMToken.transfer(address(0), 1_000); + } + + function test_transfer_insufficientBalance_fromNonEarner_toNonEarner() external { + _smartMToken.setAccountOf(_alice, 999); + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.InsufficientBalance.selector, _alice, 999, 1_000)); + vm.prank(_alice); + _smartMToken.transfer(_bob, 1_000); + } + + function test_transfer_insufficientBalance_fromEarner_toNonEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 999, _currentIndex, false, false); + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.InsufficientBalance.selector, _alice, 999, 1_000)); + vm.prank(_alice); + _smartMToken.transfer(_bob, 1_000); + } + + function test_transfer_fromNonEarner_toNonEarner() external { + _smartMToken.setTotalNonEarningSupply(1_500); + + _smartMToken.setAccountOf(_alice, 1_000); + _smartMToken.setAccountOf(_bob, 500); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 500); + + vm.prank(_alice); + _smartMToken.transfer(_bob, 500); + + assertEq(_smartMToken.balanceOf(_alice), 500); + + assertEq(_smartMToken.balanceOf(_bob), 1_000); + + assertEq(_smartMToken.totalNonEarningSupply(), 1_500); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 0); + } + + function testFuzz_transfer_fromNonEarner_toNonEarner( + uint256 supply_, + uint256 aliceBalance_, + uint256 transferAmount_ + ) external { + supply_ = bound(supply_, 1, type(uint112).max); + aliceBalance_ = bound(aliceBalance_, 1, supply_); + transferAmount_ = bound(transferAmount_, 1, aliceBalance_); + uint256 bobBalance = supply_ - aliceBalance_; + + _smartMToken.setTotalNonEarningSupply(supply_); + + _smartMToken.setAccountOf(_alice, aliceBalance_); + _smartMToken.setAccountOf(_bob, bobBalance); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, transferAmount_); + + vm.prank(_alice); + _smartMToken.transfer(_bob, transferAmount_); + + assertEq(_smartMToken.balanceOf(_alice), aliceBalance_ - transferAmount_); + assertEq(_smartMToken.balanceOf(_bob), bobBalance + transferAmount_); + + assertEq(_smartMToken.totalNonEarningSupply(), supply_); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 0); + } + + function test_transfer_fromEarner_toNonEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setPrincipalOfTotalEarningSupply(909); + _smartMToken.setTotalEarningSupply(1_000); + + _smartMToken.setTotalNonEarningSupply(500); + + _smartMToken.setAccountOf(_alice, 1_000, _currentIndex, false, false); + _smartMToken.setAccountOf(_bob, 500); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 500); + + vm.prank(_alice); + _smartMToken.transfer(_bob, 500); + + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 500); + + assertEq(_smartMToken.balanceOf(_bob), 1_000); + + assertEq(_smartMToken.totalNonEarningSupply(), 1_000); + assertEq(_smartMToken.totalEarningSupply(), 500); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 1); + + vm.prank(_alice); + _smartMToken.transfer(_bob, 1); + + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 499); + + assertEq(_smartMToken.balanceOf(_bob), 1_001); + + assertEq(_smartMToken.totalNonEarningSupply(), 1_001); + assertEq(_smartMToken.totalEarningSupply(), 499); + } + + function test_transfer_fromNonEarner_toEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setPrincipalOfTotalEarningSupply(454); + _smartMToken.setTotalEarningSupply(500); + + _smartMToken.setTotalNonEarningSupply(1_000); + + _smartMToken.setAccountOf(_alice, 1_000); + _smartMToken.setAccountOf(_bob, 500, _currentIndex, false, false); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 500); + + vm.prank(_alice); + _smartMToken.transfer(_bob, 500); + + assertEq(_smartMToken.balanceOf(_alice), 500); + + assertEq(_smartMToken.lastIndexOf(_bob), _currentIndex); + assertEq(_smartMToken.balanceOf(_bob), 1_000); + + assertEq(_smartMToken.totalNonEarningSupply(), 500); + assertEq(_smartMToken.totalEarningSupply(), 1_000); + } + + function test_transfer_fromEarner_toEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setPrincipalOfTotalEarningSupply(1_363); + _smartMToken.setTotalEarningSupply(1_500); + + _smartMToken.setAccountOf(_alice, 1_000, _currentIndex, false, false); + _smartMToken.setAccountOf(_bob, 500, _currentIndex, false, false); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, 500); + + vm.prank(_alice); + _smartMToken.transfer(_bob, 500); + + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 500); + + assertEq(_smartMToken.lastIndexOf(_bob), _currentIndex); + assertEq(_smartMToken.balanceOf(_bob), 1_000); + + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalEarningSupply(), 1_500); + } + + function test_transfer_nonEarnerToSelf() external { + _smartMToken.setTotalNonEarningSupply(1_000); + + _smartMToken.setAccountOf(_alice, 1_000); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _alice, 500); + + vm.prank(_alice); + _smartMToken.transfer(_alice, 500); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + + assertEq(_smartMToken.totalNonEarningSupply(), 1_000); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.principalOfTotalEarningSupply(), 0); + } + + function test_transfer_earnerToSelf() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setPrincipalOfTotalEarningSupply(909); + _smartMToken.setTotalEarningSupply(1_000); + + _smartMToken.setAccountOf(_alice, 1_000, _currentIndex, false, false); + + _mToken.setCurrentIndex((_currentIndex * 5) / 3); // 1_833333447838 + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + assertEq(_smartMToken.accruedYieldOf(_alice), 666); + + vm.expectEmit(); + emit IERC20.Transfer(_alice, _alice, 500); + + vm.prank(_alice); + _smartMToken.transfer(_alice, 500); + + assertEq(_smartMToken.balanceOf(_alice), 1_666); + } + + function testFuzz_transfer( + bool earningEnabled_, + bool aliceEarning_, + bool bobEarning_, + uint240 aliceBalance_, + uint240 bobBalance_, + uint128 aliceIndex_, + uint128 bobIndex_, + uint128 currentIndex_, + uint240 amount_ + ) external { + aliceEarning_ = earningEnabled_ && aliceEarning_; + bobEarning_ = earningEnabled_ && bobEarning_; + + if (earningEnabled_) { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _smartMToken.enableEarning(); + } + + aliceIndex_ = uint128(bound(aliceIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + aliceBalance_ = uint240(bound(aliceBalance_, 0, _getMaxAmount(aliceIndex_) / 4)); + + if (aliceEarning_) { + _smartMToken.setAccountOf(_alice, aliceBalance_, aliceIndex_, false, false); + _smartMToken.setTotalEarningSupply(aliceBalance_); + + _smartMToken.setPrincipalOfTotalEarningSupply( + IndexingMath.getPrincipalAmountRoundedDown(aliceBalance_, aliceIndex_) + ); + } else { + _smartMToken.setAccountOf(_alice, aliceBalance_); + _smartMToken.setTotalNonEarningSupply(aliceBalance_); + } + + bobIndex_ = uint128(bound(bobIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + bobBalance_ = uint240(bound(bobBalance_, 0, _getMaxAmount(bobIndex_) / 4)); + + if (bobEarning_) { + _smartMToken.setAccountOf(_bob, bobBalance_, bobIndex_, false, false); + _smartMToken.setTotalEarningSupply(_smartMToken.totalEarningSupply() + bobBalance_); + + _smartMToken.setPrincipalOfTotalEarningSupply( + IndexingMath.getPrincipalAmountRoundedDown( + _smartMToken.totalEarningSupply() + bobBalance_, + aliceIndex_ > bobIndex_ ? aliceIndex_ : bobIndex_ + ) + ); + } else { + _smartMToken.setAccountOf(_bob, bobBalance_); + _smartMToken.setTotalNonEarningSupply(_smartMToken.totalNonEarningSupply() + bobBalance_); + } + + currentIndex_ = uint128( + bound(currentIndex_, aliceIndex_ > bobIndex_ ? aliceIndex_ : bobIndex_, 10 * _EXP_SCALED_ONE) + ); + + _mToken.setCurrentIndex(_currentIndex = currentIndex_); + + uint240 aliceAccruedYield_ = _smartMToken.accruedYieldOf(_alice); + uint240 bobAccruedYield_ = _smartMToken.accruedYieldOf(_bob); + + amount_ = uint240(bound(amount_, 0, aliceBalance_ + aliceAccruedYield_)); + + if (amount_ > aliceBalance_ + aliceAccruedYield_) { + vm.expectRevert( + abi.encodeWithSelector( + ISmartMToken.InsufficientBalance.selector, + _alice, + aliceBalance_ + aliceAccruedYield_, + amount_ + ) + ); + } else { + vm.expectEmit(); + emit IERC20.Transfer(_alice, _bob, amount_); + } + + vm.prank(_alice); + _smartMToken.transfer(_bob, amount_); + + if (amount_ > aliceBalance_ + aliceAccruedYield_) return; + + assertEq(_smartMToken.balanceOf(_alice), aliceBalance_ + aliceAccruedYield_ - amount_); + assertEq(_smartMToken.balanceOf(_bob), bobBalance_ + bobAccruedYield_ + amount_); + + if (aliceEarning_ && bobEarning_) { + assertEq( + _smartMToken.totalEarningSupply(), + aliceBalance_ + aliceAccruedYield_ + bobBalance_ + bobAccruedYield_ + ); + } else if (aliceEarning_) { + assertEq(_smartMToken.totalEarningSupply(), aliceBalance_ + aliceAccruedYield_ - amount_); + assertEq(_smartMToken.totalNonEarningSupply(), bobBalance_ + bobAccruedYield_ + amount_); + } else if (bobEarning_) { + assertEq(_smartMToken.totalNonEarningSupply(), aliceBalance_ + aliceAccruedYield_ - amount_); + assertEq(_smartMToken.totalEarningSupply(), bobBalance_ + bobAccruedYield_ + amount_); + } else { + assertEq( + _smartMToken.totalNonEarningSupply(), + aliceBalance_ + aliceAccruedYield_ + bobBalance_ + bobAccruedYield_ + ); + } + } + + /* ============ startEarningFor ============ */ + function test_startEarningFor_earningIsDisabled() external { + vm.expectRevert(ISmartMToken.EarningIsDisabled.selector); + _smartMToken.startEarningFor(_alice); + } + + function test_startEarningFor_notApprovedEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.NotApprovedEarner.selector, _alice)); + _smartMToken.startEarningFor(_alice); + } + + function test_startEarning_overflow() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + uint256 aliceBalance_ = uint256(type(uint112).max) + 20; + + _mToken.setCurrentIndex(_currentIndex = _EXP_SCALED_ONE); + + _smartMToken.setTotalNonEarningSupply(aliceBalance_); + + _smartMToken.setAccountOf(_alice, aliceBalance_); + + _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); + + vm.expectRevert(UIntMath.InvalidUInt112.selector); + _smartMToken.startEarningFor(_alice); + } + + function test_startEarningFor() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setTotalNonEarningSupply(1_000); + + _smartMToken.setAccountOf(_alice, 1_000); + + _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); + + vm.expectEmit(); + emit ISmartMToken.StartedEarning(_alice); + + _smartMToken.startEarningFor(_alice); + + assertEq(_smartMToken.isEarning(_alice), true); + assertEq(_smartMToken.lastIndexOf(_alice), _currentIndex); + assertEq(_smartMToken.balanceOf(_alice), 1000); + + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalEarningSupply(), 1_000); + } + + function testFuzz_startEarningFor(uint240 balance_, uint128 index_) external { + balance_ = uint240(bound(balance_, 0, _getMaxAmount(_currentIndex))); + index_ = uint128(bound(index_, _currentIndex, 10 * _EXP_SCALED_ONE)); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setTotalNonEarningSupply(balance_); + + _smartMToken.setAccountOf(_alice, balance_); + + _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); + + _mToken.setCurrentIndex(index_); + + vm.expectEmit(); + emit ISmartMToken.StartedEarning(_alice); + + _smartMToken.startEarningFor(_alice); + + assertEq(_smartMToken.isEarning(_alice), true); + assertEq(_smartMToken.lastIndexOf(_alice), index_); + assertEq(_smartMToken.balanceOf(_alice), balance_); + + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalEarningSupply(), balance_); + } + + /* ============ startEarningFor batch ============ */ + function test_startEarningFor_batch_earningIsDisabled() external { + vm.expectRevert(ISmartMToken.EarningIsDisabled.selector); + _smartMToken.startEarningFor(new address[](2)); + } + + function test_startEarningFor_batch_notApprovedEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); + + _smartMToken.enableEarning(); + + address[] memory accounts_ = new address[](2); + accounts_[0] = _alice; + accounts_[1] = _bob; + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.NotApprovedEarner.selector, _bob)); + _smartMToken.startEarningFor(accounts_); + } + + function test_startEarningFor_batch() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); + _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); + + _smartMToken.enableEarning(); + + address[] memory accounts_ = new address[](2); + accounts_[0] = _alice; + accounts_[1] = _bob; + + vm.expectEmit(); + emit ISmartMToken.StartedEarning(_alice); + + vm.expectEmit(); + emit ISmartMToken.StartedEarning(_bob); + + _smartMToken.startEarningFor(accounts_); + } + + /* ============ stopEarningFor ============ */ + function test_stopEarningFor_isApprovedEarner() external { + _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.IsApprovedEarner.selector, _alice)); + _smartMToken.stopEarningFor(_alice); + } + + function test_stopEarningFor() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setPrincipalOfTotalEarningSupply(909); + _smartMToken.setTotalEarningSupply(1_000); + + _smartMToken.setAccountOf(_alice, 999, _currentIndex, false, false); + + vm.expectEmit(); + emit ISmartMToken.StoppedEarning(_alice); + + _smartMToken.stopEarningFor(_alice); + + assertEq(_smartMToken.balanceOf(_alice), 999); + assertEq(_smartMToken.isEarning(_alice), false); + + assertEq(_smartMToken.totalNonEarningSupply(), 999); + assertEq(_smartMToken.totalEarningSupply(), 1); + } + + function testFuzz_stopEarningFor(uint240 balance_, uint128 accountIndex_, uint128 index_) external { + accountIndex_ = uint128(bound(index_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); + balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); + index_ = uint128(bound(index_, accountIndex_, 10 * _EXP_SCALED_ONE)); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setTotalEarningSupply(balance_); + + _smartMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); + + _mToken.setCurrentIndex(index_); + + uint240 accruedYield_ = _smartMToken.accruedYieldOf(_alice); + + vm.expectEmit(); + emit ISmartMToken.StoppedEarning(_alice); + + _smartMToken.stopEarningFor(_alice); + + assertEq(_smartMToken.balanceOf(_alice), balance_ + accruedYield_); + assertEq(_smartMToken.isEarning(_alice), false); + + assertEq(_smartMToken.totalNonEarningSupply(), balance_ + accruedYield_); + assertEq(_smartMToken.totalEarningSupply(), 0); + } + + /* ============ setClaimRecipient ============ */ + function test_setClaimRecipient() external { + (, , , , bool hasClaimRecipient_) = _smartMToken.getAccountOf(_alice); + + assertFalse(hasClaimRecipient_); + assertEq(_smartMToken.getInternalClaimRecipientOf(_alice), address(0)); + + vm.prank(_alice); + _smartMToken.setClaimRecipient(_alice); + + (, , , , hasClaimRecipient_) = _smartMToken.getAccountOf(_alice); + + assertTrue(hasClaimRecipient_); + assertEq(_smartMToken.getInternalClaimRecipientOf(_alice), _alice); + + vm.prank(_alice); + _smartMToken.setClaimRecipient(_bob); + + (, , , , hasClaimRecipient_) = _smartMToken.getAccountOf(_alice); + + assertTrue(hasClaimRecipient_); + assertEq(_smartMToken.getInternalClaimRecipientOf(_alice), _bob); + + vm.prank(_alice); + _smartMToken.setClaimRecipient(address(0)); + + (, , , , hasClaimRecipient_) = _smartMToken.getAccountOf(_alice); + + assertFalse(hasClaimRecipient_); + assertEq(_smartMToken.getInternalClaimRecipientOf(_alice), address(0)); + } + + /* ============ stopEarningFor batch ============ */ + function test_stopEarningFor_batch_isApprovedEarner() external { + _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); + + address[] memory accounts_ = new address[](2); + accounts_[0] = _alice; + accounts_[1] = _bob; + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.IsApprovedEarner.selector, _bob)); + _smartMToken.stopEarningFor(accounts_); + } + + function test_stopEarningFor_batch() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 0, _currentIndex, false, false); + _smartMToken.setAccountOf(_bob, 0, _currentIndex, false, false); + + address[] memory accounts_ = new address[](2); + accounts_[0] = _alice; + accounts_[1] = _bob; + + vm.expectEmit(); + emit ISmartMToken.StoppedEarning(_alice); + + vm.expectEmit(); + emit ISmartMToken.StoppedEarning(_bob); + + _smartMToken.stopEarningFor(accounts_); + } + + /* ============ enableEarning ============ */ + function test_enableEarning_notApprovedEarner() external { + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.NotApprovedEarner.selector, address(_smartMToken))); + _smartMToken.enableEarning(); + } + + function test_enableEarning_earningCannotBeReenabled() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), false); + + _smartMToken.disableEarning(); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + vm.expectRevert(ISmartMToken.EarningCannotBeReenabled.selector); + _smartMToken.enableEarning(); + } + + function test_enableEarning() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + vm.expectEmit(); + emit ISmartMToken.EarningEnabled(_currentIndex); + + _smartMToken.enableEarning(); + } + + /* ============ disableEarning ============ */ + function test_disableEarning_earningIsDisabled() external { + vm.expectRevert(ISmartMToken.EarningIsDisabled.selector); + _smartMToken.disableEarning(); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), false); + + _smartMToken.disableEarning(); + + vm.expectRevert(ISmartMToken.EarningIsDisabled.selector); + _smartMToken.disableEarning(); + } + + function test_disableEarning_approvedEarner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + vm.expectRevert(abi.encodeWithSelector(ISmartMToken.IsApprovedEarner.selector, address(_smartMToken))); + _smartMToken.disableEarning(); + } + + function test_disableEarning() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), false); + + vm.expectEmit(); + emit ISmartMToken.EarningDisabled(_currentIndex); + + _smartMToken.disableEarning(); + } + + /* ============ balanceOf ============ */ + function test_balanceOf_nonEarner() external { + _smartMToken.setAccountOf(_alice, 500); + + assertEq(_smartMToken.balanceOf(_alice), 500); + + _smartMToken.setAccountOf(_alice, 1_000); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + } + + function test_balanceOf_earner() external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + _smartMToken.setAccountOf(_alice, 500, _EXP_SCALED_ONE, false, false); + + assertEq(_smartMToken.balanceOf(_alice), 500); + + _smartMToken.setAccountOf(_alice, 1_000); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + + _smartMToken.setLastIndexOf(_alice, 2 * _EXP_SCALED_ONE); + + assertEq(_smartMToken.balanceOf(_alice), 1_000); + } + + /* ============ claimRecipientFor ============ */ + function test_claimRecipientFor_hasClaimRecipient() external { + assertEq(_smartMToken.claimRecipientFor(_alice), address(0)); + + _smartMToken.setAccountOf(_alice, 0, 0, false, true); + _smartMToken.setInternalClaimRecipient(_alice, _bob); + + assertEq(_smartMToken.claimRecipientFor(_alice), _bob); + } + + function test_claimRecipientFor_hasClaimOverrideRecipient() external { + assertEq(_smartMToken.claimRecipientFor(_alice), address(0)); + + _registrar.set( + keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), + bytes32(uint256(uint160(_charlie))) + ); + + assertEq(_smartMToken.claimRecipientFor(_alice), _charlie); + } + + function test_claimRecipientFor_hasClaimRecipientAndOverrideRecipient() external { + assertEq(_smartMToken.claimRecipientFor(_alice), address(0)); + + _smartMToken.setAccountOf(_alice, 0, 0, false, true); + _smartMToken.setInternalClaimRecipient(_alice, _bob); + + _registrar.set( + keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), + bytes32(uint256(uint160(_charlie))) + ); + + assertEq(_smartMToken.claimRecipientFor(_alice), _bob); + } + + /* ============ totalSupply ============ */ + function test_totalSupply_onlyTotalNonEarningSupply() external { + _smartMToken.setTotalNonEarningSupply(500); + + assertEq(_smartMToken.totalSupply(), 500); + + _smartMToken.setTotalNonEarningSupply(1_000); + + assertEq(_smartMToken.totalSupply(), 1_000); + } + + function test_totalSupply_onlyTotalEarningSupply() external { + _smartMToken.setTotalEarningSupply(500); + + assertEq(_smartMToken.totalSupply(), 500); + + _smartMToken.setTotalEarningSupply(1_000); + + assertEq(_smartMToken.totalSupply(), 1_000); + } + + function test_totalSupply() external { + _smartMToken.setTotalEarningSupply(400); + + _smartMToken.setTotalNonEarningSupply(600); + + assertEq(_smartMToken.totalSupply(), 1_000); + + _smartMToken.setTotalEarningSupply(700); + + assertEq(_smartMToken.totalSupply(), 1_300); + + _smartMToken.setTotalNonEarningSupply(1_000); + + assertEq(_smartMToken.totalSupply(), 1_700); + } + + /* ============ currentIndex ============ */ + function test_currentIndex() external { + assertEq(_smartMToken.currentIndex(), 0); + + _mToken.setCurrentIndex(2 * _EXP_SCALED_ONE); + + assertEq(_smartMToken.currentIndex(), 0); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + + _smartMToken.enableEarning(); + + assertEq(_smartMToken.currentIndex(), 2 * _EXP_SCALED_ONE); + + _mToken.setCurrentIndex(3 * _EXP_SCALED_ONE); + + assertEq(_smartMToken.currentIndex(), 3 * _EXP_SCALED_ONE); + + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), false); + + _smartMToken.disableEarning(); + + assertEq(_smartMToken.currentIndex(), 3 * _EXP_SCALED_ONE); + + _mToken.setCurrentIndex(4 * _EXP_SCALED_ONE); + + assertEq(_smartMToken.currentIndex(), 3 * _EXP_SCALED_ONE); + } + + /* ============ misc ============ */ + function testFuzz_wrap_transfer_unwrap( + bool aliceIsEarning_, + uint240 aliceWrap_, + bool bobIsEarning_, + uint240 bobWrap_, + uint240 transfer_, + uint128 index_ + ) external { + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); + _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); + _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); + + _smartMToken.enableEarning(); + + _mToken.setCurrentIndex(index_ = uint128(bound(index_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE))); + + aliceWrap_ = uint240(bound(aliceWrap_, 0, _getMaxAmount(index_) / 3)); + bobWrap_ = uint240(bound(bobWrap_, 0, _getMaxAmount(index_) / 3)); + + _mToken.setBalanceOf(_alice, aliceWrap_); + _mToken.setBalanceOf(_bob, bobWrap_); + + if (aliceIsEarning_) { + _smartMToken.startEarningFor(_alice); + } + + if (aliceWrap_ != 0) { + vm.prank(_alice); + _smartMToken.wrap(_alice, aliceWrap_); + } + + _mToken.setCurrentIndex(index_ = uint128(bound(index_, index_, 10 * _EXP_SCALED_ONE))); + + if (bobIsEarning_) { + _smartMToken.startEarningFor(_bob); + } + + if (bobWrap_ != 0) { + vm.prank(_bob); + _smartMToken.wrap(_bob, bobWrap_); + } + + _mToken.setCurrentIndex(index_ = uint128(bound(index_, index_, 10 * _EXP_SCALED_ONE))); + + uint240 aliceYield_ = _smartMToken.accruedYieldOf(_alice); + uint240 bobYield_ = _smartMToken.accruedYieldOf(_bob); + + transfer_ = uint240(bound(transfer_, 0, _smartMToken.balanceWithYieldOf(_alice))); + + _mToken.setCurrentIndex(index_ = uint128(bound(index_, index_, 10 * _EXP_SCALED_ONE))); + + aliceYield_ += _smartMToken.accruedYieldOf(_alice); + + if (_smartMToken.balanceWithYieldOf(_alice) != 0) { + vm.prank(_alice); + _smartMToken.unwrap(_charlie); + } + + _mToken.setCurrentIndex(index_ = uint128(bound(index_, index_, 10 * _EXP_SCALED_ONE))); + + bobYield_ += _smartMToken.accruedYieldOf(_bob); + + if (_smartMToken.balanceWithYieldOf(_bob) != 0) { + vm.prank(_bob); + _smartMToken.unwrap(_charlie); + } + + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + + uint240 total_ = aliceWrap_ + aliceYield_ + bobWrap_ + bobYield_; + + if (total_ < 100e6) { + assertApproxEqAbs(_mToken.balanceOf(_charlie), total_, 100); + } else { + assertApproxEqRel(_mToken.balanceOf(_charlie), total_, 1e12); + } + } + + /* ============ utils ============ */ + function _getPrincipalAmountRoundedDown(uint240 presentAmount_, uint128 index_) internal pure returns (uint112) { + return IndexingMath.divide240By128Down(presentAmount_, index_); + } + + function _getPresentAmountRoundedDown(uint112 principalAmount_, uint128 index_) internal pure returns (uint240) { + return IndexingMath.multiply112By128Down(principalAmount_, index_); + } + + function _getMaxAmount(uint128 index_) internal pure returns (uint240 maxAmount_) { + return (uint240(type(uint112).max) * index_) / _EXP_SCALED_ONE; + } +} diff --git a/test/unit/Stories.t.sol b/test/unit/Stories.t.sol index 23d882d..3cbf0dd 100644 --- a/test/unit/Stories.t.sol +++ b/test/unit/Stories.t.sol @@ -4,9 +4,9 @@ pragma solidity 0.8.26; import { Test } from "../../lib/forge-std/src/Test.sol"; -import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; +import { ISmartMToken } from "../../src/interfaces/ISmartMToken.sol"; -import { WrappedMToken } from "../../src/WrappedMToken.sol"; +import { SmartMToken } from "../../src/SmartMToken.sol"; import { Proxy } from "../../src/Proxy.sol"; import { MockEarnerManager, MockM, MockRegistrar } from "../utils/Mocks.sol"; @@ -29,8 +29,8 @@ contract Tests is Test { MockEarnerManager internal _earnerManager; MockM internal _mToken; MockRegistrar internal _registrar; - WrappedMToken internal _implementation; - IWrappedMToken internal _wrappedMToken; + SmartMToken internal _implementation; + ISmartMToken internal _smartMToken; function setUp() external { _registrar = new MockRegistrar(); @@ -40,7 +40,7 @@ contract Tests is Test { _earnerManager = new MockEarnerManager(); - _implementation = new WrappedMToken( + _implementation = new SmartMToken( address(_mToken), address(_registrar), address(_earnerManager), @@ -48,389 +48,383 @@ contract Tests is Test { _migrationAdmin ); - _wrappedMToken = IWrappedMToken(address(new Proxy(address(_implementation)))); + _smartMToken = ISmartMToken(address(new Proxy(address(_implementation)))); } function test_story() external { _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); - _wrappedMToken.enableEarning(); + _smartMToken.enableEarning(); - _wrappedMToken.startEarningFor(_alice); + _smartMToken.startEarningFor(_alice); - _wrappedMToken.startEarningFor(_bob); + _smartMToken.startEarningFor(_bob); _mToken.setBalanceOf(_alice, 100_000000); vm.prank(_alice); - _wrappedMToken.wrap(_alice, 100_000000); + _smartMToken.wrap(_alice, 100_000000); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_smartMToken.balanceOf(_alice), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_alice), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 100_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalSupply(), 100_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 0); + assertEq(_smartMToken.totalEarningSupply(), 100_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalSupply(), 100_000000); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), 0); _mToken.setBalanceOf(_carol, 100_000000); vm.prank(_carol); - _wrappedMToken.wrap(_carol, 100_000000); + _smartMToken.wrap(_carol, 100_000000); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 100_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 100_000000); - assertEq(_wrappedMToken.totalSupply(), 200_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 0); + assertEq(_smartMToken.totalEarningSupply(), 100_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 100_000000); + assertEq(_smartMToken.totalSupply(), 200_000000); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), 0); _mToken.setCurrentIndex(2 * _EXP_SCALED_ONE); - _mToken.setBalanceOf(address(_wrappedMToken), 400_000000); + _mToken.setBalanceOf(address(_smartMToken), 400_000000); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 100_000000); + assertEq(_smartMToken.balanceOf(_alice), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_alice), 100_000000); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 100_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 100_000000); - assertEq(_wrappedMToken.totalSupply(), 200_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 100_000000); - assertEq(_wrappedMToken.excess(), 100_000000); + assertEq(_smartMToken.totalEarningSupply(), 100_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 100_000000); + assertEq(_smartMToken.totalSupply(), 200_000000); + assertEq(_smartMToken.totalAccruedYield(), 100_000000); + assertEq(_smartMToken.excess(), 100_000000); _mToken.setBalanceOf(_bob, 100_000000); vm.prank(_bob); - _wrappedMToken.wrap(_bob, 100_000000); + _smartMToken.wrap(_bob, 100_000000); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_smartMToken.balanceOf(_bob), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_bob), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 200_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 100_000000); - assertEq(_wrappedMToken.totalSupply(), 300_000000); + assertEq(_smartMToken.totalEarningSupply(), 200_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 100_000000); + assertEq(_smartMToken.totalSupply(), 300_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 100_000000); - assertEq(_wrappedMToken.excess(), 100_000000); + assertEq(_smartMToken.totalAccruedYield(), 100_000000); + assertEq(_smartMToken.excess(), 100_000000); _mToken.setBalanceOf(_dave, 100_000000); vm.prank(_dave); - _wrappedMToken.wrap(_dave, 100_000000); + _smartMToken.wrap(_dave, 100_000000); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 200_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 200_000000); - assertEq(_wrappedMToken.totalSupply(), 400_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 100_000000); - assertEq(_wrappedMToken.excess(), 100_000000); + assertEq(_smartMToken.totalEarningSupply(), 200_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 200_000000); + assertEq(_smartMToken.totalSupply(), 400_000000); + assertEq(_smartMToken.totalAccruedYield(), 100_000000); + assertEq(_smartMToken.excess(), 100_000000); - assertEq(_wrappedMToken.balanceOf(_alice), 100_000000); + assertEq(_smartMToken.balanceOf(_alice), 100_000000); - uint256 yield_ = _wrappedMToken.claimFor(_alice); + uint256 yield_ = _smartMToken.claimFor(_alice); assertEq(yield_, 100_000000); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_smartMToken.balanceOf(_alice), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_alice), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 300_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 200_000000); - assertEq(_wrappedMToken.totalSupply(), 500_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 100_000000); + assertEq(_smartMToken.totalEarningSupply(), 300_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 200_000000); + assertEq(_smartMToken.totalSupply(), 500_000000); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), 100_000000); _mToken.setCurrentIndex(3 * _EXP_SCALED_ONE); - _mToken.setBalanceOf(address(_wrappedMToken), 900_000000); // was 600 @ 2.0, so 900 @ 3.0 + _mToken.setBalanceOf(address(_smartMToken), 900_000000); // was 600 @ 2.0, so 900 @ 3.0 // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 100_000000); + assertEq(_smartMToken.balanceOf(_alice), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_alice), 100_000000); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 50_000000); + assertEq(_smartMToken.balanceOf(_bob), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_bob), 50_000000); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), 100_000000); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), 100_000000); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 300_000000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 200_000000); - assertEq(_wrappedMToken.totalSupply(), 500_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 150_000000); - assertEq(_wrappedMToken.excess(), 249_999999); + assertEq(_smartMToken.totalEarningSupply(), 300_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 200_000000); + assertEq(_smartMToken.totalSupply(), 500_000000); + assertEq(_smartMToken.totalAccruedYield(), 150_000000); + assertEq(_smartMToken.excess(), 249_999999); vm.prank(_alice); - _wrappedMToken.transfer(_carol, 100_000000); + _smartMToken.transfer(_carol, 100_000000); // Assert Alice (Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_smartMToken.balanceOf(_alice), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_alice), 0); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - 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(_smartMToken.totalEarningSupply(), 300_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 300_000000); + assertEq(_smartMToken.totalSupply(), 600_000000); + assertEq(_smartMToken.totalAccruedYield(), 50_000001); + assertEq(_smartMToken.excess(), 249_999999); vm.prank(_dave); - _wrappedMToken.transfer(_bob, 50_000000); + _smartMToken.transfer(_bob, 50_000000); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_smartMToken.balanceOf(_bob), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_bob), 0); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), 50_000000); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), 50_000000); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - 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(_smartMToken.totalEarningSupply(), 400_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 250_000000); + assertEq(_smartMToken.totalSupply(), 650_000000); + assertEq(_smartMToken.totalAccruedYield(), 2); + assertEq(_smartMToken.excess(), 249_999996); _mToken.setCurrentIndex(4 * _EXP_SCALED_ONE); - _mToken.setBalanceOf(address(_wrappedMToken), 1_200_000000); // was 900 @ 3.0, so 1200 @ 4.0 + _mToken.setBalanceOf(address(_smartMToken), 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(_smartMToken.balanceOf(_alice), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_alice), 66_666664); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 66_666664); + assertEq(_smartMToken.balanceOf(_bob), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_bob), 66_666664); // Assert Carol (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_carol), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), 50_000000); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), 50_000000); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - 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(_smartMToken.totalEarningSupply(), 400_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 250_000000); + assertEq(_smartMToken.totalSupply(), 650_000000); + assertEq(_smartMToken.totalAccruedYield(), 133_333336); + assertEq(_smartMToken.excess(), 416_666664); _earnerManager.setEarnerDetails(_alice, false, 0, address(0)); - _wrappedMToken.stopEarningFor(_alice); + _smartMToken.stopEarningFor(_alice); // Assert Alice (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 266_666664); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_smartMToken.balanceOf(_alice), 266_666664); + assertEq(_smartMToken.accruedYieldOf(_alice), 0); // Assert Globals - 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(_smartMToken.totalEarningSupply(), 200_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 516_666664); + assertEq(_smartMToken.totalSupply(), 716_666664); + assertEq(_smartMToken.totalAccruedYield(), 66_666672); + assertEq(_smartMToken.excess(), 416_666664); _earnerManager.setEarnerDetails(_carol, true, 0, address(0)); - _wrappedMToken.startEarningFor(_carol); + _smartMToken.startEarningFor(_carol); // Assert Carol (Earner) - assertEq(_wrappedMToken.balanceOf(_carol), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - 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(_smartMToken.totalEarningSupply(), 400_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 316_666664); + assertEq(_smartMToken.totalSupply(), 716_666664); + assertEq(_smartMToken.totalAccruedYield(), 66_666672); + assertEq(_smartMToken.excess(), 416_666664); _mToken.setCurrentIndex(5 * _EXP_SCALED_ONE); - _mToken.setBalanceOf(address(_wrappedMToken), 1_500_000000); // was 1200 @ 4.0, so 1500 @ 5.0 + _mToken.setBalanceOf(address(_smartMToken), 1_500_000000); // was 1200 @ 4.0, so 1500 @ 5.0 // Assert Alice (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 266_666664); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_smartMToken.balanceOf(_alice), 266_666664); + assertEq(_smartMToken.accruedYieldOf(_alice), 0); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 133_333330); + assertEq(_smartMToken.balanceOf(_bob), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_bob), 133_333330); // Assert Carol (Earner) - assertEq(_wrappedMToken.balanceOf(_carol), 200_000000); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 50_000000); + assertEq(_smartMToken.balanceOf(_carol), 200_000000); + assertEq(_smartMToken.accruedYieldOf(_carol), 50_000000); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), 50_000000); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), 50_000000); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - 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(_smartMToken.totalEarningSupply(), 400_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 316_666664); + assertEq(_smartMToken.totalSupply(), 716_666664); + assertEq(_smartMToken.totalAccruedYield(), 183_333340); + assertEq(_smartMToken.excess(), 599_999995); vm.prank(_alice); - _wrappedMToken.unwrap(_alice, 266_666664); + _smartMToken.unwrap(_alice, 266_666664); // Assert Alice (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_alice), 0); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 0); + assertEq(_smartMToken.balanceOf(_alice), 0); + assertEq(_smartMToken.accruedYieldOf(_alice), 0); // Assert Globals - 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(_smartMToken.totalEarningSupply(), 400_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 50_000000); + assertEq(_smartMToken.totalSupply(), 450_000000); + assertEq(_smartMToken.totalAccruedYield(), 183_333340); + assertEq(_smartMToken.excess(), 600_000000); vm.prank(_bob); - _wrappedMToken.unwrap(_bob, 333_333330); + _smartMToken.unwrap(_bob, 333_333330); // Assert Bob (Earner) - assertEq(_wrappedMToken.balanceOf(_bob), 0); - assertEq(_wrappedMToken.accruedYieldOf(_bob), 0); + assertEq(_smartMToken.balanceOf(_bob), 0); + assertEq(_smartMToken.accruedYieldOf(_bob), 0); // Assert Globals - 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(_smartMToken.totalEarningSupply(), 200_000000); + assertEq(_smartMToken.totalNonEarningSupply(), 50_000000); + assertEq(_smartMToken.totalSupply(), 250_000000); + assertEq(_smartMToken.totalAccruedYield(), 50_000010); + assertEq(_smartMToken.excess(), 600_000000); vm.prank(_carol); - _wrappedMToken.unwrap(_carol, 250_000000); + _smartMToken.unwrap(_carol, 250_000000); // Assert Carol (Earner) - assertEq(_wrappedMToken.balanceOf(_carol), 0); - assertEq(_wrappedMToken.accruedYieldOf(_carol), 0); + assertEq(_smartMToken.balanceOf(_carol), 0); + assertEq(_smartMToken.accruedYieldOf(_carol), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 50_000000); - assertEq(_wrappedMToken.totalSupply(), 50_000000); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 600_000010); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 50_000000); + assertEq(_smartMToken.totalSupply(), 50_000000); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), 600_000010); vm.prank(_dave); - _wrappedMToken.unwrap(_dave, 50_000000); + _smartMToken.unwrap(_dave, 50_000000); // Assert Dave (Non-Earner) - assertEq(_wrappedMToken.balanceOf(_dave), 0); - assertEq(_wrappedMToken.accruedYieldOf(_dave), 0); + assertEq(_smartMToken.balanceOf(_dave), 0); + assertEq(_smartMToken.accruedYieldOf(_dave), 0); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalSupply(), 0); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 600_000010); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalSupply(), 0); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), 600_000010); - _wrappedMToken.claimExcess(); + _smartMToken.claimExcess(); // Assert Globals - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalSupply(), 0); - assertEq(_wrappedMToken.totalAccruedYield(), 0); - assertEq(_wrappedMToken.excess(), 0); + assertEq(_smartMToken.totalEarningSupply(), 0); + assertEq(_smartMToken.totalNonEarningSupply(), 0); + assertEq(_smartMToken.totalSupply(), 0); + assertEq(_smartMToken.totalAccruedYield(), 0); + assertEq(_smartMToken.excess(), 0); } function test_noExcessCreep() external { _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); _mToken.setCurrentIndex(_EXP_SCALED_ONE + 3e11 - 1); - _wrappedMToken.enableEarning(); - _wrappedMToken.startEarningFor(_alice); + _smartMToken.enableEarning(); + _smartMToken.startEarningFor(_alice); _mToken.setBalanceOf(_alice, 1_000000); for (uint256 i_; i_ < 100; ++i_) { vm.prank(_alice); - _wrappedMToken.wrap(_alice, 9); + _smartMToken.wrap(_alice, 9); - assertLe( - _wrappedMToken.balanceOf(_alice) + _wrappedMToken.excess(), - _mToken.balanceOf(address(_wrappedMToken)) - ); + assertLe(_smartMToken.balanceOf(_alice) + _smartMToken.excess(), _mToken.balanceOf(address(_smartMToken))); } - _wrappedMToken.claimExcess(); + _smartMToken.claimExcess(); - uint256 aliceBalance_ = _wrappedMToken.balanceOf(_alice); + uint256 aliceBalance_ = _smartMToken.balanceOf(_alice); vm.prank(_alice); - _wrappedMToken.transfer(_bob, aliceBalance_); + _smartMToken.transfer(_bob, aliceBalance_); - assertLe(_wrappedMToken.balanceOf(_bob) + _wrappedMToken.excess(), _mToken.balanceOf(address(_wrappedMToken))); + assertLe(_smartMToken.balanceOf(_bob) + _smartMToken.excess(), _mToken.balanceOf(address(_smartMToken))); vm.prank(_bob); - _wrappedMToken.unwrap(_bob); + _smartMToken.unwrap(_bob); } function test_dustWrapping() external { _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); + _registrar.setListContains(_EARNERS_LIST, address(_smartMToken), true); _mToken.setCurrentIndex(_EXP_SCALED_ONE + 1); - _wrappedMToken.enableEarning(); - _wrappedMToken.startEarningFor(_alice); + _smartMToken.enableEarning(); + _smartMToken.startEarningFor(_alice); _mToken.setBalanceOf(_alice, 1_000000); for (uint256 i_; i_ < 100; ++i_) { vm.prank(_alice); - _wrappedMToken.wrap(_alice, 1); + _smartMToken.wrap(_alice, 1); - assertLe( - _wrappedMToken.balanceOf(_alice) + _wrappedMToken.excess(), - _mToken.balanceOf(address(_wrappedMToken)) - ); + assertLe(_smartMToken.balanceOf(_alice) + _smartMToken.excess(), _mToken.balanceOf(address(_smartMToken))); } _mToken.setCurrentIndex(_EXP_SCALED_ONE + (_EXP_SCALED_ONE / 10)); - assertGe(_wrappedMToken.totalAccruedYield(), _wrappedMToken.accruedYieldOf(_alice)); + assertGe(_smartMToken.totalAccruedYield(), _smartMToken.accruedYieldOf(_alice)); } } diff --git a/test/unit/WrappedMToken.t.sol b/test/unit/WrappedMToken.t.sol deleted file mode 100644 index da612f4..0000000 --- a/test/unit/WrappedMToken.t.sol +++ /dev/null @@ -1,1789 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity 0.8.26; - -import { Test, console2 } from "../../lib/forge-std/src/Test.sol"; -import { IERC20 } from "../../lib/common/src/interfaces/IERC20.sol"; -import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.sol"; -import { UIntMath } from "../../lib/common/src/libs/UIntMath.sol"; - -import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; - -import { IndexingMath } from "../../src/libs/IndexingMath.sol"; - -import { Proxy } from "../../src/Proxy.sol"; - -import { MockEarnerManager, MockM, MockRegistrar } from "../utils/Mocks.sol"; -import { WrappedMTokenHarness } from "../utils/WrappedMTokenHarness.sol"; - -// TODO: Test for `totalAccruedYield()`. -// 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. - -contract WrappedMTokenTests is Test { - uint56 internal constant _EXP_SCALED_ONE = 1e12; - uint56 internal constant _ONE_HUNDRED_PERCENT = 10_000; - bytes32 internal constant _CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX = "wm_claim_override_recipient"; - - bytes32 internal constant _EARNERS_LIST = "earners"; - - address internal _alice = makeAddr("alice"); - address internal _bob = makeAddr("bob"); - address internal _charlie = makeAddr("charlie"); - address internal _david = makeAddr("david"); - - address internal _excessDestination = makeAddr("excessDestination"); - address internal _migrationAdmin = makeAddr("migrationAdmin"); - - address[] internal _accounts = [_alice, _bob, _charlie, _david]; - - uint128 internal _currentIndex; - - MockEarnerManager internal _earnerManager; - MockM internal _mToken; - MockRegistrar internal _registrar; - WrappedMTokenHarness internal _implementation; - WrappedMTokenHarness internal _wrappedMToken; - - function setUp() external { - _registrar = new MockRegistrar(); - - _mToken = new MockM(); - _mToken.setCurrentIndex(_EXP_SCALED_ONE); - - _earnerManager = new MockEarnerManager(); - - _implementation = new WrappedMTokenHarness( - address(_mToken), - address(_registrar), - address(_earnerManager), - _excessDestination, - _migrationAdmin - ); - - _wrappedMToken = WrappedMTokenHarness(address(new Proxy(address(_implementation)))); - - _mToken.setCurrentIndex(_currentIndex = 1_100000068703); - } - - /* ============ constructor ============ */ - function test_constructor() external view { - assertEq(_wrappedMToken.migrationAdmin(), _migrationAdmin); - assertEq(_wrappedMToken.mToken(), address(_mToken)); - assertEq(_wrappedMToken.registrar(), address(_registrar)); - assertEq(_wrappedMToken.excessDestination(), _excessDestination); - assertEq(_wrappedMToken.name(), "Smart M by M^0"); - assertEq(_wrappedMToken.symbol(), "MSMART"); - assertEq(_wrappedMToken.decimals(), 6); - assertEq(_wrappedMToken.implementation(), address(_implementation)); - } - - function test_constructor_zeroMToken() external { - vm.expectRevert(IWrappedMToken.ZeroMToken.selector); - new WrappedMTokenHarness(address(0), address(0), address(0), address(0), address(0)); - } - - function test_constructor_zeroRegistrar() external { - vm.expectRevert(IWrappedMToken.ZeroRegistrar.selector); - new WrappedMTokenHarness(address(_mToken), address(0), address(0), address(0), address(0)); - } - - function test_constructor_zeroEarnerManager() external { - vm.expectRevert(IWrappedMToken.ZeroEarnerManager.selector); - new WrappedMTokenHarness(address(_mToken), address(_registrar), address(0), address(0), address(0)); - } - - function test_constructor_zeroExcessDestination() external { - vm.expectRevert(IWrappedMToken.ZeroExcessDestination.selector); - new WrappedMTokenHarness( - address(_mToken), - address(_registrar), - address(_earnerManager), - address(0), - address(0) - ); - } - - function test_constructor_zeroMigrationAdmin() external { - vm.expectRevert(IWrappedMToken.ZeroMigrationAdmin.selector); - new WrappedMTokenHarness( - address(_mToken), - address(_registrar), - address(_earnerManager), - _excessDestination, - address(0) - ); - } - - function test_constructor_zeroImplementation() external { - vm.expectRevert(); - WrappedMTokenHarness(address(new Proxy(address(0)))); - } - - /* ============ _wrap ============ */ - function test_internalWrap_insufficientAmount() external { - vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, 0)); - - _wrappedMToken.internalWrap(_alice, _alice, 0); - } - - function test_internalWrap_invalidRecipient() external { - _mToken.setBalanceOf(_alice, 1_000); - - vm.expectRevert(IWrappedMToken.ZeroAccount.selector); - - _wrappedMToken.internalWrap(_alice, address(0), 1_000); - } - - function test_internalWrap_toNonEarner() external { - _mToken.setBalanceOf(_alice, 1_000); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 1_000); - - assertEq(_wrappedMToken.internalWrap(_alice, _alice, 1_000), 1_000); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 1_000); - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 0); - } - - function test_internalWrap_toEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 0, _EXP_SCALED_ONE, false, false); - - _mToken.setBalanceOf(_alice, 1_002); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 999); - - assertEq(_wrappedMToken.internalWrap(_alice, _alice, 999), 999); - - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 909); - assertEq(_wrappedMToken.totalEarningSupply(), 999); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 1); - - assertEq(_wrappedMToken.internalWrap(_alice, _alice, 1), 1); - - // No change due to principal round down on wrap. - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 910); - assertEq(_wrappedMToken.totalEarningSupply(), 1_000); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 2); - - assertEq(_wrappedMToken.internalWrap(_alice, _alice, 2), 2); - - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 1_002); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 912); - assertEq(_wrappedMToken.totalEarningSupply(), 1_002); - } - - /* ============ wrap ============ */ - function test_wrap_invalidAmount() external { - vm.expectRevert(UIntMath.InvalidUInt240.selector); - - vm.prank(_alice); - _wrappedMToken.wrap(_alice, uint256(type(uint240).max) + 1); - } - - function testFuzz_wrap( - bool earningEnabled_, - bool accountEarning_, - uint240 balance_, - uint240 wrapAmount_, - uint128 accountIndex_, - uint128 currentIndex_ - ) external { - accountEarning_ = earningEnabled_ && accountEarning_; - - if (earningEnabled_) { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _wrappedMToken.enableEarning(); - } - - accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); - - if (accountEarning_) { - _wrappedMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); - _wrappedMToken.setTotalEarningSupply(balance_); - - _wrappedMToken.setPrincipalOfTotalEarningSupply( - IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) - ); - } else { - _wrappedMToken.setAccountOf(_alice, balance_); - _wrappedMToken.setTotalNonEarningSupply(balance_); - } - - currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); - wrapAmount_ = uint240(bound(wrapAmount_, 0, _getMaxAmount(currentIndex_) - balance_)); - - _mToken.setCurrentIndex(_currentIndex = currentIndex_); - _mToken.setBalanceOf(_alice, wrapAmount_); - - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - if (wrapAmount_ == 0) { - vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); - } else { - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, wrapAmount_); - } - - vm.startPrank(_alice); - _wrappedMToken.wrap(_alice, wrapAmount_); - - if (wrapAmount_ == 0) return; - - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); - - assertEq( - accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), - _wrappedMToken.balanceOf(_alice) - ); - } - - /* ============ wrap entire balance ============ */ - function test_wrap_entireBalance_invalidAmount() external { - _mToken.setBalanceOf(_alice, uint256(type(uint240).max) + 1); - - vm.expectRevert(UIntMath.InvalidUInt240.selector); - - vm.prank(_alice); - _wrappedMToken.wrap(_alice, uint256(type(uint240).max) + 1); - } - - function testFuzz_wrap_entireBalance( - bool earningEnabled_, - bool accountEarning_, - uint240 balance_, - uint240 wrapAmount_, - uint128 accountIndex_, - uint128 currentIndex_ - ) external { - accountEarning_ = earningEnabled_ && accountEarning_; - - if (earningEnabled_) { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _wrappedMToken.enableEarning(); - } - - accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); - - if (accountEarning_) { - _wrappedMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); - _wrappedMToken.setTotalEarningSupply(balance_); - - _wrappedMToken.setPrincipalOfTotalEarningSupply( - IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) - ); - } else { - _wrappedMToken.setAccountOf(_alice, balance_); - _wrappedMToken.setTotalNonEarningSupply(balance_); - } - - currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); - wrapAmount_ = uint240(bound(wrapAmount_, 0, _getMaxAmount(currentIndex_) - balance_)); - - _mToken.setCurrentIndex(_currentIndex = currentIndex_); - _mToken.setBalanceOf(_alice, wrapAmount_); - - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - if (wrapAmount_ == 0) { - vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); - } else { - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, wrapAmount_); - } - - vm.startPrank(_alice); - _wrappedMToken.wrap(_alice); - - if (wrapAmount_ == 0) return; - - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); - - assertEq( - accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), - _wrappedMToken.balanceOf(_alice) - ); - } - - /* ============ wrapWithPermit vrs ============ */ - function test_wrapWithPermit_vrs_invalidAmount() external { - vm.expectRevert(UIntMath.InvalidUInt240.selector); - - vm.prank(_alice); - _wrappedMToken.wrapWithPermit(_alice, uint256(type(uint240).max) + 1, 0, 0, bytes32(0), bytes32(0)); - } - - function testFuzz_wrapWithPermit_vrs( - bool earningEnabled_, - bool accountEarning_, - uint240 balance_, - uint240 wrapAmount_, - uint128 accountIndex_, - uint128 currentIndex_ - ) external { - accountEarning_ = earningEnabled_ && accountEarning_; - - if (earningEnabled_) { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _wrappedMToken.enableEarning(); - } - - accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); - - if (accountEarning_) { - _wrappedMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); - _wrappedMToken.setTotalEarningSupply(balance_); - - _wrappedMToken.setPrincipalOfTotalEarningSupply( - IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) - ); - } else { - _wrappedMToken.setAccountOf(_alice, balance_); - _wrappedMToken.setTotalNonEarningSupply(balance_); - } - - currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); - wrapAmount_ = uint240(bound(wrapAmount_, 0, _getMaxAmount(currentIndex_) - balance_)); - - _mToken.setCurrentIndex(_currentIndex = currentIndex_); - _mToken.setBalanceOf(_alice, wrapAmount_); - - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - if (wrapAmount_ == 0) { - vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); - } else { - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, wrapAmount_); - } - - vm.startPrank(_alice); - _wrappedMToken.wrapWithPermit(_alice, wrapAmount_, 0, 0, bytes32(0), bytes32(0)); - - if (wrapAmount_ == 0) return; - - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); - - assertEq( - accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), - _wrappedMToken.balanceOf(_alice) - ); - } - - /* ============ wrapWithPermit signature ============ */ - function test_wrapWithPermit_signature_invalidAmount() external { - vm.expectRevert(UIntMath.InvalidUInt240.selector); - - vm.prank(_alice); - _wrappedMToken.wrapWithPermit(_alice, uint256(type(uint240).max) + 1, 0, hex""); - } - - function testFuzz_wrapWithPermit_signature( - bool earningEnabled_, - bool accountEarning_, - uint240 balance_, - uint240 wrapAmount_, - uint128 accountIndex_, - uint128 currentIndex_ - ) external { - accountEarning_ = earningEnabled_ && accountEarning_; - - if (earningEnabled_) { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _wrappedMToken.enableEarning(); - } - - accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); - - if (accountEarning_) { - _wrappedMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); - _wrappedMToken.setTotalEarningSupply(balance_); - - _wrappedMToken.setPrincipalOfTotalEarningSupply( - IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) - ); - } else { - _wrappedMToken.setAccountOf(_alice, balance_); - _wrappedMToken.setTotalNonEarningSupply(balance_); - } - - currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); - wrapAmount_ = uint240(bound(wrapAmount_, 0, _getMaxAmount(currentIndex_) - balance_)); - - _mToken.setCurrentIndex(_currentIndex = currentIndex_); - _mToken.setBalanceOf(_alice, wrapAmount_); - - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - if (wrapAmount_ == 0) { - vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); - } else { - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, wrapAmount_); - } - - vm.startPrank(_alice); - _wrappedMToken.wrapWithPermit(_alice, wrapAmount_, 0, hex""); - - if (wrapAmount_ == 0) return; - - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ + wrapAmount_); - - assertEq( - accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), - _wrappedMToken.balanceOf(_alice) - ); - } - - /* ============ _unwrap ============ */ - function test_internalUnwrap_insufficientAmount() external { - vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, 0)); - - _wrappedMToken.internalUnwrap(_alice, _alice, 0); - } - - function test_internalUnwrap_insufficientBalance_fromNonEarner() external { - _wrappedMToken.setAccountOf(_alice, 999); - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.InsufficientBalance.selector, _alice, 999, 1_000)); - _wrappedMToken.internalUnwrap(_alice, _alice, 1_000); - } - - function test_internalUnwrap_insufficientBalance_fromEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 999, _currentIndex, false, false); - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.InsufficientBalance.selector, _alice, 999, 1_000)); - _wrappedMToken.internalUnwrap(_alice, _alice, 1_000); - } - - function test_internalUnwrap_fromNonEarner() external { - _wrappedMToken.setTotalNonEarningSupply(1_000); - - _wrappedMToken.setAccountOf(_alice, 1_000); - - _mToken.setBalanceOf(address(_wrappedMToken), 1_000); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, address(0), 500); - - assertEq(_wrappedMToken.internalUnwrap(_alice, _alice, 500), 500); - - assertEq(_wrappedMToken.balanceOf(_alice), 500); - assertEq(_wrappedMToken.totalNonEarningSupply(), 500); - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 0); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, address(0), 500); - - assertEq(_wrappedMToken.internalUnwrap(_alice, _alice, 500), 500); - - assertEq(_wrappedMToken.balanceOf(_alice), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 0); - } - - function test_internalUnwrap_fromEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setPrincipalOfTotalEarningSupply(909); - _wrappedMToken.setTotalEarningSupply(1_000); - - _wrappedMToken.setAccountOf(_alice, 1_000, _currentIndex, false, false); - - _mToken.setBalanceOf(address(_wrappedMToken), 1_000); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, address(0), 1); - - assertEq(_wrappedMToken.internalUnwrap(_alice, _alice, 1), 0); - - // Change due to principal round up on unwrap. - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 999); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningSupply(), 999); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, address(0), 999); - - assertEq(_wrappedMToken.internalUnwrap(_alice, _alice, 999), 998); - - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningSupply(), 0); - } - - /* ============ unwrap ============ */ - function test_unwrap_invalidAmount() external { - vm.expectRevert(UIntMath.InvalidUInt240.selector); - - vm.prank(_alice); - _wrappedMToken.unwrap(_alice, uint256(type(uint240).max) + 1); - } - - function testFuzz_unwrap( - bool earningEnabled_, - bool accountEarning_, - uint240 balance_, - uint240 unwrapAmount_, - uint128 accountIndex_, - uint128 currentIndex_ - ) external { - accountEarning_ = earningEnabled_ && accountEarning_; - - if (earningEnabled_) { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _wrappedMToken.enableEarning(); - } - - accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); - - if (accountEarning_) { - _wrappedMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); - _wrappedMToken.setTotalEarningSupply(balance_); - - _wrappedMToken.setPrincipalOfTotalEarningSupply( - IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) - ); - } else { - _wrappedMToken.setAccountOf(_alice, balance_); - _wrappedMToken.setTotalNonEarningSupply(balance_); - } - - currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); - unwrapAmount_ = uint240(bound(unwrapAmount_, 0, 2 * balance_)); - - _mToken.setCurrentIndex(_currentIndex = currentIndex_); - - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - _mToken.setBalanceOf(address(_wrappedMToken), balance_ + accruedYield_); - - if (unwrapAmount_ == 0) { - vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); - } else if (unwrapAmount_ > balance_ + accruedYield_) { - vm.expectRevert( - abi.encodeWithSelector( - IWrappedMToken.InsufficientBalance.selector, - _alice, - balance_ + accruedYield_, - unwrapAmount_ - ) - ); - } else { - vm.expectEmit(); - emit IERC20.Transfer(_alice, address(0), unwrapAmount_); - } - - vm.startPrank(_alice); - _wrappedMToken.unwrap(_alice, unwrapAmount_); - - if ((unwrapAmount_ == 0) || (unwrapAmount_ > balance_ + accruedYield_)) return; - - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_ - unwrapAmount_); - - assertEq( - accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), - _wrappedMToken.balanceOf(_alice) - ); - } - - /* ============ unwrap entire balance ============ */ - function testFuzz_unwrap_entireBalance( - bool earningEnabled_, - bool accountEarning_, - uint240 balance_, - uint128 accountIndex_, - uint128 currentIndex_ - ) external { - accountEarning_ = earningEnabled_ && accountEarning_; - - if (earningEnabled_) { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _wrappedMToken.enableEarning(); - } - - accountIndex_ = uint128(bound(accountIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); - - if (accountEarning_) { - _wrappedMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); - _wrappedMToken.setTotalEarningSupply(balance_); - - _wrappedMToken.setPrincipalOfTotalEarningSupply( - IndexingMath.getPrincipalAmountRoundedDown(balance_, accountIndex_) - ); - } else { - _wrappedMToken.setAccountOf(_alice, balance_); - _wrappedMToken.setTotalNonEarningSupply(balance_); - } - - currentIndex_ = uint128(bound(currentIndex_, accountIndex_, 10 * _EXP_SCALED_ONE)); - - _mToken.setCurrentIndex(_currentIndex = currentIndex_); - - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - _mToken.setBalanceOf(address(_wrappedMToken), balance_ + accruedYield_); - - if (balance_ + accruedYield_ == 0) { - vm.expectRevert(abi.encodeWithSelector(IERC20Extended.InsufficientAmount.selector, (0))); - } else { - vm.expectEmit(); - emit IERC20.Transfer(_alice, address(0), balance_ + accruedYield_); - } - - vm.startPrank(_alice); - _wrappedMToken.unwrap(_alice); - - if (balance_ + accruedYield_ == 0) return; - - assertEq(_wrappedMToken.balanceOf(_alice), 0); - - assertEq(accountEarning_ ? _wrappedMToken.totalEarningSupply() : _wrappedMToken.totalNonEarningSupply(), 0); - } - - /* ============ claimFor ============ */ - function test_claimFor_nonEarner() external { - _wrappedMToken.setAccountOf(_alice, 1_000); - - vm.prank(_alice); - assertEq(_wrappedMToken.claimFor(_alice), 0); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - } - - function test_claimFor_earner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, false, false); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 100); - - assertEq(_wrappedMToken.claimFor(_alice), 100); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_100); - } - - function test_claimFor_earner_withOverrideRecipient() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _registrar.set( - keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), - bytes32(uint256(uint160(_bob))) - ); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, false, false); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, _bob, 100); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 100); - - assertEq(_wrappedMToken.claimFor(_alice), 100); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - assertEq(_wrappedMToken.balanceOf(_bob), 100); - } - - function test_claimFor_earner_withFee() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, true, false); - - _earnerManager.setEarnerDetails(_alice, true, 1_500, _bob); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 15); - - assertEq(_wrappedMToken.claimFor(_alice), 100); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_085); - assertEq(_wrappedMToken.balanceOf(_bob), 15); - } - - function test_claimFor_earner_withFeeAboveOneHundredPercent() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, true, false); - - _earnerManager.setEarnerDetails(_alice, true, type(uint16).max, _bob); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 100); - - assertEq(_wrappedMToken.claimFor(_alice), 100); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - assertEq(_wrappedMToken.balanceOf(_bob), 100); - } - - function test_claimFor_earner_withOverrideRecipientAndFee() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _registrar.set( - keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), - bytes32(uint256(uint160(_charlie))) - ); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 1_000, _EXP_SCALED_ONE, true, false); - - _earnerManager.setEarnerDetails(_alice, true, 1_500, _bob); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, _charlie, 100); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, 100); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 15); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _charlie, 85); - - assertEq(_wrappedMToken.claimFor(_alice), 100); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - assertEq(_wrappedMToken.balanceOf(_bob), 15); - assertEq(_wrappedMToken.balanceOf(_charlie), 85); - } - - function testFuzz_claimFor( - uint240 balance_, - uint128 accountIndex_, - uint128 index_, - bool claimOverride_, - uint16 feeRate_ - ) external { - accountIndex_ = uint128(bound(index_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); - index_ = uint128(bound(index_, accountIndex_, 10 * _EXP_SCALED_ONE)); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - if (claimOverride_) { - _registrar.set( - keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), - bytes32(uint256(uint160(_charlie))) - ); - } - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setTotalEarningSupply(balance_); - - _wrappedMToken.setAccountOf(_alice, balance_, accountIndex_, feeRate_ != 0, false); - - if (feeRate_ != 0) { - _earnerManager.setEarnerDetails(_alice, true, feeRate_, _bob); - } - - _mToken.setCurrentIndex(index_); - - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - if (accruedYield_ != 0) { - vm.expectEmit(); - emit IWrappedMToken.Claimed(_alice, claimOverride_ ? _charlie : _alice, accruedYield_); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), _alice, accruedYield_); - } - - uint240 fee_ = (accruedYield_ * (feeRate_ > _ONE_HUNDRED_PERCENT ? _ONE_HUNDRED_PERCENT : feeRate_)) / - _ONE_HUNDRED_PERCENT; - - if (fee_ != 0) { - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, fee_); - } - - if (claimOverride_ && (accruedYield_ - fee_ != 0)) { - vm.expectEmit(); - emit IERC20.Transfer(_alice, _charlie, accruedYield_ - fee_); - } - - assertEq(_wrappedMToken.claimFor(_alice), accruedYield_); - - assertEq( - _wrappedMToken.totalSupply(), - _wrappedMToken.balanceOf(_alice) + _wrappedMToken.balanceOf(_bob) + _wrappedMToken.balanceOf(_charlie) - ); - } - - /* ============ claimExcess ============ */ - function testFuzz_claimExcess( - uint128 index_, - uint240 totalNonEarningSupply_, - uint112 principalOfTotalEarningSupply_, - uint240 mBalance_ - ) external { - index_ = uint128(bound(index_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - - totalNonEarningSupply_ = uint240(bound(totalNonEarningSupply_, 0, _getMaxAmount(index_))); - - uint240 totalEarningSupply_ = uint112(bound(principalOfTotalEarningSupply_, 0, _getMaxAmount(index_))); - - principalOfTotalEarningSupply_ = uint112(totalEarningSupply_ / index_); - - mBalance_ = uint240(bound(mBalance_, totalNonEarningSupply_ + totalEarningSupply_, type(uint240).max)); - - _mToken.setBalanceOf(address(_wrappedMToken), mBalance_); - _wrappedMToken.setTotalNonEarningSupply(totalNonEarningSupply_); - _wrappedMToken.setPrincipalOfTotalEarningSupply(principalOfTotalEarningSupply_); - - _mToken.setCurrentIndex(index_); - - uint240 expectedExcess_ = _wrappedMToken.excess(); - - vm.expectCall( - address(_mToken), - abi.encodeCall(_mToken.transfer, (_wrappedMToken.excessDestination(), expectedExcess_)) - ); - - vm.expectEmit(); - emit IWrappedMToken.ExcessClaimed(expectedExcess_); - - assertEq(_wrappedMToken.claimExcess(), expectedExcess_); - assertEq(_wrappedMToken.excess(), 0); - } - - /* ============ transfer ============ */ - function test_transfer_invalidRecipient() external { - _wrappedMToken.setAccountOf(_alice, 1_000); - - vm.expectRevert(IWrappedMToken.ZeroAccount.selector); - - vm.prank(_alice); - _wrappedMToken.transfer(address(0), 1_000); - } - - function test_transfer_insufficientBalance_fromNonEarner_toNonEarner() external { - _wrappedMToken.setAccountOf(_alice, 999); - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.InsufficientBalance.selector, _alice, 999, 1_000)); - vm.prank(_alice); - _wrappedMToken.transfer(_bob, 1_000); - } - - function test_transfer_insufficientBalance_fromEarner_toNonEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 999, _currentIndex, false, false); - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.InsufficientBalance.selector, _alice, 999, 1_000)); - vm.prank(_alice); - _wrappedMToken.transfer(_bob, 1_000); - } - - function test_transfer_fromNonEarner_toNonEarner() external { - _wrappedMToken.setTotalNonEarningSupply(1_500); - - _wrappedMToken.setAccountOf(_alice, 1_000); - _wrappedMToken.setAccountOf(_bob, 500); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 500); - - vm.prank(_alice); - _wrappedMToken.transfer(_bob, 500); - - assertEq(_wrappedMToken.balanceOf(_alice), 500); - - assertEq(_wrappedMToken.balanceOf(_bob), 1_000); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 1_500); - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 0); - } - - function testFuzz_transfer_fromNonEarner_toNonEarner( - uint256 supply_, - uint256 aliceBalance_, - uint256 transferAmount_ - ) external { - supply_ = bound(supply_, 1, type(uint112).max); - aliceBalance_ = bound(aliceBalance_, 1, supply_); - transferAmount_ = bound(transferAmount_, 1, aliceBalance_); - uint256 bobBalance = supply_ - aliceBalance_; - - _wrappedMToken.setTotalNonEarningSupply(supply_); - - _wrappedMToken.setAccountOf(_alice, aliceBalance_); - _wrappedMToken.setAccountOf(_bob, bobBalance); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, transferAmount_); - - vm.prank(_alice); - _wrappedMToken.transfer(_bob, transferAmount_); - - assertEq(_wrappedMToken.balanceOf(_alice), aliceBalance_ - transferAmount_); - assertEq(_wrappedMToken.balanceOf(_bob), bobBalance + transferAmount_); - - assertEq(_wrappedMToken.totalNonEarningSupply(), supply_); - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 0); - } - - function test_transfer_fromEarner_toNonEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setPrincipalOfTotalEarningSupply(909); - _wrappedMToken.setTotalEarningSupply(1_000); - - _wrappedMToken.setTotalNonEarningSupply(500); - - _wrappedMToken.setAccountOf(_alice, 1_000, _currentIndex, false, false); - _wrappedMToken.setAccountOf(_bob, 500); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 500); - - vm.prank(_alice); - _wrappedMToken.transfer(_bob, 500); - - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 500); - - assertEq(_wrappedMToken.balanceOf(_bob), 1_000); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 1_000); - assertEq(_wrappedMToken.totalEarningSupply(), 500); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 1); - - vm.prank(_alice); - _wrappedMToken.transfer(_bob, 1); - - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 499); - - assertEq(_wrappedMToken.balanceOf(_bob), 1_001); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 1_001); - assertEq(_wrappedMToken.totalEarningSupply(), 499); - } - - function test_transfer_fromNonEarner_toEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setPrincipalOfTotalEarningSupply(454); - _wrappedMToken.setTotalEarningSupply(500); - - _wrappedMToken.setTotalNonEarningSupply(1_000); - - _wrappedMToken.setAccountOf(_alice, 1_000); - _wrappedMToken.setAccountOf(_bob, 500, _currentIndex, false, false); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 500); - - vm.prank(_alice); - _wrappedMToken.transfer(_bob, 500); - - assertEq(_wrappedMToken.balanceOf(_alice), 500); - - assertEq(_wrappedMToken.lastIndexOf(_bob), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_bob), 1_000); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 500); - assertEq(_wrappedMToken.totalEarningSupply(), 1_000); - } - - function test_transfer_fromEarner_toEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setPrincipalOfTotalEarningSupply(1_363); - _wrappedMToken.setTotalEarningSupply(1_500); - - _wrappedMToken.setAccountOf(_alice, 1_000, _currentIndex, false, false); - _wrappedMToken.setAccountOf(_bob, 500, _currentIndex, false, false); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, 500); - - vm.prank(_alice); - _wrappedMToken.transfer(_bob, 500); - - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 500); - - assertEq(_wrappedMToken.lastIndexOf(_bob), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_bob), 1_000); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningSupply(), 1_500); - } - - function test_transfer_nonEarnerToSelf() external { - _wrappedMToken.setTotalNonEarningSupply(1_000); - - _wrappedMToken.setAccountOf(_alice, 1_000); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _alice, 500); - - vm.prank(_alice); - _wrappedMToken.transfer(_alice, 500); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 1_000); - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.principalOfTotalEarningSupply(), 0); - } - - function test_transfer_earnerToSelf() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setPrincipalOfTotalEarningSupply(909); - _wrappedMToken.setTotalEarningSupply(1_000); - - _wrappedMToken.setAccountOf(_alice, 1_000, _currentIndex, false, false); - - _mToken.setCurrentIndex((_currentIndex * 5) / 3); // 1_833333447838 - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - assertEq(_wrappedMToken.accruedYieldOf(_alice), 666); - - vm.expectEmit(); - emit IERC20.Transfer(_alice, _alice, 500); - - vm.prank(_alice); - _wrappedMToken.transfer(_alice, 500); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_666); - } - - function testFuzz_transfer( - bool earningEnabled_, - bool aliceEarning_, - bool bobEarning_, - uint240 aliceBalance_, - uint240 bobBalance_, - uint128 aliceIndex_, - uint128 bobIndex_, - uint128 currentIndex_, - uint240 amount_ - ) external { - aliceEarning_ = earningEnabled_ && aliceEarning_; - bobEarning_ = earningEnabled_ && bobEarning_; - - if (earningEnabled_) { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _wrappedMToken.enableEarning(); - } - - aliceIndex_ = uint128(bound(aliceIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - aliceBalance_ = uint240(bound(aliceBalance_, 0, _getMaxAmount(aliceIndex_) / 4)); - - if (aliceEarning_) { - _wrappedMToken.setAccountOf(_alice, aliceBalance_, aliceIndex_, false, false); - _wrappedMToken.setTotalEarningSupply(aliceBalance_); - - _wrappedMToken.setPrincipalOfTotalEarningSupply( - IndexingMath.getPrincipalAmountRoundedDown(aliceBalance_, aliceIndex_) - ); - } else { - _wrappedMToken.setAccountOf(_alice, aliceBalance_); - _wrappedMToken.setTotalNonEarningSupply(aliceBalance_); - } - - bobIndex_ = uint128(bound(bobIndex_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - bobBalance_ = uint240(bound(bobBalance_, 0, _getMaxAmount(bobIndex_) / 4)); - - if (bobEarning_) { - _wrappedMToken.setAccountOf(_bob, bobBalance_, bobIndex_, false, false); - _wrappedMToken.setTotalEarningSupply(_wrappedMToken.totalEarningSupply() + bobBalance_); - - _wrappedMToken.setPrincipalOfTotalEarningSupply( - IndexingMath.getPrincipalAmountRoundedDown( - _wrappedMToken.totalEarningSupply() + bobBalance_, - aliceIndex_ > bobIndex_ ? aliceIndex_ : bobIndex_ - ) - ); - } else { - _wrappedMToken.setAccountOf(_bob, bobBalance_); - _wrappedMToken.setTotalNonEarningSupply(_wrappedMToken.totalNonEarningSupply() + bobBalance_); - } - - currentIndex_ = uint128( - bound(currentIndex_, aliceIndex_ > bobIndex_ ? aliceIndex_ : bobIndex_, 10 * _EXP_SCALED_ONE) - ); - - _mToken.setCurrentIndex(_currentIndex = currentIndex_); - - uint240 aliceAccruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - uint240 bobAccruedYield_ = _wrappedMToken.accruedYieldOf(_bob); - - amount_ = uint240(bound(amount_, 0, aliceBalance_ + aliceAccruedYield_)); - - if (amount_ > aliceBalance_ + aliceAccruedYield_) { - vm.expectRevert( - abi.encodeWithSelector( - IWrappedMToken.InsufficientBalance.selector, - _alice, - aliceBalance_ + aliceAccruedYield_, - amount_ - ) - ); - } else { - vm.expectEmit(); - emit IERC20.Transfer(_alice, _bob, amount_); - } - - vm.prank(_alice); - _wrappedMToken.transfer(_bob, amount_); - - if (amount_ > aliceBalance_ + aliceAccruedYield_) return; - - assertEq(_wrappedMToken.balanceOf(_alice), aliceBalance_ + aliceAccruedYield_ - amount_); - assertEq(_wrappedMToken.balanceOf(_bob), bobBalance_ + bobAccruedYield_ + amount_); - - if (aliceEarning_ && bobEarning_) { - assertEq( - _wrappedMToken.totalEarningSupply(), - aliceBalance_ + aliceAccruedYield_ + bobBalance_ + bobAccruedYield_ - ); - } else if (aliceEarning_) { - assertEq(_wrappedMToken.totalEarningSupply(), aliceBalance_ + aliceAccruedYield_ - amount_); - assertEq(_wrappedMToken.totalNonEarningSupply(), bobBalance_ + bobAccruedYield_ + amount_); - } else if (bobEarning_) { - assertEq(_wrappedMToken.totalNonEarningSupply(), aliceBalance_ + aliceAccruedYield_ - amount_); - assertEq(_wrappedMToken.totalEarningSupply(), bobBalance_ + bobAccruedYield_ + amount_); - } else { - assertEq( - _wrappedMToken.totalNonEarningSupply(), - aliceBalance_ + aliceAccruedYield_ + bobBalance_ + bobAccruedYield_ - ); - } - } - - /* ============ startEarningFor ============ */ - function test_startEarningFor_earningIsDisabled() external { - vm.expectRevert(IWrappedMToken.EarningIsDisabled.selector); - _wrappedMToken.startEarningFor(_alice); - } - - function test_startEarningFor_notApprovedEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.NotApprovedEarner.selector, _alice)); - _wrappedMToken.startEarningFor(_alice); - } - - function test_startEarning_overflow() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - uint256 aliceBalance_ = uint256(type(uint112).max) + 20; - - _mToken.setCurrentIndex(_currentIndex = _EXP_SCALED_ONE); - - _wrappedMToken.setTotalNonEarningSupply(aliceBalance_); - - _wrappedMToken.setAccountOf(_alice, aliceBalance_); - - _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); - - vm.expectRevert(UIntMath.InvalidUInt112.selector); - _wrappedMToken.startEarningFor(_alice); - } - - function test_startEarningFor() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setTotalNonEarningSupply(1_000); - - _wrappedMToken.setAccountOf(_alice, 1_000); - - _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); - - vm.expectEmit(); - emit IWrappedMToken.StartedEarning(_alice); - - _wrappedMToken.startEarningFor(_alice); - - assertEq(_wrappedMToken.isEarning(_alice), true); - assertEq(_wrappedMToken.lastIndexOf(_alice), _currentIndex); - assertEq(_wrappedMToken.balanceOf(_alice), 1000); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningSupply(), 1_000); - } - - function testFuzz_startEarningFor(uint240 balance_, uint128 index_) external { - balance_ = uint240(bound(balance_, 0, _getMaxAmount(_currentIndex))); - index_ = uint128(bound(index_, _currentIndex, 10 * _EXP_SCALED_ONE)); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setTotalNonEarningSupply(balance_); - - _wrappedMToken.setAccountOf(_alice, balance_); - - _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); - - _mToken.setCurrentIndex(index_); - - vm.expectEmit(); - emit IWrappedMToken.StartedEarning(_alice); - - _wrappedMToken.startEarningFor(_alice); - - assertEq(_wrappedMToken.isEarning(_alice), true); - assertEq(_wrappedMToken.lastIndexOf(_alice), index_); - assertEq(_wrappedMToken.balanceOf(_alice), balance_); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - assertEq(_wrappedMToken.totalEarningSupply(), balance_); - } - - /* ============ startEarningFor batch ============ */ - function test_startEarningFor_batch_earningIsDisabled() external { - vm.expectRevert(IWrappedMToken.EarningIsDisabled.selector); - _wrappedMToken.startEarningFor(new address[](2)); - } - - function test_startEarningFor_batch_notApprovedEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); - - _wrappedMToken.enableEarning(); - - address[] memory accounts_ = new address[](2); - accounts_[0] = _alice; - accounts_[1] = _bob; - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.NotApprovedEarner.selector, _bob)); - _wrappedMToken.startEarningFor(accounts_); - } - - function test_startEarningFor_batch() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); - _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); - - _wrappedMToken.enableEarning(); - - address[] memory accounts_ = new address[](2); - accounts_[0] = _alice; - accounts_[1] = _bob; - - vm.expectEmit(); - emit IWrappedMToken.StartedEarning(_alice); - - vm.expectEmit(); - emit IWrappedMToken.StartedEarning(_bob); - - _wrappedMToken.startEarningFor(accounts_); - } - - /* ============ stopEarningFor ============ */ - function test_stopEarningFor_isApprovedEarner() external { - _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.IsApprovedEarner.selector, _alice)); - _wrappedMToken.stopEarningFor(_alice); - } - - function test_stopEarningFor() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setPrincipalOfTotalEarningSupply(909); - _wrappedMToken.setTotalEarningSupply(1_000); - - _wrappedMToken.setAccountOf(_alice, 999, _currentIndex, false, false); - - vm.expectEmit(); - emit IWrappedMToken.StoppedEarning(_alice); - - _wrappedMToken.stopEarningFor(_alice); - - assertEq(_wrappedMToken.balanceOf(_alice), 999); - assertEq(_wrappedMToken.isEarning(_alice), false); - - assertEq(_wrappedMToken.totalNonEarningSupply(), 999); - assertEq(_wrappedMToken.totalEarningSupply(), 1); - } - - function testFuzz_stopEarningFor(uint240 balance_, uint128 accountIndex_, uint128 index_) external { - accountIndex_ = uint128(bound(index_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE)); - balance_ = uint240(bound(balance_, 0, _getMaxAmount(accountIndex_))); - index_ = uint128(bound(index_, accountIndex_, 10 * _EXP_SCALED_ONE)); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setTotalEarningSupply(balance_); - - _wrappedMToken.setAccountOf(_alice, balance_, accountIndex_, false, false); - - _mToken.setCurrentIndex(index_); - - uint240 accruedYield_ = _wrappedMToken.accruedYieldOf(_alice); - - vm.expectEmit(); - emit IWrappedMToken.StoppedEarning(_alice); - - _wrappedMToken.stopEarningFor(_alice); - - assertEq(_wrappedMToken.balanceOf(_alice), balance_ + accruedYield_); - assertEq(_wrappedMToken.isEarning(_alice), false); - - assertEq(_wrappedMToken.totalNonEarningSupply(), balance_ + accruedYield_); - assertEq(_wrappedMToken.totalEarningSupply(), 0); - } - - /* ============ setClaimRecipient ============ */ - function test_setClaimRecipient() external { - (, , , , bool hasClaimRecipient_) = _wrappedMToken.getAccountOf(_alice); - - assertFalse(hasClaimRecipient_); - assertEq(_wrappedMToken.getInternalClaimRecipientOf(_alice), address(0)); - - vm.prank(_alice); - _wrappedMToken.setClaimRecipient(_alice); - - (, , , , hasClaimRecipient_) = _wrappedMToken.getAccountOf(_alice); - - assertTrue(hasClaimRecipient_); - assertEq(_wrappedMToken.getInternalClaimRecipientOf(_alice), _alice); - - vm.prank(_alice); - _wrappedMToken.setClaimRecipient(_bob); - - (, , , , hasClaimRecipient_) = _wrappedMToken.getAccountOf(_alice); - - assertTrue(hasClaimRecipient_); - assertEq(_wrappedMToken.getInternalClaimRecipientOf(_alice), _bob); - - vm.prank(_alice); - _wrappedMToken.setClaimRecipient(address(0)); - - (, , , , hasClaimRecipient_) = _wrappedMToken.getAccountOf(_alice); - - assertFalse(hasClaimRecipient_); - assertEq(_wrappedMToken.getInternalClaimRecipientOf(_alice), address(0)); - } - - /* ============ stopEarningFor batch ============ */ - function test_stopEarningFor_batch_isApprovedEarner() external { - _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); - - address[] memory accounts_ = new address[](2); - accounts_[0] = _alice; - accounts_[1] = _bob; - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.IsApprovedEarner.selector, _bob)); - _wrappedMToken.stopEarningFor(accounts_); - } - - function test_stopEarningFor_batch() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 0, _currentIndex, false, false); - _wrappedMToken.setAccountOf(_bob, 0, _currentIndex, false, false); - - address[] memory accounts_ = new address[](2); - accounts_[0] = _alice; - accounts_[1] = _bob; - - vm.expectEmit(); - emit IWrappedMToken.StoppedEarning(_alice); - - vm.expectEmit(); - emit IWrappedMToken.StoppedEarning(_bob); - - _wrappedMToken.stopEarningFor(accounts_); - } - - /* ============ enableEarning ============ */ - function test_enableEarning_notApprovedEarner() external { - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.NotApprovedEarner.selector, address(_wrappedMToken))); - _wrappedMToken.enableEarning(); - } - - function test_enableEarning_earningCannotBeReenabled() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), false); - - _wrappedMToken.disableEarning(); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - vm.expectRevert(IWrappedMToken.EarningCannotBeReenabled.selector); - _wrappedMToken.enableEarning(); - } - - function test_enableEarning() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - vm.expectEmit(); - emit IWrappedMToken.EarningEnabled(_currentIndex); - - _wrappedMToken.enableEarning(); - } - - /* ============ disableEarning ============ */ - function test_disableEarning_earningIsDisabled() external { - vm.expectRevert(IWrappedMToken.EarningIsDisabled.selector); - _wrappedMToken.disableEarning(); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), false); - - _wrappedMToken.disableEarning(); - - vm.expectRevert(IWrappedMToken.EarningIsDisabled.selector); - _wrappedMToken.disableEarning(); - } - - function test_disableEarning_approvedEarner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - vm.expectRevert(abi.encodeWithSelector(IWrappedMToken.IsApprovedEarner.selector, address(_wrappedMToken))); - _wrappedMToken.disableEarning(); - } - - function test_disableEarning() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), false); - - vm.expectEmit(); - emit IWrappedMToken.EarningDisabled(_currentIndex); - - _wrappedMToken.disableEarning(); - } - - /* ============ balanceOf ============ */ - function test_balanceOf_nonEarner() external { - _wrappedMToken.setAccountOf(_alice, 500); - - assertEq(_wrappedMToken.balanceOf(_alice), 500); - - _wrappedMToken.setAccountOf(_alice, 1_000); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - } - - function test_balanceOf_earner() external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - _wrappedMToken.setAccountOf(_alice, 500, _EXP_SCALED_ONE, false, false); - - assertEq(_wrappedMToken.balanceOf(_alice), 500); - - _wrappedMToken.setAccountOf(_alice, 1_000); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - - _wrappedMToken.setLastIndexOf(_alice, 2 * _EXP_SCALED_ONE); - - assertEq(_wrappedMToken.balanceOf(_alice), 1_000); - } - - /* ============ claimRecipientFor ============ */ - function test_claimRecipientFor_hasClaimRecipient() external { - assertEq(_wrappedMToken.claimRecipientFor(_alice), address(0)); - - _wrappedMToken.setAccountOf(_alice, 0, 0, false, true); - _wrappedMToken.setInternalClaimRecipient(_alice, _bob); - - assertEq(_wrappedMToken.claimRecipientFor(_alice), _bob); - } - - function test_claimRecipientFor_hasClaimOverrideRecipient() external { - assertEq(_wrappedMToken.claimRecipientFor(_alice), address(0)); - - _registrar.set( - keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), - bytes32(uint256(uint160(_charlie))) - ); - - assertEq(_wrappedMToken.claimRecipientFor(_alice), _charlie); - } - - function test_claimRecipientFor_hasClaimRecipientAndOverrideRecipient() external { - assertEq(_wrappedMToken.claimRecipientFor(_alice), address(0)); - - _wrappedMToken.setAccountOf(_alice, 0, 0, false, true); - _wrappedMToken.setInternalClaimRecipient(_alice, _bob); - - _registrar.set( - keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_KEY_PREFIX, _alice)), - bytes32(uint256(uint160(_charlie))) - ); - - assertEq(_wrappedMToken.claimRecipientFor(_alice), _bob); - } - - /* ============ totalSupply ============ */ - function test_totalSupply_onlyTotalNonEarningSupply() external { - _wrappedMToken.setTotalNonEarningSupply(500); - - assertEq(_wrappedMToken.totalSupply(), 500); - - _wrappedMToken.setTotalNonEarningSupply(1_000); - - assertEq(_wrappedMToken.totalSupply(), 1_000); - } - - function test_totalSupply_onlyTotalEarningSupply() external { - _wrappedMToken.setTotalEarningSupply(500); - - assertEq(_wrappedMToken.totalSupply(), 500); - - _wrappedMToken.setTotalEarningSupply(1_000); - - assertEq(_wrappedMToken.totalSupply(), 1_000); - } - - function test_totalSupply() external { - _wrappedMToken.setTotalEarningSupply(400); - - _wrappedMToken.setTotalNonEarningSupply(600); - - assertEq(_wrappedMToken.totalSupply(), 1_000); - - _wrappedMToken.setTotalEarningSupply(700); - - assertEq(_wrappedMToken.totalSupply(), 1_300); - - _wrappedMToken.setTotalNonEarningSupply(1_000); - - assertEq(_wrappedMToken.totalSupply(), 1_700); - } - - /* ============ currentIndex ============ */ - function test_currentIndex() external { - assertEq(_wrappedMToken.currentIndex(), 0); - - _mToken.setCurrentIndex(2 * _EXP_SCALED_ONE); - - assertEq(_wrappedMToken.currentIndex(), 0); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - - _wrappedMToken.enableEarning(); - - assertEq(_wrappedMToken.currentIndex(), 2 * _EXP_SCALED_ONE); - - _mToken.setCurrentIndex(3 * _EXP_SCALED_ONE); - - assertEq(_wrappedMToken.currentIndex(), 3 * _EXP_SCALED_ONE); - - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), false); - - _wrappedMToken.disableEarning(); - - assertEq(_wrappedMToken.currentIndex(), 3 * _EXP_SCALED_ONE); - - _mToken.setCurrentIndex(4 * _EXP_SCALED_ONE); - - assertEq(_wrappedMToken.currentIndex(), 3 * _EXP_SCALED_ONE); - } - - /* ============ misc ============ */ - function testFuzz_wrap_transfer_unwrap( - bool aliceIsEarning_, - uint240 aliceWrap_, - bool bobIsEarning_, - uint240 bobWrap_, - uint240 transfer_, - uint128 index_ - ) external { - _registrar.setListContains(_EARNERS_LIST, address(_wrappedMToken), true); - _earnerManager.setEarnerDetails(_alice, true, 0, address(0)); - _earnerManager.setEarnerDetails(_bob, true, 0, address(0)); - - _wrappedMToken.enableEarning(); - - _mToken.setCurrentIndex(index_ = uint128(bound(index_, _EXP_SCALED_ONE, 10 * _EXP_SCALED_ONE))); - - aliceWrap_ = uint240(bound(aliceWrap_, 0, _getMaxAmount(index_) / 3)); - bobWrap_ = uint240(bound(bobWrap_, 0, _getMaxAmount(index_) / 3)); - - _mToken.setBalanceOf(_alice, aliceWrap_); - _mToken.setBalanceOf(_bob, bobWrap_); - - if (aliceIsEarning_) { - _wrappedMToken.startEarningFor(_alice); - } - - if (aliceWrap_ != 0) { - vm.prank(_alice); - _wrappedMToken.wrap(_alice, aliceWrap_); - } - - _mToken.setCurrentIndex(index_ = uint128(bound(index_, index_, 10 * _EXP_SCALED_ONE))); - - if (bobIsEarning_) { - _wrappedMToken.startEarningFor(_bob); - } - - if (bobWrap_ != 0) { - vm.prank(_bob); - _wrappedMToken.wrap(_bob, bobWrap_); - } - - _mToken.setCurrentIndex(index_ = uint128(bound(index_, index_, 10 * _EXP_SCALED_ONE))); - - uint240 aliceYield_ = _wrappedMToken.accruedYieldOf(_alice); - uint240 bobYield_ = _wrappedMToken.accruedYieldOf(_bob); - - transfer_ = uint240(bound(transfer_, 0, _wrappedMToken.balanceWithYieldOf(_alice))); - - _mToken.setCurrentIndex(index_ = uint128(bound(index_, index_, 10 * _EXP_SCALED_ONE))); - - aliceYield_ += _wrappedMToken.accruedYieldOf(_alice); - - if (_wrappedMToken.balanceWithYieldOf(_alice) != 0) { - vm.prank(_alice); - _wrappedMToken.unwrap(_charlie); - } - - _mToken.setCurrentIndex(index_ = uint128(bound(index_, index_, 10 * _EXP_SCALED_ONE))); - - bobYield_ += _wrappedMToken.accruedYieldOf(_bob); - - if (_wrappedMToken.balanceWithYieldOf(_bob) != 0) { - vm.prank(_bob); - _wrappedMToken.unwrap(_charlie); - } - - assertEq(_wrappedMToken.totalEarningSupply(), 0); - assertEq(_wrappedMToken.totalNonEarningSupply(), 0); - - uint240 total_ = aliceWrap_ + aliceYield_ + bobWrap_ + bobYield_; - - if (total_ < 100e6) { - assertApproxEqAbs(_mToken.balanceOf(_charlie), total_, 100); - } else { - assertApproxEqRel(_mToken.balanceOf(_charlie), total_, 1e12); - } - } - - /* ============ utils ============ */ - function _getPrincipalAmountRoundedDown(uint240 presentAmount_, uint128 index_) internal pure returns (uint112) { - return IndexingMath.divide240By128Down(presentAmount_, index_); - } - - function _getPresentAmountRoundedDown(uint112 principalAmount_, uint128 index_) internal pure returns (uint240) { - return IndexingMath.multiply112By128Down(principalAmount_, index_); - } - - function _getMaxAmount(uint128 index_) internal pure returns (uint240 maxAmount_) { - return (uint240(type(uint112).max) * index_) / _EXP_SCALED_ONE; - } -} diff --git a/test/utils/Invariants.sol b/test/utils/Invariants.sol index 563488f..1eb7ae7 100644 --- a/test/utils/Invariants.sol +++ b/test/utils/Invariants.sol @@ -8,24 +8,24 @@ import { IERC20 } from "../../lib/common/src/interfaces/IERC20.sol"; import { IndexingMath } from "../../src/libs/IndexingMath.sol"; -import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; +import { ISmartMToken } from "../../src/interfaces/ISmartMToken.sol"; library Invariants { // Invariant 1: Sum of all accounts' balances is equal to total supply. // Invariant 1a: Sum of all non-earning accounts' balances is equal to total non-earning supply. // Invariant 1b: Sum of all earning accounts' balances is equal to total earning supply. - function checkInvariant1(address wrappedMToken_, address[] memory accounts_) internal view returns (bool success_) { + function checkInvariant1(address smartMToken_, address[] memory accounts_) internal view returns (bool success_) { uint256 totalNonEarningSupply_; uint256 totalEarningSupply_; uint256 totalSupply_; for (uint256 i_; i_ < accounts_.length; ++i_) { address account_ = accounts_[i_]; - uint256 balance_ = IWrappedMToken(wrappedMToken_).balanceOf(account_); + uint256 balance_ = ISmartMToken(smartMToken_).balanceOf(account_); totalSupply_ += balance_; - if (IWrappedMToken(wrappedMToken_).isEarning(account_)) { + if (ISmartMToken(smartMToken_).isEarning(account_)) { totalEarningSupply_ += balance_; } else { totalNonEarningSupply_ += balance_; @@ -35,72 +35,72 @@ library Invariants { // console2.log("Invariant 1: totalNonEarningSupply_ = %d", totalNonEarningSupply_); // console2.log( // "Invariant 1: totalNonEarningSupply() = %d", - // IWrappedMToken(wrappedMToken_).totalNonEarningSupply() + // ISmartMToken(smartMToken_).totalNonEarningSupply() // ); - if (totalNonEarningSupply_ != IWrappedMToken(wrappedMToken_).totalNonEarningSupply()) return false; + if (totalNonEarningSupply_ != ISmartMToken(smartMToken_).totalNonEarningSupply()) return false; // console2.log("Invariant 1: totalEarningSupply_ = %d", totalEarningSupply_); - // console2.log("Invariant 1: totalEarningSupply() = %d", IWrappedMToken(wrappedMToken_).totalEarningSupply()); + // console2.log("Invariant 1: totalEarningSupply() = %d", ISmartMToken(smartMToken_).totalEarningSupply()); - if (totalEarningSupply_ != IWrappedMToken(wrappedMToken_).totalEarningSupply()) return false; + if (totalEarningSupply_ != ISmartMToken(smartMToken_).totalEarningSupply()) return false; // console2.log("Invariant 1: totalSupply_ = %d", totalSupply_); - // console2.log("Invariant 1: totalSupply() = %d", IWrappedMToken(wrappedMToken_).totalSupply()); + // console2.log("Invariant 1: totalSupply() = %d", ISmartMToken(smartMToken_).totalSupply()); - if (totalSupply_ != IWrappedMToken(wrappedMToken_).totalSupply()) return false; + if (totalSupply_ != ISmartMToken(smartMToken_).totalSupply()) return false; return true; } // Invariant 2: Sum of all accounts' accrued yield is less than or equal to total accrued yield. - function checkInvariant2(address wrappedMToken_, address[] memory accounts_) internal view returns (bool success_) { + function checkInvariant2(address smartMToken_, address[] memory accounts_) internal view returns (bool success_) { uint256 totalAccruedYield_; for (uint256 i_; i_ < accounts_.length; ++i_) { - totalAccruedYield_ += IWrappedMToken(wrappedMToken_).accruedYieldOf(accounts_[i_]); + totalAccruedYield_ += ISmartMToken(smartMToken_).accruedYieldOf(accounts_[i_]); } // console2.log("Invariant 2: totalAccruedYield_ = %d", totalAccruedYield_); - // console2.log("Invariant 2: totalAccruedYield() = %d", IWrappedMToken(wrappedMToken_).totalAccruedYield()); + // console2.log("Invariant 2: totalAccruedYield() = %d", ISmartMToken(smartMToken_).totalAccruedYield()); - return IWrappedMToken(wrappedMToken_).totalAccruedYield() >= totalAccruedYield_; + return ISmartMToken(smartMToken_).totalAccruedYield() >= totalAccruedYield_; } // Invariant 3: M Balance of wrapper is greater or equal to total supply, accrued yield, and excess. - function checkInvariant3(address wrappedMToken_, address mToken_) internal view returns (bool success_) { + function checkInvariant3(address smartMToken_, address mToken_) internal view returns (bool success_) { // console2.log( // "Invariant 3: M Balance of wrapper = %d ", - // IERC20(mToken_).balanceOf(wrappedMToken_) + // IERC20(mToken_).balanceOf(smartMToken_) // ); // console2.log( // "Invariant 3: totalSupply() + totalAccruedYield() + excess() = %d", - // IWrappedMToken(wrappedMToken_).totalSupply() + - // IWrappedMToken(wrappedMToken_).totalAccruedYield() + - // IWrappedMToken(wrappedMToken_).excess() + // ISmartMToken(smartMToken_).totalSupply() + + // ISmartMToken(smartMToken_).totalAccruedYield() + + // ISmartMToken(smartMToken_).excess() // ); return - IERC20(mToken_).balanceOf(wrappedMToken_) >= - IWrappedMToken(wrappedMToken_).totalSupply() + - IWrappedMToken(wrappedMToken_).totalAccruedYield() + - IWrappedMToken(wrappedMToken_).excess(); + IERC20(mToken_).balanceOf(smartMToken_) >= + ISmartMToken(smartMToken_).totalSupply() + + ISmartMToken(smartMToken_).totalAccruedYield() + + ISmartMToken(smartMToken_).excess(); } // Invariant 4: Sum of all earning accounts' principals is less than or equal to principal of total earning supply. - function checkInvariant4(address wrappedMToken_, address[] memory accounts_) internal view returns (bool success_) { + function checkInvariant4(address smartMToken_, address[] memory accounts_) internal view returns (bool success_) { uint256 principalOfTotalEarningSupply_; for (uint256 i_; i_ < accounts_.length; ++i_) { address account_ = accounts_[i_]; - if (!IWrappedMToken(wrappedMToken_).isEarning(account_)) continue; + if (!ISmartMToken(smartMToken_).isEarning(account_)) continue; principalOfTotalEarningSupply_ += IndexingMath.getPrincipalAmountRoundedDown( - uint240(IWrappedMToken(wrappedMToken_).balanceOf(account_)), - IWrappedMToken(wrappedMToken_).lastIndexOf(account_) + uint240(ISmartMToken(smartMToken_).balanceOf(account_)), + ISmartMToken(smartMToken_).lastIndexOf(account_) ); } @@ -108,9 +108,9 @@ library Invariants { // console2.log( // "Invariant 2: principalOfTotalEarningSupply() = %d", - // IWrappedMToken(wrappedMToken_).principalOfTotalEarningSupply() + // ISmartMToken(smartMToken_).principalOfTotalEarningSupply() // ); - return IWrappedMToken(wrappedMToken_).principalOfTotalEarningSupply() >= principalOfTotalEarningSupply_; + return ISmartMToken(smartMToken_).principalOfTotalEarningSupply() >= principalOfTotalEarningSupply_; } } diff --git a/test/utils/WrappedMTokenHarness.sol b/test/utils/SmartMTokenHarness.sol similarity index 92% rename from test/utils/WrappedMTokenHarness.sol rename to test/utils/SmartMTokenHarness.sol index be0daa2..827db63 100644 --- a/test/utils/WrappedMTokenHarness.sol +++ b/test/utils/SmartMTokenHarness.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.26; -import { WrappedMToken } from "../../src/WrappedMToken.sol"; +import { SmartMToken } from "../../src/SmartMToken.sol"; -contract WrappedMTokenHarness is WrappedMToken { +contract SmartMTokenHarness is SmartMToken { constructor( address mToken_, address registrar_, address earnerManager_, address excessDestination_, address migrationAdmin_ - ) WrappedMToken(mToken_, registrar_, earnerManager_, excessDestination_, migrationAdmin_) {} + ) SmartMToken(mToken_, registrar_, earnerManager_, excessDestination_, migrationAdmin_) {} function internalWrap(address account_, address recipient_, uint240 amount_) external returns (uint240 wrapped_) { return _wrap(account_, recipient_, amount_);