@coinbase/agentkit
Version:
Coinbase AgentKit core primitives
228 lines (227 loc) • 10.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cdpApiActionProvider_1 = require("./cdpApiActionProvider");
const schemas_1 = require("./schemas");
// Mock the CDP SDK
jest.mock("@coinbase/cdp-sdk");
describe("CDP API Action Provider", () => {
let actionProvider;
let mockWalletProvider;
let mockCdpClient;
beforeEach(() => {
jest.clearAllMocks();
const mockAccount = {
swap: jest.fn(),
};
mockCdpClient = {
evm: {
requestFaucet: jest.fn(),
getAccount: jest.fn(),
},
solana: {
requestFaucet: jest.fn(),
},
};
// Set up default mock behavior
mockCdpClient.evm.getAccount.mockResolvedValue(mockAccount);
mockWalletProvider = {
getNetwork: jest.fn(),
getAddress: jest.fn(),
getClient: jest.fn(),
};
actionProvider = new cdpApiActionProvider_1.CdpApiActionProvider();
});
describe("initialization", () => {
it("should initialize with correct provider name", () => {
expect(actionProvider.name).toBe("cdp_api");
});
it("should support all networks", () => {
const mockNetwork = { protocolFamily: "evm", networkId: "base-sepolia" };
expect(actionProvider.supportsNetwork(mockNetwork)).toBe(true);
});
});
describe("faucet", () => {
it("should request faucet funds on base-sepolia", async () => {
const mockNetwork = { protocolFamily: "evm", networkId: "base-sepolia" };
mockWalletProvider.getNetwork.mockReturnValue(mockNetwork);
mockWalletProvider.getAddress.mockReturnValue("0x123456789");
mockWalletProvider.getClient.mockReturnValue(mockCdpClient);
mockCdpClient.evm.requestFaucet.mockResolvedValue({
transactionHash: "0xabcdef123456",
});
const result = await actionProvider.faucet(mockWalletProvider, { assetId: "eth" });
expect(mockCdpClient.evm.requestFaucet).toHaveBeenCalledWith({
address: "0x123456789",
token: "eth",
network: "base-sepolia",
});
expect(result).toContain("Received eth from the faucet");
expect(result).toContain("0xabcdef123456");
});
it("should request faucet funds on solana-devnet", async () => {
const mockNetwork = { protocolFamily: "svm", networkId: "solana-devnet" };
mockWalletProvider.getNetwork.mockReturnValue(mockNetwork);
mockWalletProvider.getAddress.mockReturnValue("address123");
mockWalletProvider.getClient.mockReturnValue(mockCdpClient);
mockCdpClient.solana.requestFaucet.mockResolvedValue({
signature: "signature123",
});
const result = await actionProvider.faucet(mockWalletProvider, { assetId: "sol" });
expect(mockCdpClient.solana.requestFaucet).toHaveBeenCalledWith({
address: "address123",
token: "sol",
});
expect(result).toContain("Received sol from the faucet");
expect(result).toContain("signature123");
});
it("should throw error for unsupported EVM network", async () => {
const mockNetwork = { protocolFamily: "evm", networkId: "ethereum-mainnet" };
mockWalletProvider.getNetwork.mockReturnValue(mockNetwork);
mockWalletProvider.getClient.mockReturnValue(mockCdpClient);
await expect(actionProvider.faucet(mockWalletProvider, { assetId: "eth" })).rejects.toThrow("Faucet is only supported on 'base-sepolia' or 'ethereum-sepolia' evm networks");
});
it("should throw error for unsupported Solana network", async () => {
const mockNetwork = { protocolFamily: "svm", networkId: "solana-mainnet" };
mockWalletProvider.getNetwork.mockReturnValue(mockNetwork);
mockWalletProvider.getClient.mockReturnValue(mockCdpClient);
await expect(actionProvider.faucet(mockWalletProvider, { assetId: "sol" })).rejects.toThrow("Faucet is only supported on 'solana-devnet' solana networks");
});
it("should throw error for wallet provider without client", async () => {
const mockWalletWithoutClient = {
getNetwork: jest.fn().mockReturnValue({ protocolFamily: "evm", networkId: "base-sepolia" }),
};
await expect(actionProvider.faucet(mockWalletWithoutClient, { assetId: "eth" })).rejects.toThrow("Wallet provider is not a CDP Wallet Provider");
});
});
describe("swap", () => {
it("should perform swap on base-sepolia", async () => {
const mockNetwork = { protocolFamily: "evm", networkId: "base-sepolia" };
mockWalletProvider.getNetwork.mockReturnValue(mockNetwork);
mockWalletProvider.getAddress.mockReturnValue("0x123456789");
mockWalletProvider.getClient.mockReturnValue(mockCdpClient);
const mockAccount = { swap: jest.fn() };
mockCdpClient.evm.getAccount.mockResolvedValue(mockAccount);
mockAccount.swap.mockResolvedValue({
transactionHash: "0xswap123456",
});
const result = await actionProvider.swap(mockWalletProvider, {
fromAssetId: "eth",
toAssetId: "usdc",
amount: "0.1",
});
expect(mockCdpClient.evm.getAccount).toHaveBeenCalledWith({
address: "0x123456789",
});
expect(mockAccount.swap).toHaveBeenCalledWith({
network: "base-sepolia",
from: "eth",
to: "usdc",
amount: "0.1",
slippageBps: 100,
});
expect(result).toContain("Successfully swapped 0.1 ETH to USDC");
expect(result).toContain("0xswap123456");
});
it("should perform swap on base-mainnet", async () => {
const mockNetwork = { protocolFamily: "evm", networkId: "base-mainnet" };
mockWalletProvider.getNetwork.mockReturnValue(mockNetwork);
mockWalletProvider.getAddress.mockReturnValue("0x123456789");
mockWalletProvider.getClient.mockReturnValue(mockCdpClient);
const mockAccount = { swap: jest.fn() };
mockCdpClient.evm.getAccount.mockResolvedValue(mockAccount);
mockAccount.swap.mockResolvedValue({
transactionHash: "0xswap789",
});
const result = await actionProvider.swap(mockWalletProvider, {
fromAssetId: "usdc",
toAssetId: "eth",
amount: "100",
});
expect(mockAccount.swap).toHaveBeenCalledWith({
network: "base", // Should convert base-mainnet to base
from: "usdc",
to: "eth",
amount: "100",
slippageBps: 100,
});
expect(result).toContain("Successfully swapped 100 USDC to ETH");
});
it("should throw error for non-EVM networks", async () => {
const mockNetwork = { protocolFamily: "svm", networkId: "solana-devnet" };
mockWalletProvider.getNetwork.mockReturnValue(mockNetwork);
mockWalletProvider.getClient.mockReturnValue(mockCdpClient);
await expect(actionProvider.swap(mockWalletProvider, {
fromAssetId: "sol",
toAssetId: "usdc",
amount: "1",
})).rejects.toThrow("Swap is currently only supported on EVM networks");
});
it("should throw error for wallet provider without client", async () => {
const mockWalletWithoutClient = {
getNetwork: jest.fn().mockReturnValue({ protocolFamily: "evm", networkId: "base-sepolia" }),
};
await expect(actionProvider.swap(mockWalletWithoutClient, {
fromAssetId: "eth",
toAssetId: "usdc",
amount: "0.1",
})).rejects.toThrow("Wallet provider is not a CDP Wallet Provider");
});
it("should handle swap errors", async () => {
const mockNetwork = { protocolFamily: "evm", networkId: "base-sepolia" };
mockWalletProvider.getNetwork.mockReturnValue(mockNetwork);
mockWalletProvider.getAddress.mockReturnValue("0x123456789");
mockWalletProvider.getClient.mockReturnValue(mockCdpClient);
const mockAccount = { swap: jest.fn() };
mockCdpClient.evm.getAccount.mockResolvedValue(mockAccount);
mockAccount.swap.mockRejectedValue(new Error("Insufficient liquidity"));
await expect(actionProvider.swap(mockWalletProvider, {
fromAssetId: "eth",
toAssetId: "usdc",
amount: "1000",
})).rejects.toThrow("Swap failed: Error: Insufficient liquidity");
});
});
describe("RequestFaucetFundsV2Schema", () => {
it("should validate correct input", () => {
const validInput = { assetId: "eth" };
const result = schemas_1.RequestFaucetFundsV2Schema.safeParse(validInput);
expect(result.success).toBe(true);
});
it("should allow missing assetId", () => {
const validInput = {};
const result = schemas_1.RequestFaucetFundsV2Schema.safeParse(validInput);
expect(result.success).toBe(true);
});
});
describe("SwapSchema", () => {
it("should validate correct swap input", () => {
const validInput = {
fromAssetId: "eth",
toAssetId: "usdc",
amount: "0.1",
};
const result = schemas_1.SwapSchema.safeParse(validInput);
expect(result.success).toBe(true);
expect(result.data).toEqual(validInput);
});
it("should validate swap input with network", () => {
const validInput = {
fromAssetId: "usdc",
toAssetId: "eth",
amount: "100",
network: "base-sepolia",
};
const result = schemas_1.SwapSchema.safeParse(validInput);
expect(result.success).toBe(true);
expect(result.data).toEqual(validInput);
});
it("should fail validation when missing required fields", () => {
const invalidInput = {
fromAssetId: "eth",
// missing toAssetId and amount
};
const result = schemas_1.SwapSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
});
});
});