A frontend-focused React example app demonstrating integration with the Sonar API using @echoxyz/sonar-react and @echoxyz/sonar-core libraries.
There is an integration guide for these libraries here.
This example implements a client-side OAuth flow where tokens are managed entirely in the browser by the @echoxyz/sonar-react library. For a more secure backend approach where tokens are stored server-side, see sonar-example-nextjs.
This approach is simpler to implement and can can be used by single-page applications (SPAs) that don't already have a backend. All authentication and API calls are handled client-side using React hooks.
However, since tokens are stored in the browser, this approach is less secure than a backend flow (as implemented in sonar-example-nextjs).
Copy the env template and fill in the values for your sale:
cp .env.example .envEdit .env and set VITE_SALE_UUID, VITE_OAUTH_CLIENT_UUID, VITE_SALE_CONTRACT_ADDRESS, and VITE_PAYMENT_TOKEN_ADDRESS. You can find these values for your sale on the Echo founder dashboard.
The app will throw at startup if any of the required vars are missing.
pnpm i
pnpm devThe app will be available at http://localhost:3000.
The example uses a SettlementSale contract on Base Sepolia.
In order to test committing funds, you will need to have USDC to commit and ETH to pay for the gas.
Faucets:
By default, the app uses the public Base Sepolia RPC endpoint, which is rate-limited and not suitable for production use.
For production or any meaningful testing, set the env var VITE_BASE_RPC_URL to your private RPC endpoint from Alchemy, Infura, QuickNode, or similar.
Be careful! Exposing an RPC URL on the frontend allows anyone to extract and use your private RPC keys. Only use scoped and rate-limited API keys, never expose your master private keys.
- Provider setup — configuring
SonarProviderwith wagmi and React Query - OAuth authentication with Sonar — client-side flow using
useSonarAuth()hook - Token management — handled automatically by
@echoxyz/sonar-reactin browser storage - Entity state display — prior to sale, list all user entities; during sale, show linked entity status
- Pre-purchase checks — validate eligibility before transactions using
useSonarPurchase() - Purchase transactions — generate permits and submit to the sale contract
The @echoxyz/sonar-react library handles the complete OAuth flow client-side, storing tokens in browser storage:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Browser │ │ Sonar │ │ Echo │
│ (React │ │ React │ │ OAuth │
│ App) │ │ Library │ │ │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
│ 1. User clicks │ │
│ "Connect with Sonar" │ │
├────────────────────────────>│ │
│ │ │
│ │ 2. Generate PKCE │
│ │ params & store │
│ │ in sessionStorage │
│ │ │
│ │ 3. Redirect via │
│ │ window.location │
│ │ │
│ 4. Navigate to Echo OAuth │ │
├──────────────────────────────────────────────────────────>│
│ │ │
│ 5. User authenticates & authorizes │
│ (interactive session) │ │
│ │ │
│ 6. Redirect to callback with auth code & state │
│<──────────────────────────────────────────────────────────│
│ │ │
│ 7. Callback page calls │ │
│ completeOAuth() │ │
├────────────────────────────>│ │
│ │ │
│ │ 8. Exchange code + │
│ │ verifier for tokens │
│ ├────────────────────────────>│
│ │ │
│ │ 9. Return tokens │
│ │<────────────────────────────│
│ │ │
│ │ 10. Store tokens │
│ │ in browser storage │
│ │ │
│ 11. authenticated = true │ │
│<────────────────────────────│ │
│ │ │
Once authenticated, Sonar API calls are made directly from the client using React hooks:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Browser │ │ Sonar │ │ Sonar │
│ (React │ │ React │ │ API │
│ App) │ │ Library │ │ │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
│ 1. useSonarEntities() │ │
│ hook renders │ │
├────────────────────────────>│ │
│ │ │
│ │ 2. Get token from │
│ │ browser storage │
│ │ │
│ │ 3. GET /entities │
│ │ Authorization: Bearer... │
│ ├────────────────────────────>│
│ │ │
│ │ 4. Response │
│ │<────────────────────────────│
│ │ │
│ 5. Return entities │ │
│ to component │ │
│<────────────────────────────│ │
│ │ │
src/
├── components/
│ ├── auth/ # Login/logout UI
│ ├── entity/ # Entity display components
│ ├── registration/ # Pre-sale entity list & eligibility
│ └── sale/ # Purchase flow UI
├── pages/
│ ├── Home.tsx # Main page (pre-sale & sale views)
│ └── OAuthCallback.tsx # OAuth callback handler
├── config.ts # Environment configuration
├── hooks.ts # Custom hooks (useSaleContract)
├── Provider.tsx # SonarProvider, wagmi, React Query setup
└── main.tsx # App entry point