Skip to main content
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:
address
string
The wallet’s Ethereum address (e.g., 0x1234...5678)
chainType
string
The chain type, always 'ethereum' for Ethereum wallets
walletClient
WalletClient
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

  • Ethereum
  • Solana
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>
  );
}

Displaying wallet information

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

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
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>
  );
}
Always pass the wallet object to transaction methods:
await sendTransaction({
  transaction: txData,
  wallet: user.wallet // Required
});
TypeScript helps catch wallet-related errors:
const wallet: Wallet | undefined = user?.wallet;

if (wallet) {
  // TypeScript knows wallet exists here
  console.log(wallet.address);
}
Show shortened addresses in UI for better UX:
const formatAddress = (address: string) => {
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
};

<p>{formatAddress(wallet.address)}</p>

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
If ready stays false:
  • Verify MoonKeyProvider is properly configured
  • Check browser console for errors
  • Ensure appId is correct
  • Try refreshing the page
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