MoonKey enables you to create embedded wallets for your users programmatically. These self-custodial wallets are owned by the authenticated user and secured using distributed key sharding.
You can create wallets either automatically when users authenticate, or manually when needed using the SDK.
MoonKey supports automatically creating embedded wallets for your users when they log in to your app. View the automatic wallet creation guide to learn more.
React SDK
To create an Ethereum wallet with the React SDK, use the createWallet method from the useCreateWallet hook: Usage import { useCreateWallet } from '@moon-key/react-auth/ethereum' ;
function CreateWalletButton () {
const { createWallet } = useCreateWallet ();
const handleCreate = async () => {
try {
const wallet = await createWallet ();
console . log ( 'Wallet created:' , wallet . address );
} catch ( error ) {
console . error ( 'Failed to create wallet:' , error );
}
};
return < button onClick = { handleCreate } > Create Wallet </ button > ;
}
Returns A Promise for the wallet object containing the created wallet’s details. The wallet’s Ethereum address (e.g., 0x1234...5678)
The chain type, always 'ethereum' for Ethereum wallets
The wallet client instance for performing operations
Callbacks You can optionally register onSuccess or onError callbacks on the useCreateWallet hook: import { useCreateWallet } from '@moon-key/react-auth/ethereum' ;
const { createWallet } = useCreateWallet ({
onSuccess : ( wallet ) => {
console . log ( 'Wallet created successfully:' , wallet . address );
// Navigate to wallet setup or show success message
},
onError : ( error ) => {
console . error ( 'Failed to create wallet:' , error );
// Show error message to user
}
});
Optional callback to run after a user successfully creates a wallet.
Optional callback to run if there is an error during wallet creation.
Complete example 'use client' ;
import { useCreateWallet , useMoonKey } from '@moon-key/react-auth/ethereum' ;
import { useState } from 'react' ;
export default function WalletCreation () {
const { user , isAuthenticated } = useMoonKey ();
const [ isCreating , setIsCreating ] = useState ( false );
const { createWallet } = useCreateWallet ({
onSuccess : ( wallet ) => {
console . log ( 'Wallet created:' , wallet . address );
alert ( `Wallet created: ${ wallet . address } ` );
},
onError : ( error ) => {
console . error ( 'Error:' , error );
alert ( 'Failed to create wallet' );
}
});
if ( ! isAuthenticated ) {
return < div > Please sign in first </ div > ;
}
if ( user ?. wallet ) {
return (
< div >
< p > You already have a wallet: </ p >
< p > { user . wallet . address } </ p >
</ div >
);
}
const handleCreate = async () => {
setIsCreating ( true );
try {
await createWallet ();
} finally {
setIsCreating ( false );
}
};
return (
< div >
< h2 > Create Your Wallet </ h2 >
< p > Create a self-custodial Ethereum wallet </ p >
< button onClick = { handleCreate } disabled = { isCreating } >
{ isCreating ? 'Creating...' : 'Create Wallet' }
</ button >
</ div >
);
}
REST API
To create a new wallet for a user via the REST API, make a POST request to:
POST https://api.moonkey.fun/v1/wallets/create
You must specify the user ID as the owner of the wallet. You can obtain a user ID by first creating a user before creating the wallet.
Authentication
Include your Secret API Key in the Authorization header:
Authorization: Bearer sk_test_YOUR_SECRET_KEY
Request body
wallet_type
'ethereum' | 'solana'
required
The type of wallet to create.
'ethereum' - Create an Ethereum/EVM wallet
'solana' - Create a Solana/SVM wallet
The ID of the user who will own this wallet (e.g., user_2Cu2uVhYy0OVgRcO913OsqIVaPI).
Response
Unique ID of the created wallet. This is the primary identifier for the wallet.
The ID of your MoonKey application.
The ID of the user who owns this wallet.
The wallet’s public address (Ethereum address or Solana public key).
The type of wallet created.
Whether the wallet has been verified. Always true for newly created wallets.
Whether this is the user’s default wallet for this chain type.
Unix timestamp (in seconds) when the wallet was created.
Unix timestamp (in seconds) when the wallet was last updated.
Example request
curl --request POST \
--url https://api.moonkey.fun/v1/wallets/create \
--header 'Authorization: Bearer sk_test_YOUR_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data '{
"wallet_type": "ethereum",
"user_id": "user_2Cu2uVhYy0OVgRcO913OsqIVaPI"
}'
Example response
{
"id" : "wallet_2Cu2uYcbwY9kcAFe2zd0P0SHftK" ,
"app_id" : "app_24ydphdixx2ydhF0E5WUFUKWNqi" ,
"user_id" : "user_2Cu2uVhYy0OVgRcO913OsqIVaPI" ,
"public_address" : "0xf1347fd847f19c250b4c9678ecaa27b0f6ce8804" ,
"wallet_type" : "ethereum" ,
"verified" : true ,
"is_default" : true ,
"is_read_only" : false ,
"is_imported" : false ,
"created_at" : 1659638371 ,
"updated_at" : 1659638371
}
Automatic vs manual wallet creation
MoonKey provides two approaches to wallet creation:
Automatic creation
Configure MoonKey to automatically create wallets when users authenticate:
import { MoonKeyProvider } from '@moon-key/react-auth' ;
< MoonKeyProvider
publishableKey = "your_publishable_key"
config = { {
embeddedWallets: {
createOnLogin: 'always' // Create wallet automatically for all users
}
} }
>
{ children }
</ MoonKeyProvider >
Best for:
Apps where all users need wallets
Gaming applications
NFT marketplaces
DeFi applications
Simplest onboarding experience
Learn more about automatic wallet creation .
Manual creation
Use the useCreateWallet hook to create wallets programmatically:
const { createWallet } = useCreateWallet ();
await createWallet ();
Best for:
Apps with optional blockchain features
Progressive onboarding flows
Users who opt-in to web3 features
When you want to delay wallet creation for UX reasons
Checking if a user has a wallet
Before creating a wallet, check if the user already has one:
import { useMoonKey } from '@moon-key/react-auth' ;
function MyComponent () {
const { user } = useMoonKey ();
if ( user ?. wallet ) {
// User already has a wallet
console . log ( 'Wallet address:' , user . wallet . address );
} else {
// User doesn't have a wallet yet
// Show create wallet button
}
}
Common use cases
Onboarding flow with wallet creation
'use client' ;
import { useMoonKey } from '@moon-key/react-auth' ;
import { useCreateWallet } from '@moon-key/react-auth/ethereum' ;
import { useState } from 'react' ;
export default function OnboardingFlow () {
const { user , isAuthenticated } = useMoonKey ();
const { createWallet } = useCreateWallet ();
const [ step , setStep ] = useState < 'welcome' | 'creating' | 'complete' >( 'welcome' );
if ( ! isAuthenticated ) {
return < div > Please sign in to continue </ div > ;
}
if ( user ?. wallet ) {
return < div > You already have a wallet! { user . wallet . address } </ div > ;
}
const handleCreateWallet = async () => {
setStep ( 'creating' );
try {
await createWallet ();
setStep ( 'complete' );
} catch ( error ) {
console . error ( 'Failed to create wallet:' , error );
setStep ( 'welcome' );
}
};
return (
< div >
{ step === 'welcome' && (
< div >
< h2 > Welcome to Our App! </ h2 >
< p > Create your self-custodial wallet to get started </ p >
< button onClick = { handleCreateWallet } > Create Wallet </ button >
</ div >
) }
{ step === 'creating' && (
< div >
< h2 > Creating Your Wallet... </ h2 >
< p > This will only take a moment </ p >
</ div >
) }
{ step === 'complete' && (
< div >
< h2 > Wallet Created! </ h2 >
< p > Your wallet address: { user ?. wallet ?. address } </ p >
< button onClick = { () => window . location . href = '/dashboard' } >
Continue to Dashboard
</ button >
</ div >
) }
</ div >
);
}
Optional wallet creation
import { useMoonKey } from '@moon-key/react-auth' ;
import { useCreateWallet } from '@moon-key/react-auth/ethereum' ;
export default function OptionalWalletFeature () {
const { user } = useMoonKey ();
const { createWallet } = useCreateWallet ();
if ( user ?. wallet ) {
return < BlockchainFeatures /> ;
}
return (
< div >
< h2 > Unlock Blockchain Features </ h2 >
< p > Create a wallet to access NFTs, tokens, and more </ p >
< button onClick = { () => createWallet () } >
Create Wallet
</ button >
< button onClick = { () => window . history . back () } >
Maybe Later
</ button >
</ div >
);
}
Server-side wallet creation
// On your backend
async function createWalletForUser ( userId : string ) {
const response = await fetch ( 'https://api.moonkey.fun/v1/wallets/create' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ process . env . MOONKEY_SECRET_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
wallet_type: 'ethereum' ,
user_id: userId
})
});
if ( ! response . ok ) {
throw new Error ( 'Failed to create wallet' );
}
const wallet = await response . json ();
return wallet ;
}
// Example usage in an API endpoint
app . post ( '/api/setup-wallet' , async ( req , res ) => {
const { userId } = req . body ;
try {
const wallet = await createWalletForUser ( userId );
res . json ({ success: true , wallet });
} catch ( error ) {
res . status ( 500 ). json ({ error: 'Failed to create wallet' });
}
});
Best practices
Check for existing wallets
Always check if a user already has a wallet before attempting to create one: if ( user ?. wallet ) {
console . log ( 'User already has a wallet' );
return ;
}
await createWallet ();
Provide clear feedback when wallet creation fails: try {
await createWallet ();
toast . success ( 'Wallet created successfully!' );
} catch ( error ) {
toast . error ( 'Failed to create wallet. Please try again.' );
console . error ( 'Wallet creation error:' , error );
}
Display loading indicators during wallet creation: const [ isCreating , setIsCreating ] = useState ( false );
const handleCreate = async () => {
setIsCreating ( true );
try {
await createWallet ();
} finally {
setIsCreating ( false );
}
};
Explain what a wallet is and why they’re creating one: < div className = "wallet-info" >
< h3 > What is a wallet? </ h3 >
< p > A wallet lets you own digital assets like NFTs and tokens. </ p >
< p > Your wallet is self-custodial - only you can access it. </ p >
</ div >
Use callbacks for post-creation actions
Handle navigation or UI updates after successful wallet creation: const { createWallet } = useCreateWallet ({
onSuccess : ( wallet ) => {
// Log analytics
analytics . track ( 'wallet_created' , { address: wallet . address });
// Navigate to next step
router . push ( '/wallet/funded' );
}
});
Never expose your Secret API Key in client-side code: // ❌ WRONG - Never do this
const apiKey = 'sk_live_...' ;
// ✅ CORRECT - Use environment variables on server
const apiKey = process . env . MOONKEY_SECRET_KEY ;
Error handling
Common errors and how to handle them:
try {
await createWallet ();
} catch ( error ) {
if ( error . message . includes ( 'already has a wallet' )) {
toast . info ( 'You already have a wallet' );
} else if ( error . message . includes ( 'not authenticated' )) {
toast . error ( 'Please sign in first' );
} else if ( error . message . includes ( 'network' )) {
toast . error ( 'Network error. Please check your connection.' );
} else {
toast . error ( 'Failed to create wallet. Please try again.' );
}
}
Next steps