---
title: "Token Staking System"
description: "White-label staking module with EVM adapter (viem WalletClient), supporting DAAR and DAARION pools with profile integration"
locale: "en"
---
# Token Staking System

Complete staking system for DAAR and DAARION tokens with automated rewards, profile integration, and white-label theming support.

## Overview

The Ring Platform staking system enables authenticated users to stake DAAR and DAARION tokens in multiple pools to earn rewards while supporting platform governance and liquidity. The system features automated reward distribution, wrong-network protection, and optional server-side position caching.

  The shipped EVM staking adapter (`features/staking/adapters/evm.ts`) uses a **viem `WalletClient`** injected from Wagmi — the app runtime carries **no `ethers`** in `features/`, `app/`, or `lib/`. The `ethers.*` calls in the illustrative snippets below show the on-chain operations conceptually; port them to viem (`getWalletClient`, `readContract`, `writeContract`) in real integrations. RPC and RING token addresses resolve through `getEvmRpcUrl()` / `getEvmTokenAddress()` — see [Ethereum wallets](/docs/integrations/ethereum-wallets.md).

## Key Features

### Multi-Pool Staking

**Supported Token Pools**
- **DAAR Pool**: Primary governance token staking
- **DAARION Pool**: Reward token staking with higher yields
- **Flexible Pool Management**: Dynamic pool creation and configuration

**Staking Operations**
- **Stake**: Deposit tokens into staking pools
- **Unstake**: Withdraw tokens from pools with lock periods
- **Claim**: Collect accumulated rewards
- **Compound**: Automatic reinvestment of rewards

### Automated Reward Distribution

**APR-Based Staking System**
- **Dynamic APR Calculation**: Real-time reward rate computation
- **Fee Distributor Integration**: Automated reward distribution
- **Reward Tracking**: Comprehensive earning history and analytics

**Reward Mechanism**
// APR calculation example

{`const calculateAPR = (totalStaked: number, rewardRate: number, timePeriod: number) => {
  const annualReward = (totalStaked * rewardRate * 365) / timePeriod
  return (annualReward / totalStaked) * 100 // APR percentage
}`}

### Profile Integration

**Public Profile Staking Summary**
// Public profile route with staking summary

{`/[locale]/[username]  // Includes staking portfolio`}

**Profile Features**
- **Staking Portfolio Display**: Active positions and rewards
- **Performance Metrics**: Historical returns and analytics
- **Social Sharing**: Achievement badges and milestones
- **SEO Optimization**: Staking activity in profile metadata

### Security Features

**Network Protection**
// Wrong-network protection

{`const validateNetwork = (chainId: number) => {
  const supportedChains = [1, 137] // Ethereum, Polygon
  if (!supportedChains.includes(chainId)) {
    throw new Error('Unsupported network for staking')
  }
}`}

**Smart Allowance Management**
// Safe allowance handling with zero-reset fallback

{`const manageAllowance = async (tokenContract: Contract, spender: string, amount: string) => {
  const currentAllowance = await tokenContract.allowance(owner, spender)

  // Reset to zero if current allowance exists (prevents front-running)
  if (currentAllowance > 0) {
    await tokenContract.approve(spender, 0)
  }

  // Set new allowance
  await tokenContract.approve(spender, amount)
}`}

## Implementation

### Staking Pool Operations

**Stake Tokens in Pool**
// Client-side staking with wallet connection

{`'use client'

export function StakingPanel({ userId }: { userId: string }) {
  const [selectedPool, setSelectedPool] = useState('DAAR')
  const [amount, setAmount] = useState('')
  const [isStaking, setIsStaking] = useState(false)

  const handleStake = async () => {
    setIsStaking(true)

    try {
      // Validate network
      await validateNetwork()

      // Connect wallet
      const provider = new ethers.BrowserProvider(window.ethereum)
      const signer = await provider.getSigner()

      // Get staking contract
      const stakingContract = new ethers.Contract(
        STAKING_CONTRACT_ADDRESS,
        STAKING_ABI,
        signer
      )

      // Approve token spending
      const tokenContract = new ethers.Contract(
        selectedPool === 'DAAR' ? DAAR_ADDRESS : DAARION_ADDRESS,
        ERC20_ABI,
        signer
      )

      await manageAllowance(tokenContract, STAKING_CONTRACT_ADDRESS, amount)

      // Execute stake transaction
      const stakeTx = await stakingContract.stake(
        selectedPool === 'DAAR' ? 0 : 1, // Pool ID
        ethers.parseEther(amount)
      )
      await stakeTx.wait()

      // Update backend records
      await recordStakingPosition({
        userId,
        poolId: selectedPool,
        amount,
        transactionHash: stakeTx.hash,
        timestamp: new Date()
      })

    } catch (error) {
      console.error('Staking failed:', error)
      setIsStaking(false)
    }
  }

  return (
    
       setSelectedPool(e.target.value)}>
        DAAR Pool
        DAARION Pool
      
       setAmount(e.target.value)}
        placeholder="Amount to stake"
      />
      
        {isStaking ? 'Staking...' : 'Stake Tokens'}
      
    
  )
}`}

**Claim Staking Rewards**
// Claim accumulated rewards

{`export async function claimStakingRewards(userId: string, poolId: number) {
  // Get user's staking position
  const position = await getStakingPosition(userId, poolId)

  if (!position || position.rewards <= 0) {
    throw new Error('No rewards available to claim')
  }

  // Connect to staking contract
  const stakingContract = new ethers.Contract(
    STAKING_CONTRACT_ADDRESS,
    STAKING_ABI,
    signer
  )

  // Claim rewards transaction
  const claimTx = await stakingContract.claimRewards(poolId)
  await claimTx.wait()

  // Update position records
  await updateStakingPosition(position.id, {
    claimedRewards: position.claimedRewards + position.rewards,
    lastClaimDate: new Date(),
    transactionHash: claimTx.hash
  })

  return {
    claimedAmount: position.rewards,
    transactionHash: claimTx.hash
  }
}`}

**Unstake Tokens**

{`// Unstake tokens from pool (with potential lock period)
export async function unstakeTokens(positionId: string) {
  const position = await getStakingPositionById(positionId)

  // Check if unstaking is allowed (lock period)
  const lockEndDate = new Date(position.createdAt.getTime() + (position.lockPeriod * 24 * 60 * 60 * 1000))
  const now = new Date()

  if (now < lockEndDate) {
    const remainingDays = Math.ceil((lockEndDate - now) / (24 * 60 * 60 * 1000))
    throw new Error(`Tokens are locked for ${remainingDays} more days`)
  }

  // Execute unstake transaction
  const stakingContract = new ethers.Contract(
    STAKING_CONTRACT_ADDRESS,
    STAKING_ABI,
    signer
  )

  const unstakeTx = await stakingContract.unstake(position.poolId, position.amount)
  await unstakeTx.wait()

  // Update position status
  await updateStakingPosition(positionId, {
    status: 'unstaked',
    unstakedAt: new Date(),
    transactionHash: unstakeTx.hash
  })

  return {
    unstakedAmount: position.amount,
    transactionHash: unstakeTx.hash
  }
}`}

### Profile Staking Integration

**Staking Summary Component**
// components/profile/StakingSummary.tsx

{`export function StakingSummary({ userId }: { userId: string }) {
  const [stakingData, setStakingData] = useState(null)

  useEffect(() => {
    const fetchStakingData = async () => {
      const positions = await getUserStakingPositions(userId)
      const rewards = await getUserStakingRewards(userId)
      const analytics = await getStakingAnalytics(userId)

      setStakingData({
        positions,
        rewards,
        analytics
      })
    }

    fetchStakingData()
  }, [userId])

  if (!stakingData) return Loading staking data...

  return (
    
      Staking Portfolio

      
        
          Total Staked
          
            {stakingData.analytics.totalStaked} DAAR
          
        

        
          Total Rewards
          
            {stakingData.analytics.totalRewards} DAARION
          
        

        
          Average APR
          
            {stakingData.analytics.averageAPR}%
          
        
      

      
        Active Positions
        {stakingData.positions.map(position => (
          
        ))}
      

      
        Reward History
        {stakingData.rewards.map(reward => (
          
        ))}
      
    
  )
}`}

## API Endpoints

### Staking Management
// GET /api/staking/positions - Get user's staking positions

{`// POST /api/staking/stake - Create new staking position
// POST /api/staking/unstake - Unstake tokens from position
// POST /api/staking/claim - Claim staking rewards
// GET /api/staking/pools - Get available staking pools
// GET /api/staking/rewards - Get claimable rewards
// GET /api/staking/analytics - Get staking analytics`}

### Pool Information

{`// GET /api/staking/pools/[poolId] - Get specific pool details
// GET /api/staking/pools/[poolId]/stats - Get pool statistics
// GET /api/staking/pools/[poolId]/positions - Get pool positions`}

## Data Models

### Staking Position Schema

{`id: string
  userId: string
  poolId: number // 0 = DAAR, 1 = DAARION
  amount: string // Amount staked (in wei)
  rewards: string // Accumulated rewards
  claimedRewards: string // Already claimed rewards
  lockPeriod: number // Lock period in days
  createdAt: Date
  lastRewardUpdate: Date
  status: 'active' | 'unstaked' | 'locked'
  transactionHash?: string
  unstakedAt?: Date
}`}

### Staking Pool Schema

{`id: number
  name: string
  tokenAddress: string
  tokenSymbol: string
  apr: number // Current APR percentage
  totalStaked: string // Total tokens staked
  rewardRate: string // Reward rate per second
  minStake: string // Minimum stake amount
  maxStake?: string // Maximum stake amount
  lockPeriod: number // Lock period in days
  isActive: boolean
  createdAt: Date
}`}

### Staking Reward Schema

{`id: string
  userId: string
  positionId: string
  poolId: number
  amount: string // Reward amount
  claimed: boolean
  claimDate?: Date
  transactionHash?: string
  createdAt: Date
}`}

## Smart Contract Integration

### APR Staking Contract
// Contract interface for APR-based staking

{`interface APRStakingContract {
  // View functions
  getPoolInfo(poolId: number): Promise<{
    totalStaked: bigint
    rewardRate: bigint
    lastUpdateTime: bigint
  }>

  getUserInfo(poolId: number, user: string): Promise<{
    amount: bigint
    rewardDebt: bigint
  }>

  // State-changing functions
  stake(poolId: number, amount: bigint): Promise
  unstake(poolId: number, amount: bigint): Promise
  claimRewards(poolId: number): Promise
}`}

### Fee Distributor Contract
// Contract interface for reward distribution

{`interface FeeDistributorContract {
  // View functions
  claimable(user: string, token: string): Promise

  // State-changing functions
  claim(token: string): Promise
  claimMany(tokens: string[]): Promise
}`}

## Server-Side Features

### Position Caching (Optional)

**Server-side position reading for improved performance:**
// features/staking/server/read-positions.ts

{`export async function getCachedStakingPositions(userId: string) {
  // Check cache first
  const cached = await getCachedPositions(userId)
  if (cached && isCacheValid(cached.timestamp)) {
    return cached.positions
  }

  // Fetch from blockchain
  const positions = await fetchPositionsFromContract(userId)

  // Cache results
  await cachePositions(userId, positions)

  return positions
}`}

### Background Processing

**Automated reward updates and position synchronization:**
// Automated reward calculation service

{`export async function updateStakingRewards() {
  const activePositions = await getActivePositions()

  for (const position of activePositions) {
    const rewards = await calculateRewards(position)
    await updatePositionRewards(position.id, rewards)
  }
}`}

## Error Handling

### Common Error Scenarios
// Handle staking system errors

{`export function handleStakingError(error: StakingError) {
  switch (error.code) {
    case 'INSUFFICIENT_BALANCE':
      return 'Insufficient token balance'
    case 'LOCK_PERIOD_ACTIVE':
      return 'Tokens are still locked'
    case 'NETWORK_MISMATCH':
      return 'Please switch to the correct network'
    case 'ALLOWANCE_TOO_LOW':
      return 'Token allowance too low'
    case 'CONTRACT_ERROR':
      return 'Smart contract execution failed'
    default:
      return 'Staking operation failed'
  }
}`}

### Transaction Monitoring
// Monitor staking transactions

{`export async function monitorStakingTransaction(txHash: string) {
  const provider = new ethers.JsonRpcProvider(RPC_URL)

  try {
    const receipt = await provider.waitForTransaction(txHash, 1)

    if (receipt.status === 1) {
      // Success - update position status
      await updatePositionStatus(txHash, 'confirmed')
      await invalidatePositionCache(userId)
    } else {
      // Failed - handle error
      await handleStakingFailure(txHash)
    }
  } catch (error) {
    console.error('Transaction monitoring failed:', error)
  }
}`}

## White-label Theming

**Brand Customization for Staking UI**
// features/staking/theme.config.ts

{`export const stakingTheme = {
  colors: {
    primary: '#your-brand-primary',
    secondary: '#your-brand-secondary',
    reward: '#reward-highlight-color',
    warning: '#warning-color'
  },
  components: {
    stakingCard: {
      borderRadius: '8px',
      shadow: '0 2px 8px rgba(0,0,0,0.1)'
    },
    rewardBadge: {
      background: 'linear-gradient(45deg, #gold, #yellow)',
      color: '#000'
    }
  },
  pools: {
    DAAR: {
      name: 'Your Brand DAAR Pool',
      description: 'Stake DAAR tokens for premium rewards'
    },
    DAARION: {
      name: 'Your Brand DAARION Pool',
      description: 'Stake DAARION tokens for maximum yields'
    }
  }
}`}

## Integration Checklist

### Pre-Launch Setup
- [ ] Deploy staking smart contracts
- [ ] Configure supported networks and RPC endpoints
- [ ] Set up fee distributor contracts
- [ ] Configure APR calculation parameters
- [ ] Test token approval and staking flows
- [ ] Validate wallet connection and network switching

### Production Deployment
- [ ] Enable staking pools with initial parameters
- [ ] Configure reward distribution schedules
- [ ] Set up transaction monitoring and alerting
- [ ] Enable white-label theming and branding
- [ ] Test end-to-end staking and reward flows
- [ ] Monitor gas costs and optimize contract calls

## Related documentation

  
- [integrations/ethereum-wallets](/docs/integrations/ethereum-wallets.md) — Depends-on: Wagmi v3 + viem WalletClient, getEvmRpcUrl / getEvmTokenAddress SSOT.

  
- [features/wallet](/docs/features/wallet.md) — Same-workflow: custodial balances and credit ledger alongside on-chain stakes.
