You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Added code, tests, and scripts for Staking using ERC20s, native IMX, and native IMX where the IMX is held as WIMX. The contracts can be deployed simply, or via a time delay upgrade
Co-authored-by: w3njah <[email protected]>
Copy file name to clipboardExpand all lines: contracts/deployer/README.md
-2
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,5 @@
1
1
# Contract Deployers
2
2
3
-
At present, use of the Contract Deployers described below is limited to Immutable.
4
-
5
3
This directory provides two types of contract deployers: CREATE2 and CREATE3. Both deployer types facilitate contract deployment to predictable addresses, independent of the deployer account’s nonce. The deployers offer a more reliable alternative to using a Nonce Reserver Key (a key that is only used for deploying contracts, and has specific nonces reserved for deploying specific contracts), particularly across different chains. These factories can also be utilized for contracts that don't necessarily need predictable addresses. The advantage of this method, compared to using a deployer key in conjunction with a deployment factory contract, is that it can enable better standardisation and simplification of deployment processes and enables the rotation of the deployer key without impacting the consistency of the perceived deployer address for contracts.
6
4
7
5
Deployments via these factories can only be performed by the owner of the factory.
The Immutable zkEVM staking system consists of the Staking Holder contract. This contract holds staked native IMX. Any account (EOA or contract) can stake any amount at any time. An account can remove all or some of their stake at any time. The contract has the facility to distribute rewards to stakers.
3
+
The Immutable zkEVM staking system allows any account (EOA or contract) to stake any amount of a token at any time. An account can remove all or some of their stake at any time. The contract has the facility to distribute rewards to stakers.
4
+
5
+
The staking contracts are upgradeable and operate via a proxy contract. They use the [Universal Upgradeable Proxy Standard (UUPS)](https://eips.ethereum.org/EIPS/eip-1822) upgrade pattern, where the access control for upgrade resides within the application contract (the staking contract).
6
+
7
+
The system consists of a set of contracts show in the diagram below.
`IStakeHolder.sol` is the interface that all staking implementations comply with.
12
+
13
+
`StakeHolderBase.sol` is the abstract base contract that all staking implementation use.
14
+
15
+
`StakeHolderWIMX.sol` allows the native token, IMX, to be used as the staking currency. Stake is held as wrapped IMX, WIMX, an ERC20 token.
16
+
17
+
`StakeHolderERC20.sol` allows an ERC20 token to be used as the staking currency.
18
+
19
+
`StakeHolderNative.sol` uses the native token, IMX, to be used as the staking currency. Stake is held as native IMX.
20
+
21
+
`ERC1967Proxy.sol` is a proxy contract. All calls to StakeHolder contracts go via the ERC1967Proxy contract.
22
+
23
+
`TimelockController.sol` can be used with the staking contracts to provide a one week delay between when upgrade or other admin changes are proposed and when they are executed. See below for information on how to configure the time lock controller.
24
+
25
+
`OwnableCreate3Deployer.sol` ensures contracts are deployed to the same addresses across chains. The use of this contract is optional. See [deployment scripts](../../script/staking/README.md) for more information.
4
26
5
27
## Immutable Contract Addresses
6
28
29
+
StakeHolderERC20.sol configured with IMX as the staking token:
| Threat model | Oct 21, 2024 |[`fd982abc49884af41e05f18349b13edc9eefbc1e`](https://github.com/immutable/contracts/blob/fd982abc49884af41e05f18349b13edc9eefbc1e/contracts/staking/README.md)|[202410-threat-model-stake-holder.md](../../audits/staking/202410-threat-model-stake-holder.md)|
43
+
| Threat model | April 24, 2025 |[`bf327c7abdadd48fd51ae632500510ac2b07b5f0`](https://github.com/immutable/contracts/blob/bf327c7abdadd48fd51ae632500510ac2b07b5f0/contracts/staking/README.md)|[202504-threat-model-stake-holder.md](../../audits/staking/202504-threat-model-stake-holder.md)|
19
44
20
45
21
46
22
47
# Deployment
23
48
24
-
**Deploy and verify using CREATE3 factory contract:**
25
-
26
-
This repo includes a script for deploying via a CREATE3 factory contract. The script is defined as a test contract as per the examples [here](https://book.getfoundry.sh/reference/forge/forge-script#examples) and can be found in `./script/staking/DeployStakeHolder.sol`.
27
-
28
-
See the `.env.example` for required environment variables.
Optionally, you can also specify `--ledger` or `--trezor` for hardware deployments. See docs [here](https://book.getfoundry.sh/reference/forge/forge-script#wallet-options---hardware-wallet).
49
+
See [deployment scripts](../../script/staking/README.md).
35
50
36
51
37
52
# Usage
38
53
39
-
To stake, any account should call `stake()`, passing in the amount to be staked as the msg.value.
54
+
For StakeHolderERC20 and StakeHolderWIMX, the ERC20 staking token must be specified when the contract is being initialised. The token can not be changed.
55
+
56
+
To stake, any account should call `stake(uint256 _amount)`. For the WIMX and the native IMX variants, the amount to be staked must be passed in as the msg.value.
40
57
41
58
To unstake, the account that previously staked should call, `unstake(uint256 _amountToUnstake)`.
42
59
43
-
Accounts that have DISTRIBUTE_ROLE that wish to distribute rewards should call, `distributeRewards(AccountAmount[] calldata _recipientsAndAmounts)`. The `AccountAmount` structure consists of recipient address and amount to distribute pairs. Distributions can only be made to accounts that have previously or are currently staking. The amount to be distributed must be passed in as msg.value and must equal to the sum of the amounts specified in the `_recipientsAndAmounts` array.
60
+
Accounts that have DISTRIBUTE_ROLE that wish to distribute rewards should call, `distributeRewards(AccountAmount[] calldata _recipientsAndAmounts)`. The `AccountAmount` structure consists of recipient address and amount to distribute pairs. Distributions can only be made to accounts that have previously or are currently staking. For the WIMX and the native IMX variants, the amount to be distributed must be passed in as msg.value and must equal to the sum of the amounts specified in the `_recipientsAndAmounts` array.
44
61
45
62
The `stakers` array needs to be analysed to determine which accounts have staked and how much. The following functions provide access to this data structure:
46
63
@@ -51,11 +68,7 @@ The `stakers` array needs to be analysed to determine which accounts have staked
51
68
52
69
# Administration Notes
53
70
54
-
The `StakeHolder` contract is `AccessControlEnumerableUpgradeable`, with the following minor modification:
55
-
56
-
*`_revokeRole(bytes32 _role, address _account)` has been overridden to prevent the last DEFAULT_ADMIN_ROLE (the last role admin) from either being revoked or renounced.
57
-
58
-
The `StakeHolder` contract is `UUPSUpgradeable`. Only accounts with `UPGRADE_ROLE` are authorised to upgrade the contract.
71
+
The `StakeHolderBase` contract is `AccessControlEnumerableUpgradeable`. The `StakeHolderERC20` and `StakeHolderNative` contracts are `UUPSUpgradeable`. Only accounts with `UPGRADE_ROLE` are authorised to upgrade the contract.
59
72
60
73
## Upgrade Concept
61
74
@@ -67,3 +80,11 @@ The `upgradeStorage` function should be updated each new contract version. It sh
67
80
* The value is the same as the newt version: Someone (an attacker) has called the `upgradeStorage` function after the code has been upgraded. The function should revert.
68
81
* Based on the old code version and storage format indicated by the `version`, update the storage variables. Typically, upgrades only involve code changes, and require no storage variable changes. However, in some circumstances storage variables should also be updated.
69
82
* Update the `version` storage variable to indicate the new code version.
83
+
84
+
## Time Delay Upgrade and Admin
85
+
86
+
A staking systems may wish to delay upgrade actions and the granting of additional administrative access. To do this, the only account with UPGRADE_ROLE and DEFAULT_ADMIN_ROLE roles should be an instance of Open Zeppelin's [TimelockController](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/governance/TimelockController.sol). This ensures any upgrade proposals or proposals to add more accounts with `DEFAULT_ADMIN_ROLE`, `UPGRADE_ROLE` or `DISTRIBUTE_ROLE` must go through a time delay before being actioned. The account with `DEFAULT_ADMIN_ROLE` could choose to renounce this role to ensure the `TimelockController` can not be bypassed at a later date by having a compromised account with `DEFAULT_ADMIN_ROLE` adding addtional accounts with `UPGRADE_ROLE`.
87
+
88
+
## Preventing Upgrade
89
+
90
+
A staking system could choose to have no account with DEFAULT_ADMIN_ROLE to to prevent additional accounts being granted UPGRADE_ROLE role. The system could have no acccounts with UPGRADE_ROLE, thus preventing upgrade. The system could configure this from start-up by passing in `address(0)` as the `roleAdmin` and `upgradeAdmin` to the constructor. Alternative, the `revokeRole` function can be used to revoke the roles from accounts.
0 commit comments