Skip to content

Commit 8fa79de

Browse files
authored
feat: 98% safety rate multiplier in Earner rate model (#195)
1 parent b1c6e62 commit 8fa79de

6 files changed

Lines changed: 49 additions & 19 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# M^0 Protocol
1+
# M0 Protocol
22

33
## Overview
44

5-
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.
5+
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.
66

77
## Development
88

foundry.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ verbosity = 3
2020
ignored_error_codes = []
2121
block_number = 17_740_856
2222
block_timestamp = 1_689_934_508
23+
block_gas_limit = 3000000000
2324

2425
[profile.production]
2526
evm_version = "shanghai"

src/rateModels/EarnerRateModel.sol

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ contract EarnerRateModel is IEarnerRateModel {
2525
uint32 public constant RATE_CONFIDENCE_INTERVAL = 30 days;
2626

2727
/// @inheritdoc IEarnerRateModel
28-
uint32 public constant RATE_MULTIPLIER = 9_000; // 90% in basis points.
28+
uint32 public constant RATE_MULTIPLIER = 9_800; // 98% in basis points.
2929

3030
/// @inheritdoc IEarnerRateModel
3131
uint32 public constant ONE = 10_000; // 100% in basis points.
@@ -64,20 +64,34 @@ contract EarnerRateModel is IEarnerRateModel {
6464

6565
/// @inheritdoc IRateModel
6666
function rate() external view returns (uint256) {
67-
uint256 safeEarnerRate_ = getSafeEarnerRate(
68-
IMinterGateway(minterGateway).totalActiveOwedM(),
69-
IMToken(mToken).totalEarningSupply(),
70-
IMinterGateway(minterGateway).minterRate()
71-
);
72-
73-
return UIntMath.min256(maxRate(), (RATE_MULTIPLIER * safeEarnerRate_) / ONE);
67+
return
68+
UIntMath.min256(
69+
maxRate(),
70+
getExtraSafeEarnerRate(
71+
IMinterGateway(minterGateway).totalActiveOwedM(),
72+
IMToken(mToken).totalEarningSupply(),
73+
IMinterGateway(minterGateway).minterRate()
74+
)
75+
);
7476
}
7577

7678
/// @inheritdoc IEarnerRateModel
7779
function maxRate() public view returns (uint256) {
7880
return uint256(ITTGRegistrar(ttgRegistrar).get(_MAX_EARNER_RATE));
7981
}
8082

83+
/// @inheritdoc IEarnerRateModel
84+
function getExtraSafeEarnerRate(
85+
uint240 totalActiveOwedM_,
86+
uint240 totalEarningSupply_,
87+
uint32 minterRate_
88+
) public pure returns (uint32) {
89+
uint256 safeEarnerRate_ = getSafeEarnerRate(totalActiveOwedM_, totalEarningSupply_, minterRate_);
90+
uint256 extraSafeEarnerRate_ = (safeEarnerRate_ * RATE_MULTIPLIER) / ONE;
91+
92+
return (extraSafeEarnerRate_ > type(uint32).max) ? type(uint32).max : uint32(extraSafeEarnerRate_);
93+
}
94+
8195
/// @inheritdoc IEarnerRateModel
8296
function getSafeEarnerRate(
8397
uint240 totalActiveOwedM_,

src/rateModels/interfaces/IEarnerRateModel.sol

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,18 @@ interface IEarnerRateModel is IRateModel {
5555
uint240 totalEarningSupply,
5656
uint32 minterRate
5757
) external pure returns (uint32);
58+
59+
/**
60+
* @notice Returns the extra safe earner rate - safe earner rate adjusted by `RATE_MULTIPLIER`.
61+
* @dev `extraSafeEarnerRate = safeEarnerRate * RATE_MULTIPLIER`
62+
* @param totalActiveOwedM The total active owed M.
63+
* @param totalEarningSupply The total earning supply of M Token.
64+
* @param minterRate The minter rate.
65+
* @return The extra safe earner rate.
66+
*/
67+
function getExtraSafeEarnerRate(
68+
uint240 totalActiveOwedM,
69+
uint240 totalEarningSupply,
70+
uint32 minterRate
71+
) external pure returns (uint32);
5872
}

test/Deploy.t.sol

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,17 @@ contract Deploy is Test, DeployBase {
2424
}
2525

2626
function test_deploy() external {
27+
uint64 deployerNonce_ = vm.getNonce(address(this));
2728
(address minterGateway_, address minterRateModel_, address earnerRateModel_) = deploy(
2829
address(this),
29-
2,
30+
deployerNonce_,
3031
address(_ttgRegistrar)
3132
);
3233

33-
address mToken_ = getExpectedMToken(address(this), 2);
34+
address mToken_ = getExpectedMToken(address(this), deployerNonce_);
3435

3536
// Minter Gateway assertions
36-
assertEq(minterGateway_, getExpectedMinterGateway(address(this), 2));
37+
assertEq(minterGateway_, getExpectedMinterGateway(address(this), deployerNonce_));
3738
assertEq(IMinterGateway(minterGateway_).ttgRegistrar(), address(_ttgRegistrar));
3839
assertEq(IMinterGateway(minterGateway_).ttgVault(), _TTG_VAULT);
3940
assertEq(IMinterGateway(minterGateway_).mToken(), mToken_);
@@ -43,11 +44,11 @@ contract Deploy is Test, DeployBase {
4344
assertEq(IMToken(mToken_).ttgRegistrar(), address(_ttgRegistrar));
4445

4546
// Minter Rate Model assertions
46-
assertEq(minterRateModel_, getExpectedMinterRateModel(address(this), 2));
47+
assertEq(minterRateModel_, getExpectedMinterRateModel(address(this), deployerNonce_));
4748
assertEq(IMinterRateModel(minterRateModel_).ttgRegistrar(), address(_ttgRegistrar));
4849

4950
// Earner Rate Model assertions
50-
assertEq(earnerRateModel_, getExpectedEarnerRateModel(address(this), 2));
51+
assertEq(earnerRateModel_, getExpectedEarnerRateModel(address(this), deployerNonce_));
5152
assertEq(IEarnerRateModel(earnerRateModel_).mToken(), mToken_);
5253
assertEq(IEarnerRateModel(earnerRateModel_).minterGateway(), minterGateway_);
5354
assertEq(IEarnerRateModel(earnerRateModel_).ttgRegistrar(), address(_ttgRegistrar));

test/integration/minter-gateway/Integration.t.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ contract IntegrationTests is IntegrationBaseSetup {
5959
_minterGateway.burnM(_minters[0], aliceBalance);
6060

6161
assertEq(_minterGateway.activeOwedMOf(_minters[0]), 0);
62-
assertEq(_mToken.balanceOf(_alice), aliceBalance - minterOwedM - 1);
62+
assertEq(_mToken.balanceOf(_alice), aliceBalance - minterOwedM);
6363

6464
// Minter can mint again without imposing any penalties for missed collateral updates
6565
vm.warp(vm.getBlockTimestamp() + 60 days);
@@ -311,7 +311,7 @@ contract IntegrationTests is IntegrationBaseSetup {
311311
assertEq(_minterGateway.totalActiveOwedM(), 500_457040);
312312
assertEq(_mToken.totalEarningSupply(), 249_999999);
313313
assertEq(_minterGateway.minterRate(), 40_000);
314-
assertEq(_mToken.earnerRate(), 63_090);
314+
assertEq(_mToken.earnerRate(), 68_698);
315315

316316
uint256 timestamp_ = vm.getBlockTimestamp();
317317

@@ -387,9 +387,9 @@ contract IntegrationTests is IntegrationBaseSetup {
387387
assertGe(_minterGateway.totalOwedM(), _mToken.totalSupply());
388388

389389
assertEq(_minterGateway.totalActiveOwedM(), 500_000001);
390-
assertEq(_mToken.totalEarningSupply(), 1301_316149);
390+
assertEq(_mToken.totalEarningSupply(), 1301_433245);
391391
assertEq(_minterGateway.minterRate(), 40_000);
392-
assertEq(_mToken.earnerRate(), 13_832);
392+
assertEq(_mToken.earnerRate(), 15_059);
393393

394394
uint256 timestamp_ = vm.getBlockTimestamp();
395395

0 commit comments

Comments
 (0)