Skip to main content
processBytecode is the required callback for production widget integrations. It receives bytecode transactions from Deframe and must:
  1. Ask the wallet to sign and submit transactions.
  2. Wait for on-chain updates.
  3. Emit lifecycle statuses back to the widget.

Payload Shape

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

Required Success Status Path

Emit, in order:
  1. HOST_ACK
  2. SIGNATURE_PROMPTED
  3. TX_SUBMITTED
  4. TX_CONFIRMED
  5. TX_FINALIZED
txHash should be included on TX_SUBMITTED and TX_CONFIRMED when available.

Required Error Statuses

  • SIGNATURE_DECLINED for user-rejected signatures (4001, ACTION_REJECTED)
  • SIGNATURE_ERROR for non-rejection failures before tx submission
  • TX_REVERTED for reverted on-chain transactions
  • TX_FAILED for unsupported failure paths where revert reason is unavailable

Optional Advanced Statuses

  • TX_REPLACED
  • TX_DROPPED
  • SWAP_CROSSCHAIN_DESTINATION_CONFIRMED

Minimal Host Implementation

import type { UpdateTxStatus } from 'deframe-sdk'

export async function processBytecode(
  payload: {
    clientTxId: string
    bytecodes: Array<{ chainId?: number; to: string; data: string; value: string; gasLimit?: string }>
  },
  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 sendTransactionWithWallet(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,
        confirmations: 1
      })
    }

    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 ?? 'Transaction failed')
    })
  }
}

Compatibility Fallback

If processBytecode is not provided, SDK falls back to window.parent.postMessage(...) (REQUEST_SIGNATURE). Use this only when your host already implements that message bridge. For new integrations, prefer explicit processBytecode.

Operational Recommendations

  1. Log clientTxId and wallet tx hash together in your backend.
  2. Execute bytecodes sequentially in order.
  3. Enforce chain checks before requesting signatures.
  4. Persist failures and rejections for support/audit trails.