UNPKG

@agentek/tools

Version:

Blockchain tools for AI agents

219 lines 9.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.intentTransferFromTool = exports.intentTransferTool = exports.ETH_ADDRESS = void 0; const zod_1 = require("zod"); const client_js_1 = require("../client.js"); const chains_1 = require("viem/chains"); const viem_1 = require("viem"); const utils_js_1 = require("../utils.js"); const tools_js_1 = require("../ens/tools.js"); const intentTransferChains = [chains_1.mainnet, chains_1.arbitrum, chains_1.base, chains_1.sepolia]; const intentTransferParameters = zod_1.z.object({ token: utils_js_1.addressSchema.describe("The token address"), amount: zod_1.z.string().describe("The amount to transfer"), to: zod_1.z.string().describe("The recipient address or ENS name"), chainId: zod_1.z.number().optional().describe("Optional specify chainId to use"), }); exports.ETH_ADDRESS = "0x0000000000000000000000000000000000000000"; const getTokenDecimals = async (publicClient, token) => { if (token == exports.ETH_ADDRESS) { return 18; } else { return await publicClient.readContract({ abi: viem_1.erc20Abi, functionName: "decimals", address: token, }); } }; const getTokenBalance = async (publicClient, token, from) => { if (token == exports.ETH_ADDRESS) { return publicClient.getBalance({ address: from, }); } else { return await publicClient.readContract({ abi: viem_1.erc20Abi, functionName: "balanceOf", address: token, args: [from], }); } }; exports.intentTransferTool = (0, client_js_1.createTool)({ name: "intentTransfer", description: "Creates an intent to transfer tokens", supportedChains: intentTransferChains, parameters: intentTransferParameters, execute: async (client, args) => { let { token, amount, to, chainId } = args; const chains = client.filterSupportedChains(intentTransferChains, chainId); const from = await client.getAddress(); // if `to` is an ENS name, resolve it to an address if (to.includes('.')) { to = await tools_js_1.resolveENSTool.execute(client, { name: to, }).catch(() => { throw new Error(`Failed to resolve ENS name ${to}`); }); } const chainsWithBalance = (await Promise.all(chains.map(async (chain) => { const publicClient = client.getPublicClient(chain.id); const decimals = await getTokenDecimals(publicClient, token); const balance = await getTokenBalance(publicClient, token, from); const amountBigInt = (0, viem_1.parseUnits)(amount, decimals); if (balance >= amountBigInt) { return { chain, balance, amount: amountBigInt, decimals, }; } return null; }))).filter((chain) => chain !== null); if (chainsWithBalance.length === 0) { throw new Error(`${from} doesn't have enough ${token} balance on any of the supported chains - ${chains.map((chain) => chain.name)}`); } const cheapestChain = await Promise.all(chainsWithBalance.map(async (chainInfo) => { const publicClient = client.getPublicClient(chainInfo.chain.id); const gasPrice = await publicClient.getGasPrice(); return { ...chainInfo, gasPrice, }; })).then((chains) => chains.reduce((cheapest, current) => current.gasPrice < cheapest.gasPrice ? current : cheapest)); const walletClient = client.getWalletClient(cheapestChain.chain.id); let ops = []; let tokenSymbol = ""; if (token === exports.ETH_ADDRESS) { tokenSymbol = "ETH"; ops.push({ target: to, value: cheapestChain.amount.toString(), data: "0x", }); } else { tokenSymbol = await client.getPublicClient(cheapestChain.chain.id).readContract({ address: token, abi: viem_1.erc20Abi, functionName: "symbol", }); ops.push({ target: token, value: "0", data: (0, viem_1.encodeFunctionData)({ abi: viem_1.erc20Abi, functionName: "transfer", args: [to, cheapestChain.amount], }), }); } if (!walletClient) { return { intent: `send ${amount.toString()} ${tokenSymbol} to ${to}`, ops, chain: cheapestChain.chain.id, }; } else { const hash = await client.executeOps(ops, cheapestChain.chain.id); return { intent: `send ${amount.toString()} ${tokenSymbol} to ${to}`, ops, chain: cheapestChain.chain.id, hash: hash, }; } }, }); const intentTransferFromParameters = zod_1.z.object({ token: zod_1.z.string().describe("The token address"), amount: zod_1.z.string().describe("The amount to transfer"), from: zod_1.z.string().describe("The address to transfer from"), to: zod_1.z.string().describe("The recipient address or ENS"), chainId: zod_1.z.number().optional().describe("Optional specific chain to use"), }); const intentTransferFromChains = [chains_1.mainnet, chains_1.arbitrum, chains_1.base, chains_1.sepolia]; exports.intentTransferFromTool = (0, client_js_1.createTool)({ name: "intentTransferFrom", description: "Creates an intent to transfer tokens from another address", supportedChains: intentTransferFromChains, parameters: intentTransferFromParameters, execute: async (client, args) => { let { token, amount, to, from, chainId } = args; const chains = client.filterSupportedChains(intentTransferChains, chainId); // if `to` is an ENS name, resolve it to an address if (to.includes('.')) { to = await tools_js_1.resolveENSTool.execute(client, { name: to, }).catch(() => { throw new Error(`Failed to resolve ENS name ${to}`); }); } const chainsWithBalance = (await Promise.all(chains.map(async (chain) => { const publicClient = client.getPublicClient(chain.id); const decimals = await getTokenDecimals(publicClient, token); const balance = await getTokenBalance(publicClient, token, from); const amountBigInt = (0, viem_1.parseUnits)(amount, decimals); if (balance >= amountBigInt) { return { chain, balance, amount: amountBigInt, decimals, }; } return null; }))).filter((chain) => chain !== null); if (chainsWithBalance.length === 0) { throw new Error(`${from} doesn't have enough ${token} balance on any of the supported chains - ${chains.map((chain) => chain.name)}`); } const cheapestChain = await Promise.all(chainsWithBalance.map(async (chainInfo) => { const publicClient = client.getPublicClient(chainInfo.chain.id); const gasPrice = await publicClient.getGasPrice(); return { ...chainInfo, gasPrice, }; })).then((chains) => chains.reduce((cheapest, current) => current.gasPrice < cheapest.gasPrice ? current : cheapest)); const publicClient = client.getPublicClient(cheapestChain.chain.id); const walletClient = client.getWalletClient(cheapestChain.chain.id); let ops = []; if (token === exports.ETH_ADDRESS) { // @TODO gracefully fallback to weth if eth mentioned throw new Error("ETH transferFrom not supported"); } else { ops.push({ target: token, value: "0", data: (0, viem_1.encodeFunctionData)({ abi: viem_1.erc20Abi, functionName: "transferFrom", args: [from, to, cheapestChain.amount], }), }); } if (!walletClient) { return { intent: `send ${amount.toString()} ${token} from ${from} to ${to}`, ops, chain: cheapestChain.chain.id, }; } else { const hash = await client.executeOps(ops, cheapestChain.chain.id); return { intent: `send ${amount.toString()} ${token} from ${from} to ${to}`, ops, chain: cheapestChain.chain.id, hash: hash, }; } }, }); //# sourceMappingURL=intents.js.map