@echoxyz/sonar-react
v0.12.0
Published
React provider and hooks for Echo’s Sonar APIs, built on `@echoxyz/sonar-core`.
Downloads
811
Readme
@echoxyz/sonar-react
React provider and hooks for Echo’s Sonar APIs, built on @echoxyz/sonar-core.
- Framework/router agnostic (works with React Router, Next.js, etc.).
- Handles PKCE OAuth redirect flow and token storage for the browser.
- Exposes a ready-to-use API client bound to a single Sonar oauth client.
Install
pnpm add @echoxyz/sonar-react @echoxyz/sonar-corePeer dependency: react@>=18.
Quick start
- Wrap your app with
SonarProvider:
import { SonarProvider } from "@echoxyz/sonar-react";
export function AppRoot({ children }: { children: React.ReactNode }) {
return (
<SonarProvider
config={{
clientUUID: "<your-oauth-client-id>",
redirectURI: window.location.origin + "/oauth/callback",
// Optional:
// apiURL: "https://api.echo.xyz",
// tokenStorageKey: "sonar:auth-token",
}}
>
{children}
</SonarProvider>
);
}- Trigger login with PKCE + redirect:
import { useSonarAuth } from "@echoxyz/sonar-react";
export function LoginButton() {
const { login, authenticated, ready } = useSonarAuth();
if (!ready || authenticated) {
return null;
}
return <button onClick={() => login()}>Sign in with Echo</button>;
}- Complete OAuth on your callback route/page:
import { useEffect } from "react";
import { useSonarAuth } from "@echoxyz/sonar-react";
export default function OAuthCallback() {
const { completeOAuth } = useSonarAuth();
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const code = params.get("code");
const state = params.get("state");
if (!code || !state) {
return;
}
completeOAuth({ code, state }).catch((err) => {
console.error("OAuth completion failed", err);
});
}, [completeOAuth]);
return <p>Completing sign-in…</p>;
}- Load the Sonar entity associated with the user's wallet
import { useSonarEntity } from "./hooks/useSonarEntity";
import { useAccount } from "wagmi";
const ExampleEntityPanel = () => {
const { address, isConnected } = useAccount();
const { authenticated, loading, entity, error } = useSonarEntity({
saleUUID: "<your-sale-uuid>",
wallet: { address, isConnected },
});
if (!isConnected || !authenticated) {
return <p>Connect your wallet and Sonar account to continue</p>;
}
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Error: {error.message}</p>;
}
if (!entity) {
return <p>No entity found for this wallet. Please link your wallet on Sonar to continue.</p>;
}
return (
<div>
<span>entity.Label</span>
<span>entity.EntitySetupState</span>
<span>entity.EntitySaleEligibility</span>
</div>
);
};If you want to fetch all entities associated with the logged in user, you can use the useSonarEntities hook.
- Implement the purchase flow
function Example({
entityID,
walletAddress,
}: {
entityID: string;
walletAddress: string;
}) {
const sonarPurchaser = useSonarPurchase({
saleUUID: sonarConfig.saleUUID,
entityID,
entityType,
walletAddress,
});
if (sonarPurchaser.loading) {
return <p>Loading...</p>;
}
if (sonarPurchaser.error) {
return <p>Error: {error.message}</p>;
}
return (
<div>
{sonarPurchaser.readyToPurchase && (
<PurchaseButton
generatePurchasePermit={sonarPurchaser.generatePurchasePermit}
/>
)}
{!sonarPurchaser.readyToPurchase &&
sonarPurchaser.failureReason ===
PrePurchaseFailureReason.REQUIRES_LIVENESS && (
<button
onClick={() => {
window.open(prePurchaseCheckResult.LivenessCheckURL, "_blank");
}}
>
Complete liveness check to purchase
</button>
)}
</div>
);
}
function PurchaseButton({
generatePurchasePermit,
}: {
generatePurchasePermit: () => Promise<GeneratePurchasePermitResponse>;
}) {
const purchase = async () => {
const response = await generatePurchasePermit();
const r = response as unknown as {
Signature: string;
PermitJSON: BasicPermitV2;
};
if (r.Signature && r.PermitJSON) {
console.log(permit.Signature, permit.Permit);
return;
}
};
return (
<button onClick={purchase}>
Purchase
</button>
);
}API
SonarProvider- Props
config:clientUUID: string(required) – Echo OAuth Client ID.redirectURI: string(required) – Your OAuth callback URI.apiURL?: string(default:https://api.echo.xyz) – API base URL.tokenStorageKey?: string(default:sonar:auth-token) – Browser storage key for the access token.
- Props
useSonarAuth()→{ authenticated, ready, token?, login(), completeOAuth({ code, state }), logout() }useSonarClient()→ low-levelSonarClientinstance.useSonarEntity()→{ authenticated, loading, entity?, error? }high-level convenience hook for fetching a Sonar entity by wallet address.
Notes
- Tokens are not auto-refreshed. On expiry, call
logout()and re-run the OAuth flow. - This package doesn’t depend on a specific router. Use it in Next.js, React Router, or any custom setup.
- Wallet addresses are typed as template literals
0x${string}.
