Official SDK for generating and executing transactions on Flare Networks.
This is the official Node.js Software Development Kit (SDK) for performing common actions on Flare's networks:
- Retrieving account and balance information
- Transferring native and wrapped coins
- Claiming rewards from FlareDrop, staking, FTSO delegation and rNat projects
- Delegating to FTSO providers
- Voting on Flare foundation proposals
- Interacting with C-chain contracts
- Creating and using smart (multisig) accounts
- Staking on the P-chain
The SDK is designed to simplify blockchain interactions and offer future-proof support for the above described actions, making it easier to develop apps on Flare's networks. It can be integrated with both standard and custom wallets for transaction signing, and provides detailed, step-by-step transaction tracking.
To install the library in a Node.js project, run
```
npm install @flarenetwork/flare-tx-sdk
The following is a brief overview of the available actions. Suppose
- network is an object of class Network, e.g. Network.FLAREwallet
- is an object of a suitable class that implements the interface Wallet
Deriving addresses from the wallet's public key:
``
let publicKey = await wallet.getPublicKey()
let cAddress = network.getCAddress(publicKey)
let pAddress = network.getPAddress(publicKey)
Overview of the wallet's balance:
``
let balance = await network.getBalance(publicKey)
Transferring native and wrapped native coin:
``
await network.transferNative(wallet, Amount.nats(1))
await network.transferWrapped(wallet, Amount.wnats(1))
Wrapping and unwrapping:
``
await network.wrapNative(wallet, Amount.nats(1))
await network.unwrapToNative(wallet, Amount.wnats(1))
Claiming reward from FlareDrop:
``
let amount = await network.getClaimableFlareDropReward(cAddress)
await network.claimFlareDropReward(wallet)
Claiming reward from staking:
``
let amount = await network.getClaimableStakingReward(cAddress)
await network.claimStakingReward(wallet)
Claiming reward from FTSO delegation:
``
let amount = await network.getClaimableFtsoReward(cAddress)
await network.claimFtsoReward(wallet)
Claiming reward from a rNat project and withdrawing from rNat account:
`
let amount = await network.getClaimableRNatReward(projectId, cAddress)
await network.claimRNatReward(wallet, [projectId])
let amount = await network.getUnlockedBalanceWrappedOnRNatAccount(cAddress)
await network.withdrawFromRNatAccount(wallet)
`
Delegating:
``
let share = Amount.percentages(50)
await network.delegateToFtso(wallet, address1, share, address2, share)
Delegation information:
``
let delegates = await network.getFtsoDelegatesOf(cAddress)
Calling contract methods:
``
let result = await network.invokeContractCallOnC(address, abi, method, params)
Executing contract methods:
``
await network.invokeContractMethodOnC(wallet, address, abi, method, value, params)
Delegating on the P-chain:
``
await network.delegateOnP(wallet, amount, nodeId, startTime, endTime)
Transferring funds back to the C-chain when the delegation ends:
``
await network.transferToC(wallet)
In order to generate and sign transactions on the C-chain and P-chain, a suitable implementation of the interface Wallet must be provided. Each object implementing the interface should be associated with a unique signing key.
The SDK provides proxies to facilitate the integration of standard wallets, such as Web3 based wallets (EIP-1193 standard), Ledger, and Trezor. Details are specified in Standard wallets.
In general, for transaction generation, the wallet should implement the function
``
getPublicKey(): Promise`
that returns the public key of the wallet in the hexadecimal encoding. This function is required for P-chain related operations. For generating C-chain transaction only, it is sufficient that the wallet implements the function`
getCAddress(): PromisegetPublicKey
that returns the C-chain address in the hexadecimal encoding. This function is not required if the function is available.
Moreover, in order to execute transactions, the wallet should implement one or more functions for signing. The documentation of each SDK function that receives the Wallet object provides information which of the following functions are suitable to execute the actions.
The functions that can be used for signing are the following.
- The function for signing a C-chain (EVM) transaction (recommended for C-chain related operations):
``
signCTransaction(tx: string): Promise
tx
The input is a hex encoded EIP 1559 (type 2) EVM transaction.
- The function for signing a P-chain transaction (recommended for P-chain related operations):
``
signPTransaction(tx: string): Promise
tx
The input is a hex encoded P-chain transaction.
- The function for signing the digest of a message (generally applicable):
``
signDigest(digest: string): Promise
digest
The input is a hex encoded digest of a hash function.
- The function for signing a message with ETH prefix (applicable for P-chain operations):
``
signEthMessage(message: string): Promise
message
The input is a UTF8 string.
Each of the above functions should return the signature in hexadecimal encoding.
Alternatively, for C-chain related operations, when signing and submitting of a C-chain (EVM) transaction are inseparable, the wallet can implement the function
``
signAndSubmitCTransaction(tx: string): Promisetx
The input is a hex encoded EIP 1559 (type 2) EVM transaction. If the wallet implements this function, it is considered as the default function for signing C-chain transactions. The function should return the transaction id in hexadecimal encoding.
A wallet can also be marked to act as an operation signer in a multisig process based on a Safe smart account. This is achieved by defining the property smartAccount of type string and setting it to be equal to the C-chain address of a Safe smart account, in which the wallet's C-chain address represents an owner. When the property is different from undefined, all the C-chain operations supported by the SDK produce transactions that induce either approval or execution of these operations on the smart account.
The queries and actions on the network are executed using the Network object.
- The Flare network:
``
let network = Network.FLARE
`
- The Songbird network:
`
let network = Network.SONGBIRD
`
- The Coston network (test network for Songbird):
`
let network = Network.COSTON
`
- The Coston2 network (test network for Flare):
`
let network = Network.COSTON2
Network
- For a custom network or a object with custom settings, first create a suitable Constants object constants and then use:`
`
let network = new Network(constants)
To track the process of signing and submitting of transactions to the network, the following callbacks can be registered with a Network object.
The first three callbacks return a Boolean and can be used to stop the transaction execution, e.g. if the tool is used only for transaction generation for offline signing. Note, however, that certain processes consist of several consecutive transactions that may depend on each other, and stopping a transaction may result in the failure of subsequent actions.
If set, the function of type BeforeTxSignatureCallback is called prior to each signature request on the wallet object. To set the callback, use``
network.setBeforeTxSignatureCallback(async (data: BeforeTxSignature) => { return true })BeforeTxSignature
The object of type contains the properties:txType
- the code of the transaction type;unsignedTxHex
- the unsigned transaction in the hexadecimal encoding.
The property unsignedTxHex can be used for transaction verification (see e.g. Flare: Transaction verification library).
Normally, the callback should return true, which grants permission to invoke the signature request on the wallet object. If it returns false, the transaction is not signed and submitted to the network.
If set, the function of type BeforeTxSubmissionCallback is called prior to the submission of each transaction. To set the callback, use``
network.setBeforeTxSubmissionCallback(async (data: BeforeTxSubmission) => { return true })BeforeTxSubmission
The object of type contains the properties:txType
- the code of the transaction type;signedTxHex
- the signed transaction in the hexadecimal encoding;txId
- the id of the signed transaction in the hexadecimal encoding.
Normally, the callback should return true, which grants the permission to submit the transaction to the network. If it returns false, the transaction is not submitted.
When the function signAndSubmitCTransaction is used as the wallet's signing function, this callback is not executed.
If set, the function of type AfterTxSubmissionCallback is called immediately after the submission of each transaction to the network. To set the callback, use``
network.setAfterTxSubmissionCallback(async (data: AfterTxSubmission) => { return true })AfterTxSubmission
The object of type contains the properties:txType
- the code of the transaction type;txId
- the id of the submitted transaction in the hexadecimal encoding.
Normally, the callback should return true, which signals that the confirmation of the transaction on the network is to be awaited before proceeding. If it returns false, the confirmation is not awaited.
If set, the function of type AfterTxConfirmationCallback is called immediately after the confirmation of each transaction to the network. To set the callback, use``
network.setAfterTxConfirmationCallback(async (data: AfterTxConfirmation) => { // use data })AfterTxConfirmation
The object of type contains the properties:txType
- the code of the transaction type;txId
- the id of the submitted transaction in the hexadecimal encoding;txStatus
- the status of the transaction: true if accepted and false if rejected.
The network uses two different chains for performing operations. Most of the operations are executed on the standard EVM chain, which is called the C-chain (contract chain). The chain for staking is called the P-chain.
The address of a given wallet on the C-chain differs from the address of the same wallet on the P-chain, but both addresses are derived from the same public key. Therefore, the wallet implementation must enable public key retrieval, from which the associated C-chain and P-chain addresses are derived. For a given wallet, they can be obtained by``
let publicKey = await wallet.getPublicKey()
let cAddress = network.getCAddress(publicKey)
let pAddress = network.getPAddress(publicKey)
The information on wallet balance can be obtained by
``
let balance = await network.getBalance(publicKey)balance
The resulting object is of type Balance and has the following properties:availableOnC
- The balance available on the C-chain;availableOnP
- The balance available on the P-chain;wrappedOnC
- The balance wrapped on the C-chainstakedOnP
- The balance staked on the P-chain;notImportedToC
- The balance exported from the P-chain but not imported to the C-chain;notImportedToP
- The balance exported from the C-chain but not imported to the P-chain.
Here and hereafter, all monetary amounts are considered in the Wei units and represented as BigInt.
Separately, the balance information can be obtained by
``
await network.getBalanceOnC(publicKeyOrCAddress)``
await network.getBalanceWrappedOnC(publicKeyOrCAddress)``
await network.getBalanceOnP(publicKey)``
await network.getBalanceNotImportedToC(publicKey)``
await network.getBalanceNotImportedToP(publicKey)``
await network.getBalanceStakedOnP(publicKey)
The native coin of the C-chain can be transferred using
``
await network.transferNative(wallet, recipient, amount)recipient
to the C-chain address specified by . To transfer the entire native coin balance, use``
await network.transferAllNative(wallet, recipient)
The native coin on the C-chain can be wrapped to an ERC20 token WNat, which represents the wrapped native token. The exchange is in ratio 1:1 and can be performed by
``
await network.wrapNative(wallet, amount)
The wrapped coin can be exchanged back to the native coin by calling
``
await network.unwrapToNative(wallet, amount)
The wrapped coin can be transferred by the ERC20 standard using
``
await network.transferWrapped(wallet, amount)
There are different types of rewards that can be claimed in the Flare's network.
#### FlareDrop rewards
FlareDrop is a distribution method for the remaining unreleased FLR tokens after the original airdrop. It is distributed monthly to those that wrap their FLR tokens.
The amount of claimable reward for a given public key or C-chain address publicKeyOrAddress can be obtained by``
let amount = await network.getClaimableFlareDropReward(publicKeyOrAddress)`
To claim all claimable reward, use`
await network.claimFlareDropReward(wallet, rewardOwner, recipient, wrap)wallet
The only required input parameter is . The parameter rewardOwner is the C-chain address of the reward owner and can be omitted if it is equal to the wallet's C-chain address. Similarly, recipient can be omitted if the reward is to be transferred to the wallet's C-chain address. The parameter wrap indicates if the reward is to be transferred to recipient as native coin (default) or as wrapped coin.
#### Staking rewards
Staking on the P-chain yields staking rewards on the C-chain.
The amount of claimable reward for a given public key or C-chain address publicKeyOrAddress can be obtained by``
let amount = await network.getClaimableStakingReward(publicKeyOrAddress)`
To claim all claimable reward, use`
await network.claimStakingReward(wallet, rewardOwner, recipient, wrap)wallet
The only required input parameter is . The parameter rewardOwner is the C-chain address of the reward owner and can be omitted if it is equal to the wallet's C-chain address. Similarly, recipient can be omitted if the reward is to be transferred to the wallet's C-chain address. The parameter wrap indicates if the reward is to be transferred to recipient as native coin (default) or as wrapped coin.
#### FTSO delegation rewards
Running an FTSO provider, delegation to FTSO providers, or even just staking yields FTSO rewards.
The amount of claimable reward for a given public key or C-chain address publicKeyOrAddress can be obtained by``
let amount = await network.getClaimableFTSOReward(publicKeyOrAddress)`
To claim all claimable weight based reward (i.e. reward resulting in delegation to FTSO providers and staking), use`
await network.claimFtsoReward(wallet, rewardOwner, recipient, wrap)wallet
The only required input parameter is . The parameter rewardOwner is the C-chain address of the reward owner and can be omitted if it is equal to the wallet's C-chain address. Similarly, recipient can be omitted if the reward is to be transferred to the wallet's C-chain address. The parameter wrap indicates if the reward is to be transferred to recipient as native coin (default) or as wrapped coin.
To get a more detailed overview of the reward state, use
``
let states = await network.getStateOfFtsoRewards(publicKeyOrAddress)states
The resulting object is an array of array states. An array state is an array of claimable rewards for a specific reward epoch. It can be empty or it consists of objects of type FtsoRewardState with properties:rewardEpochId
- The reward epoch id;beneficiary
- The reward owner (C-chain address or node id if claimType is MIRROR);amount
- The reward amount in weis;claimType
- The type of claim;initialised
- The flag indicating if the reward can be claimed without providing proofs.
The rewards that are not initialised can be claimed using Merkle proofs available in Flare System Protocol Reward Distribution repository by
``
await network.claimFtsoReward(wallet, rewardOwner, recipient, wrap, proofs)proofs
where is an array of objects of type FtsoRewardClaimWithProof with properties:merkleProof
- The Merkle proof represented by an array of strings in hexadecimal encoding;body
- The reward claim represented as an object of type FtsoRewardClaim with the same properties as FtsoRewardState described above (except initialised).
#### rNat rewards
Participation in Flare's networks rNat projects yields rewards in the form of rNat tokens. On claiming, rNat tokens are backed up by wrapped coins deposited to the owner's unique rNat account, where they are vested. The owner can eventually withdraw the wrapped coins from the rNat account to its own account.
The list of all rNat projects can be obtained by
``
let projects = await network.getRNatProjects()RNatProject
which returns an array of objects of type with properties:id
- The project id;name
- The name of the project;claimingDisabled
- The flag indicating if claiming of the rewards on the project is disabled.`
To get more detailed information on a particular project, use`
let projectInfo = await network.getRNatProjectInfo(projectId)projectInfo
The resulting object is of type RNatProjectInfo with properties:name
- The project name;distributor
- The address of the distributor of the rewards for the project;currentMonthDistributionEnabled
- The flag indicating if reward distribution is possible for the current month;distributionDisabled
- The flag indicating if distribution of the rewards is disabled;claimingDisabled
- The flag indicating if claiming of rewards is disabled;totalAssignedRewards
- The total amount of rewards in wei awarded by Flare's networks for this project;totalDistributedRewards
- The total amount of assigned rewards in wei distributed by the distributor;totalClaimedRewards
- The total amount of the distributed rewards in wei claimed by the users;totalUnassignedUnclaimedRewards
- The total amount of rewards in wei that are claimed back by Flare's networks if distribution and claiming is permanently disabled for the project;monthsWithRewards
- The array of months with claimable rewards.
The amount of claimable reward for a given project with projectId and a user participating in the project identified by a given public key or C-chain address publicKeyOrAddress can be obtained by``
let amount = await network.getClaimableRNatReward(projectId, publicKeyOrAddress)`
To claim all claimable rNat rewards, use`
await network.claimRNatReward(wallet, projectIds)projectIds
where is an array of project ids for which the reward is being claimed. The claimed rewards are deposited as wrapped coins to the dedicated rNat account, which is associated with the wallet's C-chain address (rNat account owner).
The wrapped coins on the rNat account are vested. To get the amount of unlocked wrapped coins, use
``
let unlockedBalance = await network.getUnlockedBalanceWrappedOnRNatAccount(publicKeyOrAddress)publicKeyOrAddress
where is the public key or C-chain address of the rNat account owner. The amount of locked wrapped coins can be obtained by``
let lockedBalance = await network.getLockedBalanceWrappedOnRNatAccount(publicKeyOrAddress)`
Full information on the rNat account balance can be obtained by`
let balance = await network.getRNatAccountBalance(publicKeyOrAddress)balance
The resulting object is of type RNatAccountBalance with properties:wNatBalance
- The balance of wrapped coins in wei;rNatBalance
- The balance of rNat tokens in wei;lockedBalance
- The balance of locked wrapped coins in wei.
The amount of rNat tokens equals the difference between received and withdrawn rNat rewards. The amount of wrapped coins on an rNat account can be larger than the amount of rNat tokens.
For example, the amount of wrapped coins can be increased by claiming the FlareDrop reward accrued from (vested) wrapped coins on the rNat account. To check the amount of unclaimed FlareDrop reward for the rNat account owned by publicKeyOrAddress, use``
let rNatAccountAddress = await network.getRNatAccount(publicKeyOrAddress)
let amount = await network.getClaimableFlareDropReward(rNatAccountAddress)`
To claim the reward, use`
await network.claimFlareDropReward(wallet, rNatAccountAddress, rNatAccountAddress, true)rNatAccountAddress
where is the C-chain address of the rNat account associated with the C-chain address of wallet. This operation transfers all claimable FlareDrop reward to the rNat account and increases its balance of wrapped coins.
Finally, to withdraw funds from the rNat account, use
``
await network.withdrawFromRNatAccount(wallet, amount, wrap)amount
where is the amount of unlocked wrapped coins to withdraw and wrap is a flag indicating if the amount is to be transferred as native coin (false) or wrapped (true). Both, amount and wrap are optional parameters. By default, amount is equal to the amount of all unlocked wrapped balance and wrap is equal to false.
It is also possible to withdraw all funds from the rNat account, including locked and unlocked balance. To achieve this, use
``
await network.withdrawAllFromRNatAccount(wallet, wrap)wrap
but note that the actual withdrawn amount is reduced as a penalty to withdrawing locked balance. The flag is again optional and is by default equal to false.
The account's vote power corresponding to the balance of the wrapped tokens can be delegated to one or two FTSO providers to earn delegation reward. The amount of delegated vote power is specified in percentages. The vote power can be delegated to one or two FTSO providers.
The status of current delegations can be obtained by
``
let delegations = await network.getFtsoDelegatesOf(publicKey)FtsoDelegate
The result is an array of objects of type with properties:address
- The C-chain address of the delegate;shareBP
- The delegation share in base points.
The shares are expressed in the base point units, a unit corresponds to 0.01%.
To delegate vote power to one provider, use
``
await network.delegateToFtso(wallet, providerAddress, shareBP)`
To delegate vote power to two providers, use`
await network.delegateToFtso(wallet, provider1Address, share1BP, provider2Address, share2BP)share1BP
The sum of and share2BP should be less than 10000, i.e., 100%.
To undelegate all vote power, use
``
await network.undelegateFromFtso(wallet)
Participants in the Flare's networks can vote on proposals issued by the Flare Foundation organization. The governance vote power depends on the amount of wrapped coins and the staking amount of the voter at a particular block related to the proposal (vote power block). A voter can also decide to delegate all the vote power to a different voter.
To obtain Flare Foundation proposals, use
``
let proposalIds = await network.getFoundationProposalIds()
which returns an array of proposal ids. Note that this includes recent proposals, but it is not a complete list of all historical proposals. To get the latter, go the the Flare Portal and check the proposals list.
To obtain detailed information about a proposal with id proposalId, use``
let proposalInfo = await network.getFoundationProposalInfo(proposalId)FoundationProposalInfo
which returns an object of type with properties:description
- The description of the proposal;state
- The state of the proposal:FoundationProposalState.PENDING
- (0) The voting has not yet started;FoundationProposalState.ACTIVE
- (1) The voting is in process;FoundationProposalState.DEFEATED
- (2) The proposal has been unsuccessful;FoundationProposalState.SUCCEEDED
- (3) The proposal has been successful;FoundationProposalState.QUEUED
- (4) The proposal is queued for execution;FoundationProposalState.EXPIRED
- (5) The proposal has expired before it has been executed;FoundationProposalState.EXECUTED
- (6) The proposal has been executed;FoundationProposalState.CANCELED
- (7) The proposal has been canceled;votePowerFor
- The accumulated vote power for the proposal.votePowerAgainst
- The accumulated vote power against the proposal.proposer
- The address of the proposal submitter.accept
- A flag indicating if the proposal is of acceptance type (true), i.e., a certain amount of vote power for the proposal must be accumulated for the proposal to be accepted, or rejection type (false), i.e., a certain amount of vote power against the proposal must be accumulated for the proposal to be rejected;votePowerBlock
- The block number used to determine the vote powers in voting process;voteStartTime
- The start time (in Unix time) of the proposal voting;voteEndTime
- The end time (in Unix time) of the proposal voting;thresholdConditionBP
- The percentage in base points of the circulating supply required for the proposal "quorum";majorityConditionBP
- The percentage in base points of the proper relation between FOR and AGAINST votes;circulatingSupply
- The circulating supply at vote power block.
A vote for a proposal with id proposalId can be cast when the state of the proposal is ACTIVE. To check if a voter identified by publicKeyOrAddress has cast a vote, use``
await network.hasCastVoteForFoundationProposal(publicKeyOrAddress, proposalId)`
A vote can be cast by`
await network.castVoteForFoundationProposal(wallet, proposalId, support)FoundationProposalSupport.AGAINST
where support is either (0) or FoundationProposalSupport.FOR (1).
The governance vote power can be delegated to a different voter identified by the C-chain address delegate by``
await network.delegateGovernanceVotePower(wallet, delegate)`
and undelegated by`
await network.undelegateGovernanceVotePower(wallet)
To check the current governance vote power of a voter identified by publicKeyOrAddress, use``
let votePower = await network.getCurrentGovernanceVotePower(publicKeyOrAddress)votePower
where the resulting is the sum of the voter's own vote power and the vote power that is currently delegated to the voter. To obtain the current vote delegate for a given publicKeyOrAddress, use``
let delegate = await network.getCurrentGovernanceVoteDelegate(publicKeyOrAddress)delegate
If is zero address, this indicates that there is no delegate. If delegate is not zero address, the vote power of the delegator is zero.
It is also possible to obtain the vote power and the delegate of a given account identified by publicKeyOrAddress as it has been effective in the voting for a particular proposal with id proposalId. In that case, use`
`
let votePower = await network.getVotePowerForFoundationProposal(publicKeyOrAddress, proposalId)
let delegate = await network.getVoteDelegateForFoundationProposal(publicKeyOrAddress, proposalId)
Note, however, that this query may fail for not so recent proposals as old governance vote power data is being regularly deleted from the C-chain storage.
The C-chain is a standard EVM blockchain populated by the official Flare contracts, as well as other contracts deployed by the community. To interact with any of these contracts, it is sufficient to know the contract address and its application binary interface abi.
To call a contract method with the name method with the purpose of obtaining information from the contract, use``
let result = await network.invokeContractCallOnC(address, abi, method, params)params
where is a sequence of parameters that the method accepts as inputs.
Similarly, to invoke a transaction method, use
``
await network.invokeContractMethodOnC(wallet, address, abi, method, value, params)value
where is the amount of native tokens to send in transaction when the calling method is marked as payable.
For the official Flare contracts, the input address can be replaced by the contract's name. The list of these contracts is returned by``
await network.getFlareContracts()
A Safe smart account is a smart contract on the C-chain operated by selected owners. The smart account can be used to execute standard operations on the C-chain provided that the actions are approved by a sufficient number of owners (smart account threshold).
A new smart account can be created by
``
let smartAccountAddress = await network.createSafeSmartAccount(wallet, owners, threshold)owners
where is an array of C-chain addresses in hexadecimal encoding representing the owners of the smart account, and threshold is an integer between 1 and the length of owners representing the smart account threshold. If the wallet's C-chain address is not included in owners, the wallet's account is only the creator of the smart account and does not participate in its operation. The result smartAccountAddress is the C-chain address of the smart account in hexadecimal encoding.
A smart account can be used in any C-chain operation supported by this SDK. To demonstrate the concept and basic workflow, the following example shows how this functionality can be used to wrap a certain amount of native coins on a smart account and how to transfer these wrapped coins from the smart account to a certain address.
First, let us deposit funds to the smart account:
``
await network.transferNative(wallet, smartAccountAddress, amount)wallet1
Suppose the smart account has 3 or more owners and threshold equal to 3. Moreover, let , wallet2, wallet3 be three wallets that represent three different owners. To make them operate with the smart account, the owners set``
wallet1.smartAccount = smartAccountAddress
wallet2.smartAccount = smartAccountAddress
wallet3.smartAccount = smartAccountAddress`
To wrap native coins on the smart account, the owners execute`
await network.wrapNative(wallet1, amount)
await network.wrapNative(wallet2, amount)
await network.wrapNative(wallet3, amount)amount
All three calls induce a transaction to the smart account. In the first two calls this is a transaction with which two owners approve the wrapping. In the third call this is a transaction with which another owner approves and also executes the operation, i.e., the balance of wrapped coins on the smart account has been increased by . Note that it is important that all three calls have identical inputs and no other operation on the same smart account interrupt the sequence of their execution.
Finally, the three owners agree to transfer wrapped coins from the smart account to address, which is achieved similarly as above by``
await network.transferWrapped(wallet1, address, amount)
await network.transferWrapped(wallet2, address, amount)
await network.transferWrapped(wallet3, address, amount)
To use the wallets in a regular way again, the relation to the smart account must be cleared:
``
wallet1.smartAccount = undefined
wallet2.smartAccount = undefined
wallet3.smartAccount = undefined
For staking, the network uses the P-chain and the information about stakes is then automatically mirrored to the C-chain.
In order to stake funds, one must delegate funds to a validator on the P-chain. This means that first a suitable amount of funds must be transferred from one's address on the C-chain to the one's address on the P-chain. After the delegation is complete, one can transfer the funds back from the P-chain to the C-chain.
`mermaid`
sequenceDiagram
participant C as C-chain
participant B as in-between
participant P as P-chain
C->>B: exportFromC
B->>P: importToP
C->>P: transferToP
P->>P: delegateOnP
P->>P: addValidatorOnP
P->>P: transferOnP
P->>C: transferToC
P->>B: exportFromP
B->>C: importToC
#### Transferring funds from the C-chain to the P-chain
The process of transferring funds from the C-chain to the P-chain consists of two transactions: export from the C-chain and import to the P-chain. The call
``
await network.transferToP(wallet, amount, allocatedFeeOnP)amount + allocatedFeeOnP
first generates an export transaction that exports from the C-chain address and spends a certain exportFeeOnC. The input parameter allocatedFeeOnP is optional and is set to a default value if omitted. After the export transaction is signed, submitted and confirmed, an import transaction is generated that imports amount + allocatedFeeOnP - importFeeOnP to the P-chain address and spends a certain importFeeOnP. After the import transaction is signed, submitted and confirmed, the call is complete. The balance on the C-chain address is reduced by amount + exportFeeOnC + allocatedFeeOnP and the balance on the P-chain address is increased by amount + allocatedFeeOnP - importFeeOnP.
Note that the values appearing in the export and import transactions invoked by transferToP may differ from the described above if a certain valueNotImportedToP had previously not been imported to the P-chain due to the import transaction failure. The export transaction then exports amount + allocatedFeeOnP - valueNotImportedToP from the C-chain address or is skipped if this export value is not positive. The import transaction imports max(amount, valueNotImportedToP) to the P-chain address and the balance of funds not imported to the P-chain becomes zero.
The value of exportFeeOnC is computed as the product of the baseTxFeeOnC and the size of the export transaction. The current value of the baseTxFeeOnC can be obtained by calling await network.getBaseTxFeeOnC(). The value of importFeeOnP is computed based on the size of the import transaction and the base fee on the P-chain, which can be obtained by calling network.getBaseTxFeeOnP().
The export and import can be executed by individual calls as well, in order to have a separate call for each transaction. To export amount from the C-chain, use``
await network.exportFromC(wallet, amount, baseTxFeeOnC)baseFeeTxOnC
The parameter is optional and can be used to override the automatically acquired base transaction fee from the C-chain. To import all exported funds from the C-chain to the P-chain, use``
await network.importToP(wallet)
#### Delegation on the P-chain
The delegation on the P-chain can be executed by
``
await network.delegateOnP(wallet, amount, nodeId, startTime, endTime, allocatedFeeOnP)amount
where is the amount to delegate, nodeId is the validator code of the form NodeID-..., and startTime and endTime are times given by the number of seconds from the Unix epoch. The input parameter allocatedFeeOnP is optional and is used to reserve funds for transaction fees. A delegation transaction is invoked. After the transaction is signed, submitted and confirmed, the balance on the P-chain address is reduced by amount + fee and the staked balance on P is increased by amount. After the delegation is complete, the staked amount is returned to the P-chain address.
If amount + allocatedFeeOnP is greater than availableOnP at the time of the call, the function attempts to transfer amount + allocatedFeeOnP - availableOnP from the C-chain to the P-chain. Therefore, in such cases, the above call also invokes the export and import transactions as described in the previous section.
#### Adding a validator on the P-chain
To add a validator on the P-chain, use
``
await network.addValidatorOnP(wallet, amount, nodeId, startTime, endTime, delegationFee, popBLSPublicKey, popBLSSignature)amount
where is the amount to be staked, nodeId is the validator code of the form NodeID-..., startTime and endTime are times given by the number of seconds from the Unix epoch, delegationFee is the fee percentage in base points (1% is 100 base points) to be charged to delegators, and popBLSPublicKey and popBLSSignature determine the proof of possession of a validator node. A transaction for adding a validator is invoked. After the transaction is signed, submitted and confirmed, the balance on the P-chain address is reduced by amount + fee and the staked balance on P is increased by amount. After the staking period is complete, the staked amount is returned to the P-chain address.
The provided amount must not be smaller than availableOnP at the time of the call. The function does not attempt to transfer any funds from the C-chain to the P-chain, this must be done manually by invoking the export and import transactions as described previously.
#### Reviewing the stakes
The functions for reviewing the current stakes on the P-chain return an array of objects of type Stake and has the following properties:
- txId The hash of the staking transaction;type
- The stake type (delegator or validator);pAddress
- The P-chain address of the stake reward owner;nodeId
- The code of the validator node the stake is assigned to;startTime
- The staking start time in seconds from unix;endTime
- The staking end time in in seconds from unix;amount
- The staked amount in weis;delegationFee
- If stake is of type validator, the percentage in base points that determines the validator's fee charged to the delegators.
To get the array of all stakes of type validator, use``
let stakes = await network.getValidatorsOnP()`
For obtaining all stakes corresponding to a specific validator, use`
let stakes = await network.getValidatorStakes(nodeId)validator
The resulting array contains all stakes (of type and delegator) to the validator's nodeId.
Moreover, to obtain an array of all stakes on the network, use
``
let stakes = await network.getStakesOnP()publicKey
The filtered array containing only stakes where the reward owner is identified by can be obtained by``
let stakes = await network.getStakesOnP(publicKey)
#### Transferring funds from the P-chain to the C-chain
The process of transferring funds from the P-chain to the C-chain consists of two transactions: export from the P-chain and import to the C-chain. The call
``
await network.transferToC(wallet, amount)amount
first generates an export transaction that exports from the P-chain address and spends a fixed exportFeeOnP. After the export transaction is signed, submitted and confirmed, an import transaction is generated that imports amount - importFeeOnC to the C-chain address and spends a certain importFeeOnC. After the import transaction is signed, submitted and confirmed, the call is complete. The balance on the P-chain address is reduced by amount + exportFeeOnP and the balance on the C-chain address is increased by amount - importFeeOnC. In that sense the function transferToC is not symmetric to the function transferToP as the importFee is difficult to predict in advance.
As a shorthand to transfer the entire balance from the P-chain address to the C-chain address, the amount parameter in the above call can be omitted, i.e., the call``
await network.transferToC(wallet)amount = balanceOnP - exportFeeOnP
is equivalent to the above with .
Note that the values appearing in the export and import transactions invoked by transferToC may differ from the described above if a certain valueNotImportedToC had previously not been imported to the C-chain due to the import transaction failure. The export transaction then exports amount - valueNotImportedToC from the P-chain address or is skipped if this export value is not positive. The import transaction imports max(amount, valueNotImportedToC) - exportFeeOnP to the P-chain address and the balance of funds not imported to the P-chain becomes zero.
The value of exportFeeOnP is fixed and can be obtained by calling network.getDefaultTxFeeOnP(). The value of importFeeOnC is computed as the product of the baseTxFeeOnC and the size of the export transaction. The current value of the baseTxFeeOnC can be obtained by calling await network.getBaseTxFeeOnC().
The export and import can be executed by individual calls as well, in order to have a separate call for each transaction. To export amount from the P-chain, use``
await network.exportFromP(wallet, amount)`
To import all exported funds from the P-chain to the C-chain, use`
await network.importToP(wallet, baseTxFeeOnC)baseFeeTxOnC
The parameter is optional and can be used to override the automatically acquired base transaction fee from the C-chain.
It is also possible to transfer funds between addresses on the P-chain. Use
``
await network.transferOnP(wallet, recipient, amount)recipient
where is the recipient P-chain address in bech32 encoding and amount is the amount in weis to be transferred. If amount is not provided, the entire wallet's P-chain balance is transferred.
As explained in Wallet implementation, for generating and signing transactions using this SDK, an implementation of the interface Wallet is required. The following describes SDK-compatible wallet implementations that are available in SDK and can be used in combination with standard wallet frameworks and libraries.
The EIP-1193 standard specifies an API for signing and submitting EVM transactions. This standard is followed by common Web3 wallets such as MetaMask, WalletConnect, Coinbase Wallet, etc. The crucial ingredient of these wallets is the EIP-1193 provider object.
To set up SDK-compatible wallets that communicate with the EIP-1193 provider, first extract provider object from the Web3 wallet framework, and then use``
let controller = new EIP1193WalletController(provider)EIP1193WalletController
to instantiate for managing SDK-compatible wallets. In particular, use``
let wallet = await controller.getActiveWallet()`
to obtain the SDK-compatible wallet associated with the currently active account in the Web3 wallet framework, and`
let wallets = await controller.getWallets()controller
to obtain a list of the SDK-compatible wallets that correspond to all available accounts. In a Web3 wallet framework, a user can dynamically change the active account. To be notified of this, assign a listener to :``
controller.onWalletChange((wallet: EIP1193Wallet) => { // use changed wallet })
The class EIP1193Wallet implements the following functions that enable generating and signing of all types of C-chain and P-chain transactions.
- getCAddressgetPublicKey
- signEthMessage
- signAndSubmitCTransaction
-
For signing on a Ledger device, an SDK-compatible wallet can be obtained by using @zondax/ledger-flare, the official Zondax Ledger client library for the Flare's networks, or @ledgerhq/hw-app-eth, the standard Ledger client library for the Ethereum network.
First, setup flrApp (an instance of FlareApp to be used for signing with Flare app on Ledger device or null) and ethApp (an instance of Eth to be used for signing with Ethereum app on Ledger device or null). Then, instantiate LedgerWalletController:``
let controller = new LedgerWalletController(flrApp, ethApp)let bip44Path = m/44'/60'/0'/0/0
To obtain an SDK-compatible wallet, provide a BIP-44 path of the account (e.g., ) and use``
let wallet = await controller.getWallet(bip44Path)"Flare Network"
The controller detects the currently active app on the Ledger device and provides a suitable wallet. Alternatively, provide a second input argument ( or "Ethereum") to explicitly specify the wallet type. Currently active app can be obtained by using``
let app = await controller.getActiveApp()
The abstract wallet class that implements the interface Wallet is LedgerWalletClass and is a base class for the classes FlrLedgerWallet and EthLedgerWallet. Instances of both classes enable generating and signing of all types of C-chain and P-chain transactions.
An instance of FlrLedgerWallet connects to the Flare app and implements the following functions:getPublicKey
- getCAddress
- signEthMessage
- signCTransaction
- signPTransaction
-
An instance of EthLedgerWallet connects to the Ethereum app and implements the following functions:getPublicKey
- getCAddress
- signEthMessage
- signCTransaction
-
For signing on a Trezor device, an SDK-compatible wallet can be obtained by using the official Trezor libraries Trezor Connect SDKs.
To obtain SDK-compatible wallets, initialize TrezorConnect object and use it to create an object of class TrezorWalletController:``
let controller = new TrezorWalletController(TrezorConnect)let bip44Path = m/44'/60'/0'/0/0
Then, provide a BIP-44 path of the account (e.g., ) and use``
let wallet = await controller.getWallet(bip44Path)
The SDK-compatible wallet is of class TrezorWallet. It implements the following functions that enable generating and signing of all types of C-chain and P-chain transactions.getPublicKey
- getCAddress
- signEthMessage
- signCTransaction`
-