SDK for interacting with the Rootstock Collective DAO protocol. This SDK provides a simple interface for backing builders, governance (proposals and voting), staking, and rewards management.
npm install @rsksmart/collective-sdk viemimport { CollectiveSDK } from '@rsksmart/collective-sdk'
import { parseEther } from 'viem'
// Initialize SDK
const sdk = new CollectiveSDK({ chainId: 31 }) // 31 = testnet, 30 = mainnet
// Get user balances
const balances = await sdk.holdings.getBalances('0x...')
console.log('RIF Balance:', balances.rif.formatted)
console.log('stRIF Balance:', balances.stRif.formatted)
// Get builders list
const builders = await sdk.backing.getBuilders()
console.log('Active builders:', builders.filter(b => b.isOperational).length)
// Get proposals
const { proposals } = await sdk.proposals.getProposals({ limit: 10 })
console.log('Latest proposals:', proposals.length)- Backing: View and manage builder backing allocations
- Holdings: Check token balances (RIF, stRIF, USDRIF, RBTC) and voting power
- Staking: Stake RIF to receive stRIF and manage staking positions
- Rewards: View and claim backer rewards
- Governance: View proposals, vote, and create new proposals
const sdk = new CollectiveSDK({
chainId: 31, // Required: 30 (mainnet) or 31 (testnet)
rpcUrl?: string, // Optional: Custom RPC URL
contractAddresses?: Partial<ContractAddresses>, // Optional: Override addresses
})Get available stRIF balance for backing builders.
const available = await sdk.backing.getAvailableForBacking('0x...')
// Returns:
{
balance: TokenAmount, // Total stRIF balance
totalAllocated: TokenAmount,// Already allocated to builders
available: TokenAmount // Available for new backing
}Get total backing amount for a user.
const backing = await sdk.backing.getTotalBacking('0x...')
// Returns:
{
amount: TokenAmount, // Total allocated amount
buildersCount: number // Number of backed builders
}Get global backers incentives statistics.
const incentives = await sdk.backing.getBackersIncentives()
// Returns:
{
annualPercentage: Percentage, // Estimated annual percentage
rewardsRif: TokenAmount, // Current cycle RIF rewards
rewardsNative: TokenAmount, // Current cycle RBTC rewards
rewardsUsdrif: TokenAmount, // Current cycle USDRIF rewards
totalPotentialReward: TokenAmount
}Get list of all builders in the protocol.
const builders = await sdk.backing.getBuilders()
// Each builder:
{
address: Address, // Builder wallet address
gauge: Address, // Builder's gauge contract
isOperational: boolean,// Active and not paused
totalAllocation: bigint,
stateFlags: {
initialized: boolean,
kycApproved: boolean,
communityApproved: boolean,
kycPaused: boolean,
selfPaused: boolean
}
}Get builders that a user is backing with their allocations.
const { backedBuilders, totalBacking } = await sdk.backing.getBackedBuilders('0x...')
// Each backed builder:
{
builder: Builder,
allocation: TokenAmount // Amount allocated to this builder
}Get token balances for a user.
const balances = await sdk.holdings.getBalances('0x...')
// Returns:
{
rif: TokenAmount, // RIF balance
stRif: TokenAmount, // stRIF balance
usdrif: TokenAmount, // USDRIF balance
rbtc: TokenAmount // Native RBTC balance
}Get voting power (stRIF balance) for governance.
const power = await sdk.holdings.getVotingPower('0x...')
// Returns:
{
votingPower: TokenAmount,
delegatedTo: Address | null
}Get aggregated unclaimed rewards for a backer.
const rewards = await sdk.holdings.getUnclaimedRewards('0x...')
// Returns:
{
rif: TokenAmount,
rbtc: TokenAmount,
usdrif: TokenAmount,
totalUsdValue: number // Estimated USD value
}Get detailed breakdown of rewards per gauge and token.
const detailed = await sdk.holdings.getDetailedRewardsList('0x...')
// Returns rewards grouped by gauge with per-token amountsClaim backer rewards.
// Claim all rewards
const result = await sdk.holdings.claimRewards(walletClient, '0x...', 'all')
// Claim specific token
const result = await sdk.holdings.claimRewards(walletClient, '0x...', 'rif')
// Options: 'rif', 'rbtc', 'usdrif', 'all'
// Wait for confirmation
const receipt = await result.wait(1)
console.log('Claimed in block:', receipt.blockNumber)Get staking information for a user.
const info = await sdk.staking.getStakingInfo('0x...')
// Returns:
{
rifBalance: TokenAmount, // RIF balance
stRifBalance: TokenAmount, // stRIF balance
allowance: TokenAmount, // RIF allowance for staking
hasAllowance: (amount: bigint) => boolean
}Approve RIF tokens for staking.
const tx = await sdk.staking.approveRIF(walletClient, parseEther('100'))
console.log('Approval tx:', tx.hash)
await tx.wait(1)Stake RIF to receive stRIF.
// First approve
await sdk.staking.approveRIF(walletClient, parseEther('100'))
// Then stake (delegate voting power to yourself)
const tx = await sdk.staking.stakeRIF(
walletClient,
parseEther('100'),
'0x...' // delegatee address (usually your own)
)
await tx.wait(1)Unstake stRIF to receive RIF back.
const tx = await sdk.staking.unstakeRIF(
walletClient,
parseEther('50'),
'0x...' // recipient address
)
await tx.wait(1)Get Governor contract statistics.
const stats = await sdk.proposals.getStats()
// Returns:
{
proposalCount: number,
proposalThreshold: TokenAmount, // Minimum stRIF to create proposal
quorumVotes: TokenAmount, // Minimum votes for quorum
votingDelay: number, // Blocks before voting starts
votingPeriod: number // Voting duration in blocks
}Get paginated list of proposals.
const { proposals, total, hasMore } = await sdk.proposals.getProposals({
offset: 0,
limit: 10
})
// Each proposal:
{
id: string,
proposer: Address,
state: ProposalState,
stateLabel: string,
votes: {
forVotes: TokenAmount,
againstVotes: TokenAmount,
abstainVotes: TokenAmount
},
startBlock: bigint,
endBlock: bigint
}Get basic proposal information (fast).
const proposal = await sdk.proposals.getProposal('123456...')Get full proposal details including description and actions.
const proposal = await sdk.proposals.getProposalDetails('123456...', {
fromBlock: 1000000n // Optional: start block for event search
})
// Includes:
{
...basicInfo,
description: string,
actions: ProposalAction[]
}Cast a vote on a proposal.
import { VoteSupport } from '@rsksmart/collective-sdk'
const result = await sdk.proposals.castVote(
walletClient,
'123456...',
VoteSupport.For, // For, Against, or Abstain
{ reason: 'Great proposal!' }
)
await result.wait(1)Check if user has already voted.
const voted = await sdk.proposals.hasVoted('123456...', '0x...')Check if user has enough voting power to create proposals.
const { canCreate, votingPower, threshold } = await sdk.proposals.canCreateProposal('0x...')Create a treasury transfer proposal.
const tx = await sdk.proposals.createTreasuryTransferProposal(walletClient, {
token: 'rif', // 'rif', 'rbtc', or 'usdrif'
recipient: '0x...',
amount: parseEther('1000'),
description: 'Fund development team'
})Create a proposal to whitelist a new builder.
const tx = await sdk.proposals.createBuilderWhitelistProposal(walletClient, {
builderAddress: '0x...',
description: 'Whitelist Builder XYZ'
})import { CollectiveSDK, VoteSupport } from '@rsksmart/collective-sdk'
import { createWalletClient, createPublicClient, http, parseEther } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { rootstockTestnet } from 'viem/chains'
async function main() {
// Initialize SDK
const sdk = new CollectiveSDK({ chainId: 31 })
// Setup wallet
const account = privateKeyToAccount('0x...')
const walletClient = createWalletClient({
account,
chain: rootstockTestnet,
transport: http(),
})
const publicClient = createPublicClient({
chain: rootstockTestnet,
transport: http(),
})
// Check balances
const balances = await sdk.holdings.getBalances(account.address)
console.log('RIF:', balances.rif.formatted)
console.log('stRIF:', balances.stRif.formatted)
// Stake RIF
const stakeAmount = parseEther('100')
const stakingInfo = await sdk.staking.getStakingInfo(account.address)
if (!stakingInfo.hasAllowance(stakeAmount)) {
console.log('Approving RIF...')
const approveTx = await sdk.staking.approveRIF(walletClient, stakeAmount)
await approveTx.wait(1)
}
console.log('Staking RIF...')
const stakeTx = await sdk.staking.stakeRIF(walletClient, stakeAmount, account.address)
await stakeTx.wait(1)
console.log('Staked!')
// View proposals
const { proposals } = await sdk.proposals.getProposals({ limit: 5 })
console.log('Recent proposals:', proposals.length)
// Vote on a proposal
const activeProposal = proposals.find(p => p.stateLabel === 'Active')
if (activeProposal) {
const hasVoted = await sdk.proposals.hasVoted(activeProposal.id, account.address)
if (!hasVoted) {
console.log('Voting...')
const voteTx = await sdk.proposals.castVote(
walletClient,
activeProposal.id,
VoteSupport.For
)
await voteTx.wait(1)
console.log('Voted!')
}
}
// Check and claim rewards
const rewards = await sdk.holdings.getUnclaimedRewards(account.address)
if (rewards.rif.value > 0n || rewards.rbtc.value > 0n) {
console.log('Claiming rewards...')
const claimTx = await sdk.holdings.claimRewards(walletClient, account.address, 'all')
await claimTx.wait(1)
console.log('Rewards claimed!')
}
}
main().catch(console.error)| Network | Chain ID | Status |
|---|---|---|
| Rootstock Mainnet | 30 | ✅ Available |
| Rootstock Testnet | 31 | ✅ Available |
The SDK exports all TypeScript types:
import type {
CollectiveConfig,
TokenBalances,
VotingPower,
StakingInfo,
Builder,
BackersIncentives,
Proposal,
ProposalState,
VoteSupport,
// ... and more
} from '@rsksmart/collective-sdk'import { ProposalState, VoteSupport } from '@rsksmart/collective-sdk'
// Proposal states
ProposalState.Pending
ProposalState.Active
ProposalState.Canceled
ProposalState.Defeated
ProposalState.Succeeded
ProposalState.Queued
ProposalState.Expired
ProposalState.Executed
// Vote options
VoteSupport.Against // 0
VoteSupport.For // 1
VoteSupport.Abstain // 2We welcome contributions from the community. Please fork the repository and submit pull requests with your changes. Ensure your code adheres to the project's main objective.
For any questions or support, please open an issue on the repository or reach out to the maintainers.
The software provided in this GitHub repository is offered "as is," without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement.
- Testing: The software has not undergone testing of any kind, and its functionality, accuracy, reliability, and suitability for any purpose are not guaranteed.
- Use at Your Own Risk: The user assumes all risks associated with the use of this software. The author(s) of this software shall not be held liable for any damages, including but not limited to direct, indirect, incidental, special, consequential, or punitive damages arising out of the use of or inability to use this software, even if advised of the possibility of such damages.
- No Liability: The author(s) of this software are not liable for any loss or damage, including without limitation, any loss of profits, business interruption, loss of information or data, or other pecuniary loss arising out of the use of or inability to use this software.
- Sole Responsibility: The user acknowledges that they are solely responsible for the outcome of the use of this software, including any decisions made or actions taken based on the software's output or functionality.
- No Endorsement: Mention of any specific product, service, or organization does not constitute or imply endorsement by the author(s) of this software.
- Modification and Distribution: This software may be modified and distributed under the terms of the license provided with the software. By modifying or distributing this software, you agree to be bound by the terms of the license.
- Assumption of Risk: By using this software, the user acknowledges and agrees that they have read, understood, and accepted the terms of this disclaimer and assumes all risks associated with the use of this software.
