MoonKey makes it easy to access a user’s embedded wallet so you can help them take onchain actions. Every authenticated user with an embedded wallet has access to their wallet through the SDK.
MoonKey currently supports embedded wallets only. Each user can have one embedded wallet per chain type (Ethereum or Solana).
Import paths:
useMoonKey comes from @moon-key/react-auth (the base package)
useWallets comes from @moon-key/react-auth/ethereum for Ethereum or @moon-key/react-auth/solana for Solana
React SDK
Usage
import { useMoonKey } from '@moon-key/react-auth' ;
function MyComponent () {
const { user , ready } = useMoonKey ();
if ( ! ready ) {
return < div > Loading... </ div > ;
}
if ( ! user ) {
return < div > Please sign in </ div > ;
}
if ( ! user . wallet ) {
return < div > No wallet found </ div > ;
}
// Access the wallet
const wallet = user . wallet ;
console . log ( 'Wallet address:' , wallet . address );
return (
< div >
< p > Your wallet: { wallet . address } </ p >
</ div >
);
}
Wallet properties
The user.wallet object contains the following properties:
The wallet’s Ethereum address (e.g., 0x1234...5678)
The chain type, always 'ethereum' for Ethereum wallets
The wallet client instance from viem for performing operations
Alternative: Using useWallets
You can also access Ethereum wallets using the useWallets hook from @moon-key/react-auth/ethereum, which returns an array of all wallets:
import { useWallets } from '@moon-key/react-auth/ethereum' ;
function MyComponent () {
const { wallets , ready } = useWallets ();
if ( ! ready ) {
return < div > Loading... </ div > ;
}
if ( wallets . length === 0 ) {
return < div > No wallets found </ div > ;
}
// Get the first (and typically only) wallet
const wallet = wallets [ 0 ];
console . log ( 'Wallet address:' , wallet . address );
return (
< div >
{ wallets . map (( wallet ) => (
< div key = { wallet . address } >
< p > Wallet: { wallet . address } </ p >
</ div >
)) }
</ div >
);
}
Waiting for wallets to be ready
When your page loads in the user’s browser, the MoonKey SDK determines what wallet the user has by loading the secure wallet infrastructure. This process happens asynchronously.
To determine if MoonKey has fully loaded the user’s wallet information, use the ready boolean returned by the useMoonKey or useWallets hooks.
The ready flag will be:
false while MoonKey is loading wallet information
true once MoonKey has determined the current wallet state
Example with ready state
import { useMoonKey } from '@moon-key/react-auth' ;
function WalletDisplay () {
const { user , ready , isAuthenticated } = useMoonKey ();
// SDK is still loading
if ( ! ready ) {
return (
< div className = "loading" >
< Spinner />
< p > Loading wallet... </ p >
</ div >
);
}
// User is not authenticated
if ( ! isAuthenticated || ! user ) {
return < div > Please sign in to view your wallet </ div > ;
}
// User doesn't have a wallet yet
if ( ! user . wallet ) {
return (
< div >
< p > You don't have a wallet yet. </ p >
< CreateWalletButton />
</ div >
);
}
// Wallet is ready to use
return (
< div >
< h2 > Your Wallet </ h2 >
< p > Address: { user . wallet . address } </ p >
< WalletActions wallet = { user . wallet } />
</ div >
);
}
useMoonKey vs useWallets
Both hooks provide access to wallet information, but they serve slightly different purposes and have different import paths:
useMoonKey
import { useMoonKey } from '@moon-key/react-auth' ;
const { user , ready , isAuthenticated } = useMoonKey ();
const wallet = user ?. wallet ;
Import from: @moon-key/react-auth (base package)
Use when:
You need access to the full user object
You want authentication state (isAuthenticated)
You need user profile information
You’re working with a single wallet per user
Returns:
Complete user object with all linked accounts
Authentication state
Wallet as part of the user object
useWallets
import { useWallets } from '@moon-key/react-auth/ethereum' ;
const { wallets , ready } = useWallets ();
const wallet = wallets [ 0 ];
Import from: @moon-key/react-auth/ethereum (for Ethereum wallets)
Use when:
You only need wallet information
You want to work with an array of wallets
You prefer a wallet-centric API
Future-proofing for multiple wallets per user
Returns:
Array of wallet objects (chain-specific)
Ready state for wallet loading
For most use cases, useMoonKey is recommended as it provides both user and wallet information in a single hook.
Common patterns
Checking for wallet existence
import { useMoonKey } from '@moon-key/react-auth' ;
function WalletRequired () {
const { user , ready } = useMoonKey ();
const hasWallet = user ?. wallet !== undefined ;
if ( ! ready ) {
return < LoadingSpinner /> ;
}
if ( ! hasWallet ) {
return (
< div >
< h2 > Wallet Required </ h2 >
< p > You need a wallet to access this feature </ p >
< CreateWalletButton />
</ div >
);
}
return < WalletFeatures wallet = { user . wallet } /> ;
}
Getting wallet for transactions
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 ;
}
const result = await sendTransaction ({
transaction: {
to: '0xRecipient...' ,
value: '0x38d7ea4c68000'
},
wallet: user . wallet
});
console . log ( 'Transaction hash:' , result . transactionHash );
};
return (
< button onClick = { handleSend } disabled = { ! user ?. wallet } >
Send 0.001 ETH
</ button >
);
}
import { useMoonKey } from '@moon-key/react-auth' ;
function WalletInfo () {
const { user , ready } = useMoonKey ();
if ( ! ready ) return < div > Loading... </ div > ;
if ( ! user ?. wallet ) return < div > No wallet </ div > ;
const { address , chainType } = user . wallet ;
// Format address for display
const shortAddress = ` ${ address . slice ( 0 , 6 ) } ... ${ address . slice ( - 4 ) } ` ;
return (
< div className = "wallet-info" >
< div className = "wallet-badge" >
< span className = "chain-type" > { chainType } </ span >
< span className = "address" title = { address } >
{ shortAddress }
</ span >
</ div >
< button onClick = { () => navigator . clipboard . writeText ( address ) } >
Copy Address
</ button >
</ div >
);
}
Finding a specific wallet (Ethereum)
import { useWallets } from '@moon-key/react-auth/ethereum' ;
function FindWalletByAddress ({ targetAddress } : { targetAddress : string }) {
const { wallets , ready } = useWallets ();
if ( ! ready ) return < div > Loading... </ div > ;
const wallet = wallets . find ( w => w . address === targetAddress );
if ( ! wallet ) {
return < div > Wallet not found: { targetAddress } </ div > ;
}
return < div > Found wallet: { wallet . address } </ div > ;
}
Complete example
Here’s a complete example showing wallet access and usage:
'use client' ;
import { useMoonKey } from '@moon-key/react-auth' ;
import { useSendTransaction } from '@moon-key/react-auth/ethereum' ;
import { useState } from 'react' ;
export default function WalletDashboard () {
const { user , ready , isAuthenticated } = useMoonKey ();
const { sendTransaction } = useSendTransaction ();
const [ isSending , setIsSending ] = useState ( false );
// Loading state
if ( ! ready ) {
return (
< div className = "loading-container" >
< div className = "spinner" />
< p > Loading wallet information... </ p >
</ div >
);
}
// Not authenticated
if ( ! isAuthenticated || ! user ) {
return (
< div className = "auth-prompt" >
< h2 > Sign In Required </ h2 >
< p > Please sign in to access your wallet </ p >
< LoginButton />
</ div >
);
}
// No wallet
if ( ! user . wallet ) {
return (
< div className = "no-wallet" >
< h2 > Create Your Wallet </ h2 >
< p > You don't have a wallet yet. Create one to get started. </ p >
< CreateWalletButton />
</ div >
);
}
// Has wallet
const { wallet } = user ;
const shortAddress = ` ${ wallet . address . slice ( 0 , 6 ) } ... ${ wallet . address . slice ( - 4 ) } ` ;
const handleSendTest = async () => {
setIsSending ( true );
try {
const result = await sendTransaction ({
transaction: {
to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' ,
value: '0x38d7ea4c68000' // 0.001 ETH
},
wallet
});
alert ( `Transaction sent! Hash: ${ result . transactionHash } ` );
} catch ( error ) {
console . error ( 'Transaction failed:' , error );
alert ( 'Transaction failed' );
} finally {
setIsSending ( false );
}
};
return (
< div className = "wallet-dashboard" >
< div className = "wallet-header" >
< h2 > Your Wallet </ h2 >
< div className = "wallet-address" >
< span className = "address" title = { wallet . address } >
{ shortAddress }
</ span >
< button
className = "copy-btn"
onClick = { () => {
navigator . clipboard . writeText ( wallet . address );
alert ( 'Address copied!' );
} }
>
Copy
</ button >
</ div >
</ div >
< div className = "wallet-info" >
< div className = "info-row" >
< span className = "label" > Chain Type: </ span >
< span className = "value" > { wallet . chainType } </ span >
</ div >
< div className = "info-row" >
< span className = "label" > Full Address: </ span >
< span className = "value mono" > { wallet . address } </ span >
</ div >
</ div >
< div className = "wallet-actions" >
< button
className = "primary-btn"
onClick = { handleSendTest }
disabled = { isSending }
>
{ isSending ? 'Sending...' : 'Send Test Transaction' }
</ button >
< button className = "secondary-btn" >
View on Explorer
</ button >
</ div >
</ div >
);
}
Best practices
Always check the ready state
Wait for the SDK to be ready before accessing wallet information: const { user , ready } = useMoonKey ();
if ( ! ready ) {
return < LoadingSpinner /> ;
}
// Now safe to access user.wallet
Handle missing wallets gracefully
Not all users will have wallets. Provide clear next steps: if ( ! user ?. wallet ) {
return (
< div >
< p > You don't have a wallet yet </ p >
< CreateWalletButton />
</ div >
);
}
Pass wallet to transaction hooks
Always pass the wallet object to transaction methods: await sendTransaction ({
transaction: txData ,
wallet: user . wallet // Required
});
Use TypeScript for type safety
TypeScript helps catch wallet-related errors: const wallet : Wallet | undefined = user ?. wallet ;
if ( wallet ) {
// TypeScript knows wallet exists here
console . log ( wallet . address );
}
Format addresses for display
Troubleshooting
If user.wallet is undefined:
Check if the user is authenticated
Verify the SDK is ready (ready === true)
Confirm the user has created a wallet
Check automatic wallet creation configuration
Ready state never becomes true
If ready stays false:
Verify MoonKeyProvider is properly configured
Check browser console for errors
Ensure appId is correct
Try refreshing the page
Cannot access wallet properties
If wallet properties are inaccessible:
Verify you’re using the correct import path
Check that you’re using the right SDK for your chain (ethereum vs solana)
Ensure the user object has loaded