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.For complete signing documentation including error handling and troubleshooting, see the Signing Guide.
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:
- Delegate your EOA on chains that need it (automatically)
- Bridge 10 USDC from source chains to the destination
- 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
Aspect | Kernel v3.3 (EIP-7702) | Role-Based |
---|
Signing method | UserOperation hash via signMessage() | Typed data via signTypedData() |
Address | Same as EOA | Predicted new address |
EntryPoint | Requires 0.7 | Not applicable |
Delegation | Required on spending chains | Not applicable |
Next Steps
Your EOA now has smart account capabilities whenever needed, without changing addresses or migrating funds.