> ## 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.

# Sign Transaction

The Sign Transaction screen is displayed when your application requests a wallet signature for a blockchain transaction. This screen shows users the transaction details and allows them to approve or reject the transaction before it's signed.

<Info>
  Transaction signing creates a signed transaction that can be broadcast to the blockchain. This is different from [Send Transaction](/authentication/user-authentication/ui-components/send-transaction), which both signs and broadcasts the transaction automatically.
</Info>

## How it works

When your application requests a transaction signature:

1. Your app calls the sign transaction method from the MoonKey SDK
2. The Sign Transaction screen appears, displaying transaction details
3. User reviews the transaction information (recipient, amount, gas, etc.)
4. User approves or rejects the signature request
5. If approved, the signed transaction is returned to your application
6. Your application can then broadcast the signed transaction to the blockchain

## Triggering the Sign Transaction screen

Use the `useSignTransaction` hook from the MoonKey SDK to trigger the Sign Transaction screen:

<Tabs>
  <Tab title="Ethereum">
    ```tsx theme={null}
    import { useMoonKey } from '@moon-key/react-auth';
    import { useSignTransaction } from '@moon-key/react-auth/ethereum';

    export default function SignTransactionButton() {
      const { user } = useMoonKey();
      const { signTransaction } = useSignTransaction();
      
      const handleSign = async () => {
        if (!user?.wallet) {
          alert('No wallet found');
          return;
        }
        
        try {
          const result = await signTransaction({
            transaction: {
              to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
              value: '0x38d7ea4c68000', // 0.001 ETH in hex
              data: '0x',
              gas: '0x5208', // 21000 in hex
              gasPrice: '0x4a817c800' // 20 gwei in hex
            },
            wallet: user.wallet,
            options: {
              uiOptions: {
                title: 'Sign Transaction',
                description: 'Review and sign this transaction',
                buttonText: 'Sign',
                showWalletUI: true,
                successHeader: 'Transaction Signed!',
                successDescription: 'Your transaction has been signed successfully',
                isCancellable: true
              }
            }
          });
          
          console.log('Signed transaction:', result.signature);
          
          // Now you can broadcast the signed transaction
          // await broadcastTransaction(result.signature);
        } catch (error) {
          console.error('User rejected transaction:', error);
        }
      };
      
      return <button onClick={handleSign}>Sign Transaction</button>;
    }
    ```
  </Tab>

  <Tab title="Solana">
    ```tsx theme={null}
    import { useMoonKey } from '@moon-key/react-auth';
    import { useSignTransaction } from '@moon-key/react-auth/solana';
    import { Transaction, SystemProgram, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';

    export default function SignTransactionButton() {
      const { user } = useMoonKey();
      const { signTransaction } = useSignTransaction();
      
      const handleSign = async () => {
        if (!user?.wallet) {
          alert('No wallet found');
          return;
        }
        
        try {
          // Create a Solana transaction
          const transaction = new Transaction().add(
            SystemProgram.transfer({
              fromPubkey: new PublicKey(user.wallet.address),
              toPubkey: new PublicKey('ReceipientAddressHere'),
              lamports: 0.001 * LAMPORTS_PER_SOL
            })
          );
          
          // Serialize the transaction
          const serialized = transaction.serialize({
            requireAllSignatures: false,
            verifySignatures: false
          });
          
          const result = await signTransaction({
            transaction: serialized,
            wallet: user.wallet,
            options: {
              uiOptions: {
                title: 'Sign Transaction',
                description: 'Review and sign this transaction',
                buttonText: 'Sign',
                showWalletUI: true,
                successHeader: 'Transaction Signed!',
                successDescription: 'Your transaction has been signed successfully',
                isCancellable: true
              }
            }
          });
          
          console.log('Signed transaction:', result.signedTransaction);
          
          // Now you can broadcast the signed transaction
          // await connection.sendRawTransaction(result.signedTransaction);
        } catch (error) {
          console.error('User rejected transaction:', error);
        }
      };
      
      return <button onClick={handleSign}>Sign Transaction</button>;
    }
    ```
  </Tab>
</Tabs>

## Customizing the Sign Transaction screen

You can customize the Sign Transaction screen by passing UI options in the `options.uiOptions` object:

```tsx theme={null}
const result = await signTransaction({
  transaction: transactionData,
  wallet: user.wallet,
  options: {
    uiOptions: {
      title: 'Approve Transaction',
      description: 'Please review the transaction details before signing',
      buttonText: 'Approve & Sign',
      showWalletUI: true,
      successHeader: 'Success!',
      successDescription: 'Transaction signed and ready to broadcast',
      isCancellable: true,
      transactionInfo: {
        // Custom transaction display options
      }
    }
  }
});
```

## Configuration options

### Basic UI Options

<ParamField path="title" type="string">
  Custom title text displayed at the top of the Sign Transaction screen.

  **Example:**

  ```tsx theme={null}
  options: {
    uiOptions: {
      title: 'Approve Transaction'
    }
  }
  ```
</ParamField>

<ParamField path="description" type="string">
  Custom description text displayed below the title, providing context for the transaction.

  **Example:**

  ```tsx theme={null}
  options: {
    uiOptions: {
      description: 'Review the transaction details carefully before signing'
    }
  }
  ```
</ParamField>

<ParamField path="buttonText" type="string">
  Custom text for the confirmation button.

  **Default:** `'Sign'`

  **Example:**

  ```tsx theme={null}
  options: {
    uiOptions: {
      buttonText: 'Approve & Sign'
    }
  }
  ```
</ParamField>

<ParamField path="showWalletUI" type="boolean">
  Whether to show wallet information (address, balance) in the Sign Transaction screen.

  **Default:** `true`

  **Example:**

  ```tsx theme={null}
  options: {
    uiOptions: {
      showWalletUI: false // Hide wallet details for a cleaner UI
    }
  }
  ```
</ParamField>

### Success Screen Options

<ParamField path="successHeader" type="string">
  Header text displayed on the success screen after the transaction is signed.

  **Example:**

  ```tsx theme={null}
  options: {
    uiOptions: {
      successHeader: 'Transaction Signed Successfully!'
    }
  }
  ```
</ParamField>

<ParamField path="successDescription" type="string">
  Description text displayed on the success screen after the transaction is signed.

  **Example:**

  ```tsx theme={null}
  options: {
    uiOptions: {
      successDescription: 'Your transaction is ready to be broadcast to the network'
    }
  }
  ```
</ParamField>

### Behavior Options

<ParamField path="isCancellable" type="boolean">
  Whether the user can cancel the transaction signing request.

  **Default:** `true`

  **Example:**

  ```tsx theme={null}
  options: {
    uiOptions: {
      isCancellable: false // Require user to sign or close the entire modal
    }
  }
  ```
</ParamField>

<ParamField path="transactionInfo" type="TransactionUIOptions">
  Custom options for displaying transaction information in the UI.

  This allows you to customize how transaction details are presented to the user.

  **Example:**

  ```tsx theme={null}
  options: {
    uiOptions: {
      transactionInfo: {
        // Custom transaction display configuration
      }
    }
  }
  ```
</ParamField>

## Common use cases

### Token transfer transaction

Sign a transaction to transfer ERC-20 tokens:

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

export default function SignTokenTransfer() {
  const { user } = useMoonKey();
  const { signTransaction } = useSignTransaction();
  
  const signTokenTransferTx = async () => {
    if (!user?.wallet) return;
    
    try {
      // Encode ERC-20 transfer function call
      const data = encodeFunctionData({
        abi: [{
          name: 'transfer',
          type: 'function',
          inputs: [
            { name: 'to', type: 'address' },
            { name: 'amount', type: 'uint256' }
          ]
        }],
        functionName: 'transfer',
        args: ['0xRecipientAddress', BigInt(1000000000000000000)] // 1 token with 18 decimals
      });
      
      const result = await signTransaction({
        transaction: {
          to: '0xTokenContractAddress',
          data,
          value: '0x0'
        },
        wallet: user.wallet,
        options: {
          uiOptions: {
            title: 'Sign Token Transfer',
            description: 'Transfer 1 TOKEN to recipient',
            buttonText: 'Sign Transfer',
            successHeader: 'Transfer Signed!',
            successDescription: 'Token transfer is ready to broadcast'
          }
        }
      });
      
      console.log('Signed transaction:', result.signature);
      // Broadcast the signed transaction
    } catch (error) {
      console.error('Failed to sign:', error);
    }
  };
  
  return <button onClick={signTokenTransferTx}>Sign Token Transfer</button>;
}
```

### Smart contract interaction

Sign a transaction to interact with a smart contract:

```tsx theme={null}
const signContractInteraction = async () => {
  if (!user?.wallet) return;
  
  const result = await signTransaction({
    transaction: {
      to: '0xSmartContractAddress',
      data: encodedFunctionCall,
      value: '0x0',
      gas: '0x30d40' // 200,000 gas limit
    },
    wallet: user.wallet,
    options: {
      uiOptions: {
        title: 'Sign Contract Interaction',
        description: 'Approve this transaction to interact with the smart contract',
        buttonText: 'Approve',
        showWalletUI: true,
        isCancellable: true
      }
    }
  });
  
  return result.signature;
};
```

### NFT minting transaction

Sign a transaction to mint an NFT:

```tsx theme={null}
const signNFTMint = async () => {
  if (!user?.wallet) return;
  
  const result = await signTransaction({
    transaction: {
      to: '0xNFTContractAddress',
      data: mintFunctionData,
      value: '0x16345785D8A0000' // 0.1 ETH mint price
    },
    wallet: user.wallet,
    options: {
      uiOptions: {
        title: 'Mint NFT',
        description: 'Sign to mint your NFT for 0.1 ETH',
        buttonText: 'Mint Now',
        successHeader: 'NFT Mint Signed!',
        successDescription: 'Your NFT mint transaction is ready to broadcast',
        showWalletUI: true
      }
    }
  });
  
  // Broadcast the signed transaction
  const txHash = await broadcastTransaction(result.signature);
  console.log('Transaction hash:', txHash);
};
```

### Batch signing multiple transactions

Sign multiple transactions in sequence:

```tsx theme={null}
const signMultipleTransactions = async (transactions: any[]) => {
  if (!user?.wallet) return;
  
  const signedTransactions = [];
  
  for (let i = 0; i < transactions.length; i++) {
    try {
      const result = await signTransaction({
        transaction: transactions[i],
        wallet: user.wallet,
        options: {
          uiOptions: {
            title: `Sign Transaction ${i + 1} of ${transactions.length}`,
            description: 'Please sign each transaction to proceed',
            buttonText: 'Sign',
            isCancellable: true
          }
        }
      });
      
      signedTransactions.push(result.signature);
    } catch (error) {
      console.error(`Failed to sign transaction ${i + 1}:`, error);
      break; // Stop if user cancels
    }
  }
  
  return signedTransactions;
};
```

## Complete example

Here's a complete example with full transaction signing flow:

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

export default function SignTransactionDemo() {
  const { user, isAuthenticated } = useMoonKey();
  const { signTransaction } = useSignTransaction();
  const [signedTx, setSignedTx] = useState<string | null>(null);
  const [broadcasting, setBroadcasting] = useState(false);
  
  if (!isAuthenticated) {
    return <div>Please sign in first</div>;
  }
  
  if (!user?.wallet) {
    return <div>No wallet found</div>;
  }
  
  const handleSignTransaction = async () => {
    try {
      const result = await signTransaction({
        transaction: {
          to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
          value: '0x38d7ea4c68000', // 0.001 ETH
          data: '0x'
        },
        wallet: user.wallet,
        options: {
          uiOptions: {
            title: 'Send 0.001 ETH',
            description: 'Sign this transaction to send 0.001 ETH',
            buttonText: 'Sign Transaction',
            showWalletUI: true,
            successHeader: 'Transaction Signed!',
            successDescription: 'Ready to broadcast to the network',
            isCancellable: true
          }
        }
      });
      
      setSignedTx(result.signature);
      console.log('Transaction signed:', result.signature);
    } catch (error) {
      console.error('Failed to sign transaction:', error);
      alert('Transaction signing failed or was cancelled');
    }
  };
  
  const handleBroadcast = async () => {
    if (!signedTx) return;
    
    setBroadcasting(true);
    try {
      // Broadcast the signed transaction using your preferred method
      const response = await fetch('/api/broadcast-transaction', {
        method: 'POST',
        body: JSON.stringify({ signedTransaction: signedTx })
      });
      
      const data = await response.json();
      alert(`Transaction broadcast! Hash: ${data.txHash}`);
    } catch (error) {
      console.error('Failed to broadcast:', error);
      alert('Failed to broadcast transaction');
    } finally {
      setBroadcasting(false);
    }
  };
  
  return (
    <div>
      <h2>Sign Transaction Example</h2>
      <p>Wallet: {user.wallet.address}</p>
      
      <button onClick={handleSignTransaction} disabled={!!signedTx}>
        Sign Transaction
      </button>
      
      {signedTx && (
        <div style={{ marginTop: '20px' }}>
          <h3>Signed Transaction:</h3>
          <pre style={{ 
            background: '#f5f5f5', 
            padding: '10px', 
            borderRadius: '5px',
            overflow: 'auto',
            fontSize: '12px'
          }}>
            {signedTx}
          </pre>
          
          <button 
            onClick={handleBroadcast} 
            disabled={broadcasting}
            style={{ marginTop: '10px' }}
          >
            {broadcasting ? 'Broadcasting...' : 'Broadcast Transaction'}
          </button>
        </div>
      )}
    </div>
  );
}
```

## User experience flow

<Steps>
  <Step title="Trigger transaction signing">
    Your application calls `signTransaction()` with transaction data, wallet, and optional UI options.
  </Step>

  <Step title="Sign Transaction screen appears">
    The customized Sign Transaction screen is displayed with transaction details.
  </Step>

  <Step title="Review transaction details">
    User reviews the transaction information including recipient, amount, gas fees, and any contract interactions.
  </Step>

  <Step title="Approve or reject">
    User clicks the confirm button to sign, or cancels (if `isCancellable: true`) to reject.
  </Step>

  <Step title="Transaction signed">
    If approved, MoonKey signs the transaction with the user's wallet private key.
  </Step>

  <Step title="Success screen">
    The success screen appears with the configured `successHeader` and `successDescription`.
  </Step>

  <Step title="Return signed transaction">
    The signed transaction is returned to your application for broadcasting.
  </Step>
</Steps>

## Sign Transaction vs. Send Transaction

Understanding the difference between these two operations:

<AccordionGroup>
  <Accordion title="Sign Transaction (this screen)">
    * **Only signs** the transaction
    * **Does not broadcast** to the blockchain
    * Returns a signed transaction that you must broadcast manually
    * Useful when you need control over broadcasting timing
    * Can sign transactions offline
    * Good for batch operations or complex flows
    * Example: Sign now, broadcast later when conditions are met
  </Accordion>

  <Accordion title="Send Transaction">
    * **Signs and broadcasts** in one step
    * Automatically submits the transaction to the blockchain
    * Returns the transaction hash after broadcasting
    * Simpler for basic send operations
    * Requires active network connection
    * Best for immediate transactions
    * Example: Send ETH/tokens directly to a recipient
  </Accordion>
</AccordionGroup>

## Broadcasting signed transactions

After signing a transaction, you need to broadcast it to the blockchain:

<Tabs>
  <Tab title="Ethereum">
    ```tsx theme={null}
    import { createPublicClient, http } from 'viem';
    import { mainnet } from 'viem/chains';

    // After signing
    const result = await signTransaction({
      transaction: txData,
      wallet: user.wallet
    });

    // Broadcast using viem
    const publicClient = createPublicClient({
      chain: mainnet,
      transport: http()
    });

    const txHash = await publicClient.sendRawTransaction({
      serializedTransaction: result.signature as `0x${string}`
    });

    console.log('Transaction hash:', txHash);

    // Wait for confirmation
    const receipt = await publicClient.waitForTransactionReceipt({
      hash: txHash
    });

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

  <Tab title="Solana">
    ```tsx theme={null}
    import { Connection } from '@solana/web3.js';

    // After signing
    const result = await signTransaction({
      transaction: serializedTx,
      wallet: user.wallet
    });

    // Broadcast using Solana web3.js
    const connection = new Connection('https://api.mainnet-beta.solana.com');

    const signature = await connection.sendRawTransaction(
      result.signedTransaction
    );

    console.log('Transaction signature:', signature);

    // Wait for confirmation
    const confirmation = await connection.confirmTransaction(signature);

    console.log('Transaction confirmed:', confirmation);
    ```
  </Tab>
</Tabs>

## Best practices

<AccordionGroup>
  <Accordion title="Provide clear transaction context">
    Always explain what the transaction does in the `description`:

    ```tsx theme={null}
    options: {
      uiOptions: {
        title: 'Transfer USDC',
        description: 'Send 100 USDC to 0x1234...5678. This will cost approximately $2 in gas fees.'
      }
    }
    ```
  </Accordion>

  <Accordion title="Show wallet UI for transactions">
    Enable `showWalletUI` so users can verify they have sufficient balance:

    ```tsx theme={null}
    options: {
      uiOptions: {
        showWalletUI: true // Show balance to prevent insufficient funds errors
      }
    }
    ```
  </Accordion>

  <Accordion title="Use descriptive success messages">
    Inform users about next steps after signing:

    ```tsx theme={null}
    options: {
      uiOptions: {
        successHeader: 'Transaction Signed!',
        successDescription: 'Broadcasting to the network. This may take a few seconds.'
      }
    }
    ```
  </Accordion>

  <Accordion title="Handle errors gracefully">
    Users may reject transactions or encounter errors:

    ```tsx theme={null}
    try {
      const result = await signTransaction({ 
        transaction: txData, 
        wallet: user.wallet 
      });
      await broadcastTransaction(result.signature);
    } catch (error) {
      if (error.code === 4001) {
        // User rejected
        toast.info('Transaction cancelled');
      } else {
        // Other error
        toast.error('Failed to sign transaction');
      }
    }
    ```
  </Accordion>

  <Accordion title="Validate before signing">
    Check transaction validity before prompting the user:

    ```tsx theme={null}
    // Check balance
    if (balance < transactionAmount + estimatedGas) {
      alert('Insufficient funds for transaction and gas');
      return;
    }

    // Then proceed with signing
    const result = await signTransaction({ ... });
    ```
  </Accordion>

  <Accordion title="Allow cancellation for non-critical transactions">
    Set `isCancellable: true` for most transactions:

    ```tsx theme={null}
    options: {
      uiOptions: {
        isCancellable: true // Let users back out if needed
      }
    }
    ```

    Only set `isCancellable: false` for critical flows where cancellation would break your app state.
  </Accordion>

  <Accordion title="Store and verify transaction data">
    Before broadcasting, verify the signed transaction matches your intent:

    ```tsx theme={null}
    const result = await signTransaction({ transaction: txData, wallet: user.wallet });

    // Store signature for later
    await savePendingTransaction({
      signature: result.signature,
      originalData: txData,
      timestamp: Date.now()
    });

    // Verify before broadcast
    const verified = await verifyTransaction(result.signature);
    if (verified) {
      await broadcastTransaction(result.signature);
    }
    ```
  </Accordion>
</AccordionGroup>

## Security considerations

<Warning>
  Always review transaction details carefully before signing. Malicious applications could trick users into signing transactions that drain their wallets or approve unlimited token spending.
</Warning>

**For developers:**

1. **Display full transaction details** - Show recipient, amount, gas fees, and any contract interactions
2. **Validate transaction data** - Verify the transaction is well-formed before requesting signature
3. **Check balances** - Ensure users have sufficient funds before signing
4. **Rate limit signing** - Prevent abuse by limiting how many transactions can be signed
5. **Log signed transactions** - Keep records for debugging and user support
6. **Use secure RPC endpoints** - Broadcast to trusted nodes only

**For users:**

The Sign Transaction screen helps users by:

* Displaying transaction details (recipient, amount, gas)
* Showing their wallet balance
* Providing clear context via title and description
* Allowing them to cancel if `isCancellable` is true
* Showing success confirmation before closing

## Next steps

<CardGroup cols={2}>
  <Card title="Send Transaction" icon="paper-plane" href="/authentication/user-authentication/ui-components/send-transaction">
    Sign and send transactions in one step
  </Card>

  <Card title="Sign Message" icon="signature" href="/authentication/user-authentication/ui-components/sign-message">
    Sign messages for authentication
  </Card>

  <Card title="UI Components Overview" icon="window-maximize" href="/authentication/user-authentication/ui-components/overview">
    Explore all UI components
  </Card>

  <Card title="Configure Appearance" icon="palette" href="/get-started/frontend-sdks/react/advance/configure-appearance">
    Global appearance settings
  </Card>
</CardGroup>
