Interacting with the Staking Precompile¶
Introduction¶
Moonbeam uses a Delegated Proof of Stake system through the Parachain Staking Pallet, allowing token holders (delegators) to express exactly which collator candidates they would like to support and with what quantity of stake. The design of the Parachain Staking Pallet is such that it enforces shared risk/reward on chain between delegators and candidates. For general information on staking, such as general terminology, staking variables, and more, please refer to the Staking on Moonbeam page.
The staking module is coded in Rust and it is part of a pallet that is normally not accessible from the Ethereum side of Moonbeam. However, a staking precompile allows developers to access the staking features using the Ethereum API in a precompiled contract located at address:
0x0000000000000000000000000000000000000800
0x0000000000000000000000000000000000000800
0x0000000000000000000000000000000000000800
This guide will cover the available methods in the staking precompile interface. In addition, it will show you how to interact with the Parachain Staking Pallet through the staking precompile and the Ethereum API. The examples in this guide are done on Moonbase Alpha, but they can be adapted for Moonbeam or Moonriver.
Note
There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the Security Considerations page for more information.
Exit Delays¶
Some of the Parachain Staking Pallet extrinsics include exit delays that you must wait before the request can be executed. The exit delays to note are as follows:
Variable | Value |
---|---|
Decrease candidate bond | 28 rounds (168 hours) |
Decrease delegator bond | 28 rounds (168 hours) |
Revoke delegation | 28 rounds (168 hours) |
Leave candidates | 28 rounds (168 hours) |
Leave delegators | 28 rounds (168 hours) |
Variable | Value |
---|---|
Decrease candidate bond | 24 rounds (48 hours) |
Decrease delegator bond | 24 rounds (48 hours) |
Revoke delegation | 24 rounds (48 hours) |
Leave candidates | 24 rounds (48 hours) |
Leave delegators | 24 rounds (48 hours) |
Variable | Value |
---|---|
Decrease candidate bond | 2 rounds (4 hours) |
Decrease delegator bond | 2 rounds (4 hours) |
Revoke delegation | 2 rounds (4 hours) |
Leave candidates | 2 rounds (4 hours) |
Leave delegators | 2 rounds (4 hours) |
Parachain Staking Solidity Interface¶
StakingInterface.sol
is an interface through which Solidity contracts can interact with parachain-staking. The beauty is that Solidity developers don’t have to learn the Substrate API. Instead, they can interact with staking functions using the Ethereum interface they are familiar with.
The Solidity interface includes the following functions:
isDelegator(address delegator) - read-only function that checks whether the specified address is currently a staking delegator. Uses the delegatorState
method of the Parachain Staking Pallet
delegator
- address to check if they are currently a delegator
bool
whether the address is currently a delegator
isCandidate(address candidate) - read-only function that checks whether the specified address is currently a collator candidate. Uses the candidateState
method of the Parachain Staking Pallet
candidate
- address to check if they are currently a collator candidate
bool
whether the address is currently a candidate
isSelectedCandidate(address candidate) - read-only function that checks whether the specified address is currently part of the active collator set. Uses the selectedCandidates
method of the Parachain Staking Pallet
candidate
- address to check if they are currently an active collator
bool
whether the address is currently an active collator
points(uint256 round) - read-only function that gets the total points awarded to all collators in a given round. Uses the points
method of the Parachain Staking Pallet
round
- uint256 round number to query points for
uint256
total points awarded in the specified round
awardedPoints(uint32 round, address candidate) - read-only function that returns the total points awarded in a given round to a given collator. If 0
is returned, it could be because no blocks were produced or the storage for that round has been removed. Uses the points
method of the Parachain Staking Pallet
round
- uint32 round number to querycandidate
- address of the collator to query points for
uint256
points awarded to the collator in the specified round
delegationAmount(address delegator, address candidate) - read-only function that returns the amount delegated by a given delegator in support of a given candidate. Uses the delegatorState
method of the Parachain Staking Pallet
delegator
- address of the delegatorcandidate
- address of the candidate
uint256
amount delegated
isInTopDelegations(address delegator, address candidate) - read-only function that returns a boolean indicating whether the given delegator is in the top delegations for the given candidate. Uses the topDelegations
method of the Parachain Staking Pallet
delegator
- address of the delegator to checkcandidate
- address of the candidate
bool
whether the delegator is in the top delegations
minDelegation() - read-only function that gets the minimum delegation amount. Uses the minDelegation
method of the Parachain Staking Pallet
None.
uint256
minimum delegation amount
candidateCount() - read-only function that gets the current amount of collator candidates. Uses the candidatePool
method of the Parachain Staking Pallet
None.
uint256
current number of collator candidates
round() - read-only function that returns the current round number. Uses the round
method of the Parachain Staking Pallet
None.
uint256
current round number
candidateDelegationCount(address candidate) - read-only function that returns the number of delegations for the specified collator candidate address. Uses the candidateInfo
method of the Parachain Staking Pallet
candidate
- address of the collator candidate to query
uint256
number of delegations for the candidate
candidateAutoCompoundingDelegationCount(address candidate) - a read-only function that returns the number of auto-compounding delegations for the specified candidate. Uses the autoCompoundingDelegations
method of the Parachain Staking Pallet
candidate
- address of the candidate to query
uint256
number of auto-compounding delegations
delegatorDelegationCount(address delegator) - read-only function that returns the number of delegations for the specified delegator address. Uses the delegatorState
method of the Parachain Staking Pallet
delegator
- address of the delegator to query
uint256
number of delegations for the delegator
selectedCandidates() - read-only function that gets the selected candidates for the current round. Uses the selectedCandidates
method of the Parachain Staking Pallet
None.
address[]
array of selected candidate addresses
delegationRequestIsPending(address delegator, address candidate) - returns a boolean to indicate whether there is a pending delegation request made by a given delegator for a given candidate
delegator
- address of the delegatorcandidate
- address of the candidate
bool
whether there is a pending delegation request
candidateExitIsPending(address candidate) - returns a boolean to indicate whether a pending exit exists for a specific candidate. Uses the candidateInfo
method of the Parachain Staking Pallet
candidate
- address of the candidate to check
bool
whether there is a pending exit request
candidateRequestIsPending(address candidate) - returns a boolean to indicate whether there is a pending bond less request made by a given candidate. Uses the candidateInfo
method of the Parachain Staking Pallet
candidate
- address of the candidate to check
bool
whether there is a pending bond less request
delegationAutoCompound(address delegator, address candidate) - returns the auto-compound percentage for a delegation given the delegator and candidate
delegator
- address of the delegatorcandidate
- address of the candidate
uint256
auto-compound percentage
getDelegatorTotalStaked(address delegator) - read-only function that returns the total staked amount of a given delegator, regardless of the candidate. Uses the delegatorState
method of the Parachain Staking Pallet
delegator
- address of the delegator to query
uint256
total staked amount
getCandidateTotalCounted(address candidate) - read-only function that returns the total amount staked for a given candidate. Uses the candidateInfo
method of the Parachain Staking Pallet
candidate
- address of the candidate to query
uint256
total amount staked for the candidate
joinCandidates(uint256 amount, uint256 candidateCount) - allows the account to join the set of collator candidates with the specified bond amount and the current candidate count. Uses the joinCandidates
method of the Parachain Staking Pallet
amount
- uint256 bond amount to stake as a candidatecandidateCount
- uint256 current number of candidates in the pool
None.
scheduleLeaveCandidates(uint256 candidateCount) - schedules a request for a candidate to remove themselves from the candidate pool. Scheduling the request does not automatically execute it. There is an exit delay that must be waited before you can execute the request via the executeLeaveCandidates
extrinsic. Uses the scheduleLeaveCandidates
method of the Parachain Staking Pallet
candidateCount
- uint256 current number of candidates in the pool
None.
executeLeaveCandidates(address candidate, uint256 candidateDelegationCount) - executes the due request to leave the set of collator candidates. Uses the executeLeaveCandidates
method of the Parachain Staking Pallet
candidate
- address of the candidate leaving the poolcandidateDelegationCount
- uint256 number of delegations for the candidate
None.
cancelLeaveCandidates(uint256 candidateCount) - allows a candidate to cancel a pending scheduled request to leave the candidate pool. Given the current number of candidates in the pool. Uses the cancelLeaveCandidates
method of the Parachain Staking Pallet
candidateCount
- uint256 current number of candidates in the pool
None.
goOffline() - temporarily leave the set of collator candidates without unbonding. Uses the goOffline
method of the Parachain Staking Pallet
None.
None.
goOnline() - rejoin the set of collator candidates after previously calling goOffline()
. Uses the goOnline
method of the Parachain Staking Pallet
None.
None.
candidateBondMore(uint256 more) - collator candidate increases bond by the specified amount. Uses the candidateBondMore
method of the Parachain Staking Pallet
more
- uint256 amount to increase the bond by
None.
scheduleCandidateBondLess(uint256 less) - schedules a request to decrease a candidates bond by the specified amount. Scheduling the request does not automatically execute it. There is an exit delay that must be waited before you can execute the request via the execute_candidate_bond_request
extrinsic. Uses the scheduleCandidateBondLess
method of the Parachain Staking Pallet
less
- uint256 amount to decrease the bond by
None.
executeCandidateBondLess(address candidate) - executes any due requests to decrease a specified candidate's bond amount. Uses the executeCandidateBondLess
method of the Parachain Staking Pallet
candidate
- address of the candidate to execute the bond decrease for
None.
cancelCandidateBondLess() - allows a candidate to cancel a pending scheduled request to decrease a candidates bond. Uses the cancelCandidateBondLess
method of the Parachain Staking Pallet
None.
None.
delegateWithAutoCompound(address candidate, uint256 amount, uint8 autoCompound, uint256 candidateDelegationCount, uint256 candidateAutoCompoundingDelegationCount, uint256 delegatorDelegationCount) - makes a delegation in support of a collator candidate and automatically sets the percent of rewards to auto-compound given an integer (no decimals) for autoCompound
between 0-100. Uses the delegateWithAutoCompound
method of the Parachain Staking Pallet
candidate
- address of the candidate to delegate toamount
- uint256 amount to delegateautoCompound
- uint8 percentage of rewards to auto-compound (0-100)candidateDelegationCount
- uint256 current number of delegations for the candidatecandidateAutoCompoundingDelegationCount
- uint256 current number of auto-compounding delegations for the candidatedelegatorDelegationCount
- uint256 current number of delegations from the delegator
None.
scheduleRevokeDelegation(address candidate) - schedules a request to revoke a delegation given the address of a candidate. Scheduling the request does not automatically execute it. There is an exit delay that must be waited before you can execute the request via the executeDelegationRequest
extrinsic. Uses the scheduleRevokeDelegation
method of the Parachain Staking Pallet
candidate
- address of the candidate to revoke delegation from
None.
delegatorBondMore(address candidate, uint256 more) - delegator increases bond to a collator by the specified amount. Uses the delegatorBondMore
method of the Parachain Staking Pallet
candidate
- address of the candidate to increase delegation formore
- uint256 amount to increase the delegation by
None.
scheduleDelegatorBondLess(address candidate, uint256 less) - schedules a request for a delegator to bond less with respect to a specific candidate. Scheduling the request does not automatically execute it. There is an exit delay that must be waited before you can execute the request via the executeDelegationRequest
extrinsic. Uses the scheduleDelegatorBondLess
method of the Parachain Staking Pallet
candidate
- address of the candidate to decrease delegation forless
- uint256 amount to decrease the delegation by
None.
executeDelegationRequest(address delegator, address candidate) - executes any due delegation requests provided the address of a delegator and a candidate. Uses the executeDelegationRequest
method of the Parachain Staking Pallet
delegator
- address of the delegatorcandidate
- address of the candidate
None.
cancelDelegationRequest(address candidate) - cancels any pending delegation requests provided the address of a candidate. Uses the cancelDelegationRequest
method of the Parachain Staking Pallet
candidate
- address of the candidate to cancel the delegation request for
None.
setAutoCompound(address candidate, uint8 value, uint256 candidateAutoCompoundingDelegationCount, uint256 delegatorDelegationCount) - sets an auto-compound value for an existing delegation given an integer (no decimals) for the value
between 0-100. Uses the setAutoCompound
method of the Parachain Staking Pallet
candidate
- address of the candidatevalue
- uint8 percentage to auto-compound (0-100)candidateAutoCompoundingDelegationCount
- uint256 current number of auto-compounding delegations for the candidatedelegatorDelegationCount
- uint256 current number of delegations from the delegator
None.
As of runtime 2400, the following methods are deprecated:
delegate(address candidate, uint256 amount, uint256 candidateDelegationCount, uint256 delegatorDelegationCount) - makes a delegation in support of a collator candidate and automatically sets the percent of rewards to auto-compound to 0
. Use delegateWithAutoCompound
instead
candidate
- address of the candidate to delegate toamount
- uint256 amount to delegatecandidateDelegationCount
- uint256 current number of delegations for the candidatedelegatorDelegationCount
- uint256 current number of delegations from the delegator
None.
As of runtime 1800, the following methods are deprecated and, as of runtime 2500, have been removed:
scheduleLeaveDelegators() - schedules a request to leave the set of delegators and revoke all ongoing delegations. Scheduling the request does not automatically execute it. There is an exit delay that must be waited before you can execute the request via the executeLeaveDelegators
extrinsic. Use the batch utility with scheduleRevokeDelegation
for all delegations instead
None.
None.
executeLeaveDelegators(address delegator, uint256 delegatorDelegationCount) - executes the due request to leave the set of delegators and revoke all delegations. Use the batch utility with executeDelegationRequest
for all delegations instead
delegator
- address of the delegator leavingdelegatorDelegationCount
- uint256 current number of delegations from the delegator
None.
cancelLeaveDelegators() - cancels a pending scheduled request to leave the set of delegators. Use the batch utility with cancelDelegationRequest
for all delegations instead
None.
None.
As of runtime 1001, the following methods are deprecated and, as of runtime 1800, have been removed:
is_nominator(address nominator) - read-only function that checks whether the specified address is currently a staking delegator. Use isDelegator
instead
nominator
- address to check if they are currently a delegator
bool
whether the address is currently a delegator
min_nomination() - read-only function that gets the minimum delegation amount. Use minDelegation
instead
None.
uint256
minimum delegation amount
collator_nomination_count(address collator) - read-only function that returns the number of delegations for the specified collator address. Use candidateDelegationCount
instead
collator
- address of the collator to query
uint256
number of delegations for the collator
nominator_nomination_count(address nominator) - read-only function that returns the number of delegations for the specified delegator address. Use delegatorDelegationCount
instead
nominator
- address of the delegator to query
uint256
number of delegations for the delegator
leave_candidates(uint256 amount, uint256 candidateCount) - immediately removes the account from the candidate pool to prevent others from selecting it as a collator and triggers unbonding. Use scheduleLeaveCandidates
and executeLeaveCandidates
instead
amount
- uint256 bond amountcandidateCount
- uint256 current number of candidates in the pool
None.
candidate_bond_less(uint256 less) - collator candidate decreases bond by the specified amount. Use scheduleCandidateBondLess
and executeCandidateBondLess
instead
less
- uint256 amount to decrease the bond by
None.
nominate(address collator, uint256 amount, uint256 collatorNominationCount, uint256 nominatorNominationCount) - if the caller is not a delegator, this function adds them to the set of delegators. If the caller is already a delegator, then it adjusts their delegation amount. Use delegateWithWithAutoCompound
instead
collator
- address of the collator to nominateamount
- uint256 amount to delegatecollatorNominationCount
- uint256 current number of nominations for the collatornominatorNominationCount
- uint256 current number of nominations from the nominator
None.
leave_nominators(uint256 nominatorNominationCount) - leave the set of delegators and revoke all ongoing delegations. Use the batch utility with scheduleRevokeDelegation
for all delegations instead
nominatorNominationCount
- uint256 current number of nominations from the nominator
None.
revoke_nominations(address collator) - revoke a specific delegation. Use scheduleRevokeDelegation
and executeDelegationRequest
instead
collator
- address of the collator to revoke nomination from
None.
nominator_bond_more(address collator, uint256 more) - delegator increases bond to a collator by the specified amount. Use delegatorBondMore
instead
collator
- address of the collator to increase bond formore
- uint256 amount to increase the bond by
None.
nominator_bond_less(address collator, uint256 less) - delegator decreases bond to a collator by the specified amount. Use scheduleDelegatorBondLess
and executeDelegationRequest
instead
collator
- address of the collator to decrease bond forless
- uint256 amount to decrease the bond by
None.
Interact with the Solidity Interface¶
Checking Prerequisites¶
The below example is demonstrated on Moonbase Alpha, however, similar steps can be taken for Moonbeam and Moonriver.
- Have MetaMask installed and connected to Moonbase Alpha
- Have an account with at least
1
token. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the Moonbase Alpha Faucet
Note
The example below requires more than 1
token due to the minimum delegation amount plus gas fees. If you need more than the faucet dispenses, please contact us on Discord and we will be happy to help you.
Remix Set Up¶
- Click on the File explorer tab
- Get a copy of
StakingInterface.sol
and paste the file contents into a Remix file namedStakingInterface.sol
Compile the Contract¶
- Click on the Compile tab, second from top
- Then to compile the interface, click on Compile StakingInterface.sol
Access the Contract¶
- Click on the Deploy and Run tab, directly below the Compile tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed
- Make sure Injected Provider - Metamask is selected in the ENVIRONMENT drop down
- Ensure ParachainStaking - StakingInterface.sol is selected in the CONTRACT dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the precompile in the At Address field
- Provide the address of the staking precompile for Moonbase Alpha:
0x0000000000000000000000000000000000000800
and click At Address - The Parachain Staking precompile will appear in the list of Deployed Contracts
Delegate a Collator with Auto-Compounding¶
For this example, you are going to be delegating a collator and setting up the percentage of rewards to auto-compound on Moonbase Alpha. Delegators are token holders who stake tokens, vouching for specific candidates. Any user that holds a minimum amount of 1 token in their free balance can become a delegator. When delegating a candidate, you can simultaneously set up auto-compounding. You'll be able to specify a percentage of your rewards that will automatically be applied to your total delegation. You don't have to set up auto-compounding right away, you can always do it at a later time.
You can do your own research and select the candidate you desire. For this guide, the following candidate address will be used: 0x12E7BCCA9b1B15f33585b5fc898B967149BDb9a5
.
In order to delegate a candidate, you'll need to determine the candidate's current delegation count, their auto-compounding delegation count, and your own delegation count.
The candidate delegation count is the number of delegations backing a specific candidate. To obtain the candidate delegator count, you can call a function that the staking precompile provides. Expand the PARACHAINSTAKING contract found under the Deployed Contracts list, then:
- Find and expand the candidateDelegationCount function
- Enter the candidate address (
0x12E7BCCA9b1B15f33585b5fc898B967149BDb9a5
) - Click call
- After the call is complete, the results will be displayed
The auto-compounding delegation count is the amount of delegations that have auto-compounding configured. To determine the number of delegations that have auto-compounding set up, you can
- Find and expand the candidateAutoCompoundingDelegationCount function
- Enter the candidate address (
0x12E7BCCA9b1B15f33585b5fc898B967149BDb9a5
) - Click call
- After the call is complete, the results will be displayed
The last item you'll need to retrieve is your delegation count. If you don't know your existing number of delegations, you can easily get them by following these steps:
- Find and expand the delegatorDelegationCount function
- Enter your address
- Click call
- After the call is complete, the results will be displayed
Now that you have obtained the candidate delegator count, the auto-compounding delegation count, and your number of existing delegations, you have all of the information you need to delegate a candidate and set up auto-compounding. To get started:
- Find and expand the delegateWithAutoCompound function
- Enter the candidate address you would like to delegate. For this example you can use
0x12E7BCCA9b1B15f33585b5fc898B967149BDb9a5
- Provide the amount to delegate in Wei. There is a minimum of
1
token to delegate, so the lowest amount in Wei is1000000000000000000
- Enter an integer (no decimals) between 0-100 to represent the percentage of rewards to auto-compound
- Enter the delegation count for the candidate
- Enter the auto-compounding delegation count for the candidate
- Enter your delegation count
- Press transact
- MetaMask will pop-up, you can review the details and confirm the transaction
If you want to delegate without setting up auto-compounding, you can follow the previous steps, but instead of using delegateWithAutoCompound, you can use the delegate extrinsic.
Verify Delegation¶
To verify your delegation was successful, you can check the chain state in Polkadot.js Apps. First, add your MetaMask address to the address book in Polkadot.js Apps.
Navigate to Accounts and then Address Book, click on Add contact, and enter the following information:
- Add your MetaMask address
- Provide a nickname for the account
- Click Save
To verify your delegation was successful, head to Polkadot.js Apps and navigate to Developer and then Chain State
- Select the parachainStaking pallet
- Select the delegatorState query
- Enter your address
- Optionally, you can enable the include option slider if you want to provide a specific blockhash to query
- Click the + button to return the results and verify your delegation
Note
You do not have to enter anything in the blockhash to query at field if you are looking for an overview of your delegations.
Confirm Auto-Compounding Percentage¶
You can confirm the percentage of rewards you've set to auto-compound in Remix using the delegationAutoCompound
function of the Solidity interface:
- Find and expand the delegationAutoCompound function
- Enter your account you used to delegate with
- Enter the candidate you've delegated
- Click call
- The response will appear below the call button
Set or Change the Auto-Compounding Percentage¶
If you initially set up your delegation without auto-compounding or if you want to update the percentage on an existing delegation with auto-compounding set up, you can use the setAutoCompound
function of the Solidity interface.
You'll need to get the number of delegations with auto-compounding set up for the candidate you want to set or update auto-compounding for. You'll also need to retrieve your own delegation count. You can follow the instructions in the Delegate a Collator with Auto-Compounding section to get both of these items.
Once you have the necessary information, you can take the following steps in Remix:
- Find and expand the setAutoCompound function
- Enter the candidate's account you want to set or update auto-compounding for
- Enter a number 0-100 to represent the percentage of rewards you want to auto-compound
- Enter the auto-compounding delegation count for the candidate
- Enter your delegation count
- Press transact
- MetaMask will pop-up, you can review the details and confirm the transaction
Revoke a Delegation¶
As of runtime version 1001, there have been significant changes to the way users can interact with various staking features. Including the way staking exits are handled.
Exits now require you to schedule a request to exit or revoke a delegation, wait a delay period, and then execute the request.
To revoke a delegation for a specific candidate and receive your tokens back, you can use the scheduleRevokeDelegation
extrinsic. Scheduling a request does not automatically revoke your delegation, you must wait an exit delay, and then execute the request by using the executeDelegationRequest
method.
To revoke a delegation and receive your tokens back, head back over to Remix, then:
- Find and expand the scheduleRevokeDelegation function
- Enter the candidate address you would like to revoke the delegation for
- Click transact
- MetaMask will pop, you can review the transaction details, and click Confirm
Once the transaction is confirmed, you must wait the duration of the exit delay before you can execute and revoke the delegation request. If you try to revoke it before the exit delay is up, your extrinsic will fail.
After the exit delay has passed, you can go back to Remix and follow these steps to execute the due request:
- Find and expand the executeDelegationRequest function
- Enter the address of the delegator you would like to revoke the delegation for
- Enter the candidate address you would like to revoke the delegation from
- Click transact
- MetaMask will pop, you can review the transaction details, and click Confirm
After the call is complete, the results will be displayed and the delegation will be revoked for the given delegator and from the specified candidate. You can also check your delegator state again on Polkadot.js Apps to confirm.
If for any reason you need to cancel a pending scheduled request to revoke a delegation, you can do so by following these steps in Remix:
- Find and expand the cancelDelegationRequest function
- Enter the candidate address you would like to cancel the pending request for
- Click transact
- MetaMask will pop, you can review the transaction details, and click Confirm
You can check your delegator state again on Polkadot.js Apps to confirm that your delegation is still intact.
| Created: June 24, 2021