UNPKG

@coinbase/agentkit

Version:

Coinbase AgentKit core primitives

339 lines (338 loc) 14.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const viemWalletProvider_1 = require("./viemWalletProvider"); const viem = __importStar(require("viem")); global.fetch = jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({}), })); jest.mock("../analytics", () => ({ sendAnalyticsEvent: jest.fn().mockImplementation(() => Promise.resolve()), })); // ========================================================= // consts // ========================================================= const MOCK_CHAIN_ID = 84532; const MOCK_ADDRESS = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"; const MOCK_ADDRESS_TO = "0x1234567890123456789012345678901234567890"; const MOCK_TRANSACTION_HASH = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; const MOCK_SIGNATURE = "0x123456789abcdef"; const MOCK_BALANCE = 10000000000000000000n; const MOCK_BLOCK_NUMBER = 12345n; const MOCK_MESSAGE = "Hello, World!"; const MOCK_MAX_FEE_PER_GAS = BigInt(1000000000); const MOCK_MAX_PRIORITY_FEE_PER_GAS = BigInt(100000000); const MOCK_CHAIN = { id: MOCK_CHAIN_ID, name: "Base Sepolia", nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 }, rpcUrls: { default: { http: ["https://sepolia.base.org"], }, }, }; const MOCK_DATA = { domain: { name: "Example", version: "1", chainId: 1, verifyingContract: "0x0000000000000000000000000000000000000000", }, types: { Person: [ { name: "name", type: "string" }, { name: "wallet", type: "address" }, ], }, primaryType: "Person", message: { name: "John Doe", wallet: "0x0000000000000000000000000000000000000000", }, }; // ========================================================= // mocks // ========================================================= jest.mock("../network/network", () => ({ CHAIN_ID_TO_NETWORK_ID: { 1: "mainnet", 5: "goerli", 11155111: "sepolia", }, })); jest.mock("viem", () => { return { createPublicClient: jest.fn(), createWalletClient: jest.fn(), http: jest.fn(() => jest.fn()), parseEther: jest.fn(() => BigInt(1000000000000000000)), hexToString: jest.fn(), fromHex: jest.fn(), formatEther: jest.fn(), privateKeyToAccount: jest.fn(), }; }); jest.mock("viem/accounts", () => { return { privateKeyToAccount: jest.fn(), }; }); describe("ViemWalletProvider", () => { let provider; let mockPublicClient; let mockWalletClient; beforeEach(() => { jest.clearAllMocks(); const mockAccount = { address: MOCK_ADDRESS, }; mockPublicClient = { chain: MOCK_CHAIN, getBalance: jest.fn(), getChainId: jest.fn(), waitForTransactionReceipt: jest.fn(), readContract: jest.fn(), estimateFeesPerGas: jest.fn(), estimateGas: jest.fn(), }; mockPublicClient.getBalance.mockResolvedValue(MOCK_BALANCE); mockPublicClient.getChainId.mockResolvedValue(MOCK_CHAIN_ID); mockPublicClient.waitForTransactionReceipt.mockResolvedValue({ transactionHash: MOCK_TRANSACTION_HASH, blockNumber: MOCK_BLOCK_NUMBER, status: "success", }); mockPublicClient.readContract.mockResolvedValue("mock_result"); mockPublicClient.estimateFeesPerGas.mockResolvedValue({ maxFeePerGas: MOCK_MAX_FEE_PER_GAS, maxPriorityFeePerGas: MOCK_MAX_PRIORITY_FEE_PER_GAS, }); mockPublicClient.estimateGas.mockResolvedValue(BigInt(21000)); mockWalletClient = { chain: MOCK_CHAIN, transport: { name: "HTTP JSON-RPC", request: jest.fn(), value: { url: "https://ethereum.example.com" }, }, account: mockAccount, signMessage: jest.fn(), signTypedData: jest.fn(), signTransaction: jest.fn(), sendTransaction: jest.fn(), }; mockWalletClient.signMessage.mockResolvedValue(MOCK_SIGNATURE); mockWalletClient.signTypedData.mockResolvedValue(MOCK_SIGNATURE); mockWalletClient.signTransaction.mockResolvedValue(MOCK_SIGNATURE); mockWalletClient.sendTransaction.mockResolvedValue(MOCK_TRANSACTION_HASH); viem.createPublicClient.mockReturnValue(mockPublicClient); viem.createWalletClient.mockReturnValue(mockWalletClient); viem.http.mockReturnValue(jest.fn()); provider = new viemWalletProvider_1.ViemWalletProvider(mockWalletClient); }); describe("configuration and initialization", () => { it("should create a provider with default gas multipliers", () => { const provider = new viemWalletProvider_1.ViemWalletProvider(mockWalletClient); expect(provider).toBeInstanceOf(viemWalletProvider_1.ViemWalletProvider); expect(viem.createPublicClient).toHaveBeenCalledWith({ chain: MOCK_CHAIN, transport: expect.any(Function), }); }); it("should create a provider with custom gas multipliers", () => { const gasConfig = { gasLimitMultiplier: 1.5, feePerGasMultiplier: 1.2, }; const provider = new viemWalletProvider_1.ViemWalletProvider(mockWalletClient, gasConfig); expect(provider).toBeInstanceOf(viemWalletProvider_1.ViemWalletProvider); }); }); describe("basic wallet methods", () => { it("should get wallet address", () => { const address = provider.getAddress(); expect(address).toBe(MOCK_ADDRESS); }); it("should get network", () => { const network = provider.getNetwork(); expect(network.chainId).toBe(String(MOCK_CHAIN_ID)); expect(network.protocolFamily).toBe("evm"); }); it("should get wallet name", () => { const name = provider.getName(); expect(name).toBe("viem_wallet_provider"); }); it("should get balance", async () => { const balance = await provider.getBalance(); expect(balance).toBe(MOCK_BALANCE); expect(mockPublicClient.getBalance).toHaveBeenCalledWith({ address: MOCK_ADDRESS, }); }); it("should handle connection errors during balance check", async () => { mockPublicClient.getBalance.mockRejectedValueOnce(new Error("Network connection error")); await expect(provider.getBalance()).rejects.toThrow("Network connection error"); }); }); describe("signing operations", () => { it("should sign a message", async () => { const signature = await provider.signMessage(MOCK_MESSAGE); expect(mockWalletClient.signMessage).toHaveBeenCalledWith({ account: mockWalletClient.account, message: MOCK_MESSAGE, }); expect(signature).toBe(MOCK_SIGNATURE); }); it("should sign typed data", async () => { const signature = await provider.signTypedData(MOCK_DATA); expect(mockWalletClient.signTypedData).toHaveBeenCalledWith({ account: mockWalletClient.account, ...MOCK_DATA, }); expect(signature).toBe(MOCK_SIGNATURE); }); it("should sign a transaction", async () => { const transaction = { to: MOCK_ADDRESS_TO, value: BigInt(1000000000000000000), }; await provider.signTransaction(transaction); expect(mockWalletClient.signTransaction).toHaveBeenCalledWith({ account: mockWalletClient.account, chain: mockWalletClient.chain, to: transaction.to, value: transaction.value, data: transaction.data, }); }); it("should handle signing errors", async () => { mockWalletClient.signMessage.mockRejectedValueOnce(new Error("Signing failed")); await expect(provider.signMessage("test message")).rejects.toThrow("Signing failed"); }); it("should handle typed data signing errors", async () => { mockWalletClient.signTypedData.mockRejectedValueOnce(new Error("Signing failed")); await expect(provider.signTypedData(MOCK_DATA)).rejects.toThrow("Signing failed"); }); }); describe("transaction operations", () => { it("should send a transaction", async () => { const transaction = { to: MOCK_ADDRESS_TO, value: BigInt(1000000000000000000), }; const hash = await provider.sendTransaction(transaction); expect(mockWalletClient.sendTransaction).toHaveBeenCalled(); expect(hash).toBe(MOCK_TRANSACTION_HASH); }); it("should wait for a transaction receipt", async () => { const receipt = await provider.waitForTransactionReceipt(MOCK_TRANSACTION_HASH); expect(mockPublicClient.waitForTransactionReceipt).toHaveBeenCalledWith({ hash: MOCK_TRANSACTION_HASH, }); expect(receipt).toEqual({ transactionHash: MOCK_TRANSACTION_HASH, blockNumber: MOCK_BLOCK_NUMBER, status: "success", }); }); it("should handle transaction send errors", async () => { mockWalletClient.sendTransaction.mockRejectedValueOnce(new Error("Transaction failed")); const transaction = { to: MOCK_ADDRESS_TO, value: BigInt(1000000000000000000), }; await expect(provider.sendTransaction(transaction)).rejects.toThrow("Transaction failed"); }); it("should handle transaction receipt wait errors", async () => { mockPublicClient.waitForTransactionReceipt.mockRejectedValueOnce(new Error("Receipt retrieval failed")); await expect(provider.waitForTransactionReceipt(MOCK_TRANSACTION_HASH)).rejects.toThrow("Receipt retrieval failed"); }); }); describe("native token operations", () => { it("should transfer native tokens", async () => { viem.parseEther.mockReturnValueOnce(BigInt(1000000000000000000)); const hash = await provider.nativeTransfer(MOCK_ADDRESS_TO, "1.0"); expect(viem.parseEther).toHaveBeenCalledWith("1.0"); expect(mockWalletClient.sendTransaction).toHaveBeenCalled(); expect(hash).toBe(MOCK_TRANSACTION_HASH); }); it("should handle native transfer errors", async () => { mockWalletClient.sendTransaction.mockRejectedValueOnce(new Error("Transaction failed")); await expect(provider.nativeTransfer(MOCK_ADDRESS_TO, "1.0")).rejects.toThrow("Transaction failed"); }); it("should handle invalid address in native transfer", async () => { const invalidAddress = "not_a_valid_address"; mockWalletClient.sendTransaction.mockImplementationOnce(() => { throw new Error("Invalid address"); }); await expect(provider.nativeTransfer(invalidAddress, "1.0")).rejects.toThrow(); }); }); describe("contract interactions", () => { it("should read contract data", async () => { const contractParams = { address: MOCK_ADDRESS_TO, abi: [], functionName: "balanceOf", args: [MOCK_ADDRESS], }; const result = await provider.readContract(contractParams); expect(mockPublicClient.readContract).toHaveBeenCalledWith(contractParams); expect(result).toBe("mock_result"); }); it("should handle contract read errors", async () => { mockPublicClient.readContract.mockRejectedValueOnce(new Error("Contract read failed")); const contractParams = { address: MOCK_ADDRESS_TO, abi: [], functionName: "balanceOf", args: [MOCK_ADDRESS], }; await expect(provider.readContract(contractParams)).rejects.toThrow("Contract read failed"); }); it("should handle invalid ABI in contract read", async () => { mockPublicClient.readContract.mockRejectedValueOnce(new TypeError("Invalid ABI format")); const contractParams = { address: MOCK_ADDRESS_TO, abi: "invalid abi", functionName: "balanceOf", args: [MOCK_ADDRESS], }; await expect(provider.readContract(contractParams)).rejects.toThrow("Invalid ABI format"); }); }); });