diff --git a/README.md b/README.md index 2048b496..43a883b7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# M^0 Protocol +# M0 Protocol ## Overview -M^0 is an EVM-compatible, immutable protocol that enables minting and burning of the ERC20 token $M. It also allows for $M distributions to yield earners and governance token ($ZERO) holders. There are three main types of actors in the protocol - Minters, Validators, and Yield Earners - all of which are permissioned via governance. Protocol variables are also managed by governance and are stored in a Registrar configuration contract. +M0 is an EVM-compatible, immutable protocol that enables minting and burning of the ERC20 token $M. It also allows for $M distributions to yield earners and governance token ($ZERO) holders. There are three main types of actors in the protocol - Minters, Validators, and Yield Earners - all of which are permissioned via governance. Protocol variables are also managed by governance and are stored in a Registrar configuration contract. ## Development diff --git a/foundry.toml b/foundry.toml index 5210c6c2..eb7a1e95 100644 --- a/foundry.toml +++ b/foundry.toml @@ -20,6 +20,7 @@ verbosity = 3 ignored_error_codes = [] block_number = 17_740_856 block_timestamp = 1_689_934_508 +block_gas_limit = 3000000000 [profile.production] evm_version = "shanghai" diff --git a/src/rateModels/EarnerRateModel.sol b/src/rateModels/EarnerRateModel.sol index 5ce6098d..be5bf286 100644 --- a/src/rateModels/EarnerRateModel.sol +++ b/src/rateModels/EarnerRateModel.sol @@ -25,7 +25,7 @@ contract EarnerRateModel is IEarnerRateModel { uint32 public constant RATE_CONFIDENCE_INTERVAL = 30 days; /// @inheritdoc IEarnerRateModel - uint32 public constant RATE_MULTIPLIER = 9_000; // 90% in basis points. + uint32 public constant RATE_MULTIPLIER = 9_800; // 98% in basis points. /// @inheritdoc IEarnerRateModel uint32 public constant ONE = 10_000; // 100% in basis points. @@ -64,13 +64,15 @@ contract EarnerRateModel is IEarnerRateModel { /// @inheritdoc IRateModel function rate() external view returns (uint256) { - uint256 safeEarnerRate_ = getSafeEarnerRate( - IMinterGateway(minterGateway).totalActiveOwedM(), - IMToken(mToken).totalEarningSupply(), - IMinterGateway(minterGateway).minterRate() - ); - - return UIntMath.min256(maxRate(), (RATE_MULTIPLIER * safeEarnerRate_) / ONE); + return + UIntMath.min256( + maxRate(), + getExtraSafeEarnerRate( + IMinterGateway(minterGateway).totalActiveOwedM(), + IMToken(mToken).totalEarningSupply(), + IMinterGateway(minterGateway).minterRate() + ) + ); } /// @inheritdoc IEarnerRateModel @@ -78,6 +80,18 @@ contract EarnerRateModel is IEarnerRateModel { return uint256(ITTGRegistrar(ttgRegistrar).get(_MAX_EARNER_RATE)); } + /// @inheritdoc IEarnerRateModel + function getExtraSafeEarnerRate( + uint240 totalActiveOwedM_, + uint240 totalEarningSupply_, + uint32 minterRate_ + ) public pure returns (uint32) { + uint256 safeEarnerRate_ = getSafeEarnerRate(totalActiveOwedM_, totalEarningSupply_, minterRate_); + uint256 extraSafeEarnerRate_ = (safeEarnerRate_ * RATE_MULTIPLIER) / ONE; + + return (extraSafeEarnerRate_ > type(uint32).max) ? type(uint32).max : uint32(extraSafeEarnerRate_); + } + /// @inheritdoc IEarnerRateModel function getSafeEarnerRate( uint240 totalActiveOwedM_, diff --git a/src/rateModels/interfaces/IEarnerRateModel.sol b/src/rateModels/interfaces/IEarnerRateModel.sol index a84715c9..4ede100d 100644 --- a/src/rateModels/interfaces/IEarnerRateModel.sol +++ b/src/rateModels/interfaces/IEarnerRateModel.sol @@ -55,4 +55,18 @@ interface IEarnerRateModel is IRateModel { uint240 totalEarningSupply, uint32 minterRate ) external pure returns (uint32); + + /** + * @notice Returns the extra safe earner rate - safe earner rate adjusted by `RATE_MULTIPLIER`. + * @dev `extraSafeEarnerRate = safeEarnerRate * RATE_MULTIPLIER` + * @param totalActiveOwedM The total active owed M. + * @param totalEarningSupply The total earning supply of M Token. + * @param minterRate The minter rate. + * @return The extra safe earner rate. + */ + function getExtraSafeEarnerRate( + uint240 totalActiveOwedM, + uint240 totalEarningSupply, + uint32 minterRate + ) external pure returns (uint32); } diff --git a/test/Deploy.t.sol b/test/Deploy.t.sol index 2bec989a..ccada0cb 100644 --- a/test/Deploy.t.sol +++ b/test/Deploy.t.sol @@ -24,16 +24,17 @@ contract Deploy is Test, DeployBase { } function test_deploy() external { + uint64 deployerNonce_ = vm.getNonce(address(this)); (address minterGateway_, address minterRateModel_, address earnerRateModel_) = deploy( address(this), - 2, + deployerNonce_, address(_ttgRegistrar) ); - address mToken_ = getExpectedMToken(address(this), 2); + address mToken_ = getExpectedMToken(address(this), deployerNonce_); // Minter Gateway assertions - assertEq(minterGateway_, getExpectedMinterGateway(address(this), 2)); + assertEq(minterGateway_, getExpectedMinterGateway(address(this), deployerNonce_)); assertEq(IMinterGateway(minterGateway_).ttgRegistrar(), address(_ttgRegistrar)); assertEq(IMinterGateway(minterGateway_).ttgVault(), _TTG_VAULT); assertEq(IMinterGateway(minterGateway_).mToken(), mToken_); @@ -43,11 +44,11 @@ contract Deploy is Test, DeployBase { assertEq(IMToken(mToken_).ttgRegistrar(), address(_ttgRegistrar)); // Minter Rate Model assertions - assertEq(minterRateModel_, getExpectedMinterRateModel(address(this), 2)); + assertEq(minterRateModel_, getExpectedMinterRateModel(address(this), deployerNonce_)); assertEq(IMinterRateModel(minterRateModel_).ttgRegistrar(), address(_ttgRegistrar)); // Earner Rate Model assertions - assertEq(earnerRateModel_, getExpectedEarnerRateModel(address(this), 2)); + assertEq(earnerRateModel_, getExpectedEarnerRateModel(address(this), deployerNonce_)); assertEq(IEarnerRateModel(earnerRateModel_).mToken(), mToken_); assertEq(IEarnerRateModel(earnerRateModel_).minterGateway(), minterGateway_); assertEq(IEarnerRateModel(earnerRateModel_).ttgRegistrar(), address(_ttgRegistrar)); diff --git a/test/integration/minter-gateway/Integration.t.sol b/test/integration/minter-gateway/Integration.t.sol index 6ce37fa2..f62028cb 100644 --- a/test/integration/minter-gateway/Integration.t.sol +++ b/test/integration/minter-gateway/Integration.t.sol @@ -59,7 +59,7 @@ contract IntegrationTests is IntegrationBaseSetup { _minterGateway.burnM(_minters[0], aliceBalance); assertEq(_minterGateway.activeOwedMOf(_minters[0]), 0); - assertEq(_mToken.balanceOf(_alice), aliceBalance - minterOwedM - 1); + assertEq(_mToken.balanceOf(_alice), aliceBalance - minterOwedM); // Minter can mint again without imposing any penalties for missed collateral updates vm.warp(vm.getBlockTimestamp() + 60 days); @@ -311,7 +311,7 @@ contract IntegrationTests is IntegrationBaseSetup { assertEq(_minterGateway.totalActiveOwedM(), 500_457040); assertEq(_mToken.totalEarningSupply(), 249_999999); assertEq(_minterGateway.minterRate(), 40_000); - assertEq(_mToken.earnerRate(), 63_090); + assertEq(_mToken.earnerRate(), 68_698); uint256 timestamp_ = vm.getBlockTimestamp(); @@ -387,9 +387,9 @@ contract IntegrationTests is IntegrationBaseSetup { assertGe(_minterGateway.totalOwedM(), _mToken.totalSupply()); assertEq(_minterGateway.totalActiveOwedM(), 500_000001); - assertEq(_mToken.totalEarningSupply(), 1301_316149); + assertEq(_mToken.totalEarningSupply(), 1301_433245); assertEq(_minterGateway.minterRate(), 40_000); - assertEq(_mToken.earnerRate(), 13_832); + assertEq(_mToken.earnerRate(), 15_059); uint256 timestamp_ = vm.getBlockTimestamp();