This guide shows you how to implement EIP-7702 with OneBalance using a complete, runnable example. You’ll build a cross-chain USDC transfer that automatically handles delegation.
Working Code Examples: Complete, production-ready EIP-7702 examples are available in our open-source repository: OneBalance Examples - EIP-7702. These examples include proper signature handling, multi-chain operations, and error recovery patterns.

Prerequisites

  • OneBalance API key (Get one here)
  • Wallet library with signAuthorization support (viem)
  • TypeScript/JavaScript environment

Account Configuration

EIP-7702 accounts use your existing EOA address:
const account = {
  type: "kernel-v3.3-ecdsa",
  deploymentType: "EIP7702", 
  signerAddress: "0x5Cb2369421F8a00Ef556d662D6E97C1419B1d37c", // Your EOA
  accountAddress: "0x5Cb2369421F8a00Ef556d662D6E97C1419B1d37c"  // Same address
};
Unlike ERC-4337 accounts, you don’t need to predict a new address. Your EOA address becomes your smart account address through delegation.

Step 1: Prepare Quote

Call prepare-call-quote to analyze your requirements. If your EOA needs delegation, you’ll receive delegation objects to sign:
const prepareResponse = await fetch('https://be.onebalance.io/api/quotes/prepare-call-quote', {
  method: 'POST',
  headers: {
    'x-api-key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    account: {
      type: "kernel-v3.3-ecdsa",
      deploymentType: "EIP7702",
      signerAddress: "0x5Cb2369421F8a00Ef556d662D6E97C1419B1d37c",
      accountAddress: "0x5Cb2369421F8a00Ef556d662D6E97C1419B1d37c"
    },
    targetChain: "eip155:42161", // Arbitrum
    calls: [{
      to: "0xaf88d065e77c8cc2239327c5edb3a432268e5831", // USDC on Arbitrum
      data: "0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b844bc454e4438f44e0000000000000000000000000000000000000000000000000000000000989680", // transfer(address,uint256)
      value: "0x0"
    }],
    tokensRequired: [{
      assetType: "eip155:42161/erc20:0xaf88d065e77c8cc2239327c5edb3a432268e5831",
      amount: "10000000" // 10 USDC
    }]
  })
});

const prepareData = await prepareResponse.json();

Step 2: Sign Delegations

If your EOA isn’t delegated yet, sign the delegation objects using viem’s signAuthorization method: EIP-7702 enables EOAs to delegate execution to smart contracts. Delegation is required on source chains (for spending) and destination chains (only for contract calls).

Delegation Signature Structure

When your EOA needs delegation, OneBalance returns a delegation object that you need to sign:
interface DelegationSignature {
  chainId: number;
  contractAddress: Hex; // Kernel v3.3 smart contract address
  nonce: number; // Current delegation nonce for the EOA
  r: Hex;
  s: Hex;
  v: Hex;
  yParity: number;
  type: 'Signed' | 'Unsigned';
}

Signing Process

Kernel Account Signing: EIP-7702 accounts using Kernel v3.3 require special signing patterns. Unlike role-based accounts that sign typed data, Kernel accounts sign the UserOperation hash directly.
import { privateKeyToAccount } from 'viem/accounts';
import { entryPoint07Address, getUserOperationHash } from 'viem/account-abstraction';

const signerAccount = privateKeyToAccount('0x...' as `0x${string}`);
const operation = prepareData.chainOperation;
const chainId = Number(operation.typedDataToSign.domain.chainId);

// Step 1: Sign delegation if needed
if (operation.delegation) {
  const authTuple = {
    contractAddress: operation.delegation.contractAddress,
    nonce: operation.delegation.nonce,
    chainId: chainId,
  };
  
  const signedTuple = await signerAccount.signAuthorization(authTuple);
  
  if (signedTuple.yParity == null) {
    throw new Error('Y parity is required for EIP-7702 delegation');
  }

  // Add signature to delegation object
  operation.delegation.signature = {
    chainId: chainId,
    contractAddress: signedTuple.address,
    nonce: signedTuple.nonce,
    r: signedTuple.r,
    s: signedTuple.s,
    v: `0x${Number(signedTuple.v).toString(16).padStart(2, '0')}`,
    yParity: signedTuple.yParity,
    type: 'Signed',
  };
}

// Step 2: Sign UserOperation hash (Kernel v3.3 specific)
// Deserialize UserOp with proper BigInt conversions
const deserializedUserOp = {
  sender: operation.userOp.sender,
  nonce: BigInt(operation.userOp.nonce),
  factory: operation.userOp.factory,
  factoryData: operation.userOp.factoryData,
  callData: operation.userOp.callData,
  callGasLimit: BigInt(operation.userOp.callGasLimit),
  verificationGasLimit: BigInt(operation.userOp.verificationGasLimit),
  preVerificationGas: BigInt(operation.userOp.preVerificationGas),
  maxFeePerGas: BigInt(operation.userOp.maxFeePerGas),
  maxPriorityFeePerGas: BigInt(operation.userOp.maxPriorityFeePerGas),
  paymaster: operation.userOp.paymaster,
  paymasterVerificationGasLimit: operation.userOp.paymasterVerificationGasLimit 
    ? BigInt(operation.userOp.paymasterVerificationGasLimit) : undefined,
  paymasterPostOpGasLimit: operation.userOp.paymasterPostOpGasLimit 
    ? BigInt(operation.userOp.paymasterPostOpGasLimit) : undefined,
  paymasterData: operation.userOp.paymasterData,
  signature: operation.userOp.signature,
};

// Get UserOperation hash for EntryPoint 0.7
const userOpHash = getUserOperationHash({
  userOperation: deserializedUserOp,
  entryPointAddress: entryPoint07Address,
  entryPointVersion: '0.7',
  chainId: chainId,
});

// Sign the hash directly (not typed data)
const signature = await signerAccount.signMessage({ 
  message: { raw: userOpHash } 
});

operation.userOp.signature = signature;
The signAuthorization method is part of viem’s EIP-7702 support. It signs an authorization that allows your EOA to delegate execution to the Kernel v3.3 smart contract. Other wallet providers like Privy also support this method.
You must sign delegation objects for ALL chain operations returned by prepare-call-quote - both source and destination chains.

Step 3: Get Quote

Submit the signed delegation to call-quote to get the executable quote:
const quoteResponse = await fetch('https://be.onebalance.io/api/quotes/call-quote', {
  method: 'POST',
  headers: {
    'x-api-key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    account: prepareData.account,
    chainOperation: operation, // Contains signed delegation
    tamperProofSignature: prepareData.tamperProofSignature,
    fromAggregatedAssetId: 'ds:usdc' // Specify which aggregated asset to use
  })
});

const quote = await quoteResponse.json();

Step 4: Execute

Execute the quote using execute-quote. Important: You must also sign any origin chain operations:
// Sign origin chain operations using the same pattern as Step 2
for (let i = 0; i < quote.originChainsOperations.length; i++) {
  const originOperation = quote.originChainsOperations[i];
  // Use same signing logic as Step 2 for each origin chain operation
  // (delegation + UserOperation hash signing)
  quote.originChainsOperations[i] = await signOperation(originOperation);
}
const executeResponse = await fetch('https://be.onebalance.io/api/quotes/execute-quote', {
  method: 'POST',
  headers: {
    'x-api-key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(quote)
});

const result = await executeResponse.json();
console.log('Transaction executed:', result);
The operation will:
  1. Delegate your EOA on chains that need it (automatically)
  2. Bridge 10 USDC from source chains to the destination
  3. Execute your contract call on the destination chain
All in a single user interaction.

Implementation Notes

Critical Requirements

  • Account type: Must use type: "kernel-v3.3-ecdsa" and deploymentType: "EIP7702"
  • Address configuration: Both accountAddress and signerAddress must be the same EOA address
  • Signing method: Kernel accounts sign UserOperation hash (signMessage()) not typed data
  • Y parity validation: Always check signedTuple.yParity is not null
  • Multi-chain signing: Sign both destination operation (Step 2) and origin operations (Step 4)

Key Differences from Role-Based Accounts

AspectKernel v3.3 (EIP-7702)Role-Based
Signing methodUserOperation hash via signMessage()Typed data via signTypedData()
AddressSame as EOAPredicted new address
EntryPointRequires 0.7Not applicable
DelegationRequired on spending chainsNot applicable

Next Steps

Your EOA now has smart account capabilities whenever needed, without changing addresses or migrating funds.