execute_swap
Facilitates swap transactions on DeFi platforms by executing trades using pre-fetched quote data, enabling efficient and precise trade automation across multiple blockchains.
Instructions
Execute a swap transaction (requires quote data)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| quoteData | Yes | Quote data from get_swap_quote |
Input Schema (JSON Schema)
{
"properties": {
"quoteData": {
"description": "Quote data from get_swap_quote",
"type": "object"
}
},
"required": [
"quoteData"
],
"type": "object"
}
Implementation Reference
- src/toolService.js:72-109 (handler)Main handler function for execute_swap tool. Validates quoteData and user private key, extracts chainId, calls blockchainService.signAndBroadcastTransaction to execute the swap.async executeSwap(quoteData) { if (!quoteData) { throw new Error("Quote data is required for swap execution"); } if (!this.userPrivateKey) { throw new Error("User private key is required for swap execution"); } try { // Extract chain ID from quote data const chainId = quoteData.chainId || quoteData.transaction?.chainId; if (!chainId) { throw new Error("Chain ID not found in quote data"); } console.log("š Executing swap transaction..."); // Sign and broadcast the transaction using blockchain service const result = await this.blockchain.signAndBroadcastTransaction( chainId, quoteData ); return { message: "Swap executed successfully", data: result, nextSteps: [ "1. Transaction has been broadcasted to the blockchain", "2. Wait for confirmation (usually 1-3 minutes)", "3. Check transaction status on block explorer", `4. Transaction hash: ${result.hash}`, ], }; } catch (error) { throw new Error(`Swap execution failed: ${error.message}`); } }
- src/index.js:157-170 (schema)JSON schema defining the input for execute_swap tool: requires quoteData object from get_swap_quote.{ name: TOOL_NAMES.EXECUTE_SWAP, description: "Execute a swap transaction (requires quote data)", inputSchema: { type: "object", properties: { quoteData: { type: "object", description: "Quote data from get_swap_quote", }, }, required: ["quoteData"], }, },
- src/index.js:992-994 (registration)MCP server registration: maps 'execute_swap' tool calls to toolService.executeSwap(quoteData). TOOL_NAMES.EXECUTE_SWAP resolves to 'execute_swap'.case TOOL_NAMES.EXECUTE_SWAP: result = await toolService.executeSwap(args.quoteData); break;
- Core helper function that implements the swap execution logic: signs Permit2 EIP-712 message if needed, embeds signature in transaction calldata, handles token allowance, signs and broadcasts the legacy transaction using ethers.js.async signAndBroadcastTransaction(chainId, quoteData) { try { if (!this.wallet) { throw new Error("No private key configured for transaction signing"); } const connectedWallet = this.getConnectedWallet(chainId); const { transaction, permit2, sellToken, sellAmount } = quoteData; if (!transaction) { throw new Error("No transaction data found in quote"); } console.log(`š Processing transaction for chain ${chainId}:`, { hasTransaction: !!transaction, hasPermit2: !!permit2, transactionTo: transaction.to, transactionValue: transaction.value || "0", }); // Step 1: Handle token allowance for Permit2 (if needed) const PERMIT2_CONTRACT = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; let allowanceResult = null; if (permit2 && sellToken && sellAmount) { console.log("š Checking token allowance for Permit2..."); try { allowanceResult = await this.ensureTokenAllowance( chainId, sellToken, PERMIT2_CONTRACT, sellAmount ); if (allowanceResult.approved) { console.log("ā Token allowance set for Permit2 contract"); } else { console.log("ā Sufficient token allowance already exists"); } } catch (error) { console.warn( "ā ļø Token allowance check failed, proceeding anyway:", error.message ); // Continue with swap execution even if allowance check fails // The transaction will fail if allowance is actually insufficient } } // Step 2: Prepare transaction data - start with original data let transactionData = transaction.data; // Step 3: Handle Permit2 signature if present let permit2Signature = null; if (permit2 && permit2.eip712) { console.log("š Processing Permit2 signature..."); permit2Signature = await this.signPermit2Message(permit2); console.log("ā Permit2 signature created"); const signature = permit2Signature.signature; // Ensure signature has 0x prefix and is properly formatted const cleanSignature = signature.startsWith("0x") ? signature : "0x" + signature; // Validate signature format (should be 65 bytes = 130 hex chars + 0x prefix = 132 total) if (cleanSignature.length !== 132) { throw new Error(`Invalid signature length: expected 132 chars (65 bytes), got ${cleanSignature.length}`); } // Calculate signature size in bytes (following viem's size() function logic) const signatureBytes = ethers.getBytes(cleanSignature); const signatureSize = signatureBytes.length; // Should be 65 bytes // Create signature length as 32-byte unsigned big-endian integer (following viem's numberToHex) const signatureLengthInHex = ethers.zeroPadValue( ethers.toBeHex(signatureSize), 32 ); // Append signature length and signature data to transaction data (following viem's concat) transactionData = ethers.concat([ transaction.data, signatureLengthInHex, cleanSignature, ]); console.log("š§ Permit2 signature embedded in transaction data:", { originalDataLength: transaction.data.length, signatureSize: signatureSize, newDataLength: ethers.hexlify(transactionData).length, signatureLengthHex: ethers.hexlify(signatureLengthInHex).substring(0, 20) + "...", signaturePreview: cleanSignature.substring(0, 20) + "...", }); } // Step 4: Validate quote data if (!transaction.gas) { throw new Error("No gas estimate found in quote data"); } if (!transaction.gasPrice) { throw new Error("No gasPrice found in quote data"); } // Step 5: Get nonce const nonce = await this.getTransactionCount( chainId, connectedWallet.address ); // Step 6: Create legacy transaction with embedded Permit2 signature const legacyTxData = { to: transaction.to, data: transactionData, // Use modified data with embedded signature value: transaction.value || "0", gasLimit: transaction.gas, gasPrice: transaction.gasPrice, nonce: nonce, type: 0, // Force legacy transaction }; console.log("š Sending transaction with Permit2 signature embedded:", { to: legacyTxData.to, gasLimit: legacyTxData.gasLimit, gasPrice: legacyTxData.gasPrice, nonce: legacyTxData.nonce, type: "legacy", hasPermit2Embedded: !!permit2Signature, dataLength: transactionData.length, allowanceHandled: !!allowanceResult, }); // Step 7: Sign and send transaction console.log("š Broadcasting transaction to network..."); const signedTx = await connectedWallet.sendTransaction(legacyTxData); console.log(`ā Transaction broadcasted: ${signedTx.hash}`); // Return transaction details return { hash: signedTx.hash, from: signedTx.from, to: signedTx.to, value: signedTx.value?.toString(), gasLimit: signedTx.gasLimit?.toString(), gasPrice: signedTx.gasPrice?.toString(), nonce: signedTx.nonce?.toString(), chainId: signedTx.chainId?.toString(), type: signedTx.type?.toString(), permit2Signed: !!permit2Signature, permit2Hash: permit2Signature?.hash, permit2Embedded: !!permit2Signature, allowanceResult: allowanceResult, steps: [ "1. ā Token allowance checked/set for Permit2", "2. ā Permit2 EIP-712 message signed", "3. ā Signature embedded in transaction data", "4. ā Transaction broadcasted to network", ], }; } catch (error) { console.error("Transaction signing/broadcasting failed:", error); throw new Error( `Failed to sign and broadcast transaction: ${error.message}` ); } }
- src/constants.js:2-6 (registration)Constant definition mapping EXECUTE_SWAP to tool name string 'execute_swap', used in MCP server registration.export const TOOL_NAMES = { // Agg API Tools GET_SWAP_PRICE: "get_swap_price", GET_SWAP_QUOTE: "get_swap_quote", EXECUTE_SWAP: "execute_swap",