Implement sophisticated contract call patterns for production applications. Learn advanced techniques for complex workflows and robust error handling.
Transaction Batching
Aggregate, Swap, and Stake Pattern
This pattern combines multiple DeFi operations into a single transaction, using aggregated funds to open leveraged positions across protocols.
async function openLeveragedPosition (
marginAmount : bigint ,
leverage : number ,
marginAsset : string ,
isLong : boolean
) {
const calls = [];
// 1. Swap aggregated USDC to margin asset if needed
if ( marginAsset !== USDC_ADDRESS ) {
calls . push ({
to: DEX_ROUTER ,
data: encodeFunctionData ({
abi: parseAbi ([ 'function swap(address,address,uint256,uint256,address)' ]),
functionName: 'swap' ,
args: [ USDC_ADDRESS , marginAsset , marginAmount , minOut , account . accountAddress ]
}),
value: '0'
});
}
// 2. Deposit margin to perpetual protocol
calls . push ({
to: PERP_CONTRACT ,
data: encodeFunctionData ({
abi: parseAbi ([ 'function depositMargin(address,uint256)' ]),
functionName: 'depositMargin' ,
args: [ marginAsset , marginAmount ]
}),
value: '0'
});
// 3. Open leveraged position
calls . push ({
to: PERP_CONTRACT ,
data: encodeFunctionData ({
abi: parseAbi ([ 'function openPosition(uint256,bool)' ]),
functionName: 'openPosition' ,
args: [ marginAmount * BigInt ( leverage ), isLong ]
}),
value: '0'
});
return await executeCall ({
targetChain: 'eip155:42161' , // Arbitrum
calls ,
tokensRequired: [{
assetType: `eip155:42161/erc20: ${ marginAsset } ` ,
amount: marginAmount . toString ()
}],
allowanceRequirements: [
{
assetType: `eip155:42161/erc20: ${ USDC_ADDRESS } ` ,
amount: marginAmount . toString (),
spender: DEX_ROUTER
},
{
assetType: `eip155:42161/erc20: ${ marginAsset } ` ,
amount: marginAmount . toString (),
spender: PERP_CONTRACT
}
],
fromAggregatedAssetId: 'ds:usdc' // Pull from any chain
});
}
Cross-Chain Swap and Stake
Use USDC from multiple chains to swap and stake in a single operation. This pattern optimizes for users who want to participate in staking protocols using their distributed token holdings.
const crossChainStake = async ( stakeAmount : bigint ) => {
const WETH = '0x4200000000000000000000000000000000000006' ;
const STAKING_CONTRACT = '0x796f1793599D7b6acA6A87516546DdF8E5F3aA9d' ;
const UNISWAP_ROUTER = '0x2626664c2603336E57B271c5C0b26F421741e481' ;
// 1. Swap USDC to WETH on Base
const swapData = encodeFunctionData ({
abi: parseAbi ([
'function exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160)) returns (uint256)'
]),
functionName: 'exactInputSingle' ,
args: [{
tokenIn: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' , // USDC
tokenOut: WETH ,
fee: 3000 ,
recipient: account . accountAddress ,
deadline: Math . floor ( Date . now () / 1000 ) + 1800 ,
amountIn: stakeAmount ,
amountOutMinimum: 0 n ,
sqrtPriceLimitX96: 0 n
}]
});
// 2. Stake WETH (approval handled by allowanceRequirements)
const stakeData = encodeFunctionData ({
abi: parseAbi ([ 'function stake(uint256 amount)' ]),
functionName: 'stake' ,
args: [ stakeAmount ] // Will use actual WETH received from swap
});
return await executeCall ({
targetChain: 'eip155:8453' , // Base
calls: [
{
to: UNISWAP_ROUTER ,
data: swapData ,
value: '0'
},
{
to: STAKING_CONTRACT ,
data: stakeData ,
value: '0'
}
],
tokensRequired: [{
assetType: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' ,
amount: stakeAmount . toString ()
}],
allowanceRequirements: [
{
assetType: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' ,
amount: stakeAmount . toString (),
spender: UNISWAP_ROUTER
},
{
assetType: `eip155:8453/erc20: ${ WETH } ` ,
amount: stakeAmount . toString (), // Approximate amount for approval
spender: STAKING_CONTRACT
}
],
fromAggregatedAssetId: 'ds:usdc'
});
};
Batch Operations
Multi-Recipient Payment System
Process multiple payments efficiently by grouping them by token type and using multicall patterns to reduce gas costs.
interface Payment {
recipient : string ;
amount : bigint ;
token : string ;
}
async function batchPayout ( payments : Payment []) {
const calls = [];
const tokensRequired = [];
// Group payments by token to optimize gas usage
const paymentsByToken = payments . reduce (( acc , payment ) => {
if ( ! acc [ payment . token ]) acc [ payment . token ] = [];
acc [ payment . token ]. push ( payment );
return acc ;
}, {} as Record < string , Payment []>);
for ( const [ token , tokenPayments ] of Object . entries ( paymentsByToken )) {
// Use multicall for same-token transfers
const multicallData = encodeFunctionData ({
abi: parseAbi ([ 'function multicall(bytes[] calldata data)' ]),
functionName: 'multicall' ,
args: [
tokenPayments . map ( p =>
encodeFunctionData ({
abi: parseAbi ([ 'function transfer(address,uint256)' ]),
functionName: 'transfer' ,
args: [ p . recipient , p . amount ]
})
)
]
});
calls . push ({
to: token ,
data: multicallData ,
value: '0'
});
const totalAmount = tokenPayments . reduce (
( sum , p ) => sum + p . amount ,
0 n
);
tokensRequired . push ({
assetType: `eip155:8453/erc20: ${ token } ` ,
amount: totalAmount . toString ()
});
}
return await executeCall ({
targetChain: 'eip155:8453' ,
calls ,
tokensRequired ,
fromAggregatedAssetId: 'ds:usdc'
});
}
Gas Optimization Patterns
Conditional Execution Pattern
Skip unnecessary operations by checking on-chain state before execution, reducing gas costs for operations that may not be needed.
async function optimizedDeFiOperation ( amount : bigint ) {
const calls = [];
const AAVE_POOL = '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5' ;
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' ;
// Check if we need to withdraw from lending protocol first
const currentDeposit = await checkAaveDeposit ( account . accountAddress );
if ( currentDeposit > 0 n ) {
calls . push ({
to: AAVE_POOL ,
data: encodeFunctionData ({
abi: parseAbi ([ 'function withdraw(address,uint256,address)' ]),
functionName: 'withdraw' ,
args: [ USDC_ADDRESS , currentDeposit , account . accountAddress ]
}),
value: '0'
});
}
// Main operation (e.g., swap, stake, etc.)
calls . push ({
to: TARGET_CONTRACT ,
data: mainOperationData ,
value: '0'
});
// Only re-deposit excess if amount justifies gas cost
const minDepositAmount = parseUnits ( '100' , 6 ); // $100 minimum
if ( amount > minDepositAmount ) {
calls . push ({
to: AAVE_POOL ,
data: encodeFunctionData ({
abi: parseAbi ([ 'function supply(address,uint256,address,uint16)' ]),
functionName: 'supply' ,
args: [ USDC_ADDRESS , amount , account . accountAddress , 0 ]
}),
value: '0'
});
}
return await executeCall ({
targetChain: 'eip155:8453' ,
calls ,
tokensRequired: [{
assetType: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' ,
amount: amount . toString ()
}],
allowanceRequirements: [{
assetType: 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' ,
amount: amount . toString (),
spender: AAVE_POOL
}]
});
}
Error Recovery Patterns
Safe Execution with Fallbacks
Implement fallback mechanisms for DEX operations when primary routes fail due to liquidity or slippage issues.
interface CallData {
to : string ;
data : string ;
value : string ;
}
async function safeExecuteWithFallback (
primaryOperation : CallData [],
fallbackOperation : CallData []
) {
try {
// Attempt primary operation first
return await executeCall ({
targetChain: 'eip155:8453' ,
calls: primaryOperation ,
tokensRequired: calculateTokensRequired ( primaryOperation )
});
} catch ( error : any ) {
// Handle specific errors with fallback strategies
if ( error . message . includes ( 'INSUFFICIENT_LIQUIDITY' ) ||
error . message . includes ( 'EXCESSIVE_SLIPPAGE' )) {
console . log ( 'Primary route failed, attempting fallback...' );
return await executeCall ({
targetChain: 'eip155:8453' ,
calls: fallbackOperation ,
tokensRequired: calculateTokensRequired ( fallbackOperation )
});
}
// Re-throw unexpected errors
throw error ;
}
}
function calculateTokensRequired ( calls : CallData []) {
// Implementation to analyze calls and determine token requirements
return [];
}
State Management Patterns
Transaction Lifecycle Tracking
Track and monitor complex transactions with automatic retry and status monitoring capabilities.
interface OperationStatus {
status : 'PENDING' | 'COMPLETED' | 'FAILED' | 'IN_PROGRESS' | 'REFUNDED' ;
}
interface OperationDetails {
hash : string ;
chainId : number ;
explorerUrl : string ;
}
interface OriginAssetUsed {
aggregatedAssetId : string ;
amount : string ;
assetType : string [];
fiatValue : Array <{
assetType : string ;
fiatValue : string ;
}>;
}
interface DestinationAssetUsed {
aggregatedAssetId : string ;
amount : string ;
assetType : string ;
fiatValue : string ;
minimumAmount ?: string ;
minimumFiatValue ?: string ;
}
interface Transaction {
quoteId : string ;
status : OperationStatus ;
user : string ;
recipientAccountId : string ;
originChainOperations : OperationDetails [];
destinationChainOperations : OperationDetails [];
type : string ;
originToken : OriginAssetUsed ;
destinationToken : DestinationAssetUsed ;
timestamp : string ;
}
interface LocalTransactionState {
id : string ;
request : any ;
localStatus : 'preparing' | 'executing' | 'monitoring' | 'completed' | 'failed' ;
preparedAt : number ;
transaction ?: Transaction ;
error ?: any ;
}
class TransactionTracker {
private pending = new Map < string , LocalTransactionState >();
async executeAndTrack ( request : any ) : Promise < Transaction > {
const txId = this . generateId ();
// Store initial transaction state
this . pending . set ( txId , {
id: txId ,
request ,
localStatus: 'preparing' ,
preparedAt: Date . now ()
});
try {
// Step 1: Prepare and sign
const quote = await this . prepareAndSign ( request );
this . updateLocalStatus ( txId , 'executing' );
// Step 2: Execute
const result = await this . executeQuote ( quote );
this . updateLocalStatus ( txId , 'monitoring' );
// Step 3: Monitor with exponential backoff
const finalResult = await this . monitorWithBackoff ( quote . id );
this . updateLocalStatus ( txId , 'completed' , finalResult );
return finalResult ;
} catch ( error ) {
this . updateLocalStatus ( txId , 'failed' , undefined , error );
throw error ;
} finally {
// Cleanup after 24 hours
setTimeout (() => this . pending . delete ( txId ), 86400000 );
}
}
private async monitorWithBackoff ( quoteId : string ) : Promise < Transaction > {
const delays = [ 1000 , 2000 , 5000 , 10000 , 15000 ]; // Progressive delays
const maxAttempts = 30 ;
for ( let attempt = 0 ; attempt < maxAttempts ; attempt ++ ) {
const status = await getQuoteStatus ( quoteId );
if ([ 'COMPLETED' , 'FAILED' , 'REFUNDED' ]. includes ( status . status . status )) {
return status ;
}
const delay = delays [ Math . min ( attempt , delays . length - 1 )];
await new Promise ( resolve => setTimeout ( resolve , delay ));
}
throw new Error ( 'Transaction monitoring timeout' );
}
private generateId () : string {
return `tx_ ${ Date . now () } _ ${ Math . random (). toString ( 36 ). substr ( 2 , 9 ) } ` ;
}
private updateLocalStatus (
id : string ,
localStatus : LocalTransactionState [ 'localStatus' ],
transaction ?: Transaction ,
error ?: any
) {
const tx = this . pending . get ( id );
if ( tx ) {
tx . localStatus = localStatus ;
if ( transaction ) tx . transaction = transaction ;
if ( error ) tx . error = error ;
}
}
// Get current transaction status
getTransactionStatus ( id : string ) : LocalTransactionState | undefined {
return this . pending . get ( id );
}
// Get all pending transactions
getPendingTransactions () : LocalTransactionState [] {
return Array . from ( this . pending . values ());
}
}
Integration Patterns
Wallet-Agnostic Implementation
Create universal interfaces that work with any wallet provider, making your integration flexible across different wallet ecosystems.
interface WalletAdapter {
signTypedData ( data : any ) : Promise < string >;
getAddress () : Promise < string >;
isConnected () : Promise < boolean >;
}
class UniversalOneBalance {
constructor (
private apiKey : string ,
private wallet : WalletAdapter
) {}
async executeCall ( params : any ) {
// Ensure wallet is connected
const isConnected = await this . wallet . isConnected ();
if ( ! isConnected ) {
throw new Error ( 'Wallet not connected' );
}
// Prepare operation
const account = await this . getAccount ();
const preparedQuote = await this . prepare ({
... params ,
account
});
// Sign with wallet adapter
const signature = await this . wallet . signTypedData (
preparedQuote . chainOperation . typedDataToSign
);
preparedQuote . chainOperation . userOp . signature = signature ;
// Execute operation
return await this . execute ( preparedQuote );
}
private async getAccount () {
const address = await this . wallet . getAddress ();
return {
sessionAddress: address ,
adminAddress: address , // Simplified for basic account
accountAddress: await this . predictAccountAddress ( address )
};
}
}
// MetaMask adapter example
const metamaskAdapter : WalletAdapter = {
signTypedData : ( data ) => window . ethereum . request ({
method: 'eth_signTypedData_v4' ,
params: [ address , JSON . stringify ( data )]
}),
getAddress : async () => {
const accounts = await window . ethereum . request ({
method: 'eth_accounts'
});
return accounts [ 0 ];
},
isConnected : async () => {
const accounts = await window . ethereum . request ({
method: 'eth_accounts'
});
return accounts . length > 0 ;
}
};
// Usage
const oneBalance = new UniversalOneBalance ( apiKey , metamaskAdapter );
Batch Operations Group similar operations to reduce transaction overhead and gas costs
Use Multicall Combine multiple calls to the same contract in a single transaction
Cache Quotes Store prepared quotes for retry scenarios (valid for ~30 seconds)
Async Monitoring Don’t block UI while monitoring transaction status - use background polling
Implementation Best Practices
Plan Your Operations
Design your call sequence to minimize gas usage and maximize success probability
Handle Edge Cases
Implement fallbacks for common failure scenarios like insufficient liquidity
Monitor Actively
Track transaction status and provide users with real-time updates
Test Thoroughly
Validate complex patterns with small amounts before production deployment
Advanced Features : For even more sophisticated patterns, consider combining these techniques with OneBalance’s resource locks and time-based permissions for enhanced security and user experience.