Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.deframe.io/llms.txt

Use this file to discover all available pages before exploring further.

This example demonstrates how to deposit funds into a DeFi yield strategy (e.g., Aave lending) using the Deframe API with JavaScript/TypeScript.

Overview

This example covers:
  • Fetching available strategies
  • Getting strategy details
  • Requesting a quote
  • Generating transaction bytecode
  • Executing transactions
  • Verifying the deposit

Prerequisites

  • Node.js 18+
  • Deframe API key
  • Ethereum wallet with USDC balance
  • Basic understanding of async/await

Installation

npm install axios ethers

Environment Variables

Create a .env file:
DEFRAME_API_KEY=your-api-key
WALLET_ADDRESS=0x...
PRIVATE_KEY=0x...
ETHEREUM_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/your-key
POLYGON_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/your-key
Never commit your .env file to version control. Add it to .gitignore.

Step-by-Step Walkthrough

1. Initialize API Client

import axios from 'axios'

const deframe = axios.create({
  baseURL: 'https://api.deframe.io',
  headers: {
    'x-api-key': process.env.DEFRAME_API_KEY,
    'Content-Type': 'application/json'
  }
})

2. Fetch Available Strategies

const { data: strategies } = await deframe.get('/strategies', {
  params: { page: '1', limit: '10' }
})

console.log('Available strategies:')
strategies.data.forEach(strategy => {
  console.log(`- ${strategy.id} (${strategy.protocol})`)
  console.log(`  APY: ${(strategy.apy * 100).toFixed(2)}%`)
})

3. Get Strategy Details

const strategyId = 'aave-usdc-polygon'
const { data: strategy } = await deframe.get(`/strategies/${strategyId}`)

console.log('Strategy:', strategy.id)
console.log('Protocol:', strategy.protocol)
console.log('Network:', strategy.network)
console.log('Asset:', strategy.assetName)
console.log('APY:', (strategy.apy * 100).toFixed(2) + '%')
console.log('Status:', strategy.paused ? 'Paused' : 'Active')

4. Generate Bytecode

const { data: result } = await deframe.get('/strategies/aave-usdc-polygon/bytecode', {
  params: {
    action: 'lend',
    amount: '1000000000',
    wallet: walletAddress
  }
})

console.log('ID:', result.id)
console.log('Transactions to execute:', result.bytecode.length)

Same-Chain Swap + Deposit

If you hold a different token (e.g., WETH) and want to deposit into a USDC strategy, pass the fromTokenAddress parameter. The API will return swap transaction(s) followed by the deposit bytecode:
const { data: result } = await deframe.get('/strategies/aave-usdc-polygon/bytecode', {
  params: {
    action: 'lend',
    amount: '1000000000000000000', // 1 WETH (amount is in fromTokenAddress units)
    wallet: walletAddress,
    fromChainId: 137,
    fromTokenAddress: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619' // WETH on Polygon
  }
})

console.log('Cross-chain:', result.crossChain.isCrossChain) // false
console.log('Transactions to execute:', result.bytecode.length) // e.g. 2 (swap + deposit)
When fromTokenAddress is provided, include fromChainId or chainIdIn. For same-chain swap + deposit, use the strategy chain. The amount parameter refers to the amount of that token to swap, not the strategy’s underlying asset.

Cross-Chain Deposit

If the funds start on a different chain, pass fromChainId or chainIdIn plus fromTokenAddress. Store result.id; it is used for asynchronous tracking.
const { data: result } = await deframe.get('/strategies/aave-usdc-arbitrum/bytecode', {
  params: {
    action: 'lend',
    amount: '1000000',
    wallet: walletAddress,
    fromChainId: 8453,
    fromTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' // USDC on Base
  }
})

console.log('ID:', result.id)
console.log('Bridge:', result.quote.bridge)
console.log('Source chain:', result.chainIdIn)
console.log('Destination chain:', result.chainIdOut)

5. Execute Transactions

import { ethers } from 'ethers'

const provider = new ethers.JsonRpcProvider(process.env.POLYGON_RPC_URL)
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider)

for (const tx of result.bytecode) {
  const transaction = await wallet.sendTransaction({
    to: tx.to,
    data: tx.data,
    value: tx.value || '0',
    chainId: tx.chainId
  })

  console.log('Transaction sent:', transaction.hash)

  const receipt = await transaction.wait()
  console.log('Confirmed in block:', receipt.blockNumber)
}

6. Verify Deposit

// Wait for indexing
await new Promise(resolve => setTimeout(resolve, 5000))

const { data: walletData } = await deframe.get(`/wallets/${walletAddress}`)

console.log('Wallet positions:')
walletData.positions.forEach(position => {
  console.log(`${position.strategy.id}:`)
  console.log(`  Balance: ${position.spotPosition.currentPosition.humanized} ${position.spotPosition.currentPosition.symbol}`)
  console.log(`  Value: $${position.spotPosition.underlyingBalanceUSD}`)
  console.log(`  APY: ${(position.spotPosition.apy * 100).toFixed(2)}%`)
})

console.log('Total portfolio value: $' + walletData.summary.totalUnderlyingBalanceUSD)

7. Track Cross-Chain Deposit Status (Cross-Chain Only)

If you made a cross-chain deposit, the bridge and the destination-chain deposit happen in separate transactions and complete asynchronously. Use the id returned in the bytecode response to poll /actions/:id until the status reaches a terminal state.
const id = result.id // returned from /strategies/:id/bytecode

// (Optional) preview the bridge & expected output before signing
if (result.crossChain.isCrossChain) {
  console.log(`Bridging via ${result.quote.bridge}`)
  console.log(`Expected out: ${result.quote.outputAmount.humanized} ${result.quote.outputAmount.symbol}`)
  console.log(`Min out:      ${result.quote.minimumOutputAmount.humanized} ${result.quote.minimumOutputAmount.symbol}`)
}

async function waitForDeposit (id) {
  const TERMINAL = ['SUCCESS', 'FAILED', 'REFUNDED', 'EXPIRED']

  while (true) {
    const { data: statusRecord } = await deframe.get(`/actions/${id}`)
    console.log(`Status: ${statusRecord.status} (${statusRecord.transactions.length} tx)`)

    if (TERMINAL.includes(statusRecord.status)) return statusRecord

    await new Promise(resolve => setTimeout(resolve, 5000))
  }
}

const finalStatus = await waitForDeposit(id)
console.log('Final transactions:')
finalStatus.transactions.forEach(tx => {
  console.log(`  ${tx.chain}: ${tx.txHash} (${tx.status})`)
})

8. Track Cross-Chain Deposit Status (Cross-Chain Only)

If you made a cross-chain deposit (you passed fromChainId in Step 5), the bridge and the destination-chain deposit happen in separate transactions and complete asynchronously. Use the id returned in the bytecode response to poll /actions/:id until the action reaches a terminal state.
const actionId = result.id // returned from /strategies/:id/bytecode

// (Optional) preview the bridge & expected output before signing
if (result.crossChain.isCrossChain) {
  console.log(`Bridging via ${result.quote.bridge}`)
  console.log(`Expected out: ${result.quote.outputAmount.humanized} ${result.quote.outputAmount.symbol}`)
  console.log(`Min out:      ${result.quote.minimumOutputAmount.humanized} ${result.quote.minimumOutputAmount.symbol}`)
}

async function waitForDeposit (actionId) {
  const TERMINAL = ['SUCCESS', 'FAILED', 'REFUNDED', 'EXPIRED']

  while (true) {
    const { data: action } = await deframe.get(`/actions/${actionId}`)
    console.log(`Status: ${action.status} (${action.transactions.length} tx)`)

    if (TERMINAL.includes(action.status)) return action

    await new Promise(resolve => setTimeout(resolve, 5000))
  }
}

const finalAction = await waitForDeposit(actionId)
console.log('Final transactions:')
finalAction.transactions.forEach(tx => {
  console.log(`  ${tx.chain}: ${tx.txHash} (${tx.status})`)
})
Same-chain deposits do not require polling — the deposit completes inline with the user’s signed transaction. You can still call GET /actions/:id with the returned id for record-keeping.

Error Handling

The example includes comprehensive error handling:
try {
  // Your code here
} catch (error) {
  if (error.response?.data?.error) {
    const apiError = error.response.data.error
    console.error('API Error:', apiError.code)
    console.error('Message:', apiError.message)

    // Handle specific errors
    if (apiError.code === 'STRATEGY_NOT_FOUND') {
      console.log('Strategy does not exist or is not available')
    } else if (apiError.code === 'INSUFFICIENT_BALANCE') {
      console.log('Wallet does not have enough tokens')
    }
  } else {
    console.error('Unexpected error:', error.message)
  }
}

Running the Example

# Install dependencies
npm install

# Set environment variables
cp .env.example .env
# Edit .env with your values

# Run the example
node strategy-deposit.js

Expected Output

1️⃣  Fetching available strategies...
✅ Available strategies:
   - Aave-USDC-polygon (Aave)
     APY: 4.50%
   - Morpho-BTC-base (Morpho)
     APY: 3.20%

2️⃣  Getting details for Aave-USDC-polygon...
✅ Strategy details:
   Strategy: Aave-USDC-polygon
   Protocol: Aave
   Network: polygon
   Asset: USDC
   APY: 4.50%
   Status: ✅ Active

4️⃣  Generating deposit bytecode...
✅ Bytecode generated:
   ID: 65f3a1c8e9b2d4f8a7c1d2e3
   Transactions: 2

5️⃣  Executing deposit transactions...
   Executing transaction 1/2...
   Transaction sent: 0x123...
   ✅ Transaction confirmed in block 12345678
   Executing transaction 2/2...
   Transaction sent: 0x456...
   ✅ Transaction confirmed in block 12345679

6️⃣  Verifying deposit...
✅ Wallet positions:
   Aave-USDC-polygon:
      Balance: 1000.00 USDC
      Value: $1000.00
      APY: 4.17%

   Total Portfolio Value: $1000.00

🎉 Strategy deposit completed successfully!

Next Steps

Strategy Withdrawal

Withdraw from strategies, including cross-chain withdrawals

Cross-Chain Swap

Execute cross-chain token swaps

Check Positions

Track wallet positions

API Reference

View complete API documentation