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

# Solana (SVM)

> Integrate Sonar into a Solana application using the Solana wallet adapter.

<CardGroup cols={2}>
  <Card title="React SVM Example" icon="github" href="https://github.com/sunrisedotdev/sonar-example-react-svm">
    React SPA example using the Solana wallet adapter.
  </Card>

  <Card title="Next.js SVM Example" icon="github" href="https://github.com/sunrisedotdev/sonar-example-nextjs-svm">
    Next.js example with server-side token management.
  </Card>
</CardGroup>

This guide covers the Solana-specific parts of a Sonar integration. The Sonar API calls (authentication, entities, pre-purchase checks, purchase permits) work the same way as in the EVM guides. Only the wallet adapter and contract interaction differ.

<Note>
  Follow the [Frontend-only guide](/sonar/integration-guides/frontend-only) for a React SPA, or the [Frontend with Backend guide](/sonar/integration-guides/frontend-with-backend) for Next.js, to handle Sonar OAuth and API calls. Then use this guide for the Solana transaction layer.
</Note>

## Installation

```bash theme={null}
npm install @echoxyz/sonar-core @echoxyz/sonar-react \
  @solana/wallet-adapter-react @solana/wallet-adapter-react-ui \
  @solana/web3.js @solana/spl-token \
  @coral-xyz/anchor uuid
```

## Provider setup

Wrap your app with the Solana connection and wallet providers alongside `SonarProvider`:

```tsx theme={null}
import { SonarProvider } from "@echoxyz/sonar-react";
import { ConnectionProvider, WalletProvider } from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import "@solana/wallet-adapter-react-ui/styles.css";

const RPC_URL = "https://api.mainnet-beta.solana.com";

function App() {
  return (
    <SonarProvider config={sonarConfig}>
      <ConnectionProvider endpoint={RPC_URL}>
        <WalletProvider wallets={[]} autoConnect>
          <WalletModalProvider>
            {/* Your application */}
          </WalletModalProvider>
        </WalletProvider>
      </ConnectionProvider>
    </SonarProvider>
  );
}
```

## Configuration

You'll need three sale-specific values alongside the standard Sonar config:

| Variable             | Description                                                                       |
| -------------------- | --------------------------------------------------------------------------------- |
| `PROGRAM_ID`         | The Sonar settlement sale program: `3XxHCh947y1PAMK5y8oecBtaLU7TtTj9r4yMYwzFbJYs` |
| `PAYMENT_TOKEN_MINT` | The SPL token mint address accepted as payment                                    |
| `SALE_UUID`          | Your sale's UUID from the Echo founder dashboard                                  |

## Committing funds to the sale

Submitting a bid on Solana requires a two-instruction transaction:

1. An Ed25519 signature verification instruction (proving the purchase permit was signed by Sonar)
2. The `place_bid` program instruction

Both instructions must be in the same transaction with the Ed25519 instruction immediately preceding `place_bid`. The program validates the permit signature via instruction introspection.

### Deriving PDAs

The program uses Program Derived Addresses for all state. Derive these before building the transaction:

```ts theme={null}
import { PublicKey } from "@solana/web3.js";
import { parse as uuidParse } from "uuid";

// IDs from the Sonar API may be 0x-prefixed or UUID-formatted
function parseIdBytes(id: string): Uint8Array {
  const s = id.replace(/^0x/i, "");
  return s.includes("-") ? uuidParse(s) : Buffer.from(s, "hex");
}

const programPublicKey = new PublicKey(PROGRAM_ID);

// Sale PDA — derived from the sale UUID
const [salePDA] = PublicKey.findProgramAddressSync(
  [Buffer.from("settlement_sale"), Buffer.from(parseIdBytes(saleUUID))],
  programPublicKey
);

// Entity state PDA — derived from the sale PDA and the sale-specific entity ID
const [entityStatePDA] = PublicKey.findProgramAddressSync(
  [Buffer.from("entity_state"), salePDA.toBuffer(), Buffer.from(parseIdBytes(saleSpecificEntityID))],
  programPublicKey
);

// Wallet binding PDA — derived from the sale PDA and the bidder's wallet
const [walletBindingPDA] = PublicKey.findProgramAddressSync(
  [Buffer.from("wallet_binding"), salePDA.toBuffer(), wallet.publicKey.toBuffer()],
  programPublicKey
);
```

### Encoding the permit

The purchase permit must be Borsh-encoded using the program IDL to produce the message bytes for Ed25519 verification. Copy the IDL from the [example app](https://github.com/sunrisedotdev/sonar-example-react-svm/tree/main/src/idl) into your project.

```ts theme={null}
import { BorshCoder, BN } from "@coral-xyz/anchor";
import { IDL } from "./idl/settlement_sale";

// permit is the PermitJSON field from Sonar's generatePurchasePermit response
const permitData = {
  saleSpecificEntityId: parseIdBytes(permit.SaleSpecificEntityID),
  saleUuid: parseIdBytes(permit.SaleUUID),
  wallet: Array.from(new PublicKey(permit.Wallet).toBytes()),
  expiresAt: new BN(permit.ExpiresAt),
  minAmount: new BN(permit.MinAmount),
  maxAmount: new BN(permit.MaxAmount),
  minPrice: new BN(permit.MinPrice),
  maxPrice: new BN(permit.MaxPrice),
  opensAt: new BN(permit.OpensAt),
  closesAt: new BN(permit.ClosesAt),
  payload: Buffer.from(permit.Payload.replace(/^0x/, ""), "hex"),
};

const coder = new BorshCoder(IDL);
const messageBytes = coder.types.encode("purchasePermitV3", permitData);
```

### Building and submitting the transaction

```ts theme={null}
import {
  Ed25519Program,
  PublicKey,
  SYSVAR_INSTRUCTIONS_PUBKEY,
  SystemProgram,
  Transaction,
} from "@solana/web3.js";
import { AnchorProvider, Program } from "@coral-xyz/anchor";
import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token";

const provider = new AnchorProvider(connection, wallet, { commitment: "confirmed" });
const program = new Program(IDL, provider);

// Fetch the sale account to get the permit signer public key and vault address
const saleAccount = await program.account.settlementSale.fetch(salePDA);

// Ed25519 verify instruction — must immediately precede place_bid
const ed25519Ix = Ed25519Program.createInstructionWithPublicKey({
  publicKey: saleAccount.permitSigner.toBytes(),
  message: messageBytes,
  signature: Buffer.from(purchasePermitResp.Signature.replace(/^0x/, ""), "hex"),
});

const bidderTokenAccount = getAssociatedTokenAddressSync(
  new PublicKey(PAYMENT_TOKEN_MINT),
  wallet.publicKey
);

const placeBidIx = await program.methods
  .placeBid(permitData, new BN(amount.toString()), new BN(0), false)
  .accounts({
    bidder:             wallet.publicKey,
    sale:               salePDA,
    entityState:        entityStatePDA,
    walletBinding:      walletBindingPDA,
    bidderTokenAccount,
    vault:              saleAccount.vault,
    paymentTokenMint:   new PublicKey(PAYMENT_TOKEN_MINT),
    tokenProgram:       TOKEN_PROGRAM_ID,
    systemProgram:      SystemProgram.programId,
    instructions:       SYSVAR_INSTRUCTIONS_PUBKEY,
  })
  .instruction();

const tx = new Transaction();
tx.add(ed25519Ix, placeBidIx);
tx.feePayer = wallet.publicKey;
const { blockhash } = await connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;

const signed = await wallet.signTransaction(tx);
const sig = await connection.sendRawTransaction(signed.serialize());
await connection.confirmTransaction(sig, "confirmed");
```

## Reading committed amounts

To show a participant their current committed amount, poll the `entityState` account:

```ts theme={null}
const state = await program.account.entityState.fetchNullable(entityStatePDA);
const committedAmount = state ? BigInt(state.currentAmount.toString()) : BigInt(0);
```

A `null` result means the entity hasn't placed a bid yet; treat the committed amount as zero.

## Further reading

<CardGroup cols={2}>
  <Card title="SettlementSale (SVM)" href="/sonar/reference/contracts/settlement-sale-svm" icon="cube">
    Program reference: PDAs, instructions, and account types
  </Card>

  <Card title="Frontend-only" href="/sonar/integration-guides/frontend-only" icon="react">
    React SPA auth and Sonar API patterns
  </Card>

  <Card title="Frontend with Backend" href="/sonar/integration-guides/frontend-with-backend" icon="server">
    Next.js server-side auth patterns
  </Card>

  <Card title="Purchase Permits" href="/sonar/core-features/purchase-permits" icon="shield-check">
    How Sonar authorizes participation
  </Card>
</CardGroup>
