@agentek/tools
Version:
Blockchain tools for AI agents
96 lines • 3.76 kB
JavaScript
import { z } from "zod";
import { createTool } from "../client.js";
import { encodeFunctionData, erc20Abi, parseUnits, } from "viem";
import { erc20Chains } from "./constants.js";
const intentApproveParameters = z.object({
token: z.string().describe("The token address"),
amount: z.string().describe("The amount to approve"),
spender: z.string().describe("The spender address to approve"),
chainId: z.number().optional().describe("Optional specific chain to use"),
});
export const ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
const getTokenDecimals = async (publicClient, token) => {
if (token == ETH_ADDRESS) {
return 18;
}
else {
return await publicClient.readContract({
abi: erc20Abi,
functionName: "decimals",
address: token,
});
}
};
export const intentApproveTool = createTool({
name: "intentApprove",
description: "Creates an intent to approve token spending",
supportedChains: erc20Chains,
parameters: intentApproveParameters,
execute: async (client, args) => {
const { token, amount, spender, chainId } = args;
const chains = client.filterSupportedChains(erc20Chains, chainId);
const from = await client.getAddress();
if (token === ETH_ADDRESS) {
throw new Error("Cannot approve ETH, only ERC20 tokens");
}
const chainsInfo = (await Promise.all(chains.map(async (chain) => {
try {
const publicClient = client.getPublicClient(chain.id);
const decimals = await getTokenDecimals(publicClient, token);
const amountBigInt = parseUnits(amount, decimals);
return {
chain,
amount: amountBigInt,
decimals,
};
}
catch (error) {
console.error(`Error processing chain ${chain.id}:`, error);
return undefined;
}
}))).filter((chainInfo) => chainInfo !== undefined);
let executionChain;
if (chainsInfo.length === 0) {
throw new Error("No supported chains found with valid token information");
}
else {
executionChain = await Promise.all(chainsInfo.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(executionChain.chain.id);
const ops = [
{
target: token,
value: "0",
data: encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [spender, executionChain.amount],
}),
},
];
if (!walletClient) {
return {
intent: `approve ${amount.toString()} ${token} for spender ${spender}`,
ops,
chain: executionChain.chain.id,
};
}
else {
const hash = await client.executeOps(ops, executionChain.chain.id);
return {
intent: `Approve ${amount.toString()} ${token} from ${from} for spender ${spender}`,
ops,
chain: executionChain.chain.id,
hash: hash,
};
}
},
});
//# sourceMappingURL=intents.js.map