Stake with Code
This guide will show you how to stake ZCX by directly interacting with ZenChain's staking smart contracts.
Staking Smart Contracts
ZenChain supports staking through precompiled smart contracts. These precompiles expose the staking functionality as smart contracts that you can interact with, yet are more computationally efficient than a user-deployed smart contract.
Unless stated otherwise, the smart contract methods referenced in this document can be found in the NativeStaking
precompile.
For more detail, see the section on Precompiled Smart Contracts.
Contract | Ethereum Address | Solidity Interface |
---|---|---|
NativeStaking | 0x0000000000000000000000000000000000000800 | GitHub |
NativeFastUnstake | 0x0000000000000000000000000000000000000801 | GitHub |
Stake
Staking involves two steps: bonding tokens and activating your stake.
Bond ZCX
To start staking, you first need to bond your tokens using one of two methods: bondWithRewardDestination
or bondWithPayeeAddress
. The choice of these two methods depends on where you want your rewards to go when they are claimed.
Function | Description |
---|---|
bondWithRewardDestination(value, dest) | Stake tokens and set reward destination. |
bondWithPayeeAddress(value, payee) | Stake tokens and set a custom payee address for rewards. |
The method bondWithRewardDestination
requires you to supply a reward destination from the RewardDestination
enum.
enum RewardDestination {
Staked,
Stash,
None
}
Let's say you want to stake 1,000 ZCX. You could do so with any of the following method calls.
- To restake rewards for compounding gains:
bondWithRewardDestination(1_000_000_000_000_000_000_000, 0)
- To send rewards to your address:
bondWithRewardDestination(1_000_000_000_000_000_000_000, 1)
- To receive no rewards:
bondWithRewardDestination(1_000_000_000_000_000_000_000, 2)
- To send rewards to another account:
bondWithPayeeAddress(1_000_000_000_000_000_000_000, 0x...)
To participate in consensus and earn rewards, you must bond a minimum amount of ZCX. Learn more about staking and the minimum amount of staked ZCX required for validators and nominators.
Activate Your Stake
Once you've bonded ZCX, you can declare your intention to participate in consensus, secure the blockchain, and earn rewards. To become a validator, you will call the validate
method. To nominate validators, you will call the nominate
method. You can only be either a nominator or a validator. You cannot do both simultaneously.
Function | Description |
---|---|
validate(commission, blocked) | Declare intent to become a validator with specified commission and blocking status. |
nominate(targets) | As a nominator, select the validators you want to back. |
The validate
method takes two parameters. The commission
parameter represented as parts per billion. The value can range from 0 to 1_000_000_000. For example, 10% is represented as 100_000_000 (0.1 * 10^9). The blocked
parameter, if set to true, allows the validator to prevent new nominations.
- To validate with a 0% commission rate:
validate(0, false)
- To validate with a 15% commission rate:
validate(150_000_000, false)
- To validate with a 100% commission rate and block nominations:
validate(1_000_000_000, true)
The nominate
method takes an array of validator addresses corresponding to the validators the caller wants to nominate. A nominator can select up to 16 targets. Each call to nominate
will overwrite the values in the previous call, so you can update your nominations by calling nominate
again with the full set of validators you want to support.
- To nominate two validators:
nominate(["0x46A148316EBA94539642f3fD6908dcAB10994D1A", "0x10e4f95a5655b932A724eaDea4766eb6671d0cA5"])
You cannot nominate and validate at the same time.
Claim Rewards
As explained in Staking, rewards are stored in pages, with up to 64 nominators' rewards stored on each page. If a page is claimed, rewards are distributed for the validator and all the nominators on the page.
Function | Description |
---|---|
payoutStakersByPage(validatorStash, era, page) | Trigger payout of rewards for a specific validator, era, and page of nominators. |
Let's say I am nominating the two validators used in the example above, and I want to claim rewards from era 42.
- To claim the first page from the first validator:
payoutStakersByPage("0x46A148316EBA94539642f3fD6908dcAB10994D1A", 42, 0)
- To claim the first page from the second validator:
payoutStakersByPage("0x10e4f95a5655b932A724eaDea4766eb6671d0cA5", 42, 0)
Increase Your Stake
You can increase your stake amount by calling bondExtra
with the additional amount of ZCX you want to stake.
Function | Description |
---|---|
bondExtra(value) | Add more tokens to your existing stake. |
For example, if you are staking 1,000 ZCX and you want to stake an additional 500 ZCX, you can submit a transaction calling bondExtra(500_000_000_000_000_000_000)
.
Change Stake Configuration
Users can change their stake configuration using several methods. Here we highlight three important methods: chill
, setPayee
, and setRewardDestination
.
Function | Description |
---|---|
setPayee(payee) | Set a custom address to receive rewards. |
setRewardDestination(dest) | Set the reward destination (Staked, Stash, or None). |
If your staking rewards are being distributed to another account, you can use setPayee
to change the account to which your staking rewards are distributed.
You can call setRewardDestination
to either restake (compound) rewards or have rewards distributed to your own account.
Calling either setPayee
or setRewardDestination
will overwrite the previous reward distribution configuration.
Unstake
To stop validating or nominating and unbond your staked tokens using the regular unbonding method, you will need to call two functions: chill
and unbond
. The process is simpler for those eligible for fast unstaking.
Regular Unbonding
Function | Description |
---|---|
chill() | Stop validating or nominating. |
unbond(value) | Schedule a portion of your staked tokens for unbonding. |
rebond(value) | Re-stake a portion of your unbonding tokens. |
To unstake your ZCX, you must first call chill
. The chill
function will pause validating or nominating without unbonding ZCX. This is useful to prevent slashing for a short period of time while a node is down, or to otherwise represent a state in which your ZCX is not exposed but still bonded. You will not earn rewards while your account is "chilled".
You can call unbond
immediately after chilling (or call both together in a batch transaction). A call to unbond
will start the "bonding duration", which lasts for 2 eras. Your ZCX cannot be withdrawn until after the bonding duration is completed. The ZCX will not be exposed, so it will not earn rewards or be slashed during this time. When the bonding duration is completed, you will need to submit a separate transaction to withdraw the tokens to your account.
If you change your mind about unbonding, you can call rebond
at any time after the call to unbond
(and before withdrawing your funds) to rebond all or a portion of your unbonding ZCX. After calling rebond
, your ZCX will stop unlocking and become bonded once again. You will still be in the "chilled" state, so you will need to follow up with a call to either validate
or nominate
to continue earning rewards.
Fast Unstake
Function | Description |
---|---|
registerFastUnstake() | Initiate a fast unstake request. |
deregister() | Cancel a fast unstake request and return to regular unbonding. |
If your ZCX has not been "exposed", meaning you are not actively nominating or validating, for 2 eras (the bonding duration), you can unstake significantly more quickly using the FastUnstake
precompile. A single call to registerFastUnstake
will chill your account, fully unbond your ZCX, and add your account to a queue. When you reach the head of the queue--usually within a few blocks--your fast unstake eligibility will be validated and your ZCX will be withdrawn to your account with no further action required from you.
Fast unstaking requires submitting a modest deposit of 1 Gwei ZCX. The deposit will be returned to you when your ZCX is successfully unstaked.
If you are not eligible for fast unstaking and you register anyway, your deposit will be lost when your account is validated in the fast unstake queue. Your ZCX will remain locked for the bonding duration.
If you registered for fast unstaking but you have changed your mind--e.g. you realized you are ineligible--you can call deregister
in the FastUnstake
precompile to cancel the fast unstake request. If successful, your ZCX will continue unbonding through the normal unbonding process unless you take further action.
Withdraw
After you unbond your ZCX and waited the full bonding duration, you can call withdrawUnbonded
to fully withdraw your ZCX into your account.
Function | Description |
---|---|
withdrawUnbonded(numSlashingSpans) | Withdraw your unbonded tokens after the unbonding period. |
historyDepth() | Get the number of eras stored in history. |
The withdrawUnbonded
method takes one argument, which you can reliably fill in with the value returned by a call to the historyDepth
view function.
- To withdraw all unlocked ZCX:
withdrawUnbonded(84)