Build SPC Staking dApps Guide
This guide covers essential staking operations like creating validators, editing their information, and delegating. Developers can use these interfaces to build stake-related dApps.
StakeHub Contract
The SPC staking mainly uses the smart contracts StakeHub for validator and delegation management.
StakeHub: Manages validator creations, user delegations, and executes penalty for validator slash. For the full interfaces ofStakeHub, please refer to the ABI file. (Address0x0000000000000000000000000000000000002002)
Creating Validator
To create a validator, use the createValidator function with the following parameters:
function createValidator(
address consensusAddress,
bytes calldata voteAddress,
bytes calldata blsProof,
Commission calldata commission,
Description calldata description
) external payable
consensusAddress: The consensus address of the validator.voteAddress: The vote address of the validator.blsProof: The BLS signature as proof of the vote address.commission: The commission structure, including rate, maxRate, and maxChangeRate.description: The description of the validator, including moniker, identity, website, and details.
Note: Creating a validator requires locking 1 SRW, and the transaction must be sent with a sufficient SRW amount to cover this lock amount plus any self-delegation, in total 50000001 SRW.
Edit Validator
Edit Consensus Address
To change the consensus address of a validator, use the editConsensusAddress function with the following parameters:
function editConsensusAddress(address newConsensusAddress) external
newConsensusAddress: The new consensus address of the validator.
Edit Commission Rate
To update the commission rate of a validator, use the editCommissionRate function with the following parameters:
function editCommissionRate(uint64 newCommissionRate) external
newCommissionRate: The new commission structure, including rate, maxRate, and maxChangeRate.
Edit Description
To update the description of a validator, use the editDescription function with the following parameters:
function editDescription(Description memory newDescription) external
newDescription: The new description of the validator, including moniker, identity, website, and details.
Edit Vote Address
To change the vote address of a validator, use the editVoteAddress function with the following parameters:
function editVoteAddress(bytes calldata newVoteAddress, bytes calldata blsProof) external
newVoteAddress: The new vote address of the validator.blsProof: The BLS signature as proof of the vote address.
Delegation Operations
Delegate
To delegate SRW to a validator, call the delegate function with the following parameters:
function delegate(address operatorAddress, bool delegateVotePower) external payable
operatorAddress: The operator address of the validator.delegateVotePower: The flag to indicate whether the delegator would like to delegate his/her voting power to the validator for governance.
Undelegate
To undelegate SRW from a validator, use the undelegate function with the following parameters:
function undelegate(address operatorAddress, uint256 shares) external
operatorAddress: The operator address of the validator.shares: The amount of shares to undelegate from the validator.
Redelegate
To redelegate SRW from one validator to another, use the redelegate function with the following parameters:
function redelegate(address srcValidator, address dstValidator, uint256 shares, bool delegateVotePower) external
srcValidator: The operator address of the source validator to redelegate from.dstValidator: The operator address of the destination validator to redelegate to.delegateVotePower: The flag to indicate whether the delegator would like to delegate his/her voting power to the destination validator for governance.
Claim
To claim undelegated SRW after the unbonding period, use the claim function for a single request or claimBatch for
multiple requests:
function claim(address operatorAddress, uint256 requestNumber) external
operatorAddress: The operator address of the validator.requestNumber: The number of unbonding requests to claim from.0means claiming from all unbonding requests.
function claimBatch(address[] calldata operatorAddresses, uint256[] calldata requestNumbers) external
operatorAddress: The operator addresses of the validators.requestNumber: The numbers of unbonding requests to claim from the validators.
Precision Loss
During the conversion process between credit tokens and SRW, it is inevitably encounter the usage of integer division, which may results in a precision loss. It can lead to tangible issues. For example, a user who delegates 1 SRW and then decides to undelegate immediately. Due to the aforementioned precision loss, they will only be able to claim back 0.99..99 SRW, which is essentially 1 minus a tiny fraction (1e-18) of SRW.
In staking pools like Lido and Rocket Pool, users might encounter similar issues. However, these issues can be effectively addressed through thoughtful product design. For instance, when displaying information to users, rounding up to only preserve eight decimal places could be one solution. Or instead of undelegating, users can exchange their credit tokens for SRW, with the exact conversion results prominently displayed.
FAQs
What is validator's credit contract?
For each validator, there is a credit contract which will be automatically deployed when it is created. Meanwhile, the contract cannot be upgraded or changed by any validator operator.
The credit contract is a BEP20 contract, and the ABI is the same as Stake Credit contract.
It provides functions for querying delegations, including:
balanceOf(address): Get the credit balance of a delegator.getPooledBNB(address): Get the pooled SRW amount of a delegator.getPooledBNBByShares(uint256): Get the pooled SRW amount for a specific amount of shares.getSharesByPooledBNB(uint256): Get the shares for a specific amount of pooled SRW.pendingUnbondingRequests(address): Get the count of unbonding requests for a delegator.unbondRequest(address, uint256): Get the details of a unbond request for a delegator.claimableUnbondRequest(address): Get the count of claimable unbonding requests for a delegator.lockedBNBs(address, uint256): Get the locked SRWs for a delegator's unbond queue.
How to get the shares/SRW for a delegator?
For any specific validator, please call the balanceOf function of the validator's creat contract to get the
delegator's shares. To get the SRW amount instead of shares, the function getPooledBNB can be used.
To get the shares of all validators, please call the balanceOf function for each validator and sum up
the results. Please refer to the following to see how to get the information of all validators, and use a
muticall contract to improve the efficiency.
How to calculate the SRW amount for a specific amount of shares?
The credit contract provides the getPooledBNBByShares function to calculate the SRW amount for some specific amount of
shares.
To do the vice visa, please use the getSharesByPooledBNB function to calculate the shares for some
specific SRW amount.
How to calculate the APR/APY of a validator?
Please be noted that each validator will have its own APR/APY, and the staking system will auto compound the rewards.
The reward is distributed to each validator's SRW pool at 00:00:00 UTC time every day. To calculate the APR/APY of a validator, the total pooled SRW amount and the propagandising reward amount for the same day are needed.
The StakeHub contract provides the getValidatorTotalPooledBNBRecord(address,uint256)(uint256)
and getValidatorRewardRecord(address,uint256)(uint256) for the purpose.
The following code shows how to calculate the APY at a given day:
// example code, do not use it in production
// stakehub is the instance of StakeHub contract
stakeHub, _ := contracts.NewStakeHub(ethcommon.HexToAddress("0x0000000000000000000000000000000000002002"), client.GetEthClient())
// get how many blocks are in a day
interval, _ := stakeHub.BREATHEBLOCKINTERVAL(nil)
// get the block time of a given block
header, _ := p.client.GetBlockHeader(blockHeight)
// calculate the index paramter to call the following functions
index := int64(header.Time) / interval.Int64()
// get the total pooled SRW amount and the crrospanding reward amount for the given validator and index
totalPooledBNB, _ := stakeHub.GetValidatorTotalPooledBNBRecord(nil, validatorOperatorAddress, index)
reward, _ := stakeHub.GetValidatorRewardRecord(nil, validatorOperatorAddress, index)
// calculate the APY
rate, _ := big.NewFloat(0).Quo(big.NewFloat(0).SetInt(reward), big.NewFloat(0).SetInt(totalPooledBNB)).Float64()
apy := math.Pow(1+rate, 365) - 1.0
How to get the unbonding delegations of a delegator, and the unbonded requests which can be claimed?
The credit contract provides the pendingUnbondRequest function to get the unbonding delegation count for a
delegator.
To review the details of an unbond request, please call the unbondRequest function with a index parameter to
define which unbond request will be returned.
To get the claimable unbonded requests, please call the claimableUnbondRequest function to get the count of
claimable ones.
To get the locked SRWs for unbonding requests, please use the lockedBNBs function. It has the parameter number to
define the sum of first number unbonding requests' SRW locked in the delegator's unbond queue. Set the number
to 0 to get all the locked BSRWs.
How to get the total staking address of a validator?
The contract does not provide a function to get the total staking address of a validator.
It needs an offchain service to index Delegated, Redelegated, Undelegated events for the purpose.
How to get all validators' information?
The StakeHub contract provides the getValidators function to get all validators' information, including the
operator addresses and credit contract addresses.
To get more information of a specific validator, please refer to the following functions:
getValidatorConsensusAddressgetValidatorCreditContractgetValidatorVoteAddressgetValidatorBasicInfogetValidatorDescriptiongetValidatorCommission