> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streambird.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Send Transaction

Send transactions from a user's embedded Ethereum wallet to interact with smart contracts, transfer ETH, or execute any onchain action.

## Overview

The `useSendTransaction` hook from MoonKey's React SDK provides a simple interface for sending transactions from embedded wallets. It handles transaction preparation, signing, and broadcasting to the network.

<Info>
  MoonKey automatically handles transaction population (gas estimation, nonce management, etc.) and prompts users for confirmation before broadcasting.
</Info>

## React SDK

<Tabs>
  <Tab title="Basic Usage">
    To send a transaction, use the `sendTransaction` method from the `useSendTransaction` hook:

    ```tsx theme={null}
    import { useMoonKey } from '@moon-key/react-auth';
    import { useSendTransaction } from '@moon-key/react-auth/ethereum';

    function SendEthButton() {
      const { user } = useMoonKey();
      const { sendTransaction } = useSendTransaction();

      const handleSend = async () => {
        if (!user?.wallet) {
          alert('No wallet found');
          return;
        }

        try {
          const result = await sendTransaction({
            to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
            value: '0x38d7ea4c68000' // 0.001 ETH in wei (hex)
          });

          console.log('Transaction sent:', result.transactionHash);
          alert(`Success! Hash: ${result.transactionHash}`);
        } catch (error) {
          console.error('Transaction failed:', error);
          alert('Transaction failed');
        }
      };

      return (
        <button onClick={handleSend}>
          Send 0.001 ETH
        </button>
      );
    }
    ```
  </Tab>

  <Tab title="With UI Customization">
    Customize the transaction confirmation modal:

    ```tsx theme={null}
    import { useMoonKey } from '@moon-key/react-auth';
    import { useSendTransaction } from '@moon-key/react-auth/ethereum';

    function CustomTransactionButton() {
      const { user } = useMoonKey();
      const { sendTransaction } = useSendTransaction();

      const handleSend = async () => {
        if (!user?.wallet) return;

        try {
          const result = await sendTransaction({
            to: '0xRecipient...',
            value: '0x38d7ea4c68000'
          }, {
            uiConfig: {
              title: 'Send Payment',
              description: 'Sending 0.001 ETH to recipient',
              confirmButtonText: 'Confirm Payment',
              cancelButtonText: 'Cancel',
              transactionInfo: {
                amount: '0.001 ETH',
                recipient: '0xd8dA...6045'
              }
            }
          });

          console.log('Payment sent:', result.transactionHash);
        } catch (error) {
          console.error('Payment failed:', error);
        }
      };

      return (
        <button onClick={handleSend}>
          Send Payment
        </button>
      );
    }
    ```
  </Tab>
</Tabs>

## Parameters

### Transaction Object

The first parameter to `sendTransaction` is the transaction object:

<ParamField path="to" type="string" required>
  The recipient address for the transaction.

  **Example:**

  ```tsx theme={null}
  to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
  ```
</ParamField>

<ParamField path="value" type="string">
  The amount of ETH to send in wei, as a hexadecimal string. Omit for contract interactions with no ETH transfer.

  **Example:**

  ```tsx theme={null}
  value: '0x38d7ea4c68000' // 0.001 ETH
  ```
</ParamField>

<ParamField path="data" type="string">
  The encoded function call data for contract interactions, as a hexadecimal string.

  **Example:**

  ```tsx theme={null}
  data: '0x095ea7b3000000000000000000000000...'
  ```
</ParamField>

<ParamField path="chainId" type="number">
  The chain ID for the transaction. If not provided, uses the wallet's current chain.

  **Example:**

  ```tsx theme={null}
  chainId: 8453 // Base
  ```
</ParamField>

<ParamField path="gasLimit" type="string">
  Optional gas limit as a hexadecimal string. MoonKey estimates gas automatically if not provided.

  **Example:**

  ```tsx theme={null}
  gasLimit: '0x5208' // 21000 in decimal
  ```
</ParamField>

<ParamField path="maxFeePerGas" type="string">
  Optional maximum fee per gas for EIP-1559 transactions, as a hexadecimal string.

  **Example:**

  ```tsx theme={null}
  maxFeePerGas: '0x59682f00' // 1.5 gwei
  ```
</ParamField>

<ParamField path="maxPriorityFeePerGas" type="string">
  Optional maximum priority fee per gas for EIP-1559 transactions, as a hexadecimal string.

  **Example:**

  ```tsx theme={null}
  maxPriorityFeePerGas: '0x3b9aca00' // 1 gwei
  ```
</ParamField>

<ParamField path="nonce" type="number">
  Optional nonce for the transaction. MoonKey determines this automatically if not provided.

  **Example:**

  ```tsx theme={null}
  nonce: 5
  ```
</ParamField>

### Options Object

The second parameter is an optional configuration object:

<ParamField path="uiConfig" type="object">
  Configuration for the transaction confirmation modal UI.

  <Expandable title="uiConfig properties">
    <ParamField path="title" type="string">
      Custom title for the confirmation modal.
    </ParamField>

    <ParamField path="description" type="string">
      Description text shown in the modal.
    </ParamField>

    <ParamField path="confirmButtonText" type="string">
      Text for the confirm button.
    </ParamField>

    <ParamField path="cancelButtonText" type="string">
      Text for the cancel button.
    </ParamField>

    <ParamField path="transactionInfo" type="object">
      Additional transaction details to display.
    </ParamField>

    <ParamField path="showWalletUI" type="boolean">
      Whether to show the confirmation modal. Set to `false` to send without confirmation.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="wallet" type="Wallet">
  Specific wallet to use for the transaction. If not provided, uses the user's default wallet.

  **Example:**

  ```tsx theme={null}
  wallet: user.wallet
  ```
</ParamField>

## Returns

The `sendTransaction` method returns a Promise that resolves with:

<ResponseField name="transactionHash" type="string">
  The hash of the broadcasted transaction.

  **Example:**

  ```tsx theme={null}
  "0x7c91ba85d67ef92cc15f3e9c8d8c5788e982cf83fabe9bfcc66a747aa0bd3701"
  ```
</ResponseField>

<ResponseField name="chainId" type="number">
  The chain ID where the transaction was sent.
</ResponseField>

<Warning>
  A successful return indicates the transaction was **broadcasted** to the network. It may still fail during confirmation. Always verify transaction status onchain or listen for confirmation events.
</Warning>

## Complete Examples

### Basic ETH Transfer

```tsx theme={null}
'use client';
import { useMoonKey } from '@moon-key/react-auth';
import { useSendTransaction } from '@moon-key/react-auth/ethereum';
import { useState } from 'react';

export default function SendEthComponent() {
  const { user, isAuthenticated } = useMoonKey();
  const { sendTransaction } = useSendTransaction();
  const [recipientAddress, setRecipientAddress] = useState('');
  const [amount, setAmount] = useState('');
  const [txHash, setTxHash] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [isSending, setIsSending] = useState(false);

  const handleSend = async () => {
    if (!user?.wallet) {
      setError('No wallet found');
      return;
    }

    if (!recipientAddress || !amount) {
      setError('Please fill in all fields');
      return;
    }

    setError(null);
    setTxHash(null);
    setIsSending(true);

    try {
      // Convert ETH amount to wei (hex)
      const amountInWei = BigInt(parseFloat(amount) * 10**18);
      const valueHex = '0x' + amountInWei.toString(16);

      const result = await sendTransaction({
        to: recipientAddress,
        value: valueHex
      });

      setTxHash(result.transactionHash);
      alert('Transaction sent successfully!');
    } catch (err: any) {
      console.error('Transaction failed:', err);
      setError(err.message || 'Transaction failed');
    } finally {
      setIsSending(false);
    }
  };

  if (!isAuthenticated || !user?.wallet) {
    return <p>Please sign in and create a wallet</p>;
  }

  return (
    <div className="send-eth-container">
      <h2>Send ETH</h2>

      <div className="form-group">
        <label>Recipient Address</label>
        <input
          type="text"
          value={recipientAddress}
          onChange={(e) => setRecipientAddress(e.target.value)}
          placeholder="0x..."
          disabled={isSending}
        />
      </div>

      <div className="form-group">
        <label>Amount (ETH)</label>
        <input
          type="number"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          placeholder="0.001"
          step="0.001"
          disabled={isSending}
        />
      </div>

      <button
        onClick={handleSend}
        disabled={isSending || !recipientAddress || !amount}
        className="send-button"
      >
        {isSending ? 'Sending...' : 'Send ETH'}
      </button>

      {error && (
        <div className="error-message">
          {error}
        </div>
      )}

      {txHash && (
        <div className="success-message">
          <p>Transaction sent!</p>
          <p className="tx-hash">Hash: {txHash}</p>
          <a
            href={`https://etherscan.io/tx/${txHash}`}
            target="_blank"
            rel="noopener noreferrer"
          >
            View on Etherscan
          </a>
        </div>
      )}
    </div>
  );
}
```

### ERC-20 Token Transfer

```tsx theme={null}
'use client';
import { useMoonKey } from '@moon-key/react-auth';
import { useSendTransaction } from '@moon-key/react-auth/ethereum';
import { encodeFunctionData } from 'viem';
import { useState } from 'react';

// ERC-20 ABI for transfer function
const ERC20_ABI = [{
  name: 'transfer',
  type: 'function',
  inputs: [
    { name: 'to', type: 'address' },
    { name: 'amount', type: 'uint256' }
  ],
  outputs: [{ type: 'bool' }]
}];

export default function SendTokenComponent() {
  const { user } = useMoonKey();
  const { sendTransaction } = useSendTransaction();
  const [tokenAddress, setTokenAddress] = useState('');
  const [recipientAddress, setRecipientAddress] = useState('');
  const [amount, setAmount] = useState('');
  const [decimals, setDecimals] = useState('18');
  const [isSending, setIsSending] = useState(false);

  const handleSendToken = async () => {
    if (!user?.wallet) return;

    setIsSending(true);

    try {
      // Convert token amount to smallest unit
      const amountInSmallestUnit = BigInt(
        parseFloat(amount) * 10**parseInt(decimals)
      );

      // Encode the transfer function call
      const data = encodeFunctionData({
        abi: ERC20_ABI,
        functionName: 'transfer',
        args: [recipientAddress, amountInSmallestUnit]
      });

      const result = await sendTransaction({
        to: tokenAddress,
        data
      }, {
        uiConfig: {
          title: 'Send Token',
          description: `Sending ${amount} tokens to ${recipientAddress.slice(0, 6)}...${recipientAddress.slice(-4)}`,
          confirmButtonText: 'Confirm Transfer'
        }
      });

      console.log('Token transfer successful:', result.transactionHash);
      alert('Token transfer successful!');
    } catch (error) {
      console.error('Token transfer failed:', error);
      alert('Token transfer failed');
    } finally {
      setIsSending(false);
    }
  };

  return (
    <div className="send-token-container">
      <h2>Send ERC-20 Token</h2>

      <div className="form-group">
        <label>Token Contract Address</label>
        <input
          type="text"
          value={tokenAddress}
          onChange={(e) => setTokenAddress(e.target.value)}
          placeholder="0x..."
        />
      </div>

      <div className="form-group">
        <label>Recipient Address</label>
        <input
          type="text"
          value={recipientAddress}
          onChange={(e) => setRecipientAddress(e.target.value)}
          placeholder="0x..."
        />
      </div>

      <div className="form-group">
        <label>Amount</label>
        <input
          type="number"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          placeholder="100"
          step="0.01"
        />
      </div>

      <div className="form-group">
        <label>Token Decimals</label>
        <input
          type="number"
          value={decimals}
          onChange={(e) => setDecimals(e.target.value)}
          placeholder="18"
        />
      </div>

      <button
        onClick={handleSendToken}
        disabled={isSending || !tokenAddress || !recipientAddress || !amount}
      >
        {isSending ? 'Sending...' : 'Send Token'}
      </button>
    </div>
  );
}
```

## UI Customization

Customize the transaction confirmation modal to match your application's branding:

```tsx theme={null}
import { useSendTransaction } from '@moon-key/react-auth/ethereum';

const { sendTransaction } = useSendTransaction();

await sendTransaction({
  to: '0x...',
  value: '0x38d7ea4c68000'
}, {
  uiConfig: {
    title: 'Confirm Payment',
    description: 'You are about to send 0.001 ETH',
    confirmButtonText: 'Send Payment',
    cancelButtonText: 'Cancel',
    transactionInfo: {
      amount: '0.001 ETH',
      recipient: 'vitalik.eth',
      fee: '~$0.50'
    }
  }
});
```

### Hide Modal

For certain flows, you may want to send transactions without showing the UI modal:

```tsx theme={null}
await sendTransaction({
  to: '0x...',
  value: '0x38d7ea4c68000'
}, {
  uiConfig: {
    showWalletUI: false
  }
});
```

<Warning>
  Hiding the confirmation modal removes the user's ability to review transaction details before signing. Only use this for trusted operations or after obtaining explicit user consent.
</Warning>

## Working with Viem

MoonKey's embedded wallets are compatible with [viem](https://viem.sh), allowing you to leverage its powerful utilities:

```tsx theme={null}
import { useMoonKey } from '@moon-key/react-auth';
import { useSendTransaction } from '@moon-key/react-auth/ethereum';
import { parseEther, encodeFunctionData } from 'viem';

function ViemExample() {
  const { user } = useMoonKey();
  const { sendTransaction } = useSendTransaction();

  const sendWithViem = async () => {
    if (!user?.wallet) return;

    // Use viem utilities
    const value = parseEther('0.001'); // Returns bigint

    const result = await sendTransaction({
      to: '0xRecipient...',
      value: '0x' + value.toString(16) // Convert to hex
    });

    console.log('Transaction sent:', result.transactionHash);
  };

  return <button onClick={sendWithViem}>Send with Viem</button>;
}
```

## Error Handling

Handle common transaction errors gracefully:

```tsx theme={null}
import { useMoonKey } from '@moon-key/react-auth';
import { useSendTransaction } from '@moon-key/react-auth/ethereum';

function TransactionWithErrorHandling() {
  const { user } = useMoonKey();
  const { sendTransaction } = useSendTransaction();

  const handleTransaction = async () => {
    if (!user?.wallet) return;

    try {
      const result = await sendTransaction({
        to: '0x...',
        value: '0x38d7ea4c68000'
      });

      console.log('Success:', result.transactionHash);
    } catch (error: any) {
      // Handle specific error types
      if (error.code === 'USER_REJECTED') {
        console.log('User cancelled the transaction');
        alert('Transaction cancelled');
      } else if (error.code === 'INSUFFICIENT_FUNDS') {
        console.log('Insufficient balance');
        alert('Not enough ETH for this transaction');
      } else if (error.code === 'NETWORK_ERROR') {
        console.log('Network error');
        alert('Network error. Please try again');
      } else {
        console.error('Transaction failed:', error);
        alert('Transaction failed: ' + error.message);
      }
    }
  };

  return <button onClick={handleTransaction}>Send Transaction</button>;
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always validate user inputs">
    Validate addresses and amounts before sending transactions:

    ```tsx theme={null}
    import { isAddress } from 'viem';

    const handleSend = async () => {
      // Validate address
      if (!isAddress(recipientAddress)) {
        alert('Invalid recipient address');
        return;
      }

      // Validate amount
      if (parseFloat(amount) <= 0) {
        alert('Amount must be greater than 0');
        return;
      }

      // Proceed with transaction
      await sendTransaction({ to: recipientAddress, value: amountInWei });
    };
    ```
  </Accordion>

  <Accordion title="Check wallet balance before transactions">
    Verify sufficient balance before attempting to send:

    ```tsx theme={null}
    import { useWalletBalance } from '@moon-key/react-auth/ethereum';

    const { balance } = useWalletBalance();

    const handleSend = async () => {
      const amountInWei = BigInt(parseFloat(amount) * 10**18);

      if (balance && BigInt(balance) < amountInWei) {
        alert('Insufficient balance');
        return;
      }

      await sendTransaction({ to: recipient, value: '0x' + amountInWei.toString(16) });
    };
    ```
  </Accordion>

  <Accordion title="Provide transaction feedback">
    Keep users informed throughout the transaction process:

    ```tsx theme={null}
    const [status, setStatus] = useState<'idle' | 'pending' | 'success' | 'error'>('idle');

    const handleSend = async () => {
      setStatus('pending');

      try {
        const result = await sendTransaction({ to: '0x...', value: '0x38d7ea4c68000' });
        setStatus('success');
        showToast('Transaction sent! Hash: ' + result.transactionHash);
      } catch (error) {
        setStatus('error');
        showToast('Transaction failed');
      }
    };
    ```
  </Accordion>

  <Accordion title="Use proper gas estimation">
    Let MoonKey handle gas estimation, but allow overrides when needed:

    ```tsx theme={null}
    // MoonKey handles gas estimation automatically
    await sendTransaction({
      to: '0x...',
      value: '0x38d7ea4c68000'
      // gasLimit is optional - only provide if you need a specific value
    });

    // Or provide custom gas parameters for advanced use cases
    await sendTransaction({
      to: '0x...',
      value: '0x38d7ea4c68000',
      gasLimit: '0x5208',
      maxFeePerGas: '0x59682f00',
      maxPriorityFeePerGas: '0x3b9aca00'
    });
    ```
  </Accordion>

  <Accordion title="Handle transaction confirmations">
    Wait for transaction confirmations before considering it final:

    ```tsx theme={null}
    import { createPublicClient, http } from 'viem';
    import { mainnet } from 'viem/chains';

    const publicClient = createPublicClient({
      chain: mainnet,
      transport: http()
    });

    const handleSend = async () => {
      const result = await sendTransaction({ to: '0x...', value: '0x38d7ea4c68000' });

      console.log('Transaction broadcasted:', result.transactionHash);

      // Wait for confirmation
      const receipt = await publicClient.waitForTransactionReceipt({
        hash: result.transactionHash as `0x${string}`
      });

      console.log('Transaction confirmed:', receipt.status);
    };
    ```
  </Accordion>

  <Accordion title="Display transaction links">
    Help users track their transactions on block explorers:

    ```tsx theme={null}
    const getExplorerUrl = (txHash: string, chainId: number) => {
      const explorers: Record<number, string> = {
        1: 'https://etherscan.io',
        8453: 'https://basescan.org',
        137: 'https://polygonscan.com',
        42161: 'https://arbiscan.io'
      };

      const baseUrl = explorers[chainId] || 'https://etherscan.io';
      return `${baseUrl}/tx/${txHash}`;
    };

    // After transaction
    <a href={getExplorerUrl(txHash, chainId)} target="_blank">
      View on Explorer
    </a>
    ```
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Transaction fails silently">
    If transactions fail without clear errors:

    * Check that the user's wallet has sufficient balance
    * Verify the recipient address is valid
    * Ensure the contract address is correct for contract interactions
    * Check that the encoded data is properly formatted
    * Try on a testnet first to debug

    ```tsx theme={null}
    // Add detailed logging
    try {
      console.log('Sending transaction:', { to, value, data });
      const result = await sendTransaction({ to, value, data });
      console.log('Transaction sent:', result);
    } catch (error) {
      console.error('Full error:', error);
      console.error('Error details:', JSON.stringify(error, null, 2));
    }
    ```
  </Accordion>

  <Accordion title="User rejected transaction">
    Handle user cancellations gracefully:

    ```tsx theme={null}
    catch (error: any) {
      if (error.code === 'USER_REJECTED' || error.message.includes('User rejected')) {
        console.log('User cancelled the transaction');
        // Don't show error - this is expected behavior
        return;
      }
      // Handle other errors
      alert('Transaction failed: ' + error.message);
    }
    ```
  </Accordion>

  <Accordion title="Insufficient gas">
    If transactions fail due to gas issues:

    * MoonKey usually handles gas estimation automatically
    * For complex transactions, consider providing a custom gas limit
    * Ensure the wallet has enough ETH for both value + gas

    ```tsx theme={null}
    // Check if error is gas-related
    if (error.message.includes('gas') || error.message.includes('insufficient funds')) {
      alert('Insufficient ETH for gas. Please add more ETH to your wallet.');
    }
    ```
  </Accordion>

  <Accordion title="Transaction stuck or pending">
    If a transaction appears stuck:

    * Check the network status (congestion, downtime)
    * Verify the transaction on a block explorer
    * Consider the gas price used - low gas prices can cause delays
    * Wait for network confirmation (can take minutes during congestion)

    ```tsx theme={null}
    // Implement a timeout
    const TIMEOUT = 60000; // 60 seconds

    const sendWithTimeout = async () => {
      const timeoutPromise = new Promise((_, reject) =>
        setTimeout(() => reject(new Error('Transaction timeout')), TIMEOUT)
      );

      try {
        const result = await Promise.race([
          sendTransaction({ to: '0x...', value: '0x38d7ea4c68000' }),
          timeoutPromise
        ]);
        console.log('Transaction sent:', result);
      } catch (error: any) {
        if (error.message === 'Transaction timeout') {
          alert('Transaction is taking longer than expected. Check your wallet or explorer.');
        }
      }
    };
    ```
  </Accordion>

  <Accordion title="Wrong network">
    Ensure transactions are sent on the correct network:

    ```tsx theme={null}
    import { useWalletChain } from '@moon-key/react-auth/ethereum';

    const { chainId, switchChain } = useWalletChain();

    const handleSend = async () => {
      const targetChainId = 8453; // Base

      if (chainId !== targetChainId) {
        try {
          await switchChain(targetChainId);
        } catch (error) {
          alert('Please switch to Base network');
          return;
        }
      }

      await sendTransaction({ to: '0x...', value: '0x38d7ea4c68000' });
    };
    ```
  </Accordion>
</AccordionGroup>

## Related Documentation

<CardGroup cols={2}>
  <Card title="Sign Transaction" icon="file-signature" href="/wallet-as-a-service/using-wallets/ethereum/sign-transaction">
    Sign transactions without broadcasting
  </Card>

  <Card title="Sign Message" icon="signature" href="/wallet-as-a-service/using-wallets/ethereum/sign-message">
    Sign arbitrary messages
  </Card>

  <Card title="Send Transaction UI" icon="window" href="/wallet-as-a-service/ui-components/send-transaction">
    Customize the transaction confirmation UI
  </Card>

  <Card title="Switch Chains" icon="arrows-rotate" href="/wallet-as-a-service/using-wallets/ethereum/switch-chains">
    Switch between Ethereum networks
  </Card>
</CardGroup>

## Next Steps

* Learn about [signing transactions](/wallet-as-a-service/using-wallets/ethereum/sign-transaction) without broadcasting
* Explore [contract interactions](/wallet-as-a-service/using-wallets/ethereum/interfacing-libraries) with viem and ethers
* Understand [chain management](/wallet-as-a-service/using-wallets/ethereum/switch-chains) for multi-network apps
* Customize the [transaction UI](/wallet-as-a-service/ui-components/send-transaction) to match your brand
