From 6777ffea143158f2317cd90470937c67b3bad358 Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 5 Nov 2024 11:34:16 -0500 Subject: [PATCH 1/6] Change rate multiplier to 98% --- src/rateModels/EarnerRateModel.sol | 15 ++++++++++++--- src/rateModels/interfaces/IEarnerRateModel.sol | 14 ++++++++++++++ test/integration/minter-gateway/Integration.t.sol | 8 ++++---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/rateModels/EarnerRateModel.sol b/src/rateModels/EarnerRateModel.sol index 5ce6098d..e40e02ee 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,13 @@ contract EarnerRateModel is IEarnerRateModel { /// @inheritdoc IRateModel function rate() external view returns (uint256) { - uint256 safeEarnerRate_ = getSafeEarnerRate( + uint256 extraSafeEarnerRate_ = getExtraSafeEarnerRate( IMinterGateway(minterGateway).totalActiveOwedM(), IMToken(mToken).totalEarningSupply(), IMinterGateway(minterGateway).minterRate() ); - return UIntMath.min256(maxRate(), (RATE_MULTIPLIER * safeEarnerRate_) / ONE); + return UIntMath.min256(maxRate(), extraSafeEarnerRate_); } /// @inheritdoc IEarnerRateModel @@ -78,6 +78,15 @@ contract EarnerRateModel is IEarnerRateModel { return uint256(ITTGRegistrar(ttgRegistrar).get(_MAX_EARNER_RATE)); } + function getExtraSafeEarnerRate( + uint240 totalActiveOwedM_, + uint240 totalEarningSupply_, + uint32 minterRate_ + ) public pure returns (uint256) { + uint256 safeEarnerRate_ = getSafeEarnerRate(totalActiveOwedM_, totalEarningSupply_, minterRate_); + return (RATE_MULTIPLIER * safeEarnerRate_) / ONE; + } + /// @inheritdoc IEarnerRateModel function getSafeEarnerRate( uint240 totalActiveOwedM_, diff --git a/src/rateModels/interfaces/IEarnerRateModel.sol b/src/rateModels/interfaces/IEarnerRateModel.sol index a84715c9..f2cb34c2 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 (uint256); } 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(); From 062e902e6522364323a4a77e44ee30eeff05ae7a Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 5 Nov 2024 12:28:42 -0500 Subject: [PATCH 2/6] change return type to uint32 --- src/rateModels/EarnerRateModel.sol | 7 +++++-- src/rateModels/interfaces/IEarnerRateModel.sol | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rateModels/EarnerRateModel.sol b/src/rateModels/EarnerRateModel.sol index e40e02ee..35b764d0 100644 --- a/src/rateModels/EarnerRateModel.sol +++ b/src/rateModels/EarnerRateModel.sol @@ -78,13 +78,16 @@ 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 (uint256) { + ) public pure returns (uint32) { uint256 safeEarnerRate_ = getSafeEarnerRate(totalActiveOwedM_, totalEarningSupply_, minterRate_); - return (RATE_MULTIPLIER * safeEarnerRate_) / ONE; + uint256 extraSafeEarnerRate_ = (safeEarnerRate_ * RATE_MULTIPLIER) / ONE; + + return (extraSafeEarnerRate_ > type(uint32).max) ? type(uint32).max : uint32(extraSafeEarnerRate_); } /// @inheritdoc IEarnerRateModel diff --git a/src/rateModels/interfaces/IEarnerRateModel.sol b/src/rateModels/interfaces/IEarnerRateModel.sol index f2cb34c2..4ede100d 100644 --- a/src/rateModels/interfaces/IEarnerRateModel.sol +++ b/src/rateModels/interfaces/IEarnerRateModel.sol @@ -68,5 +68,5 @@ interface IEarnerRateModel is IRateModel { uint240 totalActiveOwedM, uint240 totalEarningSupply, uint32 minterRate - ) external pure returns (uint256); + ) external pure returns (uint32); } From 29dda15ad51d1dbf9f333765fbad37f4fe4caad3 Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 5 Nov 2024 13:20:07 -0500 Subject: [PATCH 3/6] One more change to simplify rate function --- src/rateModels/EarnerRateModel.sol | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/rateModels/EarnerRateModel.sol b/src/rateModels/EarnerRateModel.sol index 35b764d0..be5bf286 100644 --- a/src/rateModels/EarnerRateModel.sol +++ b/src/rateModels/EarnerRateModel.sol @@ -64,13 +64,15 @@ contract EarnerRateModel is IEarnerRateModel { /// @inheritdoc IRateModel function rate() external view returns (uint256) { - uint256 extraSafeEarnerRate_ = getExtraSafeEarnerRate( - IMinterGateway(minterGateway).totalActiveOwedM(), - IMToken(mToken).totalEarningSupply(), - IMinterGateway(minterGateway).minterRate() - ); - - return UIntMath.min256(maxRate(), extraSafeEarnerRate_); + return + UIntMath.min256( + maxRate(), + getExtraSafeEarnerRate( + IMinterGateway(minterGateway).totalActiveOwedM(), + IMToken(mToken).totalEarningSupply(), + IMinterGateway(minterGateway).minterRate() + ) + ); } /// @inheritdoc IEarnerRateModel From 33f02b8e6bd295337b8ed9ce4b59372703b3ba50 Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 3 Jun 2025 22:50:26 -0400 Subject: [PATCH 4/6] Update foundry file --- foundry.toml | 1 + 1 file changed, 1 insertion(+) 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" From 2c98e7cc7634b1261824802c0e9883893f02aefd Mon Sep 17 00:00:00 2001 From: toninorair Date: Wed, 4 Jun 2025 09:09:07 -0400 Subject: [PATCH 5/6] Fix test with deployerNonce --- test/Deploy.t.sol | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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)); From 1b218f1a5a4d9fb66963fea5669530f25881bab5 Mon Sep 17 00:00:00 2001 From: toninorair Date: Wed, 4 Jun 2025 09:57:14 -0400 Subject: [PATCH 6/6] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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