@orangecheck/react
React components for OC Attest. All client-side, built on @orangecheck/sdk.
Install
npm i @orangecheck/react @orangecheck/sdk
React 18 or 19. Works with Next.js (Pages + App Router), Vite, Remix, CRA, and any standard React toolchain.
<OcBadge>
Render a live proof-of-stake badge for a Bitcoin address.
import { OcBadge } from '@orangecheck/react';
<OcBadge address="bc1qalice..." />;
- Queries Nostr for the most recent attestation at that address.
- Verifies BIP-322 and fetches current chain state via Esplora.
- Renders the tier, sats bonded, days unspent, and links to the verification URL.
- Falls back to a "no proof" skeleton if no attestation is found.
Props:
interface OcBadgeProps {
address: string;
variant?: 'compact' | 'full'; // default 'full'
onClick?: (result) => void; // fires when the user clicks the badge
relays?: string[]; // custom relay set
className?: string;
}
<OcGate>
Conditionally render children based on the user's stake.
import { OcGate } from '@orangecheck/react';
<OcGate address={userAddress} minSats={100_000} minDays={30}>
<PostEditor />
</OcGate>;
Above the threshold, <PostEditor> renders. Below, a default "not-enough-stake"
placeholder shows (customizable via the fallback prop).
Warning.
<OcGate>is a UI convenience, not a security boundary. An adversary bypasses client state trivially. Real access control must happen on the server — use@orangecheck/gateor call/api/checkdirectly.
Props:
interface OcGateProps {
address: string;
minSats?: number;
minDays?: number;
fallback?: React.ReactNode;
loading?: React.ReactNode;
children: React.ReactNode;
}
<OcChallengeButton>
Button that kicks off the signed-challenge flow.
import { OcChallengeButton } from '@orangecheck/react';
<OcChallengeButton
address={userAddress}
audience="https://example.com"
purpose="login"
onVerified={(result) => {
// result.address is cryptographically proven
openSession(result.address);
}}
/>;
Handles:
- Fetching the challenge from
/auth/challenge. - Opening the user's connected wallet (UniSat / Xverse / Leather / Alby).
- Comparing the wallet's active address to the intended address and bailing out if they differ.
- POSTing the signature to
/auth/verify(or your custom endpoint).
Styling
The components ship with zero CSS baggage — they use Tailwind class hooks when
available and unstyled semantic HTML otherwise. Override with className or
wrap the default markup.
Typescript types
Every prop and return type is exported:
import type {
BadgeVariant,
OcBadgeProps,
OcChallengeButtonProps,
OcGateProps,
} from '@orangecheck/react';
See also
@orangecheck/sdk— the underlying logic@orangecheck/wallet-adapter— cross-wallet signing- Sign in with Bitcoin — server-side
flow that pairs with
<OcChallengeButton>