@agentek/tools
Version:
Blockchain tools for AI agents
160 lines • 7.02 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMatchSwapTool = void 0;
const zod_1 = require("zod");
const viem_1 = require("viem");
const client_js_1 = require("../client.js");
const chains_1 = require("viem/chains");
/**
* Returns the appropriate 0x aggregator endpoint for a given chainId.
*/
function get0xApiEndpoint(chainId) {
switch (chainId) {
case chains_1.mainnet.id:
return "https://api.0x.org";
case chains_1.optimism.id:
return "https://optimism.api.0x.org";
case chains_1.arbitrum.id:
return "https://arbitrum.api.0x.org";
case chains_1.base.id:
return "https://base.api.0x.org";
default:
throw new Error(`Chain ID ${chainId} is not supported by 0x aggregator (or endpoint not known).`);
}
}
/**
* Helper to handle 'ETH' vs. token addresses.
*/
function normalize(token) {
if (!token)
return "";
if (token.toLowerCase() === "eth")
return "ETH";
return token;
}
const matchaSwapChains = [chains_1.mainnet, chains_1.optimism, chains_1.arbitrum, chains_1.base];
/**
* A tool that performs token swaps across multiple networks (Mainnet, Optimism,
* Arbitrum and Base*) via the 0x/Matcha aggregator.
*
* If a wallet client is available, it will execute the swap immediately.
* If no wallet client is present, it will return a RequestIntent.
*/
const createMatchSwapTool = ({ zeroxApiKey, }) => {
return (0, client_js_1.createTool)({
name: "intent0xSwap",
description: "Perform a token swap on multiple EVM networks via 0x aggregator (Matcha)",
// Include all chains you wish to support
supportedChains: matchaSwapChains,
parameters: zod_1.z.object({
chainId: zod_1.z.number().describe("Chain ID (e.g. 1, 10, 42161, 8453)"),
fromToken: zod_1.z
.string()
.describe('Source token address, or "ETH" for native'),
toToken: zod_1.z.string().describe("Destination token address"),
amount: zod_1.z.number().describe("Amount of source token to swap"),
}),
execute: async (client, args) => {
const { chainId, fromToken, toToken, amount } = args;
// Prepare addresses
const sellToken = normalize(fromToken);
const buyToken = normalize(toToken);
// Retrieve the relevant wallet + public clients
const walletClient = client.getWalletClient(chainId);
const publicClient = client.getPublicClient(chainId);
const swapIntentDescription = `Swap ${amount} of ${fromToken} for ${toToken} on chainId ${chainId}`;
try {
// Determine decimals
const sellDecimals = sellToken === "ETH"
? 18
: (await publicClient.readContract({
address: sellToken,
abi: viem_1.erc20Abi,
functionName: "decimals",
})) || 18;
const sellAmount = (0, viem_1.parseUnits)(`${amount}`, sellDecimals);
const ops = [];
const userAddress = await client.getAddress();
// Check user's balance
const userBalance = sellToken === "ETH"
? await publicClient.getBalance({ address: userAddress })
: (await publicClient.readContract({
address: sellToken,
abi: viem_1.erc20Abi,
functionName: "balanceOf",
args: [userAddress],
}));
if (userBalance < sellAmount) {
throw new Error(`Insufficient balance: You have ${userBalance.toString()} ${sellToken} but trying to sell ${sellAmount.toString()} ${sellToken}`);
}
// Check allowance if we're selling ERC20
if (sellToken !== "ETH") {
const EXCHANGE_PROXY = "0xdef1c0ded9bec7f1a1670819833240f027b25eff";
const currentAllowance = (await publicClient.readContract({
address: sellToken,
abi: viem_1.erc20Abi,
functionName: "allowance",
args: [userAddress, EXCHANGE_PROXY],
}));
if (sellAmount > currentAllowance) {
ops.push({
target: sellToken,
value: "0",
data: (0, viem_1.encodeFunctionData)({
abi: viem_1.erc20Abi,
functionName: "approve",
args: [EXCHANGE_PROXY, viem_1.maxUint256],
}),
});
}
}
// Fetch quote from 0x
const zeroXEndpoint = get0xApiEndpoint(chainId);
const params = new URLSearchParams({
sellToken,
buyToken,
sellAmount: sellAmount.toString(),
takerAddress: userAddress,
});
const quoteUrl = `${zeroXEndpoint}/swap/v1/quote?${params}`;
const quoteResp = await fetch(quoteUrl, {
headers: { "0x-api-key": zeroxApiKey },
});
if (!quoteResp.ok) {
throw new Error(`Failed to get swap quote: ${quoteResp.status} ${quoteResp.statusText}`);
}
const quote = await quoteResp.json();
if (!quote || quote.code) {
throw new Error(quote.message || "Failed to retrieve a valid swap quote");
}
// Build aggregator swap call
ops.push({
target: quote.to,
value: sellToken === "ETH" ? quote.value : "0",
data: quote.data,
});
// If no wallet client, return an unexecuted intent
if (!walletClient) {
return {
intent: swapIntentDescription,
ops,
chain: chainId,
};
}
// If walletClient is present, execute ops
const hash = await client.executeOps(ops, chainId);
return {
intent: swapIntentDescription,
ops,
chain: chainId,
hash,
};
}
catch (error) {
throw new Error(`Matcha Swap Failed: ${error instanceof Error ? error.message : error}`);
}
},
});
};
exports.createMatchSwapTool = createMatchSwapTool;
//# sourceMappingURL=intents.js.map