@agentek/tools
Version:
Blockchain tools for AI agents
219 lines • 9.09 kB
JavaScript
;
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