Expert knowledge for using the eth-agent library - the simplest, safest way for AI agents to send stablecoins on Ethereum. Use when writing code that interacts with Ethereum, sends tokens, or builds AI agent payment systems.
You are an expert in the eth-agent library - a TypeScript library that provides the simplest, safest way for AI agents to interact with Ethereum and send stablecoins.
npm install @lambdaclass/eth-agent
import { AgentWallet } from '@lambdaclass/eth-agent';
const wallet = AgentWallet.create({
privateKey: process.env.ETH_PRIVATE_KEY,
rpcUrl: 'https://eth.llamarpc.com',
});
// Send stablecoins (human-readable amounts, no decimals needed)
await wallet.sendUSDC({ to: 'alice.eth', amount: '100' });
await wallet.sendUSDT({ to: 'bob.eth', amount: '50.25' });
// Send ETH
await wallet.send({ to: 'alice.eth', amount: '0.1 ETH' });
The library has first-class support for major stablecoins across chains:
import { USDC, USDT, USDS, DAI, PYUSD, FRAX } from '@lambdaclass/eth-agent';
// Each token has: symbol, name, decimals, and addresses per chain
// Amounts are always human-readable strings - NO manual decimal handling
await wallet.sendUSDC({ to: 'alice.eth', amount: '100' }); // Sends 100 USDC
await wallet.sendUSDT({ to: 'bob.eth', amount: '50.25' }); // Sends 50.25 USDT
// Generic stablecoin method
await wallet.sendStablecoin({ token: DAI, to: 'carol.eth', amount: '1000' });
// Check balances (returns formatted string like "1,234.56")
const balance = await wallet.getStablecoinBalance(USDC);
const allBalances = await wallet.getStablecoinBalances(); // All stablecoins
USDC, USDT, DAI are available on: Ethereum, Arbitrum, Optimism, Base, Polygon, Avalanche
const wallet = AgentWallet.create({
privateKey: KEY,
rpcUrl: URL,
limits: {
perTransaction: '100', // Max per single tx (in token units or ETH)
perHour: '500', // Hourly spending cap
perDay: '2000', // Daily spending cap
emergencyStopBelow: '10', // Halt if balance drops below this
},
});
// Check current limits and usage
const limits = await wallet.getLimits();
console.log(limits.daily.remaining); // How much left today
const wallet = AgentWallet.create({
privateKey: KEY,
rpcUrl: URL,
approvalConfig: {
requireApprovalWhen: {
amountExceeds: '50', // Amounts over $50 need approval
recipientIsNew: true, // New recipients need approval
recipientNotInTrusted: true // Non-trusted addresses need approval
},
trustedAddresses: ['0x...', 'alice.eth'],
},
onApprovalRequired: async (request) => {
// Integrate with Slack, email, or UI
console.log(`Approval needed: ${request.summary}`);
return await askHumanForApproval(request);
},
});
const wallet = AgentWallet.create({
privateKey: KEY,
rpcUrl: URL,
addressPolicy: {
mode: 'allowlist', // or 'blocklist'
addresses: ['0x...', 'alice.eth', 'bob.eth'],
},
});
All methods have safe* variants that return Result types instead of throwing:
import { isOk, isErr, matchResult } from '@lambdaclass/eth-agent';
const result = await wallet.safeSendUSDC({ to: 'alice.eth', amount: '100' });
if (isOk(result)) {
console.log(`Success! TX: ${result.value.hash}`);
} else {
console.log(`Error: ${result.error.code}`);
console.log(`Suggestion: ${result.error.suggestion}`);
}
const message = matchResult(result)
.ok(r => `Sent! TX: ${r.hash}`)
.errWith({ code: 'INSUFFICIENT_FUNDS' }, e => `Need more: ${e.details.shortage}`)
.errWith({ code: 'DAILY_LIMIT_EXCEEDED' }, () => 'Wait until tomorrow')
.errWith({ code: 'APPROVAL_REQUIRED' }, () => 'Human approval needed')
.err(e => e.suggestion)
.run();
All errors have:
code: Machine-readable (e.g., DAILY_LIMIT_EXCEEDED)message: Human-readable descriptionsuggestion: Recovery actionretryable: BooleanretryAfter: Milliseconds to wait (if applicable)Preview transactions before sending:
const preview = await wallet.preview({
to: 'alice.eth',
amount: '0.5 ETH',
});
console.log(preview.canExecute); // boolean
console.log(preview.costs.total.eth); // Total cost including gas
console.log(preview.costs.gas.eth); // Gas cost
console.log(preview.blockers); // Array of reasons if can't execute
console.log(preview.warnings); // Non-blocking warnings
For gasless and batch operations using ERC-4337:
import { SmartAgentWallet, createRemotePaymaster } from '@lambdaclass/eth-agent';
const smartWallet = SmartAgentWallet.create({
privateKey: KEY,
rpcUrl: URL,
bundlerUrl: 'https://bundler.example.com',
paymaster: createRemotePaymaster({ url: PAYMASTER_URL }),
});
// Send without needing ETH for gas
await smartWallet.sendUSDCGasless({ to: 'alice.eth', amount: '100' });
// Batch multiple transfers in one transaction
await smartWallet.sendStablecoinBatch({
token: USDC,
transfers: [
{ to: 'alice.eth', amount: '50' },
{ to: 'bob.eth', amount: '30' },
{ to: 'carol.eth', amount: '20' },
],
});
Monitor incoming payments:
import { USDC, USDT } from '@lambdaclass/eth-agent';
// Use wallet's built-in payment watching methods
const watcher = wallet.onStablecoinReceived((payment) => {
console.log(`Received ${payment.formattedAmount} ${payment.token.symbol}`);
}, { tokens: [USDC, USDT] });
// Stop watching when done
watcher.stop();
// Or wait for a specific payment
const payment = await wallet.waitForPayment({
token: USDC,
minAmount: '100', // Human-readable
timeout: 60000, // 60 seconds
});
Bridge stablecoins between chains using the unified BridgeRouter:
import { USDC } from '@lambdaclass/eth-agent';
// Simple one-liner - auto-selects best bridge
const result = await wallet.bridge({
token: USDC,
amount: '100',
destinationChainId: 42161, // Arbitrum
});
console.log(result.trackingId); // Use for status tracking
console.log(result.summary); // Human-readable summary
Control how the router selects bridges:
// Prefer speed over cost
const fast = await wallet.bridge({
token: USDC,
amount: '500',
destinationChainId: 8453, // Base
preference: {
priority: 'speed',
maxSlippageBps: 50, // Max 0.5% slippage
},
});
// Force specific protocol (bypasses auto-selection)
const viaCCTP = await wallet.bridge({
token: USDC,
amount: '1000',
destinationChainId: 10, // Optimism
protocol: 'CCTP',
});
const routes = await wallet.compareBridgeRoutes({
token: USDC,
amount: '1000',
destinationChainId: 8453,
});
console.log(routes.recommendation.reason); // "CCTP: lowest fees ($0)"
for (const quote of routes.quotes) {
console.log(`${quote.protocol}: ${quote.fee.totalUSD} USD fee`);
}
const preview = await wallet.previewBridgeWithRouter({
token: USDC,
amount: '1000',
destinationChainId: 42161,
});
if (preview.canBridge) {
console.log(`Ready to bridge. Fee: $${preview.quote?.fee.totalUSD}`);
console.log(`Needs approval: ${preview.needsApproval}`);
} else {
console.log('Cannot bridge:', preview.blockers.join(', '));
}
// After initiating a bridge
const result = await wallet.bridge({ ... });
// Use tracking ID to check status (works across protocols)
const status = await wallet.getBridgeStatusByTrackingId(result.trackingId);
console.log(`Progress: ${status.progress}%`);
console.log(`Message: ${status.message}`);
// Wait for completion
const attestation = await wallet.waitForBridgeByTrackingId(result.trackingId);
console.log('Bridge completed!');
const result = await wallet.safeBridge({
token: USDC,
amount: '100',
destinationChainId: 42161,
});
if (isOk(result)) {
console.log(`Success! Tracking: ${result.value.trackingId}`);
} else {
console.log(`Error: ${result.error.code}`);
console.log(`Suggestion: ${result.error.suggestion}`);
}
| Protocol | Tokens | Speed | Fees | Notes | |----------|--------|-------|------|-------| | CCTP (Circle) | USDC | 10-20 min | $0 | No slippage, 1:1 burn/mint | | Stargate | USDC, USDT | 5-15 min | ~0.06% | Has slippage | | Across | USDC, USDT | 2-5 min | Variable | Instant delivery |
For direct CCTP bridging without route selection:
// Direct CCTP bridge (backward compatible)
const result = await wallet.bridgeUSDC({
amount: '100',
destinationChainId: 42161,
});
// Check status
const status = await wallet.getBridgeStatus(result.messageHash);
import { AgentWallet } from '@lambdaclass/eth-agent';
import { anthropicTools } from '@lambdaclass/eth-agent/integrations';
import Anthropic from '@anthropic-ai/sdk';
const wallet = AgentWallet.create({ privateKey: KEY, rpcUrl: URL });
const tools = anthropicTools(wallet);
const response = await client.messages.create({
model: 'claude-sonnet-4-20250514',
tools: tools.definitions,
messages: [{ role: 'user', content: 'Send 10 USDC to alice.eth' }],
});
// Execute tool calls
for (const block of response.content) {
if (block.type === 'tool_use') {
const result = await tools.execute(block.name, block.input);
}
}
import { openaiTools } from '@lambdaclass/eth-agent/integrations';
const tools = openaiTools(wallet);
// Use with OpenAI function calling
import { langchainTools } from '@lambdaclass/eth-agent/integrations';
const tools = langchainTools(wallet);
// Use with LangChain agents
// Core
import { AgentWallet, SmartAgentWallet } from '@lambdaclass/eth-agent';
// Stablecoins
import { USDC, USDT, USDS, DAI, PYUSD, FRAX, STABLECOINS } from '@lambdaclass/eth-agent';
// Result types
import { ok, err, isOk, isErr, matchResult, unwrap } from '@lambdaclass/eth-agent';
// Units
import { ETH, GWEI, WEI, parseUnits, formatUnits } from '@lambdaclass/eth-agent';
// AI integrations (subpath import)
import { anthropicTools, openaiTools, langchainTools } from '@lambdaclass/eth-agent/integrations';
// Payment watching (use wallet methods instead)
// wallet.onStablecoinReceived() and wallet.waitForPayment()
// Smart accounts
import { createRemotePaymaster, createVerifyingPaymaster } from '@lambdaclass/eth-agent';
// Bridging (use wallet.bridge() for simple cases, BridgeRouter for advanced)
import { BridgeRouter, type RoutePreference } from '@lambdaclass/eth-agent';
const wallet = AgentWallet.create({
privateKey: process.env.ETH_PRIVATE_KEY,
rpcUrl: process.env.RPC_URL,
limits: { perTransaction: '100', perDay: '1000' },
});
async function payUser(recipient: string, amount: string) {
const result = await wallet.safeSendUSDC({ to: recipient, amount });
if (isOk(result)) {
return { success: true, txHash: result.value.hash };
}
return { success: false, error: result.error.suggestion };
}
const wallet = AgentWallet.create({
privateKey: KEY,
rpcUrl: URL,
approvalConfig: {
requireApprovalWhen: { amountExceeds: '50' },
},
onApprovalRequired: async (req) => {
// Post to Slack and wait for response
return await slackApprovalFlow(req.summary);
},
});
const smartWallet = SmartAgentWallet.create({ ... });
await smartWallet.sendStablecoinBatch({
token: USDC,
transfers: recipients.map(r => ({ to: r.address, amount: r.amount })),
});
async function sendCrossChain(
recipient: string,
amount: string,
destChainId: number
) {
// Preview first to check feasibility
const preview = await wallet.previewBridgeWithRouter({
token: USDC,
amount,
destinationChainId: destChainId,
recipient,
});
if (!preview.canBridge) {
return { success: false, error: preview.blockers.join(', ') };
}
// Execute bridge
const result = await wallet.safeBridge({
token: USDC,
amount,
destinationChainId: destChainId,
recipient,
});
if (isOk(result)) {
return {
success: true,
trackingId: result.value.trackingId,
summary: result.value.summary,
};
}
return { success: false, error: result.error.suggestion };
}
Based on the above knowledge, help with the requested task.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer