Details for custodians, exchanges, and other services that intend to custody Celo assets such as Celo Dollar and CELO on behalf of a user.
Generally speaking, custodying CELO, the native token on the Celo network, requires understanding the various states that CELO can exist in at any time. This is to provide useful services beyond custody such as allowing users to lock up their CELO and vote with it. Many of these "states" are implemented as smart contracts, and involve sending CELO from a user owned account to a contract address. Thus, in order to be able to show a user's true balance, services need to be able to observe every balance changing operation and reconcile CELO balances from all the various contracts and states CELO can be in.
As a fork of Ethereum, Celo retains the account model to keep track of users' balances. Celo Dollar and CELO implement the ERC20 interface. As mentioned previously, it is common for smart contracts to hold balances on behalf of other addresses. One example is the
LockedGold smart contract that holds the "locked portion of a user's
CELO balance". Another one is the
ReleaseGold smart contract that holds
CELO that is being released to a beneficiary address over time according to some schedule.
Celo assets assets exist on an independent blockchain, and although they implement the ERC20 interface, they cannot be accessed through wallets that connect to the Ethereum network. Wallets and other integrations must connect to the Celo network to transfer tokens on Celo.
Applications that display balances may need to be written to be aware of this possibility.
CELO and Celo Dollars implement the ERC20 interface, as will any future core stable Celo currencies. CELO, as the native currency of the network, can also be transferred by specifying the value field of a transaction, in the same way that ETH can be transferred in Ethereum. Therefore, for CELO, application developers should be aware that transactions can be specified in both ways.
CELO State Machine
CELO as described previously can also exist in various states that represent a specific user behavior. For example, if a user wants to lock CELO to either participate in consensus directly or vote, that CELO will be sent to the
LockedGold smart contract. To understand the high level flow, please read this description of the various states CELO can exist in.
The following smart contracts are helpful to understand in order to map the conceptual states to actual accounts and function calls.
Accounts.sol allows the mapping of an address to an account in storage, after which all further functionality (locking, voting, etc.) can be accessed.
createAccount function indexes the address as an account in storage, and is required to differentiate an arbitrary key-pair from a user-owned account in the Celo network.
Accounts contract also allows for the authorization of various signer keys, such as a vote signer key. This allows for the user who owns the primary account key to authorize a separate key that can only vote on behalf of the account. This allows for the ability to custody keys in a manner corresponding to their exposure or "warmth". Eg. the primary account private key can be kept in cold storage after authorizing the signer keys, which can be in warmer environments, and potentially more exposed to the network. See the key management guide for more details.
LockedGold.sol, which references Celo Gold, the deprecated name for the native token, is used as part of Celo's proof-of-stake mechanism. Users can lock CELO by sending it to the
LockedGold contract after creating an account via the
Accounts contract as described above. This allows users to vote in validator elections, receive epoch rewards, and participate in on-chain governance.
There are two ways in which users can vote:
- Directly, by sending voting transactions with the same key used to lock up CELO
- Via an authorized vote signer, which can submit voting transactions on behalf of the account with locked CELO
LockedGold has a mapping of addresses to
balances which is a type that contains both the
nonvoting amount of CELO as well as
pendingWithdrawals, which contain values corresponding to timestamps at which they can be withdrawn. The reason for the latter is because all locked CELO has an unlocking period that is set at time of contract initialization, which is 3 days in the Celo network's deployed
LockedGold contract. Hence, if users unlock CELO in tranches, multiple pending withdrawals could exist at once. Once the timestamp has eclipsed, CELO can be withdrawn back to the user's address.
Once CELO has been locked via
LockedGold, it can then be used to vote for validator groups. Election.sol is the contract that manages this functionality.
votes in this contract are tracked by a Votes type which has
total votes. Pending votes are those that have been cast for a validator group, and active votes are those that have been activated after an epoch, meaning that these votes generate voter rewards.
Votes are cast for a validator group using the
vote function. This increments the
total votes in the
Election contract, and decrements the equivalent amount of CELO from the
nonvoting balance in the
LockedGold contract, for the associated account.
activate function can then be called to shift
pending votes into
active votes in a following epoch. Votes in either state can then be revoked, which decrements votes from the
Election contract and returns them to the
LockedGold balance for the associated account. Users can revoke votes at any time and this takes effect instantly.
A common problem in other proof-of-stake protocols is the tension between wanting early token holders' balances to release over time to ensure long-term alignment, while also wanting them to be able to participate in consensus to increase the security of the network. To bridge both goals, many early token balances in the Celo network are released via the
ReleaseGold contract. Beneficiaries of these contracts can then participate in the proof-of-stake system by staking and voting with CELO that has not yet been "released" for transfers. Please find more high level information about the
ReleaseGold contract here.
From a technical perspective,
ReleaseGold can be thought of as a "puppet" account controlled by the "puppeteer", or the beneficiary private key corresponding to the
beneficiary address in the contract. This beneficiary key can then authorize validator signer and vote signer keys that can then call respective functions associated with validating or voting. Most of the required function calls described above can be made by the signer keys directly to the
Election contracts associated with the
ReleaseGold account. However, some functions in the
ReleaseGold contract are proxied to the underlying
Election contracts, and have a separate function signature that can be called by the
beneficiary address. Notably:
authorizeVoteSignerand similar functions for other signer keys
Notice that all these functions have corresponding functions that are called on the underlying contract. The
ReleaseGold contract can then just be thought of as brokering the transaction to the correct place, when necessary.
Other Balance Changing Operations
In addition to transfers (both native and ERC-20) and locking / voting flows affecting user balances, there are also several additional Celo network features that may cause user balances to change:
- Gas fee payments: the fee paid by transaction senders to use the network
- Epoch rewards distribution: reward payments to voters, validators, and validator groups
Some of these may occur as events rather than transactions on the network, and therefore when updating balances, special attention should be paid to them.
Since monitoring balance changing operations is important to be able to display user balances properly, it can be helpful to use a tracing or reconciling system. Celo Rosetta is an RPC server that exposes an API to query the Celo blockchain, obtain balance changing operations, and construct airgapped transactions. With a special focus on getting balance change operations, Celo Rosetta provides an easy way to obtain changes that are not easily queryable using the celo-blockchain RPC.