Skip to main content
Send ERC-20 tokens using the embedded wallet by encoding the transfer function call.

Implementation

import { useSendTransaction } from '@moon-key/react-auth/ethereum';
import { encodeFunctionData } from 'viem';
import { useState } from 'react';

const ERC20_ABI = [
  {
    name: 'transfer',
    type: 'function',
    stateMutability: 'nonpayable',
    inputs: [
      { name: 'to', type: 'address' },
      { name: 'amount', type: 'uint256' }
    ],
    outputs: [{ name: '', type: 'bool' }]
  }
] as const;

export default function SendTokens() {
  const { sendTransaction } = useSendTransaction();
  const [recipient, setRecipient] = useState('');
  const [amount, setAmount] = useState('');
  const [tokenAddress, setTokenAddress] = useState('');

  const handleSendTokens = async () => {
    try {
      // Encode the transfer function call
      const data = encodeFunctionData({
        abi: ERC20_ABI,
        functionName: 'transfer',
        args: [recipient as `0x${string}`, BigInt(amount)]
      });

      // Send the transaction
      const txHash = await sendTransaction({
        to: tokenAddress,
        data
      });

      console.log('Token transfer successful:', txHash);
    } catch (error) {
      console.error('Token transfer failed:', error);
    }
  };

  return (
    <div>
      <h2>Send ERC-20 Tokens</h2>
      <input
        type="text"
        placeholder="Token Contract Address"
        value={tokenAddress}
        onChange={(e) => setTokenAddress(e.target.value)}
      />
      <input
        type="text"
        placeholder="Recipient Address"
        value={recipient}
        onChange={(e) => setRecipient(e.target.value)}
      />
      <input
        type="text"
        placeholder="Amount (in smallest unit)"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
      />
      <button onClick={handleSendTokens}>Send Tokens</button>
    </div>
  );
}

Key concepts

  • ERC-20 ABI - Minimal ABI containing only the transfer function
  • Function encoding - Uses encodeFunctionData from viem to encode the function call
  • Amount handling - Amount should be in the token’s smallest unit (considering decimals)
  • Contract interaction - Sends transaction to the token contract address with encoded data

Important notes

The amount should be specified in the token’s smallest unit. For a token with 18 decimals, to send 1 token, you’d specify 1000000000000000000 (1 × 10^18).
Make sure the user has approved your contract to spend tokens if you’re building a dApp that needs to transfer tokens on behalf of users.

Next steps