approve_token_spending
Authorize a DeFi protocol or exchange to spend your ERC20 tokens. Submit private key, token and spender addresses, and amount to enable token transactions securely on EVM-compatible networks.
Instructions
Approve another address (like a DeFi protocol or exchange) to spend your ERC20 tokens. This is often required before interacting with DeFi protocols.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| amount | Yes | The amount of tokens to approve in token units, not wei (e.g., '1000' to approve spending 1000 tokens). Use a very large number for unlimited approval. | |
| network | No | Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet. | |
| privateKey | Yes | Private key of the token owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored. | |
| spenderAddress | Yes | The contract address being approved to spend your tokens (e.g., a DEX or lending protocol) | |
| tokenAddress | Yes | The contract address of the ERC20 token to approve for spending (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC on Ethereum) |
Implementation Reference
- src/core/tools.ts:728-813 (registration)Registration of the 'approve_token_spending' MCP tool, including input schema (Zod) and thin handler wrapper that calls services.approveERC20// Approve ERC20 token spending server.tool( 'approve_token_spending', 'Approve another address (like a DeFi protocol or exchange) to spend your ERC20 tokens. This is often required before interacting with DeFi protocols.', { privateKey: z .string() .describe( 'Private key of the token owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored.' ), tokenAddress: z .string() .describe( "The contract address of the ERC20 token to approve for spending (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC on Ethereum)" ), spenderAddress: z .string() .describe( 'The contract address being approved to spend your tokens (e.g., a DEX or lending protocol)' ), amount: z .string() .describe( "The amount of tokens to approve in token units, not wei (e.g., '1000' to approve spending 1000 tokens). Use a very large number for unlimited approval." ), network: z .string() .optional() .describe( "Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Defaults to Ethereum mainnet." ) }, async ({ privateKey, tokenAddress, spenderAddress, amount, network = 'ethereum' }) => { try { // Get the formattedKey with 0x prefix const formattedKey = privateKey.startsWith('0x') ? (privateKey as `0x${string}`) : (`0x${privateKey}` as `0x${string}`); const result = await services.approveERC20( tokenAddress as Address, spenderAddress as Address, amount, formattedKey, network ); return { content: [ { type: 'text', text: JSON.stringify( { success: true, txHash: result.txHash, network, tokenAddress, spender: spenderAddress, amount: result.amount.formatted, symbol: result.token.symbol }, null, 2 ) } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error approving token spending: ${error instanceof Error ? error.message : String(error)}` } ], isError: true }; } } );
- src/core/services/transfer.ts:231-312 (handler)Core implementation of ERC20 token approval: resolves addresses, fetches token metadata, parses amount, and executes the 'approve' contract call using viem./** * Approve ERC20 token spending * @param tokenAddressOrEns Token contract address or ENS name * @param spenderAddressOrEns Spender address or ENS name * @param amount Amount to approve (in token units) * @param privateKey Owner's private key * @param network Network name or chain ID * @returns Transaction details */ export async function approveERC20( tokenAddressOrEns: string, spenderAddressOrEns: string, amount: string, privateKey: string | `0x${string}`, network: string = 'ethereum' ): Promise<{ txHash: Hash; amount: { raw: bigint; formatted: string; }; token: { symbol: string; decimals: number; }; }> { // Resolve ENS names to addresses if needed const tokenAddress = (await resolveAddress( tokenAddressOrEns, network )) as Address; const spenderAddress = (await resolveAddress( spenderAddressOrEns, network )) as Address; // Ensure the private key has 0x prefix const formattedKey = typeof privateKey === 'string' && !privateKey.startsWith('0x') ? (`0x${privateKey}` as `0x${string}`) : (privateKey as `0x${string}`); // Get token details const publicClient = getPublicClient(network); const contract = getContract({ address: tokenAddress, abi: erc20TransferAbi, client: publicClient }); // Get token decimals and symbol const decimals = await contract.read.decimals(); const symbol = await contract.read.symbol(); // Parse the amount with the correct number of decimals const rawAmount = parseUnits(amount, decimals); // Create wallet client for sending the transaction const walletClient = getWalletClient(formattedKey, network); // Send the transaction const hash = await walletClient.writeContract({ address: tokenAddress, abi: erc20TransferAbi, functionName: 'approve', args: [spenderAddress, rawAmount], account: walletClient.account!, chain: walletClient.chain }); return { txHash: hash, amount: { raw: rawAmount, formatted: amount }, token: { symbol, decimals } }; }
- src/core/services/transfer.ts:17-52 (helper)ERC20 ABI fragment used for approve, transfer, decimals, and symbol reads.const erc20TransferAbi = [ { inputs: [ { type: 'address', name: 'to' }, { type: 'uint256', name: 'amount' } ], name: 'transfer', outputs: [{ type: 'bool' }], stateMutability: 'nonpayable', type: 'function' }, { inputs: [ { type: 'address', name: 'spender' }, { type: 'uint256', name: 'amount' } ], name: 'approve', outputs: [{ type: 'bool' }], stateMutability: 'nonpayable', type: 'function' }, { inputs: [], name: 'decimals', outputs: [{ type: 'uint8' }], stateMutability: 'view', type: 'function' }, { inputs: [], name: 'symbol', outputs: [{ type: 'string' }], stateMutability: 'view', type: 'function' } ] as const;