UNPKG

@coinbase/agentkit

Version:

Coinbase AgentKit core primitives

284 lines (283 loc) 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const cdp_sdk_1 = require("@coinbase/cdp-sdk"); const cdpSmartWalletProvider_1 = require("./cdpSmartWalletProvider"); // ========================================================= // consts // ========================================================= const mockPublicClient = { readContract: jest.fn(), getBalance: jest.fn(), }; // ========================================================= // mocks // ========================================================= jest.mock("../analytics", () => ({ sendAnalyticsEvent: jest.fn().mockImplementation(() => Promise.resolve()), })); jest.mock("../../package.json", () => ({ version: "1.0.0", })); jest.mock("viem", () => { return { createPublicClient: jest.fn(() => mockPublicClient), http: jest.fn(), }; }); jest.mock("../network", () => { return { NETWORK_ID_TO_CHAIN_ID: { "base-mainnet": "8453", "base-sepolia": "84532", }, NETWORK_ID_TO_VIEM_CHAIN: { "base-mainnet": { id: 8453 }, "base-sepolia": { id: 84532 }, }, }; }); // Mock CdpClient jest.mock("@coinbase/cdp-sdk", () => { const MOCK_ADDRESS = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"; const MOCK_SMART_ADDRESS = "0x1234567890123456789012345678901234567890"; const MOCK_SIGNATURE = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b01"; const MOCK_USER_OP_HASH = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"; const mockCreateAccount = jest.fn().mockImplementation(() => Promise.resolve({ address: MOCK_ADDRESS, signMessage: jest.fn().mockResolvedValue(MOCK_SIGNATURE), signTypedData: jest.fn().mockResolvedValue(MOCK_SIGNATURE), })); const mockGetSmartAccount = jest.fn().mockImplementation(() => Promise.resolve({ address: MOCK_SMART_ADDRESS, })); const mockCreateSmartAccount = jest.fn().mockImplementation(() => Promise.resolve({ address: MOCK_SMART_ADDRESS, })); const mockGetOrCreateSmartAccount = jest.fn().mockImplementation(() => Promise.resolve({ address: MOCK_SMART_ADDRESS, })); const mockSendUserOperation = jest .fn() .mockImplementation(async () => ({ userOpHash: MOCK_USER_OP_HASH })); const mockEvmClient = { createAccount: mockCreateAccount, getAccount: jest.fn(), createSmartAccount: mockCreateSmartAccount, getOrCreateSmartAccount: mockGetOrCreateSmartAccount, getSmartAccount: mockGetSmartAccount, sendUserOperation: mockSendUserOperation, }; return { CdpClient: jest.fn().mockImplementation(() => ({ evm: mockEvmClient, })), EvmServerAccount: jest.fn().mockImplementation(() => ({ address: MOCK_ADDRESS, signMessage: jest.fn().mockResolvedValue(MOCK_SIGNATURE), signTypedData: jest.fn().mockResolvedValue(MOCK_SIGNATURE), })), EvmSmartAccount: jest.fn().mockImplementation(() => ({ address: MOCK_SMART_ADDRESS, })), }; }); // ========================================================= // test constants // ========================================================= const MOCK_ADDRESS = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"; const MOCK_SMART_ADDRESS = "0x1234567890123456789012345678901234567890"; const MOCK_NETWORK_ID = "base-sepolia"; const MOCK_USER_OP_HASH = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"; const MOCK_SIGNATURE = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b01"; const MOCK_BALANCE = 1000000000000000000n; const MOCK_NETWORK = { protocolFamily: "evm", networkId: MOCK_NETWORK_ID, chainId: "84532", }; describe("CdpSmartWalletProvider", () => { let provider; let mockCdpClient; let mockSmartAccount; let mockOwnerAccount; beforeEach(async () => { jest.clearAllMocks(); mockCdpClient = new cdp_sdk_1.CdpClient({ apiKeyId: "test-key-id", apiKeySecret: "test-key-secret", walletSecret: "test-wallet-secret", }); mockOwnerAccount = { address: MOCK_ADDRESS, sign: jest.fn().mockResolvedValue(MOCK_SIGNATURE), signMessage: jest.fn().mockResolvedValue(MOCK_SIGNATURE), signTypedData: jest.fn().mockResolvedValue(MOCK_SIGNATURE), }; mockSmartAccount = { address: MOCK_SMART_ADDRESS, }; // Set up the mock smart account for the provider mockCdpClient.evm.createAccount.mockResolvedValue(mockOwnerAccount); mockCdpClient.evm.createSmartAccount.mockResolvedValue(mockSmartAccount); mockPublicClient.readContract.mockResolvedValue("mock_result"); mockPublicClient.getBalance.mockResolvedValue(MOCK_BALANCE); provider = await cdpSmartWalletProvider_1.CdpSmartWalletProvider.configureWithWallet({ apiKeyId: "test-key-id", apiKeySecret: "test-key-secret", walletSecret: "test-wallet-secret", networkId: MOCK_NETWORK_ID, }); }); // ========================================================= // initialization tests // ========================================================= describe("initialization", () => { it("should initialize with API keys", async () => { const provider = await cdpSmartWalletProvider_1.CdpSmartWalletProvider.configureWithWallet({ apiKeyId: "test-key-id", apiKeySecret: "test-key-secret", walletSecret: "test-wallet-secret", networkId: MOCK_NETWORK_ID, }); expect(provider.getAddress()).toBe(MOCK_SMART_ADDRESS); expect(provider.getNetwork()).toEqual(MOCK_NETWORK); }); it("should create smart account with name", async () => { await cdpSmartWalletProvider_1.CdpSmartWalletProvider.configureWithWallet({ apiKeyId: "test-key-id", apiKeySecret: "test-key-secret", walletSecret: "test-wallet-secret", networkId: MOCK_NETWORK_ID, smartAccountName: "MySmartWallet", }); expect(mockCdpClient.evm.getSmartAccount).toHaveBeenCalledWith({ name: "MySmartWallet", owner: mockOwnerAccount, }); }); }); // ========================================================= // basic wallet method tests // ========================================================= describe("basic wallet methods", () => { it("should get the smart wallet address", () => { expect(provider.getAddress()).toBe(MOCK_SMART_ADDRESS); }); it("should get the network", () => { expect(provider.getNetwork()).toEqual(MOCK_NETWORK); }); it("should get the name", () => { expect(provider.getName()).toBe("cdp_smart_wallet_provider"); }); it("should get the balance", async () => { const balance = await provider.getBalance(); expect(balance).toBe(MOCK_BALANCE); expect(mockPublicClient.getBalance).toHaveBeenCalledWith({ address: MOCK_SMART_ADDRESS, }); }); }); // ========================================================= // signing operation tests // ========================================================= describe("signing operations", () => { it("should sign a hash using owner account", async () => { const testHash = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; const signature = await provider.sign(testHash); expect(mockOwnerAccount.sign).toHaveBeenCalledWith({ hash: testHash }); expect(signature).toBe(MOCK_SIGNATURE); }); it("should sign typed data using smart account", async () => { const typedData = { domain: { name: "Example", version: "1", chainId: 1, verifyingContract: "0x1234567890123456789012345678901234567890", }, types: { Person: [ { name: "name", type: "string" }, { name: "wallet", type: "address" }, ], }, primaryType: "Person", message: { name: "Bob", wallet: "0x1234567890123456789012345678901234567890", }, }; // Mock the smart account's signTypedData method mockSmartAccount.signTypedData = jest.fn().mockResolvedValue(MOCK_SIGNATURE); const signature = await provider.signTypedData(typedData); // Verify the smart account was called with the correct parameters expect(mockSmartAccount.signTypedData).toHaveBeenCalledWith({ domain: typedData.domain, types: typedData.types, primaryType: typedData.primaryType, message: typedData.message, network: MOCK_NETWORK.networkId, }); expect(signature).toBe(MOCK_SIGNATURE); }); it("should throw error for direct transaction signing", async () => { const tx = { to: "0x1234567890123456789012345678901234567890", value: BigInt(1000000000000000000), }; await expect(provider.signTransaction(tx)).rejects.toThrow("Direct transaction signing not supported for smart wallets. Use sendTransaction instead."); }); }); // ========================================================= // user operation tests // ========================================================= describe("user operations", () => { it("should send user operations", async () => { const transaction = { to: "0x1234567890123456789012345678901234567890", value: BigInt(1000000000000000000), }; const userOpHash = await provider.sendTransaction(transaction); expect(mockCdpClient.evm.sendUserOperation).toHaveBeenCalledWith({ smartAccount: mockSmartAccount, network: "base-sepolia", calls: [ { to: transaction.to, value: BigInt(transaction.value.toString()), data: "0x", }, ], }); expect(userOpHash).toBe(MOCK_USER_OP_HASH); }); it("should handle native transfers", async () => { const userOpHash = await provider.nativeTransfer("0x1234567890123456789012345678901234567890", "1000000000000000000"); expect(userOpHash).toBe(MOCK_USER_OP_HASH); }); }); // ========================================================= // contract interaction tests // ========================================================= describe("contract interactions", () => { it("should read contract data", async () => { const abi = [ { name: "balanceOf", type: "function", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "balance", type: "uint256" }], stateMutability: "view", }, ]; const result = await provider.readContract({ address: "0x1234567890123456789012345678901234567890", abi, functionName: "balanceOf", args: [MOCK_SMART_ADDRESS], }); expect(result).toBe("mock_result"); expect(mockPublicClient.readContract).toHaveBeenCalled(); }); }); });