> ## 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.

# Send Transaction

# Send Transaction

Send and broadcast Solana transactions using a user's embedded wallet. MoonKey handles transaction signing and submission to the Solana network.

## Overview

Sending transactions on Solana enables:

* **SOL transfers**: Send native SOL tokens between addresses
* **SPL token transfers**: Transfer fungible tokens (SPL tokens)
* **Program interactions**: Execute instructions on Solana programs
* **NFT operations**: Mint, transfer, or burn NFTs

<Info>
  MoonKey uses `@solana/web3.js` for transaction construction. Transactions are serialized, signed, and broadcast to the network automatically.
</Info>

## React SDK

<Tabs>
  <Tab title="Basic Usage">
    To send a transaction, use the `sendTransaction` method from the `useSendTransaction` hook:

    ```tsx theme={null}
    import { useMoonKey } from '@moon-key/react-auth';
    import { useSendTransaction } from '@moon-key/react-auth/solana';
    import {
      Transaction,
      SystemProgram,
      PublicKey,
      Connection,
      LAMPORTS_PER_SOL
    } from '@solana/web3.js';

    function SendSolButton() {
      const { user } = useMoonKey();
      const { sendTransaction } = useSendTransaction();

      const handleSend = async () => {
        if (!user?.wallet) {
          alert('No wallet found');
          return;
        }

        try {
          // Create connection
          const connection = new Connection('https://api.devnet.solana.com');

          // Create transaction
          const transaction = new Transaction().add(
            SystemProgram.transfer({
              fromPubkey: new PublicKey(user.wallet.address),
              toPubkey: new PublicKey('RecipientAddressHere...'),
              lamports: 0.001 * LAMPORTS_PER_SOL // 0.001 SOL
            })
          );

          // Get recent blockhash
          const { blockhash } = await connection.getLatestBlockhash();
          transaction.recentBlockhash = blockhash;
          transaction.feePayer = new PublicKey(user.wallet.address);

          // Send transaction
          const signature = await sendTransaction({
            transaction,
            connection
          });

          console.log('Transaction sent:', signature);
          alert(`Transaction successful! Signature: ${signature}`);
        } catch (error) {
          console.error('Failed to send transaction:', error);
        }
      };

      return (
        <button onClick={handleSend}>
          Send 0.001 SOL
        </button>
      );
    }
    ```
  </Tab>

  <Tab title="With UI Customization">
    Customize the transaction confirmation modal:

    ```tsx theme={null}
    import { useMoonKey } from '@moon-key/react-auth';
    import { useSendTransaction } from '@moon-key/react-auth/solana';
    import {
      Transaction,
      SystemProgram,
      PublicKey,
      Connection,
      LAMPORTS_PER_SOL
    } from '@solana/web3.js';

    function CustomSendButton() {
      const { user } = useMoonKey();
      const { sendTransaction } = useSendTransaction();

      const handleSend = async () => {
        if (!user?.wallet) return;

        const connection = new Connection('https://api.devnet.solana.com');

        const transaction = new Transaction().add(
          SystemProgram.transfer({
            fromPubkey: new PublicKey(user.wallet.address),
            toPubkey: new PublicKey('RecipientAddressHere...'),
            lamports: 0.01 * LAMPORTS_PER_SOL
          })
        );

        const { blockhash } = await connection.getLatestBlockhash();
        transaction.recentBlockhash = blockhash;
        transaction.feePayer = new PublicKey(user.wallet.address);

        try {
          const signature = await sendTransaction({
            transaction,
            connection
          }, {
            uiConfig: {
              title: 'Confirm SOL Transfer',
              description: 'You are sending 0.01 SOL to the recipient',
              confirmButtonText: 'Send SOL',
              cancelButtonText: 'Cancel',
              transactionInfo: {
                action: 'SOL Transfer',
                cost: '0.01 SOL + fees'
              }
            }
          });

          console.log('Transaction sent:', signature);
        } catch (error) {
          console.error('Transaction failed:', error);
        }
      };

      return (
        <button onClick={handleSend}>
          Send SOL (Custom UI)
        </button>
      );
    }
    ```
  </Tab>

  <Tab title="With Callbacks">
    Use callbacks to handle success and error cases:

    ```tsx theme={null}
    import { useMoonKey } from '@moon-key/react-auth';
    import { useSendTransaction } from '@moon-key/react-auth/solana';
    import {
      Transaction,
      SystemProgram,
      PublicKey,
      Connection,
      LAMPORTS_PER_SOL
    } from '@solana/web3.js';

    function SendWithCallbacks() {
      const { user } = useMoonKey();
      
      const { sendTransaction } = useSendTransaction({
        onSuccess: ({ signature }) => {
          console.log('Transaction successful:', signature);
          alert('SOL sent successfully!');
          
          // Update UI or refresh balance
          refreshBalance();
        },
        onError: (error) => {
          console.error('Transaction failed:', error);
          alert('Failed to send transaction');
        }
      });

      const handleSend = async () => {
        if (!user?.wallet) return;

        const connection = new Connection('https://api.devnet.solana.com');

        const transaction = new Transaction().add(
          SystemProgram.transfer({
            fromPubkey: new PublicKey(user.wallet.address),
            toPubkey: new PublicKey('RecipientAddressHere...'),
            lamports: 0.001 * LAMPORTS_PER_SOL
          })
        );

        const { blockhash } = await connection.getLatestBlockhash();
        transaction.recentBlockhash = blockhash;
        transaction.feePayer = new PublicKey(user.wallet.address);

        await sendTransaction({
          transaction,
          connection
        });
      };

      return (
        <button onClick={handleSend}>
          Send SOL
        </button>
      );
    }
    ```
  </Tab>
</Tabs>

## Parameters

<ParamField path="transaction" type="Transaction" required>
  The Solana transaction to send. Must be a `Transaction` object from `@solana/web3.js`.

  **Example:**

  ```tsx theme={null}
  const transaction = new Transaction().add(
    SystemProgram.transfer({
      fromPubkey: new PublicKey(fromAddress),
      toPubkey: new PublicKey(toAddress),
      lamports: amount
    })
  );
  ```
</ParamField>

<ParamField path="connection" type="Connection" required>
  The Solana RPC connection to use for broadcasting the transaction.

  **Example:**

  ```tsx theme={null}
  const connection = new Connection('https://api.devnet.solana.com');
  ```
</ParamField>

### Options Object

The second parameter is an optional configuration object:

<ParamField path="uiConfig" type="object">
  Configuration for the transaction confirmation modal.

  <Expandable title="uiConfig properties">
    <ParamField path="title" type="string">
      Custom title for the confirmation modal.
    </ParamField>

    <ParamField path="description" type="string">
      Description text shown in the modal.
    </ParamField>

    <ParamField path="confirmButtonText" type="string">
      Text for the confirm button.
    </ParamField>

    <ParamField path="cancelButtonText" type="string">
      Text for the cancel button.
    </ParamField>

    <ParamField path="transactionInfo" type="object">
      Additional transaction information to display.

      <Expandable title="transactionInfo properties">
        <ParamField path="action" type="string">
          Description of the action being performed.
        </ParamField>

        <ParamField path="cost" type="string">
          Estimated cost of the transaction.
        </ParamField>
      </Expandable>
    </ParamField>

    <ParamField path="showWalletUI" type="boolean">
      Whether to show the confirmation modal. Set to `false` to send without confirmation.
    </ParamField>
  </Expandable>
</ParamField>

### Hook Callbacks

<ParamField path="onSuccess" type="(result: { signature: string }) => void">
  Callback executed after successful transaction. Receives the transaction signature.
</ParamField>

<ParamField path="onError" type="(error: Error) => void">
  Callback executed if transaction fails or user cancels.
</ParamField>

## Returns

<ResponseField name="signature" type="string">
  The transaction signature (base58 encoded string).
</ResponseField>

## Complete Examples

### SOL Transfer

```tsx theme={null}
'use client';
import { useMoonKey } from '@moon-key/react-auth';
import { useSendTransaction } from '@moon-key/react-auth/solana';
import {
  Transaction,
  SystemProgram,
  PublicKey,
  Connection,
  LAMPORTS_PER_SOL
} from '@solana/web3.js';
import { useState } from 'react';

export default function SolTransfer() {
  const { user, isAuthenticated } = useMoonKey();
  const [recipient, setRecipient] = useState('');
  const [amount, setAmount] = useState('');
  const [isSending, setIsSending] = useState(false);

  const { sendTransaction } = useSendTransaction({
    onSuccess: ({ signature }) => {
      console.log('Transfer successful:', signature);
      alert(`Transfer successful! Signature: ${signature}`);
      setRecipient('');
      setAmount('');
      setIsSending(false);
    },
    onError: (error) => {
      console.error('Transfer failed:', error);
      alert('Transfer failed. Please try again.');
      setIsSending(false);
    }
  });

  const handleTransfer = async () => {
    if (!user?.wallet) {
      alert('Please connect your wallet');
      return;
    }

    if (!recipient || !amount) {
      alert('Please enter recipient and amount');
      return;
    }

    setIsSending(true);

    try {
      // Validate recipient address
      const recipientPubkey = new PublicKey(recipient);

      // Create connection (use devnet for testing)
      const connection = new Connection('https://api.devnet.solana.com');

      // Check balance
      const balance = await connection.getBalance(new PublicKey(user.wallet.address));
      const transferAmount = parseFloat(amount) * LAMPORTS_PER_SOL;

      if (balance < transferAmount) {
        alert('Insufficient balance');
        setIsSending(false);
        return;
      }

      // Create transaction
      const transaction = new Transaction().add(
        SystemProgram.transfer({
          fromPubkey: new PublicKey(user.wallet.address),
          toPubkey: recipientPubkey,
          lamports: transferAmount
        })
      );

      // Get recent blockhash
      const { blockhash } = await connection.getLatestBlockhash();
      transaction.recentBlockhash = blockhash;
      transaction.feePayer = new PublicKey(user.wallet.address);

      // Send transaction
      await sendTransaction({
        transaction,
        connection
      }, {
        uiConfig: {
          title: 'Confirm Transfer',
          description: `Send ${amount} SOL to ${recipient.slice(0, 8)}...${recipient.slice(-8)}`,
          confirmButtonText: 'Send SOL',
          transactionInfo: {
            action: 'SOL Transfer',
            cost: `${amount} SOL + network fees`
          }
        }
      });
    } catch (error: any) {
      console.error('Transfer error:', error);
      if (error.message?.includes('Invalid public key')) {
        alert('Invalid recipient address');
      } else {
        alert('Transfer failed: ' + error.message);
      }
      setIsSending(false);
    }
  };

  if (!isAuthenticated) {
    return <p>Please sign in to transfer SOL</p>;
  }

  return (
    <div className="sol-transfer">
      <h2>Transfer SOL</h2>

      <div className="form">
        <div className="field">
          <label>Recipient Address:</label>
          <input
            type="text"
            placeholder="Solana address"
            value={recipient}
            onChange={(e) => setRecipient(e.target.value)}
          />
        </div>

        <div className="field">
          <label>Amount (SOL):</label>
          <input
            type="number"
            placeholder="0.001"
            step="0.001"
            value={amount}
            onChange={(e) => setAmount(e.target.value)}
          />
        </div>

        <button
          onClick={handleTransfer}
          disabled={isSending || !recipient || !amount}
        >
          {isSending ? 'Sending...' : 'Send SOL'}
        </button>
      </div>
    </div>
  );
}
```

### SPL Token Transfer

```tsx theme={null}
'use client';
import { useMoonKey } from '@moon-key/react-auth';
import { useSendTransaction } from '@moon-key/react-auth/solana';
import {
  Transaction,
  PublicKey,
  Connection
} from '@solana/web3.js';
import {
  getAssociatedTokenAddress,
  createTransferInstruction,
  TOKEN_PROGRAM_ID
} from '@solana/spl-token';
import { useState } from 'react';

export default function SPLTokenTransfer() {
  const { user } = useMoonKey();
  const { sendTransaction } = useSendTransaction();
  const [recipient, setRecipient] = useState('');
  const [amount, setAmount] = useState('');

  const handleTokenTransfer = async () => {
    if (!user?.wallet) return;

    try {
      const connection = new Connection('https://api.devnet.solana.com');

      // Token mint address (example: USDC on devnet)
      const tokenMintAddress = new PublicKey('TokenMintAddressHere...');

      // Get source and destination token accounts
      const sourceAccount = await getAssociatedTokenAddress(
        tokenMintAddress,
        new PublicKey(user.wallet.address)
      );

      const destinationAccount = await getAssociatedTokenAddress(
        tokenMintAddress,
        new PublicKey(recipient)
      );

      // Create transfer instruction
      const transferInstruction = createTransferInstruction(
        sourceAccount,
        destinationAccount,
        new PublicKey(user.wallet.address),
        parseFloat(amount) * Math.pow(10, 6), // Assuming 6 decimals
        [],
        TOKEN_PROGRAM_ID
      );

      // Create transaction
      const transaction = new Transaction().add(transferInstruction);

      const { blockhash } = await connection.getLatestBlockhash();
      transaction.recentBlockhash = blockhash;
      transaction.feePayer = new PublicKey(user.wallet.address);

      // Send transaction
      const signature = await sendTransaction({
        transaction,
        connection
      }, {
        uiConfig: {
          title: 'Transfer Tokens',
          description: `Send ${amount} tokens`,
          confirmButtonText: 'Confirm Transfer'
        }
      });

      console.log('Token transfer successful:', signature);
      alert('Tokens sent successfully!');
    } catch (error) {
      console.error('Token transfer failed:', error);
      alert('Failed to transfer tokens');
    }
  };

  return (
    <div className="token-transfer">
      <h2>Transfer SPL Tokens</h2>

      <input
        type="text"
        placeholder="Recipient address"
        value={recipient}
        onChange={(e) => setRecipient(e.target.value)}
      />

      <input
        type="number"
        placeholder="Amount"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
      />

      <button onClick={handleTokenTransfer}>
        Transfer Tokens
      </button>
    </div>
  );
}
```

### Program Interaction

```tsx theme={null}
'use client';
import { useMoonKey } from '@moon-key/react-auth';
import { useSendTransaction } from '@moon-key/react-auth/solana';
import {
  Transaction,
  TransactionInstruction,
  PublicKey,
  Connection
} from '@solana/web3.js';

export default function ProgramInteraction() {
  const { user } = useMoonKey();
  const { sendTransaction } = useSendTransaction();

  const handleProgramCall = async () => {
    if (!user?.wallet) return;

    try {
      const connection = new Connection('https://api.devnet.solana.com');

      // Your program ID
      const programId = new PublicKey('YourProgramIdHere...');

      // Create instruction data (depends on your program)
      const instructionData = Buffer.from([
        /* your instruction data */
      ]);

      // Create instruction
      const instruction = new TransactionInstruction({
        keys: [
          {
            pubkey: new PublicKey(user.wallet.address),
            isSigner: true,
            isWritable: true
          }
          // Add more account keys as needed
        ],
        programId,
        data: instructionData
      });

      // Create transaction
      const transaction = new Transaction().add(instruction);

      const { blockhash } = await connection.getLatestBlockhash();
      transaction.recentBlockhash = blockhash;
      transaction.feePayer = new PublicKey(user.wallet.address);

      // Send transaction
      const signature = await sendTransaction({
        transaction,
        connection
      }, {
        uiConfig: {
          title: 'Execute Program',
          description: 'Interact with Solana program',
          confirmButtonText: 'Execute'
        }
      });

      console.log('Program execution successful:', signature);
    } catch (error) {
      console.error('Program execution failed:', error);
    }
  };

  return (
    <button onClick={handleProgramCall}>
      Execute Program
    </button>
  );
}
```

## Transaction Construction

### Basic Transaction Setup

```tsx theme={null}
import {
  Transaction,
  SystemProgram,
  PublicKey,
  Connection,
  LAMPORTS_PER_SOL
} from '@solana/web3.js';

// Create connection
const connection = new Connection('https://api.devnet.solana.com');

// Create transaction
const transaction = new Transaction();

// Add instruction
transaction.add(
  SystemProgram.transfer({
    fromPubkey: new PublicKey(fromAddress),
    toPubkey: new PublicKey(toAddress),
    lamports: 0.001 * LAMPORTS_PER_SOL
  })
);

// Set recent blockhash and fee payer
const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(fromAddress);
```

### Multiple Instructions

```tsx theme={null}
const transaction = new Transaction();

// Add multiple instructions
transaction.add(
  SystemProgram.transfer({
    fromPubkey: senderPubkey,
    toPubkey: recipient1Pubkey,
    lamports: 0.001 * LAMPORTS_PER_SOL
  }),
  SystemProgram.transfer({
    fromPubkey: senderPubkey,
    toPubkey: recipient2Pubkey,
    lamports: 0.002 * LAMPORTS_PER_SOL
  })
);

const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderPubkey;
```

## RPC Endpoints

Use appropriate RPC endpoints for different networks:

```tsx theme={null}
// Devnet (for testing)
const devnetConnection = new Connection('https://api.devnet.solana.com');

// Testnet
const testnetConnection = new Connection('https://api.testnet.solana.com');

// Mainnet Beta
const mainnetConnection = new Connection('https://api.mainnet-beta.solana.com');

// Custom RPC (recommended for production)
const customConnection = new Connection('https://your-rpc-provider.com');
```

<Tip>
  For production applications, use a dedicated RPC provider like Helius, QuickNode, or Alchemy for better reliability and rate limits.
</Tip>

## Install Dependencies

<CodeGroup>
  ```bash npm theme={null}
  npm install @solana/web3.js @solana/spl-token
  ```

  ```bash pnpm theme={null}
  pnpm install @solana/web3.js @solana/spl-token
  ```

  ```bash yarn theme={null}
  yarn add @solana/web3.js @solana/spl-token
  ```
</CodeGroup>

## UI Customization

Customize the transaction confirmation modal:

```tsx theme={null}
await sendTransaction({
  transaction,
  connection
}, {
  uiConfig: {
    title: 'Confirm Transaction',
    description: 'Review and confirm your transaction',
    confirmButtonText: 'Confirm',
    cancelButtonText: 'Cancel',
    transactionInfo: {
      action: 'Transfer SOL',
      cost: '0.001 SOL + fees'
    }
  }
});
```

### Hide Modal

```tsx theme={null}
await sendTransaction({
  transaction,
  connection
}, {
  uiConfig: {
    showWalletUI: false
  }
});
```

<Warning>
  Hiding the modal removes the user's ability to review the transaction before signing. Only use this for trusted operations or after obtaining explicit user consent.
</Warning>

## Error Handling

Handle common transaction errors:

```tsx theme={null}
const { sendTransaction } = useSendTransaction({
  onError: (error: any) => {
    if (error.message?.includes('insufficient funds')) {
      alert('Insufficient balance to complete transaction');
    } else if (error.message?.includes('blockhash')) {
      alert('Transaction expired. Please try again.');
    } else if (error.message?.includes('simulation failed')) {
      alert('Transaction simulation failed. Check transaction parameters.');
    } else if (error.code === 'USER_REJECTED') {
      console.log('User cancelled transaction');
    } else {
      console.error('Transaction error:', error);
      alert('Transaction failed: ' + error.message);
    }
  }
});
```

## Best Practices

<AccordionGroup>
  <Accordion title="Check balance before sending">
    Always verify sufficient balance:

    ```tsx theme={null}
    const connection = new Connection('https://api.devnet.solana.com');
    const balance = await connection.getBalance(new PublicKey(user.wallet.address));
    const transferAmount = 0.001 * LAMPORTS_PER_SOL;

    if (balance < transferAmount) {
      alert('Insufficient balance');
      return;
    }

    // Proceed with transaction
    ```
  </Accordion>

  <Accordion title="Use fresh blockhashes">
    Always fetch a recent blockhash before sending:

    ```tsx theme={null}
    // ✅ Good - Fresh blockhash
    const { blockhash } = await connection.getLatestBlockhash();
    transaction.recentBlockhash = blockhash;

    // ❌ Bad - Reusing old blockhash
    // Blockhashes expire after ~60 seconds
    ```
  </Accordion>

  <Accordion title="Validate addresses">
    Validate recipient addresses before sending:

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

    function isValidSolanaAddress(address: string): boolean {
      try {
        new PublicKey(address);
        return true;
      } catch {
        return false;
      }
    }

    if (!isValidSolanaAddress(recipient)) {
      alert('Invalid recipient address');
      return;
    }
    ```
  </Accordion>

  <Accordion title="Confirm transactions">
    Wait for transaction confirmation:

    ```tsx theme={null}
    const signature = await sendTransaction({
      transaction,
      connection
    });

    // Wait for confirmation
    const confirmation = await connection.confirmTransaction(signature, 'confirmed');

    if (confirmation.value.err) {
      console.error('Transaction failed:', confirmation.value.err);
    } else {
      console.log('Transaction confirmed!');
    }
    ```
  </Accordion>

  <Accordion title="Handle network errors gracefully">
    Implement retry logic for network issues:

    ```tsx theme={null}
    async function sendWithRetry(
      transaction: Transaction,
      connection: Connection,
      maxRetries = 3
    ) {
      let attempts = 0;

      while (attempts < maxRetries) {
        try {
          const signature = await sendTransaction({
            transaction,
            connection
          });
          return signature;
        } catch (error: any) {
          attempts++;
          if (attempts >= maxRetries) throw error;
          
          console.log(`Retry ${attempts}/${maxRetries}`);
          await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Use appropriate RPC endpoints">
    Choose the right network for your use case:

    ```tsx theme={null}
    // Development and testing
    const connection = new Connection('https://api.devnet.solana.com');

    // Production
    const connection = new Connection(
      process.env.NEXT_PUBLIC_SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com'
    );
    ```
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Transaction fails with blockhash error">
    Blockhashes expire quickly on Solana (\~60 seconds):

    ```tsx theme={null}
    // Always get fresh blockhash right before sending
    const { blockhash } = await connection.getLatestBlockhash();
    transaction.recentBlockhash = blockhash;
    transaction.feePayer = new PublicKey(user.wallet.address);

    // Send immediately
    await sendTransaction({ transaction, connection });
    ```
  </Accordion>

  <Accordion title="Insufficient funds error">
    Check both SOL balance and required amount:

    ```tsx theme={null}
    const balance = await connection.getBalance(
      new PublicKey(user.wallet.address)
    );

    // Account for transfer amount + transaction fees (~0.000005 SOL)
    const requiredAmount = transferAmount + (5000); // 5000 lamports for fees

    if (balance < requiredAmount) {
      alert(`Insufficient balance. You need ${requiredAmount / LAMPORTS_PER_SOL} SOL`);
      return;
    }
    ```
  </Accordion>

  <Accordion title="Simulation failed error">
    Transaction simulation can fail for various reasons:

    ```tsx theme={null}
    // Check if accounts exist
    const accountInfo = await connection.getAccountInfo(
      new PublicKey(recipientAddress)
    );

    // For SPL tokens, check if token account exists
    const tokenAccount = await getAssociatedTokenAddress(
      tokenMintAddress,
      new PublicKey(recipientAddress)
    );

    const tokenAccountInfo = await connection.getAccountInfo(tokenAccount);

    if (!tokenAccountInfo) {
      // May need to create associated token account first
      console.log('Token account does not exist');
    }
    ```
  </Accordion>

  <Accordion title="Connection timeout">
    Use appropriate timeouts and retry logic:

    ```tsx theme={null}
    const connection = new Connection(
      'https://api.devnet.solana.com',
      {
        commitment: 'confirmed',
        confirmTransactionInitialTimeout: 60000 // 60 seconds
      }
    );
    ```
  </Accordion>
</AccordionGroup>

## Related Documentation

<CardGroup cols={2}>
  <Card title="Sign Transaction" icon="pen-to-square" href="/wallet-as-a-service/using-wallets/solana/sign-transaction">
    Sign transactions without broadcasting
  </Card>

  <Card title="Sign Message" icon="signature" href="/wallet-as-a-service/using-wallets/solana/sign-message">
    Sign messages with Solana wallet
  </Card>

  <Card title="Solana Web3.js" icon="code" href="https://solana-labs.github.io/solana-web3.js/">
    Official Solana JavaScript library
  </Card>

  <Card title="SPL Token" icon="coins" href="https://spl.solana.com/token">
    SPL Token program documentation
  </Card>
</CardGroup>

## Next Steps

* Learn about [Solana transaction signing](/wallet-as-a-service/using-wallets/solana/sign-transaction)
* Understand [Solana's transaction structure](https://docs.solana.com/developing/programming-model/transactions)
* Explore [SPL Token operations](https://spl.solana.com/token)
* Configure [custom RPC endpoints](/get-started/frontend-sdks/react/advance/configure-solana-networks)
