Use this file to discover all available pages before exploring further.
React SVM Example
React SPA example using the Solana wallet adapter.
Next.js SVM Example
Next.js example with server-side token management.
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.
Follow the Frontend-only guide for a React SPA, or the Frontend with Backend guide for Next.js, to handle Sonar OAuth and API calls. Then use this guide for the Solana transaction layer.
Submitting a bid on Solana requires a two-instruction transaction:
An Ed25519 signature verification instruction (proving the purchase permit was signed by Sonar)
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.
The program uses Program Derived Addresses for all state. Derive these before building the transaction:
import { PublicKey } from "@solana/web3.js";import { parse as uuidParse } from "uuid";// IDs from the Sonar API may be 0x-prefixed or UUID-formattedfunction 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 UUIDconst [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 IDconst [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 walletconst [walletBindingPDA] = PublicKey.findProgramAddressSync( [Buffer.from("wallet_binding"), salePDA.toBuffer(), wallet.publicKey.toBuffer()], programPublicKey);
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 into your project.
import { BorshCoder, BN } from "@coral-xyz/anchor";import { IDL } from "./idl/settlement_sale";// permit is the PermitJSON field from Sonar's generatePurchasePermit responseconst 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);