Skip to main content
Use EarnWidget when you want Deframe yield UX in your app while keeping wallet custody and transaction execution in your infrastructure.

What You Own vs What Deframe Owns

AreaOwner
Wallet connection, signatures, tx submissionYour app
Strategy data, quote/bytecode generation, protocol routingDeframe API
UI state and yield screens (overview, details, deposit, withdraw)EarnWidget

Prerequisites

API Credentials

DEFRAME_API_URL and DEFRAME_API_KEY

Wallet Adapter

EOA/smart wallet provider capable of sendTransaction

React App

React 18+ or 19+ host app

User Wallet

Connected wallet address for position and history endpoints

Install

npm install deframe-sdk @deframe-sdk/components @reduxjs/toolkit react-redux redux
deframe-sdk uses Redux internally. In many host setups, you should install the Redux peer packages explicitly (@reduxjs/toolkit, react-redux, redux) to avoid peer-resolution/runtime issues.

Styles and Theme

deframe-sdk loads widget styles from its package entrypoint. For the default integration, you do not need a separate CSS import (for example, deframe-sdk/styles.css). Treat .deframe-widget as a style boundary owned by the SDK:
  • Avoid global resets like * { ... } or broad element resets that can leak into the widget.
  • Scope host app styles to your own containers.
  • Use DeframeProvider.config.theme as the source of truth for widget theming.
.host-shell {
  /* Host app styles */
}

.widget-slot {
  /* Layout/spacing around widget */
}

/* Optional widget-scoped overrides */
.host-shell .deframe-widget {
  --deframe-brand-primary: #405eff;
  --deframe-brand-secondary: #a9abf7;
  --deframe-bg-raised: #232323;
}

Environment Variables

# Next.js / client-exposed
NEXT_PUBLIC_DEFRAME_API_URL=https://api.deframe.io
NEXT_PUBLIC_DEFRAME_API_KEY=your_api_key

# Vite equivalent
VITE_DEFRAME_API_URL=https://api.deframe.io
VITE_DEFRAME_API_KEY=your_api_key

Step 1: Implement processBytecode

processBytecode is the host bridge between widget actions and wallet execution.
import type { UpdateTxStatus } from 'deframe-sdk'

type TxIntent = {
  clientTxId: string
  bytecodes: Array<{
    chainId?: number
    to: string
    data: string
    value: string
    gasLimit?: string
  }>
}

export async function processBytecode(
  payload: TxIntent,
  ctx: { updateTxStatus: UpdateTxStatus }
) {
  const { clientTxId, bytecodes } = payload

  try {
    ctx.updateTxStatus({ type: 'HOST_ACK', clientTxId })

    for (const tx of bytecodes) {
      ctx.updateTxStatus({ type: 'SIGNATURE_PROMPTED', clientTxId })

      const txHash = await sendTransactionWithYourWallet(tx)
      ctx.updateTxStatus({ type: 'TX_SUBMITTED', clientTxId, chainId: tx.chainId, txHash })

      const receipt = await waitForReceipt(txHash)
      if (receipt.status !== 1) {
        ctx.updateTxStatus({ type: 'TX_REVERTED', clientTxId, txHash, reason: 'Execution reverted' })
        return
      }

      ctx.updateTxStatus({ type: 'TX_CONFIRMED', clientTxId, txHash, blockNumber: receipt.blockNumber })
    }

    ctx.updateTxStatus({ type: 'TX_FINALIZED', clientTxId })
  } catch (error: any) {
    if (error?.code === 4001 || error?.code === 'ACTION_REJECTED') {
      ctx.updateTxStatus({ type: 'SIGNATURE_DECLINED', clientTxId })
      return
    }

    ctx.updateTxStatus({
      type: 'SIGNATURE_ERROR',
      clientTxId,
      code: String(error?.code ?? 'UNKNOWN_ERROR'),
      message: String(error?.message ?? 'Failed to process transaction')
    })
  }
}
For the full status contract, see processBytecode Contract.

Step 2: Mount DeframeProvider + EarnWidget

import { DeframeProvider, EarnWidget } from 'deframe-sdk'
import { useState } from 'react'

export function EarnWidgetScreen({ walletAddress }: { walletAddress: string }) {
  const [routeName, setRouteName] = useState('overview')

  return (
    <DeframeProvider
      config={{
        DEFRAME_API_URL: process.env.NEXT_PUBLIC_DEFRAME_API_URL,
        DEFRAME_API_KEY: process.env.NEXT_PUBLIC_DEFRAME_API_KEY,
        walletAddress,
        language: 'EN',
        theme: {
          mode: 'dark',
          preset: 'default'
        }
      }}
      processBytecode={processBytecode}
    >
      <EarnWidget autoHeight onRouteChange={setRouteName} />
    </DeframeProvider>
  )
}

Route Names Exposed by onRouteChange

Common values: overview, details, deposit, withdraw, investment-details, history, history-deposit-details, history-withdraw-details. Use these to adapt host shell layout, breadcrumbs, and analytics tags.

API Endpoints Used in Earn Flows

  • GET /strategies for strategy discovery
  • GET /strategies/{strategyId}/bytecode for deposit/withdraw transaction data
  • GET /wallets/{wallet} for portfolio state
  • GET /wallets/{wallet}/history/{strategyId} for investment activity
Guides:

Customer Integration Checklist

  1. Create customer + API key and store mapping in your backend.
  2. Pass the connected wallet address into DeframeProvider.config.walletAddress.
  3. Implement processBytecode with your wallet stack.
  4. Emit all required tx lifecycle statuses back to the widget.
  5. Track completed transactions in your own ledger for reconciliation/support.