Skip to main content
Deframe allows apps to access diversified DeFi yield strategies across multiple protocols through a single API. Apps fetch strategies from Deframe and execute the returned transaction bytecodes using Privy wallets. This enables deposits into lending, staking, and protocol-native yield strategies without managing complex transaction flows.

Resources

Deframe Docs

Official documentation for Deframe strategies and API.

Starter repo

Full working example of Deframe + Privy integration.

Using Deframe with Privy

For this walkthrough, the examples use the Aave USDT strategy on Polygon. The same patterns work across all Deframe-supported strategies and networks—explore the complete list via the Deframe API.

Setup

Below is a minimal setup for the Privy provider. To customize the provider, follow the Privy Quickstart.
import {PrivyProvider} from '@privy-io/react-auth';

export function App() {
  return (
    <PrivyProvider appId="your-privy-app-id">{/* Your application components */}</PrivyProvider>
  );
}

Configure Deframe API access

Obtain your API credentials from the Deframe Dashboard:
const DEFRAME_API_URL = 'https://api.deframe.io';
const DEFRAME_API_KEY = 'your-deframe-api-key';

List available strategies

Fetch the yield strategies available for users:
const fetchStrategies = async () => {
  const response = await fetch(`${DEFRAME_API_URL}/strategies?limit=100`, {
    method: 'GET',
    headers: {
      'x-api-key': DEFRAME_API_KEY
    }
  });

  const {data: strategies} = await response.json();
  return strategies;
};
Each strategy includes fields like id, protocol, assetName, network, and availableActions.

Deposit into a yield strategy

1

1. Get the user's wallet address

import {useWallets} from '@privy-io/react-auth';

const {wallets} = useWallets();
const walletAddress = wallets[0]?.address;
2

2. Generate transaction bytecodes from Deframe

Request the bytecodes needed to execute a deposit action:
const strategyId = 'Aave-USDT-polygon';
const action = 'lend';
const amount = '1500000'; // 1.5 USDT (6 decimals)

const response = await fetch(
  `${DEFRAME_API_URL}/strategies/${strategyId}/bytecode?action=${action}&wallet=${walletAddress}&amount=${amount}`,
  {
    method: 'GET',
    headers: {
      'x-api-key': DEFRAME_API_KEY
    }
  }
);

const bytecodeResponse = await response.json();
The response contains an array of transactions to execute:
type DeframeBytecodeResponse = {
  feeCharged: string;
  metadata: {
    isCrossChain: boolean;
    isSameChainSwap: boolean;
    crossChainQuoteId: string;
  };
  bytecode: {
    to: string;
    value: string;
    data: string;
    chainId: string;
  }[];
};
3

3. Execute the transactions with Privy

Use Privy’s useSendTransaction to execute each bytecode:
import {useSendTransaction} from '@privy-io/react-auth';

const {sendTransaction} = useSendTransaction();

const executeDeframeStrategy = async (bytecodeResponse: DeframeBytecodeResponse) => {
  for (const tx of bytecodeResponse.bytecode) {
    await sendTransaction({
      to: tx.to as `0x${string}`,
      data: tx.data as `0x${string}`,
      value: BigInt(tx.value),
      chainId: Number(tx.chainId)
    });
  }
};

Check strategy position

Fetch the user’s current position and APY for a strategy:
const fetchPosition = async (strategyId: string, walletAddress: string) => {
  const response = await fetch(
    `${DEFRAME_API_URL}/strategies/${strategyId}?wallet=${walletAddress}`,
    {
      method: 'GET',
      headers: {
        'x-api-key': DEFRAME_API_KEY
      }
    }
  );

  const strategy = await response.json();
  return {
    apy: strategy.apy,
    avgApy: strategy.avgApy,
    position: strategy.position
  };
};

Withdraw from a strategy

To withdraw, use the withdraw action and execute the returned bytecodes:
import {useSendTransaction} from '@privy-io/react-auth';

const {sendTransaction} = useSendTransaction();

const withdrawFromStrategy = async (strategyId: string, walletAddress: string, amount: string) => {
  const response = await fetch(
    `${DEFRAME_API_URL}/strategies/${strategyId}/bytecode?action=withdraw&wallet=${walletAddress}&amount=${amount}`,
    {
      method: 'GET',
      headers: {
        'x-api-key': DEFRAME_API_KEY
      }
    }
  );

  const bytecodeResponse = await response.json();

  for (const tx of bytecodeResponse.bytecode) {
    await sendTransaction({
      to: tx.to as `0x${string}`,
      data: tx.data as `0x${string}`,
      value: BigInt(tx.value),
      chainId: Number(tx.chainId)
    });
  }
};

Deframe EarnWidget

For a faster integration, Deframe provides a pre-built EarnWidget component that handles strategy selection, deposits, and withdrawals with a built-in UI.

Install the Deframe SDK

npm install deframe-sdk @deframe-sdk/components @reduxjs/toolkit react-redux redux permissionless
If your app uses Vite, install the Node polyfills plugin and add it to vite.config.ts:
npm install --save-dev vite-plugin-node-polyfills
import {defineConfig} from 'vite';
import {nodePolyfills} from 'vite-plugin-node-polyfills';

export default defineConfig({
  plugins: [nodePolyfills()]
});

Configure the widget

import {DeframeProvider, EarnWidget} from 'deframe-sdk';
import {useSmartWallets} from '@privy-io/react-auth/smart-wallets';
import {usePrivy} from '@privy-io/react-auth';

const {user} = usePrivy();
const {client: smartWalletClient, getClientForChain} = useSmartWallets();

const config = {
  DEFRAME_API_URL: 'https://api.deframe.io',
  DEFRAME_API_KEY: 'your-deframe-api-key',
  walletAddress: smartWalletClient?.account.address,
  userId: user?.id,
  globalCurrency: 'USD',
  globalCurrencyExchangeRate: 1,
  theme: {mode: 'dark', preset: 'default'}
};

Implement the bytecode processor

The widget returns bytecode for the app to execute using Privy smart wallets:
import type {BytecodeTransaction, UpdateTxStatus} from 'deframe-sdk';

const processBytecode = async (
  payload: {clientTxId: string; bytecodes: BytecodeTransaction[]; simulateError?: boolean},
  ctx: {updateTxStatus: UpdateTxStatus}
) => {
  ctx.updateTxStatus({type: 'HOST_ACK', clientTxId: payload.clientTxId});

  const chainId = Number(payload.bytecodes[0].chainId);
  const chainClient = await getClientForChain({id: chainId});

  const calls = payload.bytecodes.map((b) => ({
    to: b.to as `0x${string}`,
    data: b.data as `0x${string}`,
    value: BigInt(b.value)
  }));

  ctx.updateTxStatus({type: 'SIGNATURE_PROMPTED', clientTxId: payload.clientTxId});
  const txHash = await chainClient.sendTransaction({calls});
  ctx.updateTxStatus({type: 'TX_SUBMITTED', clientTxId: payload.clientTxId, txHash});
};

Render the widget

<DeframeProvider config={config} processBytecode={processBytecode}>
  <EarnWidget autoHeight />
</DeframeProvider>
The EarnWidget requires Smart Wallets for transaction execution. Enable Smart Wallets in the Privy Dashboard under Wallet infrastructure.

Key integration tips

  1. Handle token decimals: The amount parameter must respect the token’s decimal places. USDT and USDC use 6 decimals; most other tokens use 18.
  2. Execute transactions in order: Deframe may return multiple bytecodes for a single action. Execute them sequentially.
  3. Verify available actions: Each strategy has an availableActions array. Check that the action exists before requesting bytecodes.
  4. Handle errors gracefully: Wrap transaction execution in try/catch blocks to handle user rejection, insufficient funds, and network issues.
For advanced use cases, refer to the Deframe Docs, or reach out in Slack.