Repository containing contracts relating to the DIVVI token.
The Divvi
contract is enabled to support arbitrary roles-based permissions. Currently,
semantics exist for only two roles, DEFAULT_ADMIN_ROLE
and MINTER_ROLE
.
-
DEFAULT_ADMIN_ROLE
is able to manage permissions (assigning/revoking addresses to roles), and is the sole address able to authorize contract upgrades. Only one address may possess this role. -
MINTER_ROLE
has access to privileged methods, namely, those that mint DIVVI tokens.
On deploy, the DEFAULT_ADMIN_ROLE
is granted to the Safe Multisig wallet we've configured. Since the DEFAULT_ADMIN_ROLE
possesses a lot of control over contract permissions, the contract is
set up to enable a handful of security measures around transferring the DEFAULT_ADMIN_ROLE
. These measures include:
- Requiring a two step process of initiating transfer, and accepting it on the new
DEFAULT_ADMIN_ROLE
holder's address. - Enabling a configurable delay between transfer being initiated, and the new
DEFAULT_ADMIN_ROLE
address being able to accept it.
By default, the transfer delay is set to one day. After deploy, the following steps should be taken. See the Operations section below.
- Safe wallet multisig calls
grantRole
withkeccak256("MINTER_ROLE")
as role on the HSM address to be used in the backend. - Safe wallet multisig calls
changeDefaultAdminDelay
to set a larger transfer delay, if desired.
For the purposes of deploying and upgrading, we maintain a number of operational wallets.
For Mainnet deplyments and upgrades, we maintain two Safe wallets. The first of these wallets is used strictly for proxy and implementation deployment, and may be shared among other services. The second wallet will act as the "owner" of the contract and can upgrade the contract, and manage roles.
Additionally, we maintain another Safe wallet for the Minter role, which will be used to mint tokens.
For Mainnet, the Safe addresses are as follows:
- Shared Deployer Safe: 0x215bde0Ec16d1358139F624d522361c431413754
- Owner Safe: 0x056E510DD2aDf29c068F84C8657D6dE80B19F139
- Minter Safe: 0x64f3196e4F8C7F3e4978bc5F79A043A49bb20fE2
We use OpenZeppelin Defender to manage deployments and upgrades on Mainnet. Before beginning a deployment, make sure that your .env
file is set up correctly. Namely, make sure to get the DEFENDER_API_KEY
, DEFENDER_API_SECRET
, ETHERSCAN_API_KEY
, ETHEREUM_RPC_URL
values from GSM and copy them in. (Ideally we could inject these config values into Hardhat automatically, but we haven't found a way to do that.) Also include OWNER_ADDRESS
to set the initial owner for the proxy. This should be the Owner Safe address listed above.
💡 For a smooth experience ensure you've been invited to Defender for Divvi and are added as a signer to the Deployer and Owner safes.
To deploy for the first time, run:
yarn hardhat run scripts/deployDivvi.ts --network mainnet
This will initiate a proxy deployment on OpenZeppelin Defender, which requires two steps to complete - the Shared Deployer Safe wallet must sign transactions to deploy both the proxy and implementation contracts. Find those steps in Defender Dashboard, in the "Deploy" section for the "Celo" production environment. It deploys the implementation contract first, then the proxy one. To approve the deployment, open the deployment details and click a button to open the Safe App. If the particular Safe requires multiple signatures, ask fellow engineers for additional ones in Slack.
After both deployments are signed and completed, you should see the output in your terminal with a command to run to verify both the proxy and implementation contracts on the block explorers.
To upgrade, ensure that the PROXY_ADDRESS
field is filled out in your .env
file, and run:
yarn hardhat run scripts/upgradeDivvi.ts --network mainnet
This will initiate a proxy upgrade on OpenZeppelin Defender, which also requires two steps to complete. First, the Deployer Safe wallet must sign a transaction to deploy the new implementation contract. Once this is done, the Owner Safe wallet must sign a transaction to upgrade the implementation of the proxy. After this is done, you should see output in your terminal with a command to run to verify both the proxy and implementation contracts on the block explorers.
Metadata about proxy and implementation deployments is automatically generated and stored in the .openzeppelin/
directory, which should be checked into version control.
💡 For a smooth experience ensure you've been added as a signer to both Shared Deployer Safe and Owner Safe listed above. All contract calls are initiated from the Safe UI using the "New Transaction" button.
- Current admin address calls
beginDefaultAdminTransfer
on the contract, with the address of the desired newDEFAULT_ADMIN_ROLE
. - Safe wallet multisig calls
acceptDefaultAdminTransfer
on the contract, transferring theDEFAULT_ADMIN_ROLE
. Note that the proposed newDEFAULT_ADMIN_ROLE
holder must wait out the transfer delay before calling this method. - At any point after initiating the transfer, but before the proposed recipient accepts, the current
DEFAULT_ADMIN_ROLE
holder can callcancelDefaultAdminTransfer
to revoke the transfer.
- Current admin address calls
grantRole
withkeccak256("MINTER_ROLE")
and address to be granted role.
- Current admin address calls
revokeRole
withkeccak256("MINTER_ROLE")
and address to have role revoked.
- Current admin address calls
changeDefaultAdminDelay
with the delay to set in number of seconds. - At any point before the current
defaultAdminDelay
period elapses, admin address can callrollbackDefaultAdminDelay
to cancel the update.