UNPKG

erc-8004-js

Version:

TypeScript SDK for ERC-8004 Trustless Agents protocol

162 lines (138 loc) 4.28 kB
/** * Viem adapter implementation * Viem is a modern TypeScript-first Ethereum library */ import { PublicClient, WalletClient, Account, Address, Hash, TransactionReceipt, decodeEventLog, } from 'viem'; import { BlockchainAdapter, ContractCallResult } from './types'; export class ViemAdapter implements BlockchainAdapter { private publicClient: PublicClient; private walletClient?: WalletClient; private account?: Account | Address; constructor( publicClient: PublicClient, walletClient?: WalletClient, account?: Account | Address ) { this.publicClient = publicClient; this.walletClient = walletClient; this.account = account; } async call( contractAddress: string, abi: any[], functionName: string, args: any[] ): Promise<any> { // Strip function signature if present (ethers format compatibility) // Viem auto-matches based on args, ethers uses "functionName(types)" const cleanFunctionName = functionName.includes('(') ? functionName.substring(0, functionName.indexOf('(')) : functionName; // ABI is already in proper JSON format, use directly const result = await this.publicClient.readContract({ address: contractAddress as Address, abi: abi as any, functionName: cleanFunctionName, args, }); return result; } async send( contractAddress: string, abi: any[], functionName: string, args: any[] ): Promise<ContractCallResult> { if (!this.walletClient || !this.account) { throw new Error('Wallet client and account required for write operations'); } // Strip function signature if present (ethers format compatibility) // Viem auto-matches based on args, ethers uses "functionName(types)" const cleanFunctionName = functionName.includes('(') ? functionName.substring(0, functionName.indexOf('(')) : functionName; // ABI is already in proper JSON format, use directly // Simulate the transaction first const { request } = await this.publicClient.simulateContract({ address: contractAddress as Address, abi: abi as any, functionName: cleanFunctionName, args, account: this.account, }); // Write the transaction const hash = await this.walletClient.writeContract(request); // Wait for transaction receipt const receipt = await this.publicClient.waitForTransactionReceipt({ hash, }); // Parse events from the receipt const events: any[] = []; for (const log of receipt.logs) { try { const decoded: any = decodeEventLog({ abi: abi as any, data: log.data, topics: log.topics, }); events.push({ name: decoded.eventName, args: decoded.args, }); } catch { // Skip logs that can't be decoded with this ABI } } return { txHash: receipt.transactionHash, blockNumber: receipt.blockNumber, receipt, events, }; } async getAddress(): Promise<string | null> { if (!this.account) { return null; } // Handle both Account objects and raw addresses if (typeof this.account === 'string') { return this.account; } return this.account.address; } async getChainId(): Promise<number> { const chainId = await this.publicClient.getChainId(); return chainId; } async signMessage(message: string | Uint8Array): Promise<string> { if (!this.walletClient || !this.account) { throw new Error('Wallet client and account required for signing'); } const signature = await this.walletClient.signMessage({ account: this.account, message: typeof message === 'string' ? message : { raw: message }, }); return signature; } async signTypedData(domain: any, types: any, value: any): Promise<string> { if (!this.walletClient || !this.account) { throw new Error('Wallet client and account required for signing'); } const signature = await this.walletClient.signTypedData({ account: this.account, domain, types, primaryType: Object.keys(types)[0], // Viem requires primaryType message: value, }); return signature; } }