-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
change staking emission from 1 to 2 percent
feat: add ecosystemFund feat: mint to ecosystem fund upd: emissionRate to 1.03 upd: emissionManager version -> 1.1.0 wip: update emission manager tests fix tests docs fix distribution adjust m1ntCap move natspec to interfaces
- Loading branch information
1 parent
1f2d13f
commit acffed0
Showing
10 changed files
with
193 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
/coverage | ||
lcov.info | ||
.DS_Store | ||
.vscode | ||
broadcast/ | ||
.env | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,15 +9,14 @@ import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/Safe | |
import {PowUtil} from "./lib/PowUtil.sol"; | ||
|
||
/// @title Default Emission Manager | ||
/// @author Polygon Labs (@DhairyaSethi, @gretzke, @qedk) | ||
/// @author Polygon Labs (@DhairyaSethi, @gretzke, @qedk, @simonDos) | ||
/// @notice A default emission manager implementation for the Polygon ERC20 token contract on Ethereum L1 | ||
/// @dev The contract allows for a 1% mint *each* per year (compounded every year) to the stakeManager and treasury contracts | ||
/// @dev The contract allows for a 3% mint per year (compounded). 2% staking layer and 1% treasury | ||
/// @custom:security-contact [email protected] | ||
contract DefaultEmissionManager is Ownable2StepUpgradeable, IDefaultEmissionManager { | ||
using SafeERC20 for IPolygonEcosystemToken; | ||
|
||
// log2(2%pa continuously compounded emission per year) in 18 decimals, see _inflatedSupplyAfter | ||
uint256 public constant INTEREST_PER_YEAR_LOG2 = 0.028569152196770894e18; | ||
uint256 public constant INTEREST_PER_YEAR_LOG2 = 0.04264433740849372e18; | ||
uint256 public constant START_SUPPLY = 10_000_000_000e18; | ||
address private immutable DEPLOYER; | ||
|
||
|
@@ -54,9 +53,7 @@ contract DefaultEmissionManager is Ownable2StepUpgradeable, IDefaultEmissionMana | |
_transferOwnership(owner_); | ||
} | ||
|
||
/// @notice Allows anyone to mint tokens to the stakeManager and treasury contracts based on current emission rates | ||
/// @dev Minting is done based on totalSupply diffs between the currentTotalSupply (maintained on POL, which includes any | ||
/// previous mints) and the newSupply (calculated based on the time elapsed since deployment) | ||
/// @inheritdoc IDefaultEmissionManager | ||
function mint() external { | ||
uint256 currentSupply = token.totalSupply(); // totalSupply after the last mint | ||
uint256 newSupply = inflatedSupplyAfter( | ||
|
@@ -65,7 +62,7 @@ contract DefaultEmissionManager is Ownable2StepUpgradeable, IDefaultEmissionMana | |
uint256 amountToMint = newSupply - currentSupply; | ||
if (amountToMint == 0) return; // no minting required | ||
|
||
uint256 treasuryAmt = amountToMint / 2; | ||
uint256 treasuryAmt = amountToMint / 3; | ||
uint256 stakeManagerAmt = amountToMint - treasuryAmt; | ||
|
||
emit TokenMint(amountToMint, msg.sender); | ||
|
@@ -77,28 +74,16 @@ contract DefaultEmissionManager is Ownable2StepUpgradeable, IDefaultEmissionMana | |
migration.unmigrateTo(stakeManager, stakeManagerAmt); | ||
} | ||
|
||
/// @notice Returns total supply from compounded emission after timeElapsed from startTimestamp (deployment) | ||
/// @param timeElapsed The time elapsed since startTimestamp | ||
/// @dev interestRatePerYear = 1.02; 2% per year | ||
/// approximate the compounded interest rate using x^y = 2^(log2(x)*y) | ||
/// where x is the interest rate per year and y is the number of seconds elapsed since deployment divided by 365 days in seconds | ||
/// log2(interestRatePerYear) = 0.028569152196770894 with 18 decimals, as the interest rate does not change, hard code the value | ||
/// @return supply total supply from compounded emission after timeElapsed | ||
/// @inheritdoc IDefaultEmissionManager | ||
function inflatedSupplyAfter(uint256 timeElapsed) public pure returns (uint256 supply) { | ||
uint256 supplyFactor = PowUtil.exp2((INTEREST_PER_YEAR_LOG2 * timeElapsed) / 365 days); | ||
supply = (supplyFactor * START_SUPPLY) / 1e18; | ||
} | ||
|
||
/// @notice Returns the implementation version | ||
/// @return Version string | ||
/// @inheritdoc IDefaultEmissionManager | ||
function getVersion() external pure returns (string memory) { | ||
return "1.0.0"; | ||
return "1.1.0"; | ||
} | ||
|
||
/** | ||
* @dev This empty reserved space is put in place to allow future versions to add new | ||
* variables without shifting down storage in the inheritance chain. | ||
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | ||
*/ | ||
uint256[48] private __gap; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ import {AccessControlEnumerable} from "openzeppelin-contracts/contracts/access/A | |
import {IPolygonEcosystemToken} from "./interfaces/IPolygonEcosystemToken.sol"; | ||
|
||
/// @title Polygon ERC20 token | ||
/// @author Polygon Labs (@DhairyaSethi, @gretzke, @qedk) | ||
/// @author Polygon Labs (@DhairyaSethi, @gretzke, @qedk, @simonDos) | ||
/// @notice This is the Polygon ERC20 token contract on Ethereum L1 | ||
/// @dev The contract allows for a 1-to-1 representation between $POL and $MATIC and allows for additional emission based on hub and treasury requirements | ||
/// @custom:security-contact [email protected] | ||
|
@@ -15,7 +15,7 @@ contract PolygonEcosystemToken is ERC20Permit, AccessControlEnumerable, IPolygon | |
bytes32 public constant CAP_MANAGER_ROLE = keccak256("CAP_MANAGER_ROLE"); | ||
bytes32 public constant PERMIT2_REVOKER_ROLE = keccak256("PERMIT2_REVOKER_ROLE"); | ||
address public constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; | ||
uint256 public mintPerSecondCap = 10e18; // 10 POL tokens per second | ||
uint256 public mintPerSecondCap = 13.37e18; | ||
uint256 public lastMint; | ||
bool public permit2Enabled; | ||
|
||
|
@@ -41,10 +41,7 @@ contract PolygonEcosystemToken is ERC20Permit, AccessControlEnumerable, IPolygon | |
_updatePermit2Allowance(true); | ||
} | ||
|
||
/// @notice Mint token entrypoint for the emission manager contract | ||
/// @dev The function only validates the sender, the emission manager is responsible for correctness | ||
/// @param to Address to mint to | ||
/// @param amount Amount to mint | ||
/// @inheritdoc IPolygonEcosystemToken | ||
function mint(address to, uint256 amount) external onlyRole(EMISSION_ROLE) { | ||
uint256 timeElapsedSinceLastMint = block.timestamp - lastMint; | ||
uint256 maxMint = timeElapsedSinceLastMint * mintPerSecondCap; | ||
|
@@ -54,31 +51,26 @@ contract PolygonEcosystemToken is ERC20Permit, AccessControlEnumerable, IPolygon | |
_mint(to, amount); | ||
} | ||
|
||
/// @notice Update the limit of tokens that can be minted per second | ||
/// @param newCap the amount of tokens in 18 decimals as an absolute value | ||
/// @inheritdoc IPolygonEcosystemToken | ||
function updateMintCap(uint256 newCap) external onlyRole(CAP_MANAGER_ROLE) { | ||
emit MintCapUpdated(mintPerSecondCap, newCap); | ||
mintPerSecondCap = newCap; | ||
} | ||
|
||
/// @notice Manages the default max approval to the permit2 contract | ||
/// @param enabled If true, the permit2 contract has full approval by default, if false, it has no approval by default | ||
/// @inheritdoc IPolygonEcosystemToken | ||
function updatePermit2Allowance(bool enabled) external onlyRole(PERMIT2_REVOKER_ROLE) { | ||
_updatePermit2Allowance(enabled); | ||
} | ||
|
||
/// @notice The permit2 contract has full approval by default. If the approval is revoked, it can still be manually approved. | ||
/// @dev The permit2 contract has full approval by default. If the approval is revoked, it can still be manually approved. | ||
function allowance(address owner, address spender) public view override(ERC20, IERC20) returns (uint256) { | ||
if (spender == PERMIT2 && permit2Enabled) return type(uint256).max; | ||
return super.allowance(owner, spender); | ||
} | ||
|
||
/// @notice Returns the implementation version | ||
/// @dev This is to support our dev pipeline, and is present despite | ||
/// this contract not being behind a proxy | ||
/// @return Version string | ||
/// @inheritdoc IPolygonEcosystemToken | ||
function getVersion() external pure returns (string memory) { | ||
return "1.0.0"; | ||
return "1.1.0"; | ||
} | ||
|
||
function _updatePermit2Allowance(bool enabled) private { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,18 +3,45 @@ pragma solidity 0.8.21; | |
|
||
import {IPolygonEcosystemToken} from "./IPolygonEcosystemToken.sol"; | ||
|
||
/// @title Default Emission Manager | ||
/// @author Polygon Labs (@DhairyaSethi, @gretzke, @qedk, @simonDos) | ||
/// @notice A default emission manager implementation for the Polygon ERC20 token contract on Ethereum L1 | ||
/// @dev The contract allows for a 3% mint per year (compounded). 2% staking layer and 1% treasury | ||
/// @custom:security-contact [email protected] | ||
interface IDefaultEmissionManager { | ||
/// @notice emitted when new tokens are minted | ||
/// @param amount the amount of tokens minted | ||
/// @param caller the caller of the mint function | ||
event TokenMint(uint256 amount, address caller); | ||
|
||
/// @notice thrown when a zero address is supplied during deployment | ||
error InvalidAddress(); | ||
|
||
event TokenMint(uint256 amount, address caller); | ||
/// @notice allows anyone to mint tokens to the stakeManager and treasury contracts based on current emission rates | ||
/// @dev minting is done based on totalSupply diffs between the currentTotalSupply (maintained on POL, which includes any previous mints) and the newSupply (calculated based on the time elapsed since deployment) | ||
function mint() external; | ||
|
||
function getVersion() external pure returns (string memory version); | ||
/// @return log2(3%pa continuously compounded emission per year) in 18 decimals, see _inflatedSupplyAfter | ||
function INTEREST_PER_YEAR_LOG2() external view returns (uint256); | ||
|
||
/// @return the start supply of the POL token in 18 decimals | ||
function START_SUPPLY() external view returns (uint256); | ||
|
||
/// @return polygonEcosystemToken address of the POL token | ||
function token() external view returns (IPolygonEcosystemToken polygonEcosystemToken); | ||
|
||
/// @return timestamp timestamp of initialisation of the contract, when emission starts | ||
function startTimestamp() external view returns (uint256 timestamp); | ||
|
||
function mint() external; | ||
|
||
/// @notice returns total supply from compounded emission after timeElapsed from startTimestamp (deployment) | ||
/// @param timeElapsedInSeconds the time elapsed since startTimestamp | ||
/// @return inflatedSupply supply total supply from compounded emission after timeElapsed | ||
/// @dev interestRatePerYear = 1.03; 3% per year | ||
/// approximate the compounded interest rate using x^y = 2^(log2(x)*y) | ||
/// where x is the interest rate per year and y is the number of seconds elapsed since deployment divided by 365 days in seconds | ||
/// log2(interestRatePerYear) = 0.04264433740849372 with 18 decimals, as the interest rate does not change, hard code the value | ||
function inflatedSupplyAfter(uint256 timeElapsedInSeconds) external pure returns (uint256 inflatedSupply); | ||
|
||
/// @return version the implementation version | ||
function getVersion() external pure returns (string memory version); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,24 +5,67 @@ import {IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensi | |
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; | ||
import {IAccessControlEnumerable} from "openzeppelin-contracts/contracts/access/AccessControlEnumerable.sol"; | ||
|
||
/// @title Polygon ERC20 token | ||
/// @author Polygon Labs (@DhairyaSethi, @gretzke, @qedk, @simonDos) | ||
/// @notice This is the Polygon ERC20 token contract on Ethereum L1 | ||
/// @dev The contract allows for a 1-to-1 representation between $POL and $MATIC and allows for additional emission based on hub and treasury requirements | ||
/// @custom:security-contact [email protected] | ||
interface IPolygonEcosystemToken is IERC20, IERC20Permit, IAccessControlEnumerable { | ||
/// @notice emitted when the mint cap is updated | ||
/// @param oldCap the old mint cap | ||
/// @param newCap the new mint cap | ||
event MintCapUpdated(uint256 oldCap, uint256 newCap); | ||
|
||
/// @notice emitted when the permit2 integration is enabled/disabled | ||
/// @param enabled whether the permit2 integration is enabled or not | ||
event Permit2AllowanceUpdated(bool enabled); | ||
|
||
/// @notice thrown when a zero address is supplied during deployment | ||
error InvalidAddress(); | ||
|
||
/// @notice thrown when the mint cap is exceeded | ||
/// @param maxMint the maximum amount of tokens that can be minted | ||
/// @param mintRequested the amount of tokens that were requested to be minted | ||
error MaxMintExceeded(uint256 maxMint, uint256 mintRequested); | ||
|
||
function mintPerSecondCap() external view returns (uint256 currentMintPerSecondCap); | ||
/// @notice mint token entrypoint for the emission manager contract | ||
/// @param to address to mint to | ||
/// @param amount amount to mint | ||
/// @dev The function only validates the sender, the emission manager is responsible for correctness | ||
function mint(address to, uint256 amount) external; | ||
|
||
function getVersion() external pure returns (string memory version); | ||
/// @notice update the limit of tokens that can be minted per second | ||
/// @param newCap the amount of tokens in 18 decimals as an absolute value | ||
function updateMintCap(uint256 newCap) external; | ||
|
||
function lastMint() external view returns (uint256 lastMintTimestamp); | ||
/// @notice manages the default max approval to the permit2 contract | ||
/// @param enabled If true, the permit2 contract has full approval by default, if false, it has no approval by default | ||
function updatePermit2Allowance(bool enabled) external; | ||
|
||
function permit2Enabled() external view returns (bool isPermit2Enabled); | ||
/// @return the role that allows minting of tokens | ||
function EMISSION_ROLE() external view returns (bytes32); | ||
|
||
function mint(address to, uint256 amount) external; | ||
/// @return the role that allows updating the mint cap | ||
function CAP_MANAGER_ROLE() external view returns (bytes32); | ||
|
||
function updateMintCap(uint256 newCap) external; | ||
/// @return the role that allows revoking the permit2 approval | ||
function PERMIT2_REVOKER_ROLE() external view returns (bytes32); | ||
|
||
function updatePermit2Allowance(bool enabled) external; | ||
/// @return the address of the permit2 contract | ||
function PERMIT2() external view returns (address); | ||
|
||
/// @return currentMintPerSecondCap the current amount of tokens that can be minted per second | ||
/// @dev 13.37 POL tokens per second. will limit emission in ~23 years | ||
function mintPerSecondCap() external view returns (uint256 currentMintPerSecondCap); | ||
|
||
/// @return lastMintTimestamp the timestamp of the last mint | ||
function lastMint() external view returns (uint256 lastMintTimestamp); | ||
|
||
/// @return isPermit2Enabled whether the permit2 default approval is currently active | ||
function permit2Enabled() external view returns (bool isPermit2Enabled); | ||
|
||
/// @notice returns the version of the contract | ||
/// @return version version string | ||
/// @dev this is to support our dev pipeline, and is present despite this contract not being behind a proxy | ||
function getVersion() external pure returns (string memory version); | ||
} |
Oops, something went wrong.