Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 118 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,134 @@
# sonar-example-react

A React example app demonstrating integration with the Sonar API using `@echoxyz/sonar-react` and `@echoxyz/sonar-core` libraries.
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](https://docs.echo.xyz/sonar/integration-guides/react).

The example app demonstrates how to:
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](https://github.com/sunrisedotdev/sonar-example-nextjs).

- Setup providers in `src/Provider.tsx`
- Authenticate with Sonar via the oauth flow
- See `src/components/auth/AuthenticationSection.tsx` on how to create the login/logout buttons
- See `src/pages/OAuthCallback.tsx` for an example of the oauth callback handler
- Prior to a sale going live, a way to list the state of all of a user's entities
- See `src/pages/Home.tsx` while in the `!saleIsLive` state
- When sale is live, display setup/eligibily state of the entity on Sonar that is linked to the currently connected wallet
- See `src/pages/Home.tsx` while in the `saleIsLive` state
- Surface the user's entity setup/eligibility state
- See components in `src/components/entity`
- Run prepurchase checks
- See `src/components/sale/PurchaseCard.tsx` for an example of how to run these checks and interpret the result
- Submit a purchase transaction to an example sale contract
- See the `ReadyToPurchaseSection` in `src/components/sale/PurchaseCard.tsx` for an example of how to generate a purchase permit and pass this to the contract,
using the `useSaleContract` hook in `src/hooks.ts`
## Why Use the Frontend Approach?

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

By default this app is configured to point to use a test sonar sale and contract and should work out of the box.
If you want to point it at a different sale or contract, you can modify the env vars in `.env.local`.
However, since tokens are stored in the browser, this approach is less secure than a backend flow (as implemented in [sonar-example-nextjs](https://github.com/sunrisedotdev/sonar-example-nextjs)).

## Running the app locally
## Running the App Locally

By default this app is configured to use a test Sonar sale and contract and should work out of the box. If you want to point it at a different sale or contract, you can modify the env vars in `.env.local`. You can find the values for your sale on the [Echo founder dashboard](https://app.echo.xyz/founder).

```sh
pnpm i
pnpm dev
```

The app will be available at `http://localhost:3000`.

## What This Example Demonstrates

- **Provider setup** — configuring `SonarProvider` with wagmi and React Query
- **OAuth authentication with Sonar** — client-side flow using `useSonarAuth()` hook
- **Token management** — handled automatically by `@echoxyz/sonar-react` in 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

## Authentication Architecture

The `@echoxyz/sonar-react` library handles the complete OAuth flow client-side, storing tokens in browser storage:

### OAuth Flow

```
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 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 │
Comment on lines +55 to +57
Copy link
Copy Markdown
Contributor

@cxkoda cxkoda Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems a bit too detailed

│ │ │
│ 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 │ │
│<────────────────────────────│ │
│ │ │
```

### API Requests

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 │ │
│<────────────────────────────│ │
│ │ │
```

## Project Structure

```
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
```
Loading