diff --git a/packages/agent-sdk/src/evm/client.ts b/packages/agent-sdk/src/evm/client.ts new file mode 100644 index 0000000..5623ae3 --- /dev/null +++ b/packages/agent-sdk/src/evm/client.ts @@ -0,0 +1,23 @@ +import { createPublicClient, http, PublicClient } from "viem"; +import * as chains from "viem/chains"; + +type Chain = chains.Chain; + +const CHAINS_BY_CHAIN_ID = Object.fromEntries( + Object.values(chains).map((chain) => [chain.id, chain]), +); + +const getChainById = (chainId: number): Chain | undefined => { + return CHAINS_BY_CHAIN_ID[chainId]; +}; + +export function getClientForChain(chainId: number): PublicClient { + const chain = getChainById(chainId); + if (!chain) { + throw new Error(`Chain with ID ${chainId} not found`); + } + return createPublicClient({ + chain, + transport: http(chain.rpcUrls.default.http[0]), + }); +} diff --git a/packages/agent-sdk/src/evm/erc20.ts b/packages/agent-sdk/src/evm/erc20.ts index 342ecf3..f93c90a 100644 --- a/packages/agent-sdk/src/evm/erc20.ts +++ b/packages/agent-sdk/src/evm/erc20.ts @@ -1,7 +1,8 @@ import { erc20Abi } from "viem"; import { encodeFunctionData, type Address } from "viem"; -import { getClient, type MetaTransaction } from "near-safe"; +import type { MetaTransaction } from "near-safe"; import type { TokenInfo } from "./types"; +import { getClientForChain } from "./client"; const NATIVE_ASSET = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; const MAX_APPROVAL = BigInt( @@ -49,7 +50,7 @@ export async function checkAllowance( spender: Address, chainId: number, ): Promise { - return getClient(chainId).readContract({ + return getClientForChain(chainId).readContract({ address: token, abi: erc20Abi, functionName: "allowance", @@ -85,7 +86,7 @@ export async function getTokenDecimals( chainId: number, address: Address, ): Promise { - const client = getClient(chainId); + const client = getClientForChain(chainId); try { return await client.readContract({ address, @@ -101,7 +102,7 @@ export async function getTokenSymbol( chainId: number, address: Address, ): Promise { - const client = getClient(chainId); + const client = getClientForChain(chainId); try { return await client.readContract({ address, diff --git a/packages/agent-sdk/src/evm/safe.ts b/packages/agent-sdk/src/evm/safe.ts index ed625ba..5ecaad1 100644 --- a/packages/agent-sdk/src/evm/safe.ts +++ b/packages/agent-sdk/src/evm/safe.ts @@ -1,7 +1,7 @@ import { type Address, checksumAddress, parseUnits } from "viem"; import { type UserToken, ZerionAPI } from "zerion-sdk"; import { scientificToDecimal } from "../misc"; -import { getClient } from "near-safe"; +import { getClientForChain } from "./client"; export interface TokenBalance { tokenAddress: string | null; // null for native token @@ -54,7 +54,7 @@ export async function getSafeBalances( address: Address, zerionKey?: string, ): Promise { - const client = await getClient(chainId); + const client = await getClientForChain(chainId); const codeAt = await client.getCode({ address }); if (!codeAt) { // Not a Safe - Get balances from Zerion. diff --git a/packages/agent-sdk/tests/evm/erc20.spec.ts b/packages/agent-sdk/tests/evm/erc20.spec.ts index cd59f53..64f3a5e 100644 --- a/packages/agent-sdk/tests/evm/erc20.spec.ts +++ b/packages/agent-sdk/tests/evm/erc20.spec.ts @@ -1,5 +1,4 @@ import { type Address, erc20Abi } from "viem"; -import { getClient } from "near-safe"; import { erc20Transfer, erc20Approve, @@ -8,9 +7,12 @@ import { getTokenDecimals, getTokenSymbol, } from "../../src"; +import { getClientForChain } from "../../src/evm/client"; // Mock the external dependencies -jest.mock("near-safe"); +jest.mock("../../src/evm/client", () => ({ + getClientForChain: jest.fn(), +})); describe("ERC20 Utilities", () => { const mockAddress = "0x1234567890123456789012345678901234567890" as Address; @@ -67,7 +69,7 @@ describe("ERC20 Utilities", () => { const mockClient = { readContract: jest.fn().mockResolvedValue(BigInt(1000)), }; - (getClient as jest.Mock).mockReturnValue(mockClient); + (getClientForChain as jest.Mock).mockReturnValue(mockClient); const result = await checkAllowance( mockAddress, @@ -94,7 +96,7 @@ describe("ERC20 Utilities", () => { .mockResolvedValueOnce(18) // decimals .mockResolvedValueOnce("TEST"), // symbol }; - (getClient as jest.Mock).mockReturnValue(mockClient); + (getClientForChain as jest.Mock).mockReturnValue(mockClient); const result = await getTokenInfo(mockChainId, mockAddress); @@ -111,7 +113,7 @@ describe("ERC20 Utilities", () => { const mockClient = { readContract: jest.fn().mockResolvedValue(18), }; - (getClient as jest.Mock).mockReturnValue(mockClient); + (getClientForChain as jest.Mock).mockReturnValue(mockClient); const result = await getTokenDecimals(mockChainId, mockAddress); @@ -122,7 +124,7 @@ describe("ERC20 Utilities", () => { const mockClient = { readContract: jest.fn().mockRejectedValue(new Error("Test error")), }; - (getClient as jest.Mock).mockReturnValue(mockClient); + (getClientForChain as jest.Mock).mockReturnValue(mockClient); await expect(getTokenDecimals(mockChainId, mockAddress)).rejects.toThrow( "Error fetching token decimals: Error: Test error", @@ -135,7 +137,7 @@ describe("ERC20 Utilities", () => { const mockClient = { readContract: jest.fn().mockResolvedValue("TEST"), }; - (getClient as jest.Mock).mockReturnValue(mockClient); + (getClientForChain as jest.Mock).mockReturnValue(mockClient); const result = await getTokenSymbol(mockChainId, mockAddress); @@ -146,7 +148,7 @@ describe("ERC20 Utilities", () => { const mockClient = { readContract: jest.fn().mockRejectedValue(new Error("Test error")), }; - (getClient as jest.Mock).mockReturnValue(mockClient); + (getClientForChain as jest.Mock).mockReturnValue(mockClient); await expect(getTokenSymbol(mockChainId, mockAddress)).rejects.toThrow( "Error fetching token decimals: Error: Test error",