> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streambird.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Solana

With funding methods enabled for your app, MoonKey will prompt users to fund their wallets at two points in their experience:

1. **Manually** - When you call MoonKey's `fundWallet` method
2. **Automatically** - When the user attempts to send a transaction but has insufficient funds

You can configure the cluster, asset, and amount that users should fund their wallets with directly in code.

## Manually invoking funding

Once you've enabled funding methods for your app in the Dashboard, use the `useFundWallet` hook to invoke the funding flow:

```tsx theme={null}
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';

function FundWalletButton() {
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet();
  
  const handleFund = async () => {
    if (wallets.length === 0) return;
    
    const selectedWallet = wallets[0];
    await fundWallet(selectedWallet.address, {
      cluster: 'solana:mainnet',
      amount: '0.1'
    });
  };
  
  return <button onClick={handleFund}>Add Funds</button>;
}
```

Once invoked, the `fundWallet` method will open a modal with funding options for the user. If only one funding method is enabled, MoonKey will navigate directly to that flow.

<Warning>
  Purchases with third-party providers (like MoonPay) are not always instantaneous. There may be some time before the user completes their purchase and the funds are available in their wallet.
</Warning>

## Automatically invoking funding

When a user attempts to send a transaction but doesn't have sufficient funds, MoonKey will automatically show them an "Add funds" button in the transaction modal that enables them to invoke funding flows.

This helps users complete their intended actions without leaving your application.

## Setting funding parameters in code

You can override your Dashboard configuration by passing parameters to `fundWallet`:

### Basic parameters

<ParamField path="address" type="string" required>
  The wallet address to fund.
</ParamField>

<ParamField path="config.cluster" type="string">
  The Solana cluster to fund on. Use `'solana:mainnet'` for mainnet, `'solana:devnet'` for devnet.

  If not specified, defaults to the cluster configured in your Dashboard.
</ParamField>

<ParamField path="config.amount" type="string">
  The amount of the asset to fund as a decimal string (e.g., `'0.1'`).

  If not specified, defaults to the amount configured in your Dashboard.
</ParamField>

<ParamField path="config.asset" type="'native-currency' | 'USDC'">
  The asset to fund with:

  * `'native-currency'` - SOL (Solana's native currency)
  * `'USDC'` - USDC stablecoin

  Defaults to `'native-currency'`.
</ParamField>

<Note>
  Only use `'solana:mainnet'` for production. Testnets are not supported for card funding.
</Note>

## Examples

### Fund with SOL

Fund with the native currency (SOL):

```tsx theme={null}
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';

function FundWithSOL() {
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet();
  
  const handleFund = async () => {
    const selectedWallet = wallets[0];
    
    await fundWallet(selectedWallet.address, {
      cluster: 'solana:mainnet',
      amount: '0.1' // Defaults to 'native-currency' (SOL)
    });
  };
  
  return <button onClick={handleFund}>Fund with 0.1 SOL</button>;
}
```

### Fund with USDC

Fund with USDC stablecoin:

```tsx theme={null}
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';

function FundWithUSDC() {
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet();
  
  const handleFund = async () => {
    const selectedWallet = wallets[0];
    
    await fundWallet(selectedWallet.address, {
      cluster: 'solana:mainnet',
      amount: '15',
      asset: 'USDC'
    });
  };
  
  return <button onClick={handleFund}>Fund with 15 USDC</button>;
}
```

### Fund with custom amounts

Allow users to choose their funding amount:

```tsx theme={null}
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';
import { useState } from 'react';

function CustomAmountFunding() {
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet();
  const [amount, setAmount] = useState('0.5');
  
  const handleFund = async () => {
    const selectedWallet = wallets[0];
    
    await fundWallet(selectedWallet.address, {
      cluster: 'solana:mainnet',
      amount
    });
  };
  
  return (
    <div>
      <input
        type="text"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
        placeholder="Amount in SOL"
      />
      <button onClick={handleFund}>Fund with {amount} SOL</button>
    </div>
  );
}
```

### Predefined funding options

Offer preset funding amounts:

```tsx theme={null}
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';

function PresetAmountButtons() {
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet();
  
  const fundWithAmount = async (amount: string) => {
    const selectedWallet = wallets[0];
    
    await fundWallet(selectedWallet.address, {
      cluster: 'solana:mainnet',
      amount
    });
  };
  
  return (
    <div>
      <h3>Quick Fund Options</h3>
      <button onClick={() => fundWithAmount('0.1')}>
        0.1 SOL (~$15)
      </button>
      <button onClick={() => fundWithAmount('0.5')}>
        0.5 SOL (~$75)
      </button>
      <button onClick={() => fundWithAmount('1.0')}>
        1.0 SOL (~$150)
      </button>
    </div>
  );
}
```

## Callbacks

To understand when users have gone through a funding flow, use the `onUserExited` callback with the `useFundWallet` hook:

```tsx theme={null}
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';
import { useRouter } from 'next/navigation';

function OnboardingWithFunding() {
  const router = useRouter();
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet({
    onUserExited({ balance }) {
      if (balance < 1000n) {
        router.push('/insufficient-funds');
      } else {
        router.push('/dashboard');
      }
    }
  });
  
  const handleFund = async () => {
    const selectedWallet = wallets[0];
    await fundWallet(selectedWallet.address, {
      cluster: 'solana:mainnet',
      amount: '0.5'
    });
  };
  
  return <button onClick={handleFund}>Fund Wallet</button>;
}
```

### Callback parameters

The `onUserExited` callback receives an object with:

<ResponseField name="address" type="string">
  The wallet address that was funded.
</ResponseField>

<ResponseField name="cluster" type="string">
  The Solana cluster where funding occurred.
</ResponseField>

<ResponseField name="fundingMethod" type="string">
  The funding method used (e.g., `'card'`).
</ResponseField>

<ResponseField name="balance" type="bigint">
  The current balance of the wallet being funded (in lamports).
</ResponseField>

## Onboarding flow example

Prompt new users to fund their wallet as part of onboarding:

```tsx theme={null}
import { useMoonKey } from '@moon-key/react-auth';
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';
import { useLogin } from '@moon-key/react-auth';
import { useRouter } from 'next/navigation';

export default function LoginWithFunding() {
  const router = useRouter();
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet({
    onUserExited({ balance }) {
      // Check if balance is sufficient (in lamports)
      const minBalance = 100_000_000n; // 0.1 SOL
      
      if (balance < minBalance) {
        router.push('/insufficient-funds');
      } else {
        router.push('/dashboard');
      }
    }
  });
  
  const { login } = useLogin({
    onComplete(user, isNewUser) {
      if (isNewUser && wallets.length > 0) {
        // Prompt new users to fund their wallet
        fundWallet(wallets[0].address, {
          cluster: 'solana:mainnet',
          amount: '0.5'
        });
      } else {
        router.push('/dashboard');
      }
    }
  });
  
  return <button onClick={login}>Sign In</button>;
}
```

## Customizing the UI

Customize the "Receive funds" screen with the `uiConfig` option:

```tsx theme={null}
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';

function CustomizedFunding() {
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet();
  
  const handleFund = async () => {
    const selectedWallet = wallets[0];
    
    await fundWallet(selectedWallet.address, {
      cluster: 'solana:mainnet',
      amount: '0.5',
      uiConfig: {
        receiveFundsTitle: 'Receive 0.5 SOL',
        receiveFundsSubtitle: 'Scan this code or copy your wallet address to receive funds on Solana.'
      }
    });
  };
  
  return <button onClick={handleFund}>Add Funds</button>;
}
```

### UI configuration options

<ParamField path="uiConfig.receiveFundsTitle" type="string">
  Custom title for the "Receive funds" screen.
</ParamField>

<ParamField path="uiConfig.receiveFundsSubtitle" type="string">
  Custom subtitle for the "Receive funds" screen.
</ParamField>

## Complete example

Here's a complete example with amount and asset selection:

```tsx theme={null}
'use client';
import { useWallets, useFundWallet } from '@moon-key/react-auth/solana';
import { useState } from 'react';
import { LAMPORTS_PER_SOL } from '@solana/web3.js';

export default function FlexibleFunding() {
  const { wallets } = useWallets();
  const { fundWallet } = useFundWallet({
    onUserExited({ balance, fundingMethod }) {
      console.log('Funding complete');
      console.log('Method:', fundingMethod);
      console.log('New balance:', (Number(balance) / LAMPORTS_PER_SOL).toFixed(4), 'SOL');
    }
  });
  
  const [amount, setAmount] = useState('0.5');
  const [asset, setAsset] = useState<'native-currency' | 'USDC'>('native-currency');
  const [isFunding, setIsFunding] = useState(false);
  
  const handleFund = async () => {
    if (wallets.length === 0) return;
    
    const selectedWallet = wallets[0];
    setIsFunding(true);
    
    try {
      await fundWallet(selectedWallet.address, {
        cluster: 'solana:mainnet',
        amount,
        asset
      });
    } catch (error) {
      console.error('Funding failed:', error);
    } finally {
      setIsFunding(false);
    }
  };
  
  if (wallets.length === 0) {
    return <div>No wallet found</div>;
  }
  
  return (
    <div className="funding-form">
      <h2>Fund Your Wallet</h2>
      
      <div className="form-group">
        <label>Asset</label>
        <select value={asset} onChange={(e) => setAsset(e.target.value as any)}>
          <option value="native-currency">SOL</option>
          <option value="USDC">USDC</option>
        </select>
      </div>
      
      <div className="form-group">
        <label>Amount</label>
        <input
          type="text"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          placeholder="0.5"
        />
      </div>
      
      <button onClick={handleFund} disabled={isFunding}>
        {isFunding ? 'Processing...' : `Fund with ${amount} ${asset === 'native-currency' ? 'SOL' : 'USDC'}`}
      </button>
      
      <p className="wallet-address">
        Wallet: {wallets[0].address}
      </p>
      
      <div className="info">
        <p>💳 Pay with card, Apple Pay, or Google Pay</p>
        <p>⚡ Funds typically arrive within minutes</p>
        <p>🔒 Secure payment processing via MoonPay</p>
      </div>
    </div>
  );
}
```

## Understanding Solana balances

Solana balances are measured in **lamports**, where 1 SOL = 1,000,000,000 lamports:

```tsx theme={null}
import { LAMPORTS_PER_SOL } from '@solana/web3.js';

// Convert lamports to SOL
const solBalance = Number(balanceInLamports) / LAMPORTS_PER_SOL;

// Convert SOL to lamports
const lamports = solAmount * LAMPORTS_PER_SOL;
```

When checking balances in the `onUserExited` callback, remember that the balance is in lamports:

```tsx theme={null}
const { fundWallet } = useFundWallet({
  onUserExited({ balance }) {
    // balance is in lamports
    const solBalance = Number(balance) / LAMPORTS_PER_SOL;
    console.log(`Balance: ${solBalance.toFixed(4)} SOL`);
    
    // Check minimum balance (0.1 SOL = 100,000,000 lamports)
    if (balance < 100_000_000n) {
      alert('Please add more funds');
    }
  }
});
```

## Best practices

<AccordionGroup>
  <Accordion title="Set appropriate amounts for Solana">
    * Solana has very low transaction fees (typically \< \$0.01)
    * Smaller amounts work well (0.1 - 1 SOL is often sufficient)
    * Consider typical transaction costs in your app
    * Don't require users to fund more than necessary
  </Accordion>

  <Accordion title="Communicate funding status">
    * Explain that purchases are not instant
    * Show expected delivery times (usually faster than Ethereum)
    * Provide transaction status updates
    * Handle edge cases gracefully
  </Accordion>

  <Accordion title="Use appropriate clusters">
    * Always use `'solana:mainnet'` for production
    * Test your integration thoroughly on mainnet with small amounts
    * Devnet is not supported for card funding
  </Accordion>

  <Accordion title="Consider USDC for price stability">
    * USDC provides price stability for users
    * Useful for applications with fixed pricing
    * Still allows for Solana transactions
    * May be preferred by users concerned about volatility
  </Accordion>

  <Accordion title="Use callbacks effectively">
    * Track funding completion with `onUserExited`
    * Navigate users appropriately based on balance
    * Log funding events for analytics
    * Handle insufficient balance scenarios
  </Accordion>

  <Accordion title="Handle rent exemption">
    * Solana accounts require minimum balance for rent exemption
    * Ensure funded amounts are sufficient for account creation
    * Consider rent costs when setting minimum balances
    * Guide users if their balance is below rent exemption
  </Accordion>
</AccordionGroup>

## Common issues

<AccordionGroup>
  <Accordion title="Balance appears as 0 after funding">
    **Possible causes:**

    * Purchase not yet complete
    * MoonPay processing payment
    * Network congestion

    **Solutions:**

    * Wait a few minutes for processing
    * Check MoonPay transaction status
    * Refresh wallet balance after delay
  </Accordion>

  <Accordion title="Funding modal doesn't appear">
    **Possible causes:**

    * Funding not enabled in Dashboard
    * Incorrect cluster configuration
    * Browser blocking popup

    **Solutions:**

    * Verify Dashboard configuration
    * Check console for errors
    * Ensure popups are allowed
  </Accordion>

  <Accordion title="Insufficient funds for transactions">
    **Possible causes:**

    * Funded amount too small
    * Account rent not covered
    * Multiple transactions depleting balance

    **Solutions:**

    * Suggest higher funding amounts
    * Explain Solana rent requirements
    * Provide balance warnings before transactions
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Ethereum Funding" icon="ethereum" href="/wallet-as-a-service/funding/fund-types/ethereum">
    Fund Ethereum wallets
  </Card>

  <Card title="Card Payments" icon="credit-card" href="/wallet-as-a-service/funding/methods/card">
    Learn about card payment options
  </Card>

  <Card title="Configure Funding" icon="gear" href="/wallet-as-a-service/funding/configure">
    Set up funding in the Dashboard
  </Card>

  <Card title="Send Transaction" icon="paper-plane" href="/wallet-as-a-service/using-wallets/solana/send-transaction">
    Use funded wallets to send transactions
  </Card>
</CardGroup>
