Skip to main content

Contract Interaction Methods

The @layerg-ua-sdk/aa-sdk package provides powerful methods for interacting with smart contracts through Universal Accounts. These methods allow for seamless contract calls with proper encoding and transaction management.

Building Contract Requests

buildContractCallRequest

buildContractCallRequest(options: ContractCallOptions): UserOperationRequest

Creates a user operation request for a contract method call.

Parameters:

  • options: ContractCallOptions - Options for the contract call:
    • sender: string - Address of the Universal Account sending the transaction
    • contractAddress: string - Address of the target contract
    • abi: Array | string - Contract ABI or ABI fragment for the method
    • method: string - Method name to call
    • params: any[] - Parameters for the method call
    • value?: string - Optional ETH value to send with the call (in wei)

Returns:

  • UserOperationRequest - Ready-to-send user operation request

Example:

import { buildContractCallRequest } from '@layerg-ua-sdk/aa-sdk';

const txRequest = buildContractCallRequest({
sender: '0xUniversalAccountAddress',
contractAddress: '0xContractAddress',
abi: CONTRACT_ABI, // Or specific method ABI fragment
method: 'mint',
params: [1, '0xRecipientAddress'],
value: '10000000000000000' // 0.01 ETH
});

// txRequest can now be sent using a provider

buildMultiCallRequest

buildMultiCallRequest(options: MultiCallOptions): UserOperationRequest

Creates a batched user operation that executes multiple contract calls in a single transaction.

Parameters:

  • options: MultiCallOptions - Options for the batch:
    • sender: string - Address of the Universal Account sending the transaction
    • calls: ContractCall[] - Array of contract calls to execute:
      • contractAddress: string - Target contract address
      • abi: Array | string - Contract ABI or method fragment
      • method: string - Method name
      • params: any[] - Method parameters
      • value?: string - Optional ETH value

Returns:

  • UserOperationRequest - Ready-to-send user operation request for batch execution

Example:

import { buildMultiCallRequest } from '@layerg-ua-sdk/aa-sdk';

const multiCallRequest = buildMultiCallRequest({
sender: '0xUniversalAccountAddress',
calls: [
{
contractAddress: '0xToken1Address',
abi: ERC20_ABI,
method: 'approve',
params: ['0xSpenderAddress', '1000000000000000000'] // 1 token
},
{
contractAddress: '0xDexAddress',
abi: DEX_ABI,
method: 'swap',
params: ['0xToken1Address', '0xToken2Address', '1000000000000000000', '0']
}
]
});

// multiCallRequest can now be sent using a provider

buildERC20TransferRequest

buildERC20TransferRequest(options: ERC20TransferOptions): UserOperationRequest

Creates a user operation specifically for ERC-20 token transfers.

Parameters:

  • options: ERC20TransferOptions - Options for the transfer:
    • sender: string - Address of the Universal Account
    • tokenAddress: string - Address of the ERC-20 token contract
    • recipient: string - Address of the recipient
    • amount: string - Amount to transfer (in smallest units)

Returns:

  • UserOperationRequest - Ready-to-send user operation request

Example:

import { buildERC20TransferRequest } from '@layerg-ua-sdk/aa-sdk';

const tokenTransferRequest = buildERC20TransferRequest({
sender: '0xUniversalAccountAddress',
tokenAddress: '0xUSDCTokenAddress',
recipient: '0xRecipientAddress',
amount: '1000000' // 1 USDC (assuming 6 decimals)
});

// tokenTransferRequest can now be sent using a provider

Contract Execution

executeContractCall

executeContractCall(provider: LayerProvider, options: ContractCallOptions, sendOptions?: SendOptions): Promise<UserOperationResponse>

Builds and executes a contract call in a single function.

Parameters:

  • provider: LayerProvider - Initialized Layer provider
  • options: ContractCallOptions - Contract call options (same as buildContractCallRequest)
  • sendOptions?: SendOptions - Optional sending options:
    • sponsor?: boolean - Whether to sponsor gas (default: false)
    • maxPriorityFeePerGas?: string - Max priority fee
    • maxFeePerGas?: string - Max fee

Returns:

  • Promise<UserOperationResponse> - Response with transaction hash and wait function

Example:

import { executeContractCall, LayerProvider } from '@layerg-ua-sdk/aa-sdk';

// With provider already initialized
const result = await executeContractCall(
layerProvider,
{
sender: accountAddress,
contractAddress: '0xContractAddress',
abi: CONTRACT_ABI,
method: 'mint',
params: [1, accountAddress]
},
{ sponsor: true }
);

// Wait for transaction confirmation
const receipt = await result.wait();
console.log(`Transaction confirmed in block ${receipt.blockNumber}`);

executeMultiCall

executeMultiCall(provider: LayerProvider, options: MultiCallOptions, sendOptions?: SendOptions): Promise<UserOperationResponse>

Builds and executes a batch of contract calls in a single function.

Parameters:

  • provider: LayerProvider - Initialized Layer provider
  • options: MultiCallOptions - Multi-call options (same as buildMultiCallRequest)
  • sendOptions?: SendOptions - Optional sending options

Returns:

  • Promise<UserOperationResponse> - Response with transaction hash and wait function

Example:

import { executeMultiCall } from '@layerg-ua-sdk/aa-sdk';

const result = await executeMultiCall(
layerProvider,
{
sender: accountAddress,
calls: [
{
contractAddress: '0xToken1Address',
abi: ERC20_ABI,
method: 'approve',
params: ['0xSpenderAddress', '1000000000000000000']
},
{
contractAddress: '0xDexAddress',
abi: DEX_ABI,
method: 'swap',
params: ['0xToken1Address', '0xToken2Address', '1000000000000000000', '0']
}
]
},
{ sponsor: true }
);

// Transaction hash is immediately available
console.log(`User operation hash: ${result.userOpHash}`);

// Wait for transaction confirmation
const receipt = await result.wait();
console.log(`Multi-call executed in block ${receipt.blockNumber}`);

Event Handling

decodeEvents

decodeEvents(receipt: TransactionReceipt, abi: any, eventName?: string): any[]

Decodes events from a transaction receipt.

Parameters:

  • receipt: TransactionReceipt - Transaction receipt
  • abi: any - Contract ABI containing event definitions
  • eventName?: string - Optional specific event name to decode

Returns:

  • any[] - Array of decoded events

Example:

import { decodeEvents } from '@layerg-ua-sdk/aa-sdk';

// After getting a transaction receipt
const transferEvents = decodeEvents(receipt, ERC20_ABI, 'Transfer');
console.log('Transfer events:', transferEvents);

// All events
const allEvents = decodeEvents(receipt, CONTRACT_ABI);
console.log('All events:', allEvents);

waitForUserOperationReceipt

waitForUserOperationReceipt(userOpHash: string, chainId: number, timeout?: number): Promise<UserOperationReceipt>

Waits for a user operation to be included in a block and returns the receipt.

Parameters:

  • userOpHash: string - User operation hash returned from sendUserOperation
  • chainId: number - Chain ID where the operation was sent
  • timeout?: number - Optional timeout in milliseconds (default: 60000)

Returns:

  • Promise<UserOperationReceipt> - User operation receipt including transaction details

Example:

import { waitForUserOperationReceipt } from '@layerg-ua-sdk/aa-sdk';

// After sending a user operation
const receipt = await waitForUserOperationReceipt(userOpHash, 2484);
console.log(`Operation included in block ${receipt.receipt.blockNumber}`);
console.log(`Transaction hash: ${receipt.receipt.transactionHash}`);

// Decode events from the receipt
const events = decodeEvents(receipt.receipt, CONTRACT_ABI);

Error Handling

Contract interaction methods throw standardized errors when issues occur. Always implement proper error handling:

try {
const result = await executeContractCall(
layerProvider,
{
sender: accountAddress,
contractAddress: contractAddress,
abi: CONTRACT_ABI,
method: 'mint',
params: [tokenId, accountAddress]
}
);

// Process successful result
console.log(`User operation hash: ${result.userOpHash}`);

} catch (error) {
if (error.code === 'CONTRACT_EXECUTION_FAILED') {
console.error('Contract execution failed:', error.message);
// Check if there's additional error data
if (error.data && error.data.reason) {
console.error('Reason:', error.data.reason);
}
} else if (error.code === 'BUNDLER_ERROR') {
console.error('Bundler error:', error.message);
} else {
console.error('Unknown error:', error);
}
}

Gas Estimation

estimateGasForContractCall

estimateGasForContractCall(provider: LayerProvider, options: ContractCallOptions): Promise<GasEstimate>

Estimates gas for a contract call.

Parameters:

  • provider: LayerProvider - Initialized Layer provider
  • options: ContractCallOptions - Contract call options

Returns:

  • Promise<GasEstimate> - Gas estimate object:
    • gas: string - Estimated gas limit
    • maxFeePerGas: string - Recommended max fee per gas
    • maxPriorityFeePerGas: string - Recommended max priority fee per gas

Example:

import { estimateGasForContractCall } from '@layerg-ua-sdk/aa-sdk';

const gasEstimate = await estimateGasForContractCall(
layerProvider,
{
sender: accountAddress,
contractAddress: '0xContractAddress',
abi: CONTRACT_ABI,
method: 'mint',
params: [1, accountAddress]
}
);

console.log(`Estimated gas: ${gasEstimate.gas}`);
console.log(`Max fee per gas: ${gasEstimate.maxFeePerGas}`);

Next Steps

  • Learn about Providers for interacting with the blockchain
  • See Account APIs for account management functionality
  • Check the aa-smc package for smart contract integration details