The Send Transaction screen is displayed when your application requests to sign and broadcast a blockchain transaction. This screen shows users the transaction details, signs it with their wallet, and automatically broadcasts it to the blockchain network.
Send Transaction combines signing and broadcasting in one step. For more control over when a transaction is broadcast, use Sign Transaction instead.
How it works
When your application requests to send a transaction:
Your app calls the send transaction method from the MoonKey SDK
The Send Transaction screen appears, displaying transaction details
User reviews the transaction information (recipient, amount, gas, etc.)
User approves or rejects the transaction
If approved, MoonKey signs the transaction with the user’s wallet
MoonKey automatically broadcasts the signed transaction to the blockchain
The transaction hash is returned to your application
Triggering the Send Transaction screen
Use the useSendTransaction hook from the MoonKey SDK to trigger the Send Transaction screen:
import { useMoonKey } from '@moon-key/react-auth' ;
import { useSendTransaction } from '@moon-key/react-auth/ethereum' ;
export default function SendTransactionButton () {
const { user } = useMoonKey ();
const { sendTransaction } = useSendTransaction ();
const handleSend = async () => {
if ( ! user ?. wallet ) {
alert ( 'No wallet found' );
return ;
}
try {
const result = await sendTransaction ({
transaction: {
to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' ,
value: '0x38d7ea4c68000' , // 0.001 ETH in hex
data: '0x'
},
wallet: user . wallet ,
uiConfig: {
title: 'Send 0.001 ETH' ,
description: 'Transfer 0.001 ETH to the recipient' ,
confirmButtonText: 'Send' ,
showWalletUI: true ,
success: {
title: 'Transaction Sent!' ,
description: 'Your transaction is being processed'
}
}
});
console . log ( 'Transaction hash:' , result . transactionHash );
alert ( `Transaction sent! Hash: ${ result . transactionHash } ` );
} catch ( error ) {
console . error ( 'Transaction failed:' , error );
}
};
return < button onClick = { handleSend } > Send 0.001 ETH </ button > ;
}
Customizing the Send Transaction screen
You can customize the Send Transaction screen by passing UI configuration in the uiConfig object:
const result = await sendTransaction ({
transaction: transactionData ,
wallet: user . wallet ,
uiConfig: {
logoUrl: 'https://your-app.com/logo.png' ,
title: 'Send Payment' ,
description: 'Review and send this payment' ,
confirmButtonText: 'Send Now' ,
cancelButtonText: 'Cancel' ,
showWalletUI: true ,
waitBeforeCloseTime: 2000 ,
advancedTransaction: {
title: 'Advanced Settings' ,
closeButtonText: 'Close'
},
success: {
title: 'Payment Sent!' ,
description: 'Your payment is on its way'
},
failed: {
title: 'Transaction Failed' ,
description: 'Your transaction could not be completed' ,
errorTitle: 'Error Details' ,
closeButtonText: 'Close'
}
}
});
Configuration options
Basic UI Options
URL to your company logo. Displayed at the top of the Send Transaction screen. Example: uiConfig : {
logoUrl : 'https://myapp.com/logo.png'
}
Custom title text displayed at the top of the Send Transaction screen. Example: uiConfig : {
title : 'Send Payment'
}
Custom description text displayed below the title, providing context for the transaction. Example: uiConfig : {
description : 'Review the payment details before sending'
}
Custom text for the confirm/send button. Default: 'Send'Example: uiConfig : {
confirmButtonText : 'Send Now'
}
Custom text for the cancel button. Default: 'Cancel'Example: uiConfig : {
cancelButtonText : 'Go Back'
}
Whether to show wallet information (address, balance) in the Send Transaction screen. Default: trueExample: uiConfig : {
showWalletUI : true // Show balance to prevent insufficient funds errors
}
Time in milliseconds to wait before automatically closing the screen after a successful transaction. Default: 1500 (1.5 seconds)Example: uiConfig : {
waitBeforeCloseTime : 2000 // Wait 2 seconds before closing
}
Advanced Transaction Options
Configuration for the advanced transaction settings screen. Title for the advanced transaction settings screen. Example: uiConfig : {
advancedTransaction : {
title : 'Advanced Settings'
}
}
Text for the close button in the advanced settings screen. Example: uiConfig : {
advancedTransaction : {
closeButtonText : 'Done'
}
}
Success Screen Options
Configuration for the success screen displayed after the transaction is sent. Title text displayed on the success screen. Example: uiConfig : {
success : {
title : 'Transaction Sent Successfully!'
}
}
Description text displayed on the success screen. Example: uiConfig : {
success : {
description : 'Your transaction has been broadcast and is being confirmed'
}
}
Failed Screen Options
Configuration for the error screen displayed if the transaction fails. Title text displayed on the failed screen. Example: uiConfig : {
failed : {
title : 'Transaction Failed'
}
}
Description text displayed on the failed screen. Example: uiConfig : {
failed : {
description : 'Your transaction could not be completed. Please try again.'
}
}
Title for the error details section. Example: uiConfig : {
failed : {
errorTitle : 'Error Details'
}
}
Text for the close button on the failed screen. Example: uiConfig : {
failed : {
closeButtonText : 'Close'
}
}
Common use cases
Simple ETH/SOL transfer
Send native currency to a recipient:
import { useMoonKey } from '@moon-key/react-auth' ;
import { useSendTransaction } from '@moon-key/react-auth/ethereum' ;
import { parseEther } from 'viem' ;
export default function SendETH () {
const { user } = useMoonKey ();
const { sendTransaction } = useSendTransaction ();
const sendETH = async ( recipient : string , amount : string ) => {
if ( ! user ?. wallet ) return ;
try {
const result = await sendTransaction ({
transaction: {
to: recipient ,
value: parseEther ( amount ). toString ( 16 ) // Convert to hex
},
wallet: user . wallet ,
uiConfig: {
title: `Send ${ amount } ETH` ,
description: `Transfer ${ amount } ETH to ${ recipient . slice ( 0 , 6 ) } ... ${ recipient . slice ( - 4 ) } ` ,
confirmButtonText: 'Send ETH' ,
showWalletUI: true ,
success: {
title: 'ETH Sent!' ,
description: 'Your ETH transfer is being confirmed on the blockchain'
}
}
});
console . log ( 'Transaction hash:' , result . transactionHash );
return result . transactionHash ;
} catch ( error ) {
console . error ( 'Failed to send ETH:' , error );
throw error ;
}
};
return (
< button onClick = { () => sendETH ( '0xRecipient...' , '0.1' ) } >
Send 0.1 ETH
</ button >
);
}
Token transfer
Send ERC-20 or SPL tokens:
import { useMoonKey } from '@moon-key/react-auth' ;
import { useSendTransaction } from '@moon-key/react-auth/ethereum' ;
import { encodeFunctionData } from 'viem' ;
export default function SendTokens () {
const { user } = useMoonKey ();
const { sendTransaction } = useSendTransaction ();
const sendTokens = async ( tokenAddress : string , recipient : string , amount : bigint ) => {
if ( ! user ?. wallet ) return ;
try {
// Encode ERC-20 transfer
const data = encodeFunctionData ({
abi: [{
name: 'transfer' ,
type: 'function' ,
inputs: [
{ name: 'to' , type: 'address' },
{ name: 'amount' , type: 'uint256' }
]
}],
functionName: 'transfer' ,
args: [ recipient , amount ]
});
const result = await sendTransaction ({
transaction: {
to: tokenAddress ,
data ,
value: '0x0'
},
wallet: user . wallet ,
uiConfig: {
title: 'Send USDC' ,
description: 'Transfer 100 USDC to recipient' ,
confirmButtonText: 'Send Tokens' ,
success: {
title: 'Tokens Sent!' ,
description: 'Your token transfer is being processed'
}
}
});
console . log ( 'Transaction hash:' , result . transactionHash );
return result . transactionHash ;
} catch ( error ) {
console . error ( 'Failed to send tokens:' , error );
throw error ;
}
};
return (
< button onClick = { () => sendTokens ( '0xUSDC...' , '0xRecipient...' , BigInt ( 100_000000 )) } >
Send 100 USDC
</ button >
);
}
NFT transfer
Transfer an NFT to another wallet:
const transferNFT = async ( nftContract : string , tokenId : string , recipient : string ) => {
if ( ! user ?. wallet ) return ;
// Encode ERC-721 transferFrom
const data = encodeFunctionData ({
abi: [{
name: 'transferFrom' ,
type: 'function' ,
inputs: [
{ name: 'from' , type: 'address' },
{ name: 'to' , type: 'address' },
{ name: 'tokenId' , type: 'uint256' }
]
}],
functionName: 'transferFrom' ,
args: [ user . wallet . address , recipient , BigInt ( tokenId )]
});
const result = await sendTransaction ({
transaction: {
to: nftContract ,
data ,
value: '0x0'
},
wallet: user . wallet ,
uiConfig: {
title: 'Transfer NFT' ,
description: `Transfer NFT # ${ tokenId } to ${ recipient . slice ( 0 , 6 ) } ... ${ recipient . slice ( - 4 ) } ` ,
confirmButtonText: 'Transfer' ,
showWalletUI: true ,
success: {
title: 'NFT Transferred!' ,
description: 'Your NFT has been sent to the recipient'
}
}
});
return result . transactionHash ;
};
Mint NFT
Mint an NFT with payment:
const mintNFT = async ( nftContract : string , mintPrice : string ) => {
if ( ! user ?. wallet ) return ;
const result = await sendTransaction ({
transaction: {
to: nftContract ,
data: encodedMintFunction ,
value: mintPrice // e.g., '0x16345785D8A0000' for 0.1 ETH
},
wallet: user . wallet ,
uiConfig: {
title: 'Mint NFT' ,
description: 'Mint your NFT for 0.1 ETH' ,
confirmButtonText: 'Mint Now' ,
showWalletUI: true ,
success: {
title: 'NFT Minted!' ,
description: 'Your NFT is being minted. Check your wallet in a few moments.'
}
}
});
console . log ( 'Mint transaction hash:' , result . transactionHash );
return result . transactionHash ;
};
Contract interaction with gas estimation
Send a transaction with custom gas settings:
const interactWithContract = async () => {
if ( ! user ?. wallet ) return ;
const result = await sendTransaction ({
transaction: {
to: '0xContractAddress' ,
data: encodedFunctionCall ,
value: '0x0' ,
gas: '0x30d40' , // 200,000 gas limit
gasPrice: '0x4a817c800' // 20 gwei
},
wallet: user . wallet ,
uiConfig: {
title: 'Execute Contract Function' ,
description: 'Call the contract function with the specified parameters' ,
confirmButtonText: 'Execute' ,
success: {
title: 'Contract Called!' ,
description: 'Your transaction is being confirmed'
}
}
});
return result . transactionHash ;
};
Complete example
Here’s a complete example with transaction tracking:
'use client' ;
import { useMoonKey } from '@moon-key/react-auth' ;
import { useSendTransaction } from '@moon-key/react-auth/ethereum' ;
import { useState } from 'react' ;
import { parseEther } from 'viem' ;
export default function SendTransactionDemo () {
const { user , isAuthenticated } = useMoonKey ();
const { sendTransaction } = useSendTransaction ();
const [ txHash , setTxHash ] = useState < string | null >( null );
const [ sending , setSending ] = useState ( false );
const [ recipient , setRecipient ] = useState ( '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' );
const [ amount , setAmount ] = useState ( '0.001' );
if ( ! isAuthenticated ) {
return < div > Please sign in first </ div > ;
}
if ( ! user ?. wallet ) {
return < div > No wallet found </ div > ;
}
const handleSendTransaction = async () => {
setSending ( true );
try {
const result = await sendTransaction ({
transaction: {
to: recipient ,
value: `0x ${ parseEther ( amount ). toString ( 16 ) } ` ,
data: '0x'
},
wallet: user . wallet ,
uiConfig: {
title: `Send ${ amount } ETH` ,
description: `Transfer ${ amount } ETH to ${ recipient . slice ( 0 , 6 ) } ... ${ recipient . slice ( - 4 ) } ` ,
confirmButtonText: 'Send Transaction' ,
cancelButtonText: 'Cancel' ,
showWalletUI: true ,
success: {
title: 'Transaction Sent!' ,
description: 'Your transaction is being confirmed on the blockchain'
},
failed: {
title: 'Transaction Failed' ,
description: 'Please try again or check your balance' ,
closeButtonText: 'Close'
}
}
});
setTxHash ( result . transactionHash );
console . log ( 'Transaction sent:' , result . transactionHash );
alert ( 'Transaction sent successfully!' );
} catch ( error ) {
console . error ( 'Failed to send transaction:' , error );
alert ( 'Transaction failed or was cancelled' );
} finally {
setSending ( false );
}
};
return (
< div style = { { padding: '20px' } } >
< h2 > Send Transaction Example </ h2 >
< p > Wallet: { user . wallet . address } </ p >
< div style = { { marginTop: '20px' } } >
< div style = { { marginBottom: '10px' } } >
< label >
Recipient Address:
< input
type = "text"
value = { recipient }
onChange = { ( e ) => setRecipient ( e . target . value ) }
style = { {
width: '100%' ,
padding: '8px' ,
marginTop: '5px'
} }
/>
</ label >
</ div >
< div style = { { marginBottom: '10px' } } >
< label >
Amount (ETH):
< input
type = "text"
value = { amount }
onChange = { ( e ) => setAmount ( e . target . value ) }
style = { {
width: '100%' ,
padding: '8px' ,
marginTop: '5px'
} }
/>
</ label >
</ div >
< button
onClick = { handleSendTransaction }
disabled = { sending || ! recipient || ! amount }
style = { {
padding: '10px 20px' ,
marginTop: '10px'
} }
>
{ sending ? 'Sending...' : 'Send Transaction' }
</ button >
</ div >
{ txHash && (
< div style = { {
marginTop: '20px' ,
padding: '15px' ,
background: '#f0f9ff' ,
borderRadius: '5px'
} } >
< h3 > Transaction Sent! </ h3 >
< p > Transaction Hash: </ p >
< pre style = { {
background: '#fff' ,
padding: '10px' ,
borderRadius: '5px' ,
overflow: 'auto' ,
fontSize: '12px'
} } >
{ txHash }
</ pre >
< a
href = { `https://etherscan.io/tx/ ${ txHash } ` }
target = "_blank"
rel = "noopener noreferrer"
style = { { color: '#0066cc' } }
>
View on Etherscan →
</ a >
</ div >
) }
</ div >
);
}
User experience flow
Trigger transaction
Your application calls sendTransaction() with transaction data, wallet, and optional UI options.
Send Transaction screen appears
The customized Send Transaction screen is displayed with transaction details.
Review transaction details
User reviews the transaction information including recipient, amount, gas fees, and any contract interactions.
Approve or reject
User clicks the send button to proceed, or cancels (if isCancellable: true) to reject.
Transaction signed and broadcast
If approved, MoonKey signs the transaction and automatically broadcasts it to the blockchain network.
Success screen
The success screen appears with the configured successHeader and successDescription.
Return transaction hash
The transaction hash (or signature for Solana) is returned to your application for tracking.
Send Transaction vs. Sign Transaction
Understanding when to use each:
Send Transaction (this screen)
Signs and broadcasts in one step
Automatically submits to the blockchain
Returns transaction hash immediately
Simpler API for basic operations
Best for immediate transactions
User can’t control broadcast timing
Most common for standard transfers
Example: Send ETH, transfer tokens, mint NFTs
Only signs the transaction
Does not broadcast automatically
Returns signed transaction data
Requires manual broadcasting
More control over when to broadcast
Can sign offline
Good for batch operations or complex flows
Example: Multi-step flows, conditional broadcasting
Use Send Transaction for most use cases. Only use Sign Transaction when you need explicit control over the broadcasting step.
Tracking transaction status
After sending a transaction, you typically want to track its confirmation status:
import { createPublicClient , http } from 'viem' ;
import { mainnet } from 'viem/chains' ;
// After sending
const result = await sendTransaction ({
transaction: txData ,
wallet: user . wallet
});
console . log ( 'Transaction hash:' , result . transactionHash );
// Track confirmation using viem
const publicClient = createPublicClient ({
chain: mainnet ,
transport: http ()
});
// Wait for transaction receipt
const receipt = await publicClient . waitForTransactionReceipt ({
hash: result . transactionHash as `0x ${ string } `
});
console . log ( 'Transaction confirmed!' , receipt );
console . log ( 'Block number:' , receipt . blockNumber );
console . log ( 'Gas used:' , receipt . gasUsed );
console . log ( 'Status:' , receipt . status ); // 'success' or 'reverted'
// Get transaction details
const transaction = await publicClient . getTransaction ({
hash: result . transactionHash as `0x ${ string } `
});
console . log ( 'Transaction details:' , transaction );
Best practices
Show clear transaction details
Always provide clear information about what the transaction does: uiConfig : {
title : 'Send 100 USDC' ,
description : 'Transfer 100 USDC to alice.eth. Estimated gas: $2.50'
}
Validate inputs before sending
Handle errors appropriately
Different errors require different handling: try {
const result = await sendTransaction ({
transaction: txData ,
wallet: user . wallet
});
toast . success ( 'Transaction sent!' );
} catch ( error ) {
if ( error . code === 4001 ) {
// User rejected
toast . info ( 'Transaction cancelled' );
} else if ( error . message ?. includes ( 'insufficient funds' )) {
toast . error ( 'Insufficient balance for transaction' );
} else if ( error . message ?. includes ( 'gas' )) {
toast . error ( 'Gas estimation failed. Transaction may fail.' );
} else {
toast . error ( 'Transaction failed. Please try again.' );
}
console . error ( 'Transaction error:' , error );
}
Provide transaction tracking
Help users track their transaction: const result = await sendTransaction ({ ... });
// Show link to block explorer
const explorerUrl = `https://etherscan.io/tx/ ${ result . transactionHash } ` ;
toast . success (
< div >
Transaction sent!
< a href = { explorerUrl } target = "_blank" > View on Etherscan </ a >
</ div >
);
// Store for user's transaction history
await saveTransaction ({
hash: result . transactionHash ,
from: user . wallet . address ,
to: recipient ,
amount ,
timestamp: Date . now ()
});
Enable wallet UI for balance checks
Always show wallet UI so users can verify they have sufficient funds: uiConfig : {
showWalletUI : true // Critical for preventing failed transactions
}
Set appropriate gas limits
For complex transactions, set appropriate gas limits: // Estimate gas first
const estimatedGas = await publicClient . estimateGas ({
to: recipient ,
data ,
value: amount
});
// Add 20% buffer
const gasLimit = ( estimatedGas * 120 n ) / 100 n ;
const result = await sendTransaction ({
transaction: {
to: recipient ,
data ,
value: amount ,
gas: `0x ${ gasLimit . toString ( 16 ) } `
},
wallet: user . wallet
});
Store transaction history
Keep a record of sent transactions for user reference: const result = await sendTransaction ({ ... });
// Store in local storage or database
const txRecord = {
hash: result . transactionHash ,
from: user . wallet . address ,
to: recipient ,
amount ,
timestamp: Date . now (),
status: 'pending'
};
await saveTxHistory ( txRecord );
// Update status when confirmed
const receipt = await waitForReceipt ( result . transactionHash );
await updateTxStatus ( result . transactionHash , receipt . status );
Use descriptive success messages
Inform users about next steps: uiConfig : {
success : {
title : 'Payment Sent!' ,
description : 'Your payment is being confirmed. This usually takes 15-30 seconds.'
}
}
Error handling
Common errors and how to handle them:
try {
const result = await sendTransaction ({ ... });
return result . transactionHash ;
} catch ( error ) {
// User rejected the transaction
if ( error . code === 4001 ) {
console . log ( 'User cancelled transaction' );
return null ;
}
// Insufficient funds
if ( error . message ?. includes ( 'insufficient funds' )) {
alert ( 'Not enough balance to complete transaction' );
return null ;
}
// Gas estimation failed
if ( error . message ?. includes ( 'gas required exceeds allowance' )) {
alert ( 'Transaction would fail. Please check parameters.' );
return null ;
}
// Network error
if ( error . message ?. includes ( 'network' )) {
alert ( 'Network error. Please check your connection.' );
return null ;
}
// Generic error
console . error ( 'Transaction error:' , error );
alert ( 'Transaction failed. Please try again.' );
return null ;
}
Security considerations
Always validate transaction parameters before sending. Malicious applications could trick users into sending funds to unintended recipients or approving dangerous contract interactions.
For developers:
Validate all inputs - Check addresses, amounts, and data before sending
Show full details - Display recipient, amount, gas fees, and contract interactions
Estimate gas - Provide accurate gas estimates to prevent failed transactions
Check balances - Verify sufficient funds before attempting to send
Rate limit - Prevent abuse by limiting transaction frequency
Log transactions - Keep records for debugging and support
Handle errors - Provide clear error messages for different failure scenarios
For users:
The Send Transaction screen helps users by:
Displaying full transaction details before sending
Showing wallet balance to prevent insufficient funds errors
Providing clear context via title and description
Allowing cancellation if isCancellable is true
Showing success confirmation with transaction hash
Next steps