Add transaction signing and SOL transfers to a React Native app using Mobile Wallet Adapter. Use when the user wants to send SOL, sign transactions, implement transfers, or add transaction functionality to their Solana mobile app.
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
npx agent-skills-cli listSkill Instructions
name: mwa-transactions description: Add transaction signing and SOL transfers to a React Native app using Mobile Wallet Adapter. Use when the user wants to send SOL, sign transactions, implement transfers, or add transaction functionality to their Solana mobile app.
MWA Transactions
Add SOL transfers and transaction signing using Mobile Wallet Adapter.
When to Use
- User wants to send SOL from their app
- User wants to sign and send transactions
- User wants to implement transfer functionality
When NOT to Use
- MWA not set up → Use
mwa-setupfirst - Wallet connection not working → Use
mwa-connectionfirst
Prerequisites
- MWA setup complete
- Wallet connection working
- User can connect/disconnect successfully
The Hook
import { useMobileWallet } from '@wallet-ui/react-native-web3js';
const { account, connection, signAndSendTransaction } = useMobileWallet();
| Property | Type | Description |
|---|---|---|
account | { address, publicKey } | Connected wallet |
connection | Connection | Solana RPC connection |
signAndSendTransaction | async (tx) => string | Sign and broadcast, returns signature |
signMessage | async (msg) => Uint8Array | Sign arbitrary message |
Implementation
SOL Transfer
import { useState } from 'react';
import { View, TextInput, Pressable, Text, Alert } from 'react-native';
import { useMobileWallet } from '@wallet-ui/react-native-web3js';
import {
PublicKey,
Transaction,
SystemProgram,
LAMPORTS_PER_SOL,
} from '@solana/web3.js';
export function SendSol() {
const { account, connection, signAndSendTransaction } = useMobileWallet();
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [loading, setLoading] = useState(false);
const handleSend = async () => {
if (!account) return;
try {
setLoading(true);
// Get fresh blockhash
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash();
// Build transaction
const transaction = new Transaction({
feePayer: account.address,
blockhash,
lastValidBlockHeight,
}).add(
SystemProgram.transfer({
fromPubkey: account.address,
toPubkey: new PublicKey(recipient),
lamports: Math.floor(parseFloat(amount) * LAMPORTS_PER_SOL),
})
);
// Sign and send
const signature = await signAndSendTransaction(transaction);
// Confirm
await connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight,
});
Alert.alert('Success', `Sent ${amount} SOL\n\nSignature: ${signature.slice(0, 20)}...`);
} catch (error: any) {
Alert.alert('Error', error.message);
} finally {
setLoading(false);
}
};
if (!account) return <Text>Connect wallet first</Text>;
return (
<View style={{ padding: 20 }}>
<TextInput
placeholder="Recipient Address"
value={recipient}
onChangeText={setRecipient}
style={{ borderWidth: 1, padding: 10, marginBottom: 10, borderRadius: 4 }}
/>
<TextInput
placeholder="Amount (SOL)"
value={amount}
onChangeText={setAmount}
keyboardType="decimal-pad"
style={{ borderWidth: 1, padding: 10, marginBottom: 10, borderRadius: 4 }}
/>
<Pressable
onPress={handleSend}
disabled={loading || !recipient || !amount}
style={{
backgroundColor: '#9945FF',
padding: 15,
borderRadius: 8,
opacity: loading ? 0.6 : 1,
}}
>
<Text style={{ color: 'white', textAlign: 'center', fontWeight: 'bold' }}>
{loading ? 'Sending...' : 'Send SOL'}
</Text>
</Pressable>
</View>
);
}
Transaction Pattern
All transactions follow this pattern:
// 1. Get fresh blockhash
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
// 2. Build transaction
const transaction = new Transaction({
feePayer: account.address,
blockhash,
lastValidBlockHeight,
}).add(/* instructions */);
// 3. Sign and send
const signature = await signAndSendTransaction(transaction);
// 4. Confirm
await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });
Key Points
-
Use hook's connection: Don't create
new Connection(). Useconnectionfrom the hook. -
Fresh blockhash: Always get a new blockhash. They expire in ~150 blocks (~1-2 minutes).
-
Recipient must exist: Some wallets reject transactions to accounts that don't exist on-chain. Test with known addresses first.
-
No Buffer: React Native doesn't have
Buffer. For encoding:// Base64 encoding (if needed) const base64 = btoa(String.fromCharCode(...uint8Array));
Troubleshooting
"Buffer doesn't exist" Error
// WRONG - Buffer doesn't exist in RN
Buffer.from(signature).toString('base64');
// CORRECT
btoa(String.fromCharCode(...signature));
Transaction Fails to Random Addresses
Some wallets require recipient to exist on-chain. Test with a known funded address first.
"payloads invalid for signing" Error
Usually means the transaction is malformed or blockhash expired. Get a fresh blockhash and rebuild.
Reference
For complete implementation with balance display, explorer links, and more error handling:
More by solana-mobile
View allVerify Seeker device ownership using Seeker Genesis Token (SGT) verification. Use when the user wants to gate content to Seeker owners, verify device ownership, implement anti-Sybil measures, or distribute device-specific rewards.
Integrate Mobile Wallet Adapter (MWA) for wallet connection and transaction signing in React Native Expo apps using Beeman's Wallet UI SDK (@wallet-ui/react-native-web3js). Use when the user requests to add wallet connection, integrate Solana wallet support, add "connect wallet" button, implement transaction signing, send SOL transfers, or set up MWA in their React Native app.
Add wallet connect/disconnect functionality to a React Native app using Mobile Wallet Adapter. Use when the user wants to add a connect wallet button, implement wallet connection, show connected wallet address, or handle wallet state.
Set up Mobile Wallet Adapter dependencies, crypto polyfills, and providers in a React Native Expo app. Use when the user needs to install MWA packages, configure polyfills, set up MobileWalletProvider, or prepare their app for wallet integration.
