UNPKG

@coinbase/agentkit

Version:

Coinbase AgentKit core primitives

303 lines (302 loc) 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const yelayActionProvider_1 = require("./yelayActionProvider"); const constants_1 = require("./constants"); const viem_1 = require("viem"); const mockFetchResult = (status, data) => { return { json: async () => data, status, ok: status >= 200 && status < 400, }; }; const MOCK_VAULT_ADDRESS = "0x1234567890123456789012345678901234567890"; const MOCK_WHOLE_ASSETS = "1"; const MOCK_RECEIVER_ID = "0x9876543210987654321098765432109876543210"; const MOCK_TX_HASH = "0xabcdef1234567890"; const MOCK_RECEIPT = { status: 1, blockNumber: 1234567 }; const MOCK_DECIMALS = 18; const BASE_CHAIN_ID = "8453"; const mockVaults = [ { address: MOCK_VAULT_ADDRESS, name: "Base WETH Vault", decimals: 18, chainId: BASE_CHAIN_ID, underlying: "0x123...", }, { address: "0x456...", name: "Base USDC Vault", decimals: 6, chainId: BASE_CHAIN_ID, underlying: "0x456...", }, ]; const mockAPYs = [ { vault: MOCK_VAULT_ADDRESS, startBlock: 1000, finishBlock: 2000, startTimestamp: 1234567890, finishTimestamp: 1234667890, yield: "100", apy: "3.4", }, { vault: "0x456...", startBlock: 1000, finishBlock: 2000, startTimestamp: 1234567890, finishTimestamp: 1234667890, yield: "50", apy: "5.2", }, ]; const mockClaimProof = [ { yelayLiteVault: MOCK_VAULT_ADDRESS, projectId: constants_1.RETAIL_POOL_ID, cycle: 1, yieldSharesTotal: "100", proof: ["0x1234567890123456789012345678901234567890123456789012345678901234"], // 32 bites string, }, ]; describe("YelayActionProvider", () => { const provider = new yelayActionProvider_1.YelayActionProvider(); let mockWallet; let mockedFetch; const originalFetch = global.fetch; beforeAll(() => { global.fetch = mockedFetch = jest.fn(); }); afterAll(() => { global.fetch = originalFetch; }); beforeEach(() => { mockWallet = { getAddress: jest.fn().mockReturnValue(MOCK_RECEIVER_ID), getNetwork: jest.fn().mockReturnValue({ protocolFamily: "evm", chainId: BASE_CHAIN_ID }), sendTransaction: jest.fn().mockResolvedValue(MOCK_TX_HASH), waitForTransactionReceipt: jest.fn().mockResolvedValue(MOCK_RECEIPT), readContract: jest.fn().mockResolvedValue(MOCK_DECIMALS), }; }); describe("network support", () => { it("should support the protocol EVM family", () => { expect(provider.supportsNetwork({ protocolFamily: "evm", chainId: "8453", })).toBe(true); }); it("should support the Base, Mainnet, Sonic, Arbitrum and Avalanche networks", () => { const networks = ["1", "146", "8453", "42161", "43114"]; networks.forEach(network => { expect(provider.supportsNetwork({ protocolFamily: "evm", chainId: network, })).toBe(true); }); }); it("should not support other networks", () => { expect(provider.supportsNetwork({ protocolFamily: "evm", chainId: "10", })).toBe(false); }); it("should not support other protocol families", () => { expect(provider.supportsNetwork({ protocolFamily: "other-protocol-family", chainId: "8453", })).toBe(false); }); }); describe("getVaults action", () => { it("returns list of vaults with their APYs", async () => { mockedFetch .mockResolvedValueOnce(mockFetchResult(200, mockVaults)) .mockResolvedValueOnce(mockFetchResult(200, mockAPYs)); const result = await provider.getVaults(mockWallet); expect(result).toBe(` Base WETH Vault: Address: ${MOCK_VAULT_ADDRESS} APY: 3.4% ---------------- Base USDC Vault: Address: 0x456... APY: 5.2% `); }); it("returns error message when vaults API fails", async () => { mockedFetch.mockResolvedValue(mockFetchResult(500, mockVaults)); const result = await provider.getVaults(mockWallet); expect(result).toContain(`Error fetching vault data:`); }); it("returns error message when APY API fails", async () => { mockedFetch.mockResolvedValue(mockFetchResult(500, mockAPYs)); const result = await provider.getVaults(mockWallet); expect(result).toContain(`Error fetching vault data:`); }); }); describe("deposit action", () => { it("should deposit assets into a specified Yelay Vault", async () => { const args = { assets: MOCK_WHOLE_ASSETS, vaultAddress: MOCK_VAULT_ADDRESS, }; mockedFetch.mockResolvedValueOnce(mockFetchResult(200, mockVaults)); const atomicAssets = (0, viem_1.parseEther)(MOCK_WHOLE_ASSETS); const response = await provider.deposit(mockWallet, args); expect(mockWallet.sendTransaction).toHaveBeenCalledWith({ to: args.vaultAddress, data: (0, viem_1.encodeFunctionData)({ abi: constants_1.YELAY_VAULT_ABI, functionName: "deposit", args: [atomicAssets, constants_1.RETAIL_POOL_ID, MOCK_RECEIVER_ID], }), }); expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledWith(MOCK_TX_HASH); expect(response).toContain(`Deposited ${MOCK_WHOLE_ASSETS}`); expect(response).toContain(MOCK_TX_HASH); }); it("should return error message when deposit fails", async () => { mockedFetch.mockResolvedValueOnce(mockFetchResult(200, mockVaults)); mockWallet.sendTransaction.mockRejectedValue(new Error("Deposit failed")); const args = { assets: MOCK_WHOLE_ASSETS, vaultAddress: MOCK_VAULT_ADDRESS, }; const response = await provider.deposit(mockWallet, args); expect(response).toContain("Deposit failed"); }); }); describe("redeem action", () => { it("should redeem assets from a specified Yelay Vault", async () => { const args = { assets: MOCK_WHOLE_ASSETS, vaultAddress: MOCK_VAULT_ADDRESS, }; mockedFetch.mockResolvedValueOnce(mockFetchResult(200, mockVaults)); const response = await provider.redeem(mockWallet, args); const atomicAssets = (0, viem_1.parseEther)(MOCK_WHOLE_ASSETS); expect(mockWallet.sendTransaction).toHaveBeenCalledWith({ to: args.vaultAddress, data: (0, viem_1.encodeFunctionData)({ abi: constants_1.YELAY_VAULT_ABI, functionName: "redeem", args: [atomicAssets, constants_1.RETAIL_POOL_ID, MOCK_RECEIVER_ID], }), }); expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledWith(MOCK_TX_HASH); expect(response).toContain(`Redeemed ${MOCK_WHOLE_ASSETS}`); expect(response).toContain(MOCK_TX_HASH); }); it("should return error message when redeem fails", async () => { mockedFetch.mockResolvedValueOnce(mockFetchResult(200, mockVaults)); mockWallet.sendTransaction.mockRejectedValue(new Error("Redeem failed")); const args = { assets: MOCK_WHOLE_ASSETS, vaultAddress: MOCK_VAULT_ADDRESS, }; const response = await provider.redeem(mockWallet, args); expect(response).toContain("Redeem failed"); }); }); describe("claim action", () => { it("should claim yield from a specified Yelay Vault", async () => { const args = { vaultAddress: MOCK_VAULT_ADDRESS, }; mockedFetch.mockResolvedValue(mockFetchResult(200, mockClaimProof)); const response = await provider.claim(mockWallet, args); expect(mockWallet.sendTransaction).toHaveBeenCalledWith({ to: constants_1.CONTRACTS_BY_CHAIN[BASE_CHAIN_ID].YieldExtractor, data: (0, viem_1.encodeFunctionData)({ abi: constants_1.YIELD_EXTRACTOR_ABI, functionName: "claim", args: [mockClaimProof], }), }); expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledWith(MOCK_TX_HASH); expect(response).toContain(`Claimed ${mockClaimProof[0].yieldSharesTotal}`); expect(response).toContain(`Yelay Vault ${MOCK_VAULT_ADDRESS}`); expect(response).toContain(MOCK_TX_HASH); }); it("should return error message when claim fails", async () => { mockWallet.sendTransaction.mockRejectedValue(new Error("Claim failed")); const args = { vaultAddress: MOCK_VAULT_ADDRESS, }; const response = await provider.claim(mockWallet, args); expect(response).toContain("Claim failed"); }); }); describe("balance action", () => { it("should get balance from a specified Yelay Vault", async () => { const args = { vaultAddress: MOCK_VAULT_ADDRESS, }; const balance = BigInt("1000000000000000000"); // 1 ETH in wei const yieldSharesClaimed = BigInt("50"); const expectedBalanceInWholeUnits = (0, viem_1.formatUnits)(balance, MOCK_DECIMALS); mockWallet.readContract .mockResolvedValueOnce(balance) .mockResolvedValueOnce(yieldSharesClaimed); mockedFetch .mockResolvedValueOnce(mockFetchResult(200, mockVaults)) .mockResolvedValueOnce(mockFetchResult(200, mockClaimProof)); const response = await provider.getBalance(mockWallet, args); expect(response).toContain(`User balance from Yelay Vault ${MOCK_VAULT_ADDRESS}: ${expectedBalanceInWholeUnits}`); expect(response).toContain(`Yield shares generated: ${mockClaimProof[0].yieldSharesTotal}`); expect(response).toContain(`Yield shares claimed: ${yieldSharesClaimed}`); }); it("should return only user balance when there is no claimable yield", async () => { const balance = BigInt("1000000000000000000"); // 1 ETH in wei const expectedBalanceInWholeUnits = (0, viem_1.formatUnits)(balance, MOCK_DECIMALS); mockWallet.readContract.mockResolvedValueOnce(balance); mockedFetch .mockResolvedValueOnce(mockFetchResult(200, mockVaults)) .mockResolvedValueOnce(mockFetchResult(200, [])); const args = { vaultAddress: MOCK_VAULT_ADDRESS, }; const response = await provider.getBalance(mockWallet, args); expect(response).toContain(`User balance from Yelay Vault ${MOCK_VAULT_ADDRESS}: ${expectedBalanceInWholeUnits}`); }); it("should return error message when vault doesn't match", async () => { mockedFetch.mockResolvedValueOnce(mockFetchResult(200, [])); const args = { vaultAddress: MOCK_VAULT_ADDRESS, }; const response = await provider.getBalance(mockWallet, args); expect(response).toContain("Vault not found"); }); it("should return error message when balance fails", async () => { mockedFetch.mockResolvedValueOnce(mockFetchResult(200, mockVaults)); mockWallet.readContract.mockRejectedValue(new Error("Balance failed")); const args = { vaultAddress: MOCK_VAULT_ADDRESS, }; const response = await provider.getBalance(mockWallet, args); expect(response).toContain("Balance failed"); }); it("should return error message when claim proof fails", async () => { mockedFetch.mockResolvedValueOnce(mockFetchResult(200, mockVaults)); mockedFetch.mockResolvedValue(mockFetchResult(500, mockClaimProof)); const args = { vaultAddress: MOCK_VAULT_ADDRESS, }; const response = await provider.getBalance(mockWallet, args); expect(response).toContain("Claim proof failed"); }); it("should return error message when yield shares claimed fails", async () => { mockedFetch.mockResolvedValueOnce(mockFetchResult(200, mockVaults)); mockWallet.readContract.mockRejectedValue(new Error("Yield shares claimed failed")); const args = { vaultAddress: MOCK_VAULT_ADDRESS, }; const response = await provider.getBalance(mockWallet, args); expect(response).toContain("Yield shares claimed failed"); }); }); });