Skip to main content

Erc20Service

The Erc20Service class provides a convenient interface for interacting with ERC20 tokens. It handles token approvals, allowance checks, and balance inquiries that are essential for deposit and withdrawal operations in the Nitrolite system.

Initialization

import { Erc20Service } from '@erc7824/nitrolite';

const erc20Service = new Erc20Service(
publicClient, // viem PublicClient
walletClient // viem WalletClient
);

Core Methods

MethodDescriptionParametersReturn Type
approveApproves a spender to use tokens.tokenAddress: Address, spender: Address, amount: bigintPromise<Hash>
getTokenAllowanceGets token allowance for a spender.tokenAddress: Address, owner: Address, spender: AddressPromise<bigint>
getTokenBalanceGets token balance for an account.tokenAddress: Address, account: AddressPromise<bigint>

Method Details

Approve Tokens

Approves a spender (typically the Custody contract) to transfer tokens on behalf of the owner.

// Approve the custody contract to spend 1000 tokens
const txHash = await erc20Service.approve(
tokenAddress, // ERC20 token address
spenderAddress, // Custody contract address
1000000000000000000n // Amount to approve (1 token with 18 decimals)
);

Important notes:

  • For security reasons, always specify the exact amount you want to approve
  • Consider using the ERC20 token decimals for the amount calculation
  • The transaction will fail if the owner has insufficient balance

Get Token Allowance

Retrieves the current allowance granted by an owner to a spender.

// Check current allowance
const allowance = await erc20Service.getTokenAllowance(
tokenAddress, // ERC20 token address
ownerAddress, // Owner's address
spenderAddress // Spender's address (custody contract)
);

console.log(`Current allowance: ${allowance}`);

// Check if allowance is sufficient
if (allowance < requiredAmount) {
console.log('Need to approve more tokens');
}

Get Token Balance

Retrieves the token balance for a specific account.

// Check token balance
const balance = await erc20Service.getTokenBalance(
tokenAddress, // ERC20 token address
accountAddress // Account to check
);

console.log(`Account balance: ${balance}`);

// Check if balance is sufficient
if (balance < requiredAmount) {
console.log('Insufficient token balance');
}

Transaction Preparation

For Account Abstraction support, Erc20Service provides a transaction preparation method:

MethodDescriptionParametersReturn Type
prepareApprovePrepares an approval transaction.tokenAddress: Address, spender: Address, amount: bigintPromise<PreparedTransaction>

Example:

// Prepare approval transaction
const tx = await erc20Service.prepareApprove(
tokenAddress,
spenderAddress,
amount
);

// Use with your Account Abstraction provider
const userOp = await aaProvider.buildUserOperation({
target: tx.to,
data: tx.data,
value: 0n // ERC20 approvals don't require ETH
});

Implementation Details

The Erc20Service uses the standard ERC20 interface methods:

  • approve: Allows a spender to withdraw tokens from the owner's account, up to the specified amount
  • allowance: Returns the remaining tokens that a spender is allowed to withdraw
  • balanceOf: Returns the token balance of the specified account

Working with Token Decimals

ERC20 tokens typically have decimal places (most commonly 18). When working with token amounts, you should account for these decimals:

import { parseUnits } from 'viem';

// For a token with 18 decimals
const tokenDecimals = 18;

// Convert 1.5 tokens to the smallest unit
const amount = parseUnits('1.5', tokenDecimals);

// Approve the amount
await erc20Service.approve(tokenAddress, spenderAddress, amount);

Error Handling

The Erc20Service throws specific error types:

  • TokenError: For token-specific errors (insufficient balance, approval failures)
  • ContractCallError: When calls to the contract fail
  • WalletClientRequiredError: When wallet client is needed but not provided

Example:

try {
await erc20Service.approve(tokenAddress, spenderAddress, amount);
} catch (error) {
if (error instanceof TokenError) {
console.error(`Token error: ${error.message}`);
console.error(`Suggestion: ${error.suggestion}`);

// Check for specific token error conditions
if (error.details?.errorName === 'InsufficientBalance') {
console.log(`Available balance: ${error.details.available}`);
}
}
}

Common Patterns

Checking and Approving Tokens

A common pattern is to check if the current allowance is sufficient before approving more tokens:

// Get current allowance
const allowance = await erc20Service.getTokenAllowance(
tokenAddress,
ownerAddress,
spenderAddress
);

// If allowance is insufficient, approve more tokens
if (allowance < requiredAmount) {
await erc20Service.approve(tokenAddress, spenderAddress, requiredAmount);
}

// Now proceed with the operation that requires the approval
// (e.g., deposit into custody contract)

Handling Multiple Tokens

If your application works with multiple tokens, you can reuse the same Erc20Service instance:

// Same service instance for different tokens
const erc20Service = new Erc20Service(publicClient, walletClient);

// Work with token A
const balanceA = await erc20Service.getTokenBalance(tokenAddressA, accountAddress);

// Work with token B
const balanceB = await erc20Service.getTokenBalance(tokenAddressB, accountAddress);

Integration with NitroliteClient

When using the NitroliteClient, you typically don't need to interact with Erc20Service directly, as the client handles these operations for you:

// NitroliteClient handles token approvals automatically during deposit
await nitroliteClient.deposit(amount);

// For explicit approval without deposit
await nitroliteClient.approveTokens(amount);

// Get token balance through the client
const balance = await nitroliteClient.getTokenBalance();

However, for advanced use cases or custom token interaction, direct access to Erc20Service can be useful.