Skip to main content
Glama

approve_token_spending

Authorize smart contracts to spend your ERC20 tokens for DeFi interactions like DEX trades or lending protocols. Set spending limits or revoke permissions as needed.

Instructions

Approve a spender (contract) to spend tokens on your behalf. Required before interacting with DEXes, lending protocols, etc.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
tokenAddressYesThe ERC20 token contract address
spenderAddressYesThe address that will be allowed to spend tokens (usually a contract)
amountYesAmount to approve (in token units). Use '0' to revoke approval.
networkNoNetwork name or chain ID. Defaults to Ethereum mainnet.

Implementation Reference

  • MCP server.tool registration, input schema with Zod, and handler function for the 'approve_token_spending' tool. The handler performs ENS resolution if needed, fetches token decimals/symbol, parses amount, signs and sends the approve transaction via viem wallet client, and returns formatted results.
    // 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
          };
        }
      }
    );
  • Core helper function approveERC20 that resolves addresses (supports ENS), fetches token metadata, parses amount using decimals, creates wallet client from private key, calls approve function on ERC20 contract, and returns transaction hash with 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
        }
      };
    }
  • ERC20 ABI fragment defining the approve, transfer, decimals, and symbol functions used by the approveERC20 helper.
    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;
  • Calls registerEVMTools to register all EVM tools including approve_token_spending.
    registerEVMTools(server);
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds valuable behavioral context beyond what annotations provide. While annotations indicate this is a non-readOnly, non-destructive, non-idempotent operation, the description clarifies that this is a prerequisite for DeFi interactions and mentions the ability to revoke approval (using '0' amount). It doesn't cover rate limits or authentication needs, but provides practical deployment context that annotations lack.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is perfectly concise with two sentences that each serve distinct purposes: the first states the core function, the second provides usage context. There's no wasted language, and the most critical information (what the tool does) appears first.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with good annotations and complete schema coverage but no output schema, the description provides adequate context about the tool's purpose and usage. It could be more complete by mentioning what happens after approval (e.g., transaction submission, gas costs, or typical next steps), but covers the essential why and when of using this tool effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the input schema already documents all parameters thoroughly. The description doesn't add significant parameter semantics beyond what's in the schema, though it does reinforce the purpose of the 'amount' parameter by mentioning revocation capability. This meets the baseline expectation when schema coverage is complete.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Approve a spender to spend tokens on your behalf') and identifies the resource (tokens). It distinguishes this from sibling tools like 'transfer_erc20' or 'write_contract' by focusing specifically on token spending authorization rather than transfers or general contract interactions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use this tool ('Required before interacting with DEXes, lending protocols, etc.'), giving practical examples. However, it doesn't explicitly state when NOT to use it or mention specific alternatives among the sibling tools, such as using 'get_allowance' to check existing approvals first.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chulanpro5/evm-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server