Skip to content

Instantly share code, notes, and snippets.

@nanthanwa
Created September 6, 2025 19:04
Show Gist options
  • Save nanthanwa/5186bfdc5cb44d2fa9987abc86fbb6e1 to your computer and use it in GitHub Desktop.
Save nanthanwa/5186bfdc5cb44d2fa9987abc86fbb6e1 to your computer and use it in GitHub Desktop.

Cap Protocol v4 - Frontend Integration Guide

This guide provides comprehensive information for frontend engineers to integrate with the Cap Protocol v4 smart contracts deployed on Arbitrum Sepolia.

Table of Contents

Quick Start

Network Configuration

const ARBITRUM_SEPOLIA = {
  chainId: 421614,
  name: 'Arbitrum Sepolia',
  currency: 'ETH',
  rpcUrls: ['https://arbitrum-sepolia-rpc.publicnode.com'],
  blockExplorerUrls: ['https://sepolia.arbiscan.io/'],
}

Contract Addresses

const CONTRACTS = {
  CHAINLINK: '0x422bC676E1b5091448E8f42308017Ae1f146a441',
  STORE: '0x3Bc4AdC426b4C6Ec200a33c419a08DD344cBE3d6', 
  TRADE: '0x55c75ae8658E6fab799E9Fedcd2Bc3A19e695F90',
  POOL: '0x10cE03e10897b9bc78977343cA59957DF2772D2F',
  CLP: '0xE416450301f4a82cD6d1781EAae572F320Ed2429',
}

Integration Scenarios

1. πŸ“Š Price Display Dashboard

Goal: Show real-time prices for ETH and BTC Flow: Chainlink β†’ Price Display Frequency: Every 10-30 seconds

2. πŸ“ˆ Trading Interface

Goal: Allow users to open/close leveraged positions
Flow: User Input β†’ Order Submission β†’ Position Management User Actions: Submit orders, close positions, view PnL

3. 🏊 Liquidity Provider Dashboard

Goal: Enable LP deposits/withdrawals and show yields Flow: Deposit β†’ Get LP Tokens β†’ View Rewards β†’ Withdraw User Actions: Add/remove liquidity, claim rewards

4. πŸ‘€ Portfolio Management

Goal: Show user's positions, balances, and history Flow: Connect Wallet β†’ Fetch User Data β†’ Display Portfolio Data: Open positions, balance, PnL, order history

Read Methods (View Functions)

πŸ”— Chainlink Contract - Price Data

getPrice(address feed) β†’ uint256

Purpose: Get current price from Chainlink oracle
When to use: Real-time price display, order validation
Returns: Price with 8 decimals (e.g., 250000000000 = $2,500.00)

// Get ETH price
const ethPrice = await chainlinkContract.getPrice('0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165')
const formattedPrice = ethPrice / 1e8 // Convert to readable format

sequencerUptimeFeed() β†’ address

Purpose: Get sequencer uptime feed address for L2 monitoring
When to use: System health checks, display network status


πŸͺ Store Contract - Configuration & User Data

getMarket(string symbol) β†’ Market

Purpose: Get market configuration (leverage, fees, limits)
When to use: Display trading limits, validate order parameters
Returns: Market struct with all trading parameters

// Get ETH-USD market config
const ethMarket = await storeContract.getMarket('ETH-USD')
// ethMarket.maxLeverage, ethMarket.fee, ethMarket.minSize, etc.

getBalance(address user) β†’ uint256

Purpose: Get user's USDC balance in the protocol
When to use: Display available trading balance
Returns: Balance in USDC (6 decimals)

getPosition(address user, string market) β†’ Position

Purpose: Get user's open position in a market
When to use: Display current positions, calculate PnL
Returns: Position struct with size, entry price, timestamp

getOrders(address user) β†’ Order[]

Purpose: Get user's pending orders
When to use: Display order book, show pending orders
Returns: Array of Order structs

getUserPositions(address user) β†’ Position[]

Purpose: Get all user positions across markets
When to use: Portfolio overview, position management
Returns: Array of all user positions


🏊 Pool Contract - Liquidity Data

getBalance() β†’ uint256

Purpose: Get total pool liquidity
When to use: Display total available liquidity
Returns: Pool balance in USDC

getUtilization() β†’ uint256

Purpose: Get pool utilization percentage
When to use: Show how much liquidity is being used
Returns: Utilization rate (basis points, 10000 = 100%)


πŸͺ™ CLP Contract - LP Token Data

totalSupply() β†’ uint256

Purpose: Get total LP tokens in circulation
When to use: Calculate LP token price, APY calculations

balanceOf(address user) β†’ uint256

Purpose: Get user's LP token balance
When to use: Display LP holdings, calculate share percentage

Write Methods (State Changes)

πŸ’Ό Trade Contract - Order Management

submitOrder(address user, string market, bool isLong, uint256 margin, uint256 size, uint256 price, bool isMarket)

Purpose: Submit a new trading order
When to use: User wants to open a leveraged position
Gas: ~200k-300k gas
Prerequisites:

  • User must have sufficient balance
  • Margin must meet minimum requirements
  • Size must be within limits
// Submit long ETH position with 10x leverage
await tradeContract.submitOrder(
  userAddress,
  'ETH-USD',
  true, // isLong
  ethers.parseUnits('100', 6), // $100 margin (USDC)
  ethers.parseUnits('1000', 6), // $1000 position size  
  0, // price (0 for market order)
  true // isMarket
)

closePosition(address user, string market, uint256 size)

Purpose: Close an existing position (full or partial)
When to use: User wants to take profits or cut losses
Gas: ~150k-250k gas

cancelOrder(uint256 orderId)

Purpose: Cancel a pending limit order
When to use: User wants to cancel unfilled order
Gas: ~50k gas


🏊 Pool Contract - Liquidity Management

deposit(address user, uint256 amount)

Purpose: Add liquidity to earn LP rewards
When to use: User wants to become a liquidity provider
Gas: ~100k-150k gas
Prerequisites: User must approve USDC spending

// First approve USDC spending
await usdcContract.approve(poolAddress, amount)
// Then deposit
await poolContract.deposit(userAddress, ethers.parseUnits('1000', 6))

withdraw(address user, uint256 amount)

Purpose: Remove liquidity and burn LP tokens
When to use: User wants to exit LP position
Gas: ~100k-150k gas

collectRewards(address user)

Purpose: Claim accumulated trading fees and rewards
When to use: User wants to harvest LP earnings
Gas: ~80k gas


πŸͺ Store Contract - Balance Management

deposit(address user, uint256 amount)

Purpose: Deposit USDC for trading
When to use: User needs to fund their trading account
Gas: ~80k gas
Prerequisites: USDC approval required

withdraw(address user, uint256 amount)

Purpose: Withdraw USDC from trading account
When to use: User wants to remove funds
Gas: ~80k gas

Trading Flow

🎯 Complete Trading Sequence

sequenceDiagram
    participant User
    participant Frontend  
    participant Store
    participant Trade
    participant Chainlink
    participant Pool

    User->>Frontend: Connect Wallet
    Frontend->>Store: getBalance(user)
    Frontend->>Chainlink: getPrice(feed)
    Frontend->>Store: getMarket(symbol)
    
    User->>Frontend: Submit Order
    Frontend->>Store: deposit(amount) [if needed]
    Frontend->>Trade: submitOrder(...)
    Trade->>Chainlink: getPrice(feed)
    Trade->>Store: updatePosition(...)
    Trade->>Pool: updateBalance(...)
    
    Frontend->>Store: getPosition(user)
    Frontend->>User: Show Updated Position
Loading

πŸ“‹ Order Submission Checklist

  1. Price Check: Get current market price from Chainlink
  2. Balance Check: Verify user has sufficient margin
  3. Market Validation: Check market is active and within limits
  4. Parameter Validation: Validate leverage, size, slippage
  5. Approval Check: Ensure USDC spending is approved
  6. Submit Transaction: Call submitOrder with validated parameters
  7. Monitor Status: Watch for transaction confirmation
  8. Update UI: Refresh positions and balances

Liquidity Provider Flow

🏊 LP Deposit Sequence

sequenceDiagram
    participant User
    participant Frontend
    participant USDC
    participant Pool
    participant CLP

    User->>Frontend: Click "Add Liquidity"
    Frontend->>USDC: allowance(user, pool)
    alt Insufficient Allowance
        Frontend->>USDC: approve(pool, amount)
    end
    Frontend->>Pool: deposit(user, amount)
    Pool->>CLP: mint(user, shares)
    Frontend->>CLP: balanceOf(user)
    Frontend->>User: Show LP Tokens Received
Loading

πŸ’° LP Reward Tracking

  1. Track LP Tokens: Monitor balanceOf(user) for LP tokens
  2. Calculate Share: userTokens / totalSupply * 100
  3. Monitor Pool Growth: Track pool balance changes
  4. Show APY: Calculate based on trading volume and fees
  5. Harvest Rewards: Allow users to collectRewards()

Error Handling

⚠️ Common Error Scenarios

Trading Errors

try {
  await tradeContract.submitOrder(...)
} catch (error) {
  if (error.message.includes('!balance')) {
    showError('Insufficient balance')
  } else if (error.message.includes('!size')) {
    showError('Position size too small')  
  } else if (error.message.includes('!leverage')) {
    showError('Leverage exceeds maximum')
  }
}

Network Errors

// Check if user is on correct network
if (chainId !== 421614) {
  await switchNetwork(ARBITRUM_SEPOLIA)
}

// Handle RPC failures
const providers = [
  'https://arbitrum-sepolia-rpc.publicnode.com',
  'https://sepolia-rollup.arbitrum.io/rpc'
]

Code Examples

πŸ”§ Complete Integration Example

import { ethers } from 'ethers'

class CapProtocolSDK {
  constructor(provider, signer) {
    this.provider = provider
    this.signer = signer
    this.contracts = this.initContracts()
  }

  initContracts() {
    return {
      chainlink: new ethers.Contract(CONTRACTS.CHAINLINK, chainlinkABI, this.signer),
      store: new ethers.Contract(CONTRACTS.STORE, storeABI, this.signer),
      trade: new ethers.Contract(CONTRACTS.TRADE, tradeABI, this.signer),
      pool: new ethers.Contract(CONTRACTS.POOL, poolABI, this.signer),
      clp: new ethers.Contract(CONTRACTS.CLP, clpABI, this.signer)
    }
  }

  // Get real-time prices
  async getPrices() {
    const [ethPrice, btcPrice] = await Promise.all([
      this.contracts.chainlink.getPrice('0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165'),
      this.contracts.chainlink.getPrice('0x56a43EB56Da12C0dc1D972ACb089c06a5dEF8e69')
    ])
    
    return {
      ETH: ethPrice / 1e8,
      BTC: btcPrice / 1e8
    }
  }

  // Get user portfolio
  async getUserPortfolio(address) {
    const [balance, ethPosition, btcPosition, lpTokens] = await Promise.all([
      this.contracts.store.getBalance(address),
      this.contracts.store.getPosition(address, 'ETH-USD'),
      this.contracts.store.getPosition(address, 'BTC-USD'), 
      this.contracts.clp.balanceOf(address)
    ])

    return {
      balance: balance / 1e6, // Convert to readable USDC
      positions: { ethPosition, btcPosition },
      lpTokens: lpTokens / 1e18
    }
  }

  // Submit market order
  async submitMarketOrder(market, isLong, marginUSD, leverageX) {
    const margin = ethers.parseUnits(marginUSD.toString(), 6)
    const size = margin * BigInt(leverageX)
    
    return await this.contracts.trade.submitOrder(
      await this.signer.getAddress(),
      market,
      isLong,
      margin,
      size,
      0, // market price
      true // is market order
    )
  }

  // Add liquidity
  async addLiquidity(amountUSD) {
    const amount = ethers.parseUnits(amountUSD.toString(), 6)
    return await this.contracts.pool.deposit(
      await this.signer.getAddress(),
      amount
    )
  }
}

πŸ“± React Hook Example

import { useContract, useContractRead } from 'wagmi'

export function useCapProtocol() {
  // Real-time price hook
  const { data: ethPrice } = useContractRead({
    address: CONTRACTS.CHAINLINK,
    abi: chainlinkABI,
    functionName: 'getPrice',
    args: ['0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165'],
    watch: true,
    select: (data) => Number(data) / 1e8
  })

  // User balance hook
  const { data: userBalance } = useContractRead({
    address: CONTRACTS.STORE,
    abi: storeABI,
    functionName: 'getBalance',
    args: [address],
    watch: true,
    select: (data) => Number(data) / 1e6
  })

  return { ethPrice, userBalance }
}

🚨 Important Notes

Security Considerations

  1. Always validate user inputs before sending transactions
  2. Check allowances before token transfers
  3. Handle slippage for market orders
  4. Monitor gas prices on Arbitrum
  5. Implement timeout handling for pending transactions

Performance Tips

  1. Batch read calls using multicall when possible
  2. Cache market configurations (they rarely change)
  3. Use websockets for real-time price updates
  4. Implement proper loading states for better UX
  5. Add transaction status tracking

Testing

  • Use Arbitrum Sepolia testnet for all development
  • Get test ETH from Chainlink Faucet
  • Test all error scenarios before mainnet deployment

πŸš€ Ready to integrate! This guide covers all scenarios for building a complete trading interface with the Cap Protocol v4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment