Skip to content

rsksmart/collective-sdk

Repository files navigation

OpenSSF Scorecard CodeQL

RSK Logo

@rsksmart/collective-sdk

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.

Installation

npm install @rsksmart/collective-sdk viem

Quick Start

import { 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)

Features

  • 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

API Reference

Initialization

const sdk = new CollectiveSDK({
  chainId: 31,                    // Required: 30 (mainnet) or 31 (testnet)
  rpcUrl?: string,                // Optional: Custom RPC URL
  contractAddresses?: Partial<ContractAddresses>, // Optional: Override addresses
})

Backing Module (sdk.backing)

getAvailableForBacking(backerAddress)

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
}

getTotalBacking(backerAddress)

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
}

getBackersIncentives()

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
}

getBuilders()

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
  }
}

getBackedBuilders(backerAddress)

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
}

Holdings Module (sdk.holdings)

getBalances(userAddress)

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
}

getVotingPower(userAddress)

Get voting power (stRIF balance) for governance.

const power = await sdk.holdings.getVotingPower('0x...')

// Returns:
{
  votingPower: TokenAmount,
  delegatedTo: Address | null
}

getUnclaimedRewards(backerAddress)

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
}

getDetailedRewardsList(backerAddress)

Get detailed breakdown of rewards per gauge and token.

const detailed = await sdk.holdings.getDetailedRewardsList('0x...')

// Returns rewards grouped by gauge with per-token amounts

claimRewards(walletClient, backerAddress, token?)

Claim 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)

Staking Module (sdk.staking)

getStakingInfo(userAddress)

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
}

approveRIF(walletClient, amount)

Approve RIF tokens for staking.

const tx = await sdk.staking.approveRIF(walletClient, parseEther('100'))
console.log('Approval tx:', tx.hash)
await tx.wait(1)

stakeRIF(walletClient, amount, delegatee)

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)

unstakeRIF(walletClient, amount, recipient)

Unstake stRIF to receive RIF back.

const tx = await sdk.staking.unstakeRIF(
  walletClient,
  parseEther('50'),
  '0x...' // recipient address
)
await tx.wait(1)

Proposals Module (sdk.proposals)

getStats()

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
}

getProposals(options?)

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
}

getProposal(proposalId)

Get basic proposal information (fast).

const proposal = await sdk.proposals.getProposal('123456...')

getProposalDetails(proposalId, options?)

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[]
}

castVote(walletClient, proposalId, support, options?)

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)

hasVoted(proposalId, voterAddress)

Check if user has already voted.

const voted = await sdk.proposals.hasVoted('123456...', '0x...')

canCreateProposal(userAddress)

Check if user has enough voting power to create proposals.

const { canCreate, votingPower, threshold } = await sdk.proposals.canCreateProposal('0x...')

createTreasuryTransferProposal(walletClient, options)

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'
})

createBuilderWhitelistProposal(walletClient, options)

Create a proposal to whitelist a new builder.

const tx = await sdk.proposals.createBuilderWhitelistProposal(walletClient, {
  builderAddress: '0x...',
  description: 'Whitelist Builder XYZ'
})

Full Example

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)

Supported Networks

Network Chain ID Status
Rootstock Mainnet 30 ✅ Available
Rootstock Testnet 31 ✅ Available

Types

The SDK exports all TypeScript types:

import type {
  CollectiveConfig,
  TokenBalances,
  VotingPower,
  StakingInfo,
  Builder,
  BackersIncentives,
  Proposal,
  ProposalState,
  VoteSupport,
  // ... and more
} from '@rsksmart/collective-sdk'

Enums

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  // 2

About

No description, website, or topics provided.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors