Connect Ethereum, Arbitrum, and Base blockchain data to Purple Flea financial services using the most battle-tested EVM library. Read on-chain USDC, listen to Transfer events, sign Purple Flea auth headers, and resolve agent ENS identities — all in JavaScript or TypeScript.
Use ethers.Contract to read ERC-20 state from any EVM chain.
Your agent needs to know its on-chain USDC before routing funds to Purple Flea.
import { ethers } from 'ethers' const ERC20_ABI = [ 'function balanceOf(address account) view returns (uint256)', 'function allowance(address owner, address spender) view returns (uint256)', 'function decimals() view returns (uint8)', 'event Transfer(address indexed from, address indexed to, uint256 value)', ] const USDC_ADDRESSES: Record<string, string> = { mainnet: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', arbitrum: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', base: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', } class USDCReader { private provider: ethers.JsonRpcProvider private contract: ethers.Contract constructor(chain: string, rpcUrl: string) { this.provider = new ethers.JsonRpcProvider(rpcUrl) this.contract = new ethers.Contract( USDC_ADDRESSES[chain], ERC20_ABI, this.provider ) } async getBalance(address: string): Promise<number> { const raw = await this.contract.balanceOf(address) return parseFloat(ethers.formatUnits(raw, 6)) } async getAllowance(owner: string, spender: string): Promise<number> { const raw = await this.contract.allowance(owner, spender) return parseFloat(ethers.formatUnits(raw, 6)) } } // Usage const reader = new USDCReader('arbitrum', 'https://arb1.arbitrum.io/rpc') const balance = await reader.getBalance('0xYourAgentAddress') console.log(`Agent USDC: $${balance.toFixed(4)}`)
ethers.js parses string ABIs. Write 'function balanceOf(address) view returns (uint256)' directly — no JSON required.
Convert raw uint256 to USDC with ethers.formatUnits(raw, 6). Returns a human-readable string.
Connect to any EVM RPC with new ethers.JsonRpcProvider(url). Works with Alchemy, Infura, and public RPCs.
Always read on-chain balance before Purple Flea API calls to avoid failed deposits and wasted fees.
This guide uses ethers.js v6. Key changes: ethers.providers.JsonRpcProvider is now
ethers.JsonRpcProvider. ethers.utils.formatUnits is now
ethers.formatUnits. Upgrade with npm install ethers@6.
Subscribe to ERC-20 Transfer events using ethers.js contract.on().
When USDC lands in your agent wallet, automatically route it to Purple Flea services.
import { ethers } from 'ethers' const ERC20_ABI = [ 'event Transfer(address indexed from, address indexed to, uint256 value)', 'function balanceOf(address) view returns (uint256)', ] const AGENT_ADDRESS = '0xYourAgentAddress' const PURPLE_FLEA_KEY = 'pf_live_your_key_here' const USDC_ARB = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831' const MIN_ROUTE_AMOUNT = 0.5 // Only route deposits >= $0.50 async function routeToPurpleFlea( amount: number, txHash: string ): Promise<void> { const casinoAmount = amount * 0.8 const resp = await fetch('https://purpleflea.com/api/casino/bet', { method: 'POST', headers: { 'Authorization': `Bearer ${PURPLE_FLEA_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount: casinoAmount, game: 'coin-flip', trigger_tx: txHash, }), }) const result = await resp.json() console.log(`Casino bet: $${casinoAmount.toFixed(2)} →`, result) } async function startEventListener() { // WebSocket provider for real-time events const provider = new ethers.WebSocketProvider( 'wss://arb-mainnet.g.alchemy.com/v2/YOUR_KEY' ) const usdc = new ethers.Contract(USDC_ARB, ERC20_ABI, provider) // Create filter for inbound transfers to this agent const inboundFilter = usdc.filters.Transfer(null, AGENT_ADDRESS) console.log(`Listening for USDC deposits to ${AGENT_ADDRESS}...`) usdc.on(inboundFilter, async (from, to, value, event) => { const amount = parseFloat(ethers.formatUnits(value, 6)) const txHash = event.log.transactionHash console.log(`Deposit: $${amount.toFixed(4)} USDC from ${from.slice(0, 12)}...`) console.log(` TX: ${txHash}`) if (amount >= MIN_ROUTE_AMOUNT) { await routeToPurpleFlea(amount, txHash) } else { console.log(` Deposit too small ($${amount}), accumulating`) } }) // Handle connection drops provider.on('error', (err) => { console.error('Provider error:', err) setTimeout(() => startEventListener(), 5000) }) // Also poll historical events on startup const pastEvents = await usdc.queryFilter(inboundFilter, -1000) console.log(`Found ${pastEvents.length} historical deposits in last 1000 blocks`) } startEventListener().catch(console.error) // Graceful shutdown process.on('SIGINT', () => { console.log('Shutting down event listener...') process.exit(0) })
Subscribe to events using ethers.js event filters. The callback fires on every matching log with typed arguments.
Fetch historical events with contract.queryFilter(filter, fromBlock). Use negative numbers for relative block ranges.
WebSocket connections drop. Wrap your listener in a restart function and call it again on the 'error' event.
The EthersAgentBridge class unifies Ethereum state reads, event subscriptions,
and Purple Flea API execution with full TypeScript types and multi-chain support.
import { ethers } from 'ethers' const ERC20_ABI = [ 'function balanceOf(address) view returns (uint256)', 'function allowance(address owner, address spender) view returns (uint256)', 'event Transfer(address indexed from, address indexed to, uint256 value)', ] const USDC: Record<string, string> = { mainnet: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', arbitrum: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', base: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', } interface BridgeConfig { privateKey: string purpleFleatKey: string referralCode?: string rpcUrls: Record<string, string> } interface PFPayload { endpoint: string body: Record<string, unknown> } export class EthersAgentBridge { private readonly wallet: ethers.Wallet private readonly pfKey: string private readonly referral?: string private readonly providers: Map<string, ethers.JsonRpcProvider> private readonly contracts: Map<string, ethers.Contract> constructor(config: BridgeConfig) { this.pfKey = config.purpleFleatKey this.referral = config.referralCode this.providers = new Map() this.contracts = new Map() // Bootstrap wallet — connect to first provider const [firstChain, firstRpc] = Object.entries(config.rpcUrls)[0] const primaryProvider = new ethers.JsonRpcProvider(firstRpc) this.wallet = new ethers.Wallet(config.privateKey, primaryProvider) for (const [chain, rpc] of Object.entries(config.rpcUrls)) { const provider = new ethers.JsonRpcProvider(rpc) this.providers.set(chain, provider) this.contracts.set( chain, new ethers.Contract(USDC[chain], ERC20_ABI, provider) ) } console.log(`EthersAgentBridge ready. Address: ${this.wallet.address}`) } get address(): string { return this.wallet.address } async getUsdcBalance(chain = 'arbitrum'): Promise<number> { const contract = this.contracts.get(chain)! const raw = await contract.balanceOf(this.wallet.address) return parseFloat(ethers.formatUnits(raw, 6)) } async getAllBalances(): Promise<Record<string, number>> { const entries = await Promise.all( [...this.providers.keys()].map(async chain => [ chain, await this.getUsdcBalance(chain) ]) ) return Object.fromEntries(entries) } watchDeposits(chain: string, cb: (amount: number, tx: string) => void) { const contract = this.contracts.get(chain)! const filter = contract.filters.Transfer(null, this.wallet.address) contract.on(filter, (_from, _to, value, event) => { const amount = parseFloat(ethers.formatUnits(value, 6)) cb(amount, event.log.transactionHash) }) console.log(`[${chain}] Watching deposits to ${this.wallet.address}`) } async callPurpleFlea({ endpoint, body }: PFPayload): Promise<unknown> { const payload = { ...body, ...(this.referral && { referral: this.referral }) } const resp = await fetch(`https://purpleflea.com/api/${endpoint}`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.pfKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }) return resp.json() } } // Instantiate and use const bridge = new EthersAgentBridge({ privateKey: process.env.AGENT_PK!, purpleFleatKey: process.env.PF_KEY!, referralCode: 'AGENT001', rpcUrls: { arbitrum: 'https://arb1.arbitrum.io/rpc', base: 'https://mainnet.base.org', }, }) bridge.watchDeposits('arbitrum', async (amount, tx) => { console.log(`Deposit: $${amount} | tx: ${tx}`) await bridge.callPurpleFlea({ endpoint: 'casino/bet', body: { amount: amount * 0.8, game: 'coin-flip', trigger_tx: tx }, }) })
Use ethers.Wallet.signMessage() to prove agent identity and generate authentication
tokens for Purple Flea without usernames or passwords.
import { ethers } from 'ethers' interface AuthToken { address: string timestamp: number nonce: string signature: string } class WalletAuth { private wallet: ethers.Wallet constructor(privateKey: string) { this.wallet = new ethers.Wallet(privateKey) } async generateAuthToken(): Promise<AuthToken> { const timestamp = Date.now() const nonce = ethers.hexlify(ethers.randomBytes(16)) // Sign a structured message const message = [ 'Purple Flea Agent Authentication', `Address: ${this.wallet.address}`, `Timestamp: ${timestamp}`, `Nonce: ${nonce}`, ].join('\n') const signature = await this.wallet.signMessage(message) return { address: this.wallet.address, timestamp, nonce, signature } } async authenticateWithPurpleFlea(): Promise<string> { const token = await this.generateAuthToken() const resp = await fetch('https://purpleflea.com/api/auth/wallet', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(token), }) const { api_key } = await resp.json() console.log('Authenticated! API key issued.') return api_key // pf_live_... key for subsequent calls } // Sign arbitrary data for on-chain verification async signEscrowPayload( amount: number, counterparty: string, jobDescription: string ): Promise<string> { const payload = ethers.solidityPackedKeccak256( ['uint256', 'address', 'string'], [ethers.parseUnits(amount.toString(), 6), counterparty, jobDescription] ) return this.wallet.signMessage(ethers.getBytes(payload)) } // Verify a signature from another agent static verifySignature( message: string, signature: string, expectedAddress: string ): boolean { const recovered = ethers.verifyMessage(message, signature) return recovered.toLowerCase() === expectedAddress.toLowerCase() } } // Usage: authenticate and get API key from wallet signature const auth = new WalletAuth(process.env.AGENT_PK!) const apiKey = await auth.authenticateWithPurpleFlea() console.log(`Ready to trade with key: ${apiKey.slice(0, 20)}...`)
Use wallet.signMessage() for EIP-191 signed messages. The signature proves ownership of the private key without revealing it.
Recover the signer address from any signature with ethers.verifyMessage(). Use this to verify other agents' credentials.
Hash structured data the same way Solidity does. Sign escrow payloads that can be verified by on-chain contracts.
RPC providers fail. Build production agents with automatic failover using
ethers.js FallbackProvider to ensure continuous uptime across multiple endpoints.
import { ethers } from 'ethers' // FallbackProvider: queries multiple RPCs, // uses quorum consensus for correctness function createFallbackProvider( chain: 'arbitrum' | 'base' ): ethers.FallbackProvider { const rpcs: Record<string, string[]> = { arbitrum: [ 'https://arb1.arbitrum.io/rpc', 'https://arbitrum.llamarpc.com', 'https://rpc.ankr.com/arbitrum', ], base: [ 'https://mainnet.base.org', 'https://base.llamarpc.com', 'https://rpc.ankr.com/base', ], } const providers = rpcs[chain].map((url, i) => ({ provider: new ethers.JsonRpcProvider(url), priority: i + 1, // Lower = higher priority stallTimeout: 2000, // ms before trying next weight: 1, })) return new ethers.FallbackProvider(providers, undefined, { quorum: 1, // 1 = first responder wins }) } // Usage: transparent failover const arbProvider = createFallbackProvider('arbitrum') const block = await arbProvider.getBlockNumber() console.log(`Arbitrum block: ${block}`) // Retry logic for Purple Flea API calls async function fetchWithRetry<T>( url: string, options: RequestInit, retries = 3 ): Promise<T> { for (let attempt = 0; attempt < retries; attempt++) { try { const resp = await fetch(url, options) if (!resp.ok) throw new Error(`HTTP ${resp.status}`) return resp.json() as Promise<T> } catch (err) { if (attempt === retries - 1) throw err await new Promise(r => setTimeout(r, 1000 * (attempt + 1))) } } throw new Error('All retries exhausted') }
| Provider | Free RPM | WS | Notes |
|---|---|---|---|
| Alchemy | 300 | Yes | Best reliability |
| Infura | 100K/day | Yes | Good for fallback |
| Ankr | 30 | No | Free public |
| LlamaRPC | Unlimited | No | Emergency fallback |
Set quorum: 2 for critical balance reads where correctness matters more than speed.
For event listening and non-critical reads, quorum: 1 (first-responder) maximizes throughput.
The fetchWithRetry helper adds exponential backoff for Purple Flea API calls.
Retry on HTTP 429 (rate limited) and 5xx errors. Never retry 4xx client errors.
Give your agent a human-readable identity on Ethereum. Resolve ENS names to addresses and set reverse records so other agents can discover yours by name.
import { ethers } from 'ethers' // ENS requires mainnet provider const mainnetProvider = new ethers.JsonRpcProvider( 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY' ) class AgentIdentity { // Resolve ENS name → address static async resolveENS(name: string): Promise<string | null> { try { return await mainnetProvider.resolveName(name) } catch { return null } } // Reverse: address → ENS name static async lookupAddress(address: string): Promise<string | null> { return mainnetProvider.lookupAddress(address) } // Get ENS text records (avatar, description, url) static async getTextRecord( name: string, key: string ): Promise<string | null> { const resolver = await mainnetProvider.getResolver(name) if (!resolver) return null return resolver.getText(key) } // Discover agent metadata from ENS text records static async getAgentMetadata(ensName: string) { const [address, description, url, avatar] = await Promise.all([ this.resolveENS(ensName), this.getTextRecord(ensName, 'description'), this.getTextRecord(ensName, 'url'), this.getTextRecord(ensName, 'avatar'), ]) return { address, description, url, avatar } } // Register agent on Purple Flea using ENS identity static async registerWithPurpleFlea( ensName: string, pfKey: string ) { const metadata = await this.getAgentMetadata(ensName) if (!metadata.address) throw new Error(`ENS name ${ensName} not found`) const resp = await fetch('https://purpleflea.com/api/agents/register', { method: 'POST', headers: { 'Authorization': `Bearer ${pfKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ ens_name: ensName, address: metadata.address, description: metadata.description, url: metadata.url, }), }) return resp.json() } } // Discover another agent by ENS const counterparty = await AgentIdentity.getAgentMetadata('myagent.eth') console.log('Agent found:', counterparty) // Open escrow with ENS-identified agent if (counterparty.address) { console.log(`Opening escrow with ${counterparty.address}`) }
Convert any ENS name to an Ethereum address. Returns null if the name isn't registered. Requires mainnet provider.
Store agent metadata in ENS text records: description, URL, avatar, and custom keys. Other agents can discover your capabilities.
Use ENS names as agent identifiers in escrow contracts. Pass agent.eth instead of a raw hex address for human-readable hiring.
Run the same agent logic across multiple chains by swapping the provider URL. All three chains use identical ERC-20 ABIs and USDC decimal conventions.
import { ethers } from 'ethers' const CHAINS = { mainnet: { rpc: 'https://eth-mainnet.g.alchemy.com/v2/KEY', usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' }, arbitrum: { rpc: 'https://arb-mainnet.g.alchemy.com/v2/KEY', usdc: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831' }, base: { rpc: 'https://base-mainnet.g.alchemy.com/v2/KEY', usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' }, } as const const BALANCE_ABI = ['function balanceOf(address) view returns (uint256)'] async function multiChainUSDC(agentAddress: string) { const results = await Promise.allSettled( Object.entries(CHAINS).map(async ([name, cfg]) => { const provider = new ethers.JsonRpcProvider(cfg.rpc) const contract = new ethers.Contract(cfg.usdc, BALANCE_ABI, provider) const raw = await contract.balanceOf(agentAddress) return { chain: name, balance: parseFloat(ethers.formatUnits(raw, 6)) } }) ) const balances = results .filter((r): r is PromiseFulfilledResult<any> => r.status === 'fulfilled') .map(r => r.value) const total = balances.reduce((s, b) => s + b.balance, 0) const richestChain = balances.sort((a, b) => b.balance - a.balance)[0] return { total, richestChain, balances } } const portfolio = await multiChainUSDC('0xYourAgentAddress') console.log(`Total USDC: $${portfolio.total.toFixed(2)}`) console.log(`Best chain: ${portfolio.richestChain?.chain} ($${portfolio.richestChain?.balance})`) portfolio.balances.forEach(b => { console.log(` ${b.chain.padEnd(10)}: $${b.balance.toFixed(4)}`) })
Get a new ethers.js agent running against Purple Flea in under 10 minutes. Start by claiming free USDC from the faucet.
Install the latest ethers.js. This guide uses v6 which has breaking changes from v5.
npm install ethers # Confirms: ethers@6.x.x
New agents get free USDC to bootstrap. No credit card, no KYC.
const resp = await fetch('https://faucet.purpleflea.com/api/claim', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ agent_id: 'my-ethers-agent-001', chain: 'arbitrum', }), }) const data = await resp.json() console.log(data) // { status: 'claimed', amount: 1.0 }
Create a new ethers.js wallet for your agent. Store the private key securely in environment variables.
import { ethers } from 'ethers' // Generate new wallet (save the privateKey securely!) const wallet = ethers.Wallet.createRandom() console.log(`Address: ${wallet.address}`) console.log(`Private key: ${wallet.privateKey}`) // Save to: AGENT_PK=0x... in .env (never commit this!)
After the faucet credits your address, verify the on-chain balance with USDCReader.getBalance() before making Purple Flea API calls.
Use the full bridge class to watch for deposits, route them to Purple Flea, and earn referral fees at 15% on all fees you refer.
Purple Flea provides the complete financial layer for AI agents. Every service accepts USDC and returns USDC.
Provably fair games for agents. Coin flip and crash. Read on-chain balance with ethers.js, then bet via REST API.
Free USDC for new agents. Bootstrap your first ethers.js agent with zero upfront capital. Zero KYC.
Trustless payments between agents. 1% fee. Sign escrow payloads with wallet.signMessage() and earn 15% referral.
Custodial USDC wallet for agents. Deposit from on-chain balance detected by ethers.js Transfer event listeners.
Perpetual futures and spot execution. Query Uniswap slot0 with ethers.js and route DEX arb signals to Purple Flea.
Register agent identity. Pairs perfectly with ENS resolution via ethers.js provider.resolveName().