@orangecheck/auth-client 2.35.1 → 2.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bond.tsx"],"sourcesContent":["'use client';\n\n/**\n * OcBond · back the signed-in account with bitcoin (oc-attest-protocol\n * BOND.md). The person signs for one coin they hold. It stays in their wallet,\n * and spending it ends the backing. Apps they sign in to see floors and a\n * number unique to that app, never the coin. Optional: every account signs in\n * without one.\n *\n * Runs against the auth host's /api/auth/bond with the family cookie, like\n * OcLinkedIdentities, so any *.ochk.io site can mount it. A change re-mints\n * the session there, and a tab pinned to this account re-pins with the fresh\n * token; `onChange` lets the site refresh its session view.\n */\nimport * as React from 'react';\n\nimport { SIGNIN_FONT } from './appearance';\nimport {\n bodyStyle,\n dangerBtnStyle,\n ErrorLine,\n FormButtons,\n ghostBtnStyle,\n hintStyle,\n inputStyle,\n LazyWalletButton,\n panelStyle,\n primaryBtnStyle,\n SectionLabel,\n V,\n} from './linked-identities';\nimport { readTabSession, writeTabSession } from './tab-session';\n\nconst DEFAULT_AUTH_ORIGIN = 'https://ochk.io';\nconst MIN_CONFIRMATIONS = 6;\n/** Smaller coins meet no floor, so the auth host refuses them (`coin_too_small`). */\nconst MIN_SATS = 1_000;\n\ninterface Floors {\n sats_floor: number;\n days_floor: number;\n}\n\n/** The account's bond as the auth host shows it to the account itself. */\nexport interface OcBondView extends Floors {\n address: string;\n outpoint: string;\n sats: number;\n confirmed_height: number;\n checked_at: string;\n checked_height: number;\n created_at: string;\n}\n\ninterface Coin extends Floors {\n outpoint: string;\n sats: number;\n age_blocks: number;\n}\n\nexport interface OcBondProps {\n /** The auth host. Default https://ochk.io; pass the current origin on ochk.io itself. */\n authOrigin?: string;\n /** After a bond is made or released, so the site can refresh its session. */\n onChange?: (bond: OcBondView | null) => void;\n className?: string;\n}\n\nconst n = (v: number) => v.toLocaleString('en-US');\n\n/** \"at least 100,000 sats, held 90 days or more\". */\nexport function bondFloorsText(f: Floors): string {\n const sats = f.sats_floor > 0 ? `at least ${n(f.sats_floor)} sats` : 'under 1,000 sats';\n const days = f.days_floor > 0 ? `held ${f.days_floor} days or more` : 'held under 7 days';\n return `${sats}, ${days}`;\n}\n\nconst short = (outpoint: string) => {\n const [txid = '', vout = ''] = outpoint.split(':');\n return `${txid.slice(0, 8)}…${txid.slice(-4)}:${vout}`;\n};\n\nconst usable = (c: Coin) => c.sats >= MIN_SATS && c.age_blocks >= MIN_CONFIRMATIONS;\n\nconst dustText = (count: number) =>\n `${n(count)} ${count === 1 ? 'coin' : 'coins'} under ${n(MIN_SATS)} sats, ` +\n 'too small to back an account';\n\nconst REFUSAL: Record<string, string> = {\n invalid_address: \"that isn't a Bitcoin address we can read\",\n chain_unavailable: \"couldn't read the chain just now · nothing changed, try again in a minute\",\n sig_invalid:\n \"that signature isn't from this coin's address · sign with the wallet that holds it\",\n bond_expired: 'the message expired · start again',\n bond_not_yet_valid: \"your device's clock looks wrong · check it and start again\",\n bond_subject_mismatch: 'that message was for another account · start again',\n bond_malformed: 'the signed message changed on the way · start again',\n bond_window_too_long: \"that message's signing window is too long · start again\",\n coin_not_found: \"that coin doesn't exist on-chain\",\n coin_address_mismatch: \"that coin isn't at this address\",\n coin_unconfirmed: \"that coin isn't confirmed yet\",\n coin_too_recent: 'that coin needs six confirmations first, about an hour',\n coin_spent: 'that coin has been spent · pick one still in your wallet',\n coin_too_small: `that coin is under ${n(MIN_SATS)} sats, too small to back an account`,\n coin_backs_another_account: 'that coin already backs another orangecheck account',\n coin_backed_another_recently:\n 'that coin backed another account until recently · ' +\n 'it can back this one 30 days after it was released',\n not_authenticated: 'your session ended · sign in again',\n rate_limited: 'too many tries · wait a minute, then try again',\n};\n\n/** A refusal from /api/auth/bond, in the component's voice. Not exported from the index. */\nexport const bondRefusal = (reason?: string): string =>\n (reason && REFUSAL[reason]) || \"your backing didn't save · nothing changed, try again\";\n\n/* Small print stays legible: 12px or more, and never dimmed below the muted tone. */\nconst smallStyle: React.CSSProperties = { ...bodyStyle, fontSize: 12, margin: 0 };\nconst explainerStyle: React.CSSProperties = {\n ...hintStyle,\n fontSize: 12,\n lineHeight: 1.55,\n opacity: 1,\n margin: 0,\n};\nconst fieldLabelStyle: React.CSSProperties = {\n display: 'block',\n color: V.foreground,\n fontSize: 13,\n fontWeight: 500,\n margin: '0 0 4px',\n};\nconst addressStyle: React.CSSProperties = { wordBreak: 'break-all', color: V.foreground };\n\nfunction coinStyle(picked: boolean, young: boolean): React.CSSProperties {\n return {\n background: V.card,\n border: `1px ${young ? 'dashed' : 'solid'} ${picked ? V.primary : V.border}`,\n padding: '0.75rem',\n display: 'flex',\n gap: 10,\n alignItems: 'flex-start',\n cursor: young ? 'not-allowed' : 'pointer',\n };\n}\n\n/** The account's bond, or null. Throws when the auth host can't be read. */\nexport async function fetchOcBond(opts?: { authOrigin?: string }): Promise<OcBondView | null> {\n const r = await fetch(`${opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN}/api/auth/bond`, {\n credentials: 'include',\n });\n const j = (await r.json().catch(() => ({}))) as { ok?: boolean; bond?: OcBondView | null };\n if (!r.ok || !j.ok) throw new Error(`http_${r.status}`);\n return j.bond ?? null;\n}\n\nexport function OcBond({\n authOrigin = DEFAULT_AUTH_ORIGIN,\n onChange,\n className,\n}: OcBondProps): React.ReactElement {\n const [bond, setBond] = React.useState<OcBondView | null | undefined>(undefined);\n const [loadErr, setLoadErr] = React.useState(false);\n const [nonce, setNonce] = React.useState(0);\n const [adding, setAdding] = React.useState(false);\n const [confirming, setConfirming] = React.useState(false);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n\n React.useEffect(() => {\n let alive = true;\n fetchOcBond({ authOrigin }).then(\n (b) => alive && setBond(b),\n () => alive && setLoadErr(true)\n );\n return () => {\n alive = false;\n };\n }, [authOrigin, nonce]);\n\n const call = React.useCallback(\n async (method: string, body?: unknown) => {\n const r = await fetch(`${authOrigin}/api/auth/bond`, {\n method,\n credentials: 'include',\n headers: body ? { 'Content-Type': 'application/json' } : undefined,\n body: body ? JSON.stringify(body) : undefined,\n });\n const j = (await r.json().catch(() => ({}))) as Record<string, unknown> & {\n ok?: boolean;\n reason?: string;\n token?: unknown;\n };\n // The host minted for the account this tab shows; keep the pin current.\n const pin = readTabSession();\n if (pin && r.ok && typeof j.token === 'string') {\n writeTabSession({ token: j.token, didOc: pin.didOc });\n }\n return { ok: r.ok && j.ok === true, ...j };\n },\n [authOrigin]\n );\n\n async function release(): Promise<void> {\n setBusy(true);\n setErr(null);\n const j = await call('DELETE').catch(() => ({ ok: false, reason: 'network' }));\n setBusy(false);\n if (!j.ok) return setErr(bondRefusal(j.reason));\n setConfirming(false);\n setBond(null);\n onChange?.(null);\n }\n\n if (loadErr) {\n return (\n <div className={className} data-oc-bond=\"\" style={panelStyle('warn')} role=\"alert\">\n <p style={bodyStyle}>couldn&apos;t read your backing · nothing was changed</p>\n <button\n type=\"button\"\n style={ghostBtnStyle(false)}\n onClick={() => {\n setLoadErr(false);\n setNonce((x) => x + 1);\n }}\n >\n try again\n </button>\n </div>\n );\n }\n if (bond === undefined) {\n return (\n <div className={className} data-oc-bond=\"\" style={panelStyle()}>\n <span style={hintStyle}>reading your backing…</span>\n </div>\n );\n }\n\n return (\n <div className={className} data-oc-bond=\"\" style={{ display: 'grid', gap: 16 }}>\n {bond && !adding ? (\n <div style={panelStyle('success')}>\n <SectionLabel tone=\"success\">§ backed by bitcoin</SectionLabel>\n <p style={{ ...bodyStyle, color: V.foreground }}>\n apps see a coin {bondFloorsText(bond)}.\n </p>\n <p style={bodyStyle}>\n coin {short(bond.outpoint)}, {n(bond.sats)} sats · last found unspent at\n block {n(bond.checked_height)} · only you and orangecheck see this line\n </p>\n {confirming ? (\n <div role=\"group\" aria-label=\"stop backing\" data-oc-bond-confirm=\"\">\n <p style={{ ...bodyStyle, color: V.foreground, margin: 0 }}>\n stop backing? apps that ask for a backed account may stop letting\n you in.\n </p>\n <FormButtons>\n <button\n type=\"button\"\n style={dangerBtnStyle(busy)}\n disabled={busy}\n onClick={() => void release()}\n >\n stop backing\n </button>\n <button\n type=\"button\"\n style={ghostBtnStyle(busy)}\n disabled={busy}\n autoFocus\n onClick={() => {\n setErr(null);\n setConfirming(false);\n }}\n >\n keep\n </button>\n </FormButtons>\n </div>\n ) : (\n <FormButtons>\n <button\n type=\"button\"\n style={ghostBtnStyle(false)}\n onClick={() => {\n setErr(null);\n setAdding(true);\n }}\n >\n use another coin\n </button>\n <button\n type=\"button\"\n style={dangerBtnStyle(false)}\n onClick={() => setConfirming(true)}\n >\n stop backing\n </button>\n </FormButtons>\n )}\n {err ? <ErrorLine>{err}</ErrorLine> : null}\n </div>\n ) : null}\n {!bond && !adding ? (\n <div style={panelStyle()}>\n <SectionLabel>§ not backed</SectionLabel>\n <p style={bodyStyle}>\n your account works everywhere as it is. some apps let in only accounts\n backed by a coin you hold. it stays in your wallet, and apps never see\n which coin.\n </p>\n <button\n type=\"button\"\n style={primaryBtnStyle(false)}\n onClick={() => setAdding(true)}\n >\n back your account\n </button>\n </div>\n ) : null}\n {adding ? (\n <BondForm\n call={call}\n onCancel={() => setAdding(false)}\n onDone={(b) => {\n setBond(b);\n setAdding(false);\n onChange?.(b);\n }}\n />\n ) : null}\n <p style={explainerStyle}>\n Apps see only rough floors, like “at least 100,000 sats, held 30 days or\n more”, and a number unique to that app. Never the coin, your address or your\n balance. An app can block that number; only moving the coin gets past it, and a\n moved coin starts its age again.\n </p>\n </div>\n );\n}\n\ntype Call = (\n method: string,\n body?: unknown\n) => Promise<Record<string, unknown> & { ok: boolean; reason?: string }>;\n\nfunction BondForm({\n call,\n onCancel,\n onDone,\n}: {\n call: Call;\n onCancel: () => void;\n onDone: (bond: OcBondView) => void;\n}): React.ReactElement {\n const [address, setAddress] = React.useState('');\n const [coins, setCoins] = React.useState<Coin[] | null>(null);\n const [chosen, setChosen] = React.useState('');\n const [message, setMessage] = React.useState<string | null>(null);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n const id = React.useId();\n\n const addr = address.trim();\n const canFind = addr.length >= 14;\n\n async function run(fn: () => Promise<void>): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n await fn();\n } catch {\n setErr(bondRefusal('chain_unavailable'));\n } finally {\n setBusy(false);\n }\n }\n\n const findCoins = () =>\n run(async () => {\n const j = await call('POST', { action: 'coins', address: addr });\n if (!j.ok) return setErr(bondRefusal(j.reason));\n const list = (j.coins as Coin[]) ?? [];\n setCoins(list);\n setChosen(list.find(usable)?.outpoint ?? '');\n });\n\n const getMessage = () =>\n run(async () => {\n const j = await call('POST', { action: 'message', address: addr, outpoint: chosen });\n if (!j.ok || typeof j.message !== 'string') return setErr(bondRefusal(j.reason));\n setMessage(j.message);\n });\n\n const submit = (signature: string) =>\n run(async () => {\n const j = await call('POST', { action: 'bond', message, signature });\n if (!j.ok || !j.bond) return setErr(bondRefusal(j.reason));\n onDone(j.bond as OcBondView);\n });\n\n /** Back one step: from signing to the coin list, or from the coins to the address. */\n function back(): void {\n setErr(null);\n if (message) setMessage(null);\n else setCoins(null);\n }\n\n const error = err ? <ErrorLine>{err}</ErrorLine> : null;\n const backButton = (\n <button type=\"button\" style={ghostBtnStyle(busy)} disabled={busy} onClick={back}>\n back\n </button>\n );\n const cancelButton = (\n <button type=\"button\" style={ghostBtnStyle(busy)} disabled={busy} onClick={onCancel}>\n cancel\n </button>\n );\n\n let step: React.ReactNode;\n if (message) {\n step = (\n <>\n <p style={bodyStyle}>\n sign with the wallet that holds <span style={addressStyle}>{addr}</span>.\n signing spends nothing, and the coin stays in your wallet.\n </p>\n <LazyWalletButton\n address={addr}\n message={message}\n showManual\n onSigned={(sig) => void submit(sig)}\n onError={(e) => setErr(e.message ?? 'the wallet refused to sign')}\n />\n {error}\n <FormButtons>\n {backButton}\n {cancelButton}\n </FormButtons>\n </>\n );\n } else if (coins) {\n const eligible = coins.filter((c) => c.sats >= MIN_SATS);\n const dust = coins.length - eligible.length;\n step = (\n <>\n {coins.length === 0 ? (\n <p style={bodyStyle}>\n no confirmed coins at that address · send at least {n(MIN_SATS)} sats\n there and wait for six confirmations, about an hour. the longer a coin\n sits, the more it says.\n </p>\n ) : eligible.length === 0 ? (\n <>\n <p style={{ ...bodyStyle, color: V.foreground }}>{dustText(dust)}</p>\n <p style={bodyStyle}>\n send at least {n(MIN_SATS)} sats there in one payment and wait for six\n confirmations, about an hour. coins aren&apos;t added together.\n </p>\n </>\n ) : (\n <>\n <p id={`${id}-pick`} style={bodyStyle}>\n pick one coin · coins aren&apos;t added together\n </p>\n <div\n role=\"radiogroup\"\n aria-labelledby={`${id}-pick`}\n style={{ display: 'grid', gap: 8 }}\n >\n {eligible.map((c) => {\n const young = c.age_blocks < MIN_CONFIRMATIONS;\n return (\n <label\n key={c.outpoint}\n style={coinStyle(chosen === c.outpoint, young)}\n >\n <input\n type=\"radio\"\n name=\"oc-bond-coin\"\n value={c.outpoint}\n checked={chosen === c.outpoint}\n disabled={young}\n onChange={() => setChosen(c.outpoint)}\n style={{ accentColor: V.primary, margin: '2px 0 0' }}\n />\n <span style={{ display: 'grid', gap: 2, minWidth: 0 }}>\n <span\n style={{\n color: young ? V.muted : V.foreground,\n fontSize: 13,\n }}\n >\n {n(c.sats)} sats, held{' '}\n {Math.floor(c.age_blocks / 144)} days\n </span>\n <span style={smallStyle}>\n {young\n ? 'too recent: needs six confirmations'\n : `apps would see ${bondFloorsText(c)}`}\n </span>\n <span\n style={{\n ...smallStyle,\n fontFamily: SIGNIN_FONT,\n wordBreak: 'break-all',\n }}\n >\n {short(c.outpoint)}\n </span>\n </span>\n </label>\n );\n })}\n </div>\n {dust > 0 ? (\n <p style={{ ...smallStyle, marginTop: 8 }}>{dustText(dust)}</p>\n ) : null}\n </>\n )}\n {error}\n <FormButtons>\n {eligible.length > 0 ? (\n <button\n type=\"button\"\n style={primaryBtnStyle(busy || !chosen)}\n disabled={busy || !chosen}\n onClick={() => void getMessage()}\n >\n continue\n </button>\n ) : null}\n {backButton}\n {cancelButton}\n </FormButtons>\n </>\n );\n } else {\n step = (\n <form\n style={{ margin: 0 }}\n onSubmit={(e) => {\n e.preventDefault();\n if (canFind && !busy) void findCoins();\n }}\n >\n <label htmlFor={`${id}-address`} style={fieldLabelStyle}>\n bitcoin address that holds the coin\n </label>\n <p id={`${id}-advice`} style={{ ...bodyStyle, margin: '0 0 8px' }}>\n use one that holds nothing else of yours: orangecheck learns its address and\n amount. apps never do.\n </p>\n <input\n id={`${id}-address`}\n aria-describedby={`${id}-advice`}\n style={inputStyle}\n value={address}\n onChange={(e) => setAddress(e.target.value)}\n placeholder=\"bc1q…\"\n spellCheck={false}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n />\n {error}\n <FormButtons>\n <button\n type=\"submit\"\n style={primaryBtnStyle(busy || !canFind)}\n disabled={busy || !canFind}\n >\n find its coins\n </button>\n {cancelButton}\n </FormButtons>\n </form>\n );\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-bond-form=\"\">\n <SectionLabel>§ back your account</SectionLabel>\n {step}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/bond.tsx"],"sourcesContent":["'use client';\n\n/**\n * OcBond · back the signed-in account with bitcoin (oc-attest-protocol\n * BOND.md). The person signs for one coin they hold. It stays in their wallet,\n * and spending it ends the backing. Apps they sign in to see floors and a\n * number unique to that app, never the coin. Optional: every account signs in\n * without one.\n *\n * Runs against the auth host's /api/auth/bond with the family cookie, like\n * OcLinkedIdentities, so any *.ochk.io site can mount it. A change re-mints\n * the session there, and a tab pinned to this account re-pins with the fresh\n * token; `onChange` lets the site refresh its session view.\n */\nimport * as React from 'react';\n\nimport { SIGNIN_FONT } from './appearance';\nimport {\n bodyStyle,\n dangerBtnStyle,\n ErrorLine,\n FormButtons,\n ghostBtnStyle,\n hintStyle,\n inputStyle,\n LazyWalletButton,\n panelStyle,\n primaryBtnStyle,\n SectionLabel,\n V,\n} from './linked-identities';\nimport { readTabSession, writeTabSession } from './tab-session';\n\nconst DEFAULT_AUTH_ORIGIN = 'https://ochk.io';\nconst MIN_CONFIRMATIONS = 6;\n/** Smaller coins meet no floor, so the auth host refuses them (`coin_too_small`). */\nconst MIN_SATS = 1_000;\n\ninterface Floors {\n sats_floor: number;\n days_floor: number;\n}\n\n/** The account's bond as the auth host shows it to the account itself. */\nexport interface OcBondView extends Floors {\n address: string;\n outpoint: string;\n sats: number;\n confirmed_height: number;\n checked_at: string;\n checked_height: number;\n created_at: string;\n}\n\ninterface Coin extends Floors {\n outpoint: string;\n sats: number;\n age_blocks: number;\n}\n\nexport interface OcBondProps {\n /** The auth host. Default https://ochk.io; pass the current origin on ochk.io itself. */\n authOrigin?: string;\n /** After a bond is made or released, so the site can refresh its session. */\n onChange?: (bond: OcBondView | null) => void;\n className?: string;\n}\n\nconst n = (v: number) => v.toLocaleString('en-US');\n\n/** \"at least 100,000 sats, held 90 days or more\". */\nexport function bondFloorsText(f: Floors): string {\n const sats = f.sats_floor > 0 ? `at least ${n(f.sats_floor)} sats` : 'under 1,000 sats';\n const days = f.days_floor > 0 ? `held ${f.days_floor} days or more` : 'held under 7 days';\n return `${sats}, ${days}`;\n}\n\nconst short = (outpoint: string) => {\n const [txid = '', vout = ''] = outpoint.split(':');\n return `${txid.slice(0, 8)}…${txid.slice(-4)}:${vout}`;\n};\n\nconst usable = (c: Coin) => c.sats >= MIN_SATS && c.age_blocks >= MIN_CONFIRMATIONS;\n\nconst dustText = (count: number) =>\n `${n(count)} ${count === 1 ? 'coin' : 'coins'} under ${n(MIN_SATS)} sats, ` +\n 'too small to back an account';\n\nconst REFUSAL: Record<string, string> = {\n invalid_address: \"that isn't a Bitcoin address we can read\",\n chain_unavailable: \"couldn't read the chain just now · nothing changed, try again in a minute\",\n sig_invalid:\n \"that signature isn't from this coin's address · sign with the wallet that holds it\",\n bond_expired: 'the message expired · start again',\n bond_not_yet_valid: \"your device's clock looks wrong · check it and start again\",\n bond_subject_mismatch: 'that message was for another account · start again',\n bond_malformed: 'the signed message changed on the way · start again',\n bond_window_too_long: \"that message's signing window is too long · start again\",\n coin_not_found: \"that coin doesn't exist on-chain\",\n coin_address_mismatch: \"that coin isn't at this address\",\n coin_unconfirmed: \"that coin isn't confirmed yet\",\n coin_too_recent: 'that coin needs six confirmations first, about an hour',\n coin_spent: 'that coin has been spent · pick one still in your wallet',\n coin_too_small: `that coin is under ${n(MIN_SATS)} sats, too small to back an account`,\n coin_backs_another_account: 'that coin already backs another orangecheck account',\n coin_backed_another_recently:\n 'that coin backed another account until recently · ' +\n 'it can back this one 30 days after it was released',\n not_authenticated: 'your session ended · sign in again',\n rate_limited: 'too many tries · wait a minute, then try again',\n};\n\n/** A refusal from /api/auth/bond, in the component's voice. Not exported from the index. */\nexport const bondRefusal = (reason?: string): string =>\n (reason && REFUSAL[reason]) || \"your backing didn't save · nothing changed, try again\";\n\n/* Small print stays legible: 12px or more, and never dimmed below the muted tone. */\nconst smallStyle: React.CSSProperties = { ...bodyStyle, fontSize: 12, margin: 0 };\nconst explainerStyle: React.CSSProperties = {\n ...hintStyle,\n fontSize: 12,\n lineHeight: 1.55,\n opacity: 1,\n margin: 0,\n};\nconst fieldLabelStyle: React.CSSProperties = {\n display: 'block',\n color: V.foreground,\n fontSize: 13,\n fontWeight: 500,\n margin: '0 0 4px',\n};\nconst addressStyle: React.CSSProperties = { wordBreak: 'break-all', color: V.foreground };\n\nfunction coinStyle(picked: boolean, young: boolean): React.CSSProperties {\n return {\n background: V.card,\n border: `1px ${young ? 'dashed' : 'solid'} ${picked ? V.primary : V.border}`,\n padding: '0.75rem',\n display: 'flex',\n gap: 10,\n alignItems: 'flex-start',\n cursor: young ? 'not-allowed' : 'pointer',\n };\n}\n\n/** The account's bond, or null. Throws when the auth host can't be read. */\nexport async function fetchOcBond(opts?: { authOrigin?: string }): Promise<OcBondView | null> {\n const r = await fetch(`${opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN}/api/auth/bond`, {\n credentials: 'include',\n });\n const j = (await r.json().catch(() => ({}))) as { ok?: boolean; bond?: OcBondView | null };\n if (!r.ok || !j.ok) throw new Error(`http_${r.status}`);\n return j.bond ?? null;\n}\n\nexport function OcBond({\n authOrigin = DEFAULT_AUTH_ORIGIN,\n onChange,\n className,\n}: OcBondProps): React.ReactElement {\n const [bond, setBond] = React.useState<OcBondView | null | undefined>(undefined);\n const [loadErr, setLoadErr] = React.useState(false);\n const [nonce, setNonce] = React.useState(0);\n const [adding, setAdding] = React.useState(false);\n const [confirming, setConfirming] = React.useState(false);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n\n React.useEffect(() => {\n let alive = true;\n fetchOcBond({ authOrigin }).then(\n (b) => alive && setBond(b),\n () => alive && setLoadErr(true)\n );\n return () => {\n alive = false;\n };\n }, [authOrigin, nonce]);\n\n const call = React.useCallback(\n async (method: string, body?: unknown) => {\n const r = await fetch(`${authOrigin}/api/auth/bond`, {\n method,\n credentials: 'include',\n headers: body ? { 'Content-Type': 'application/json' } : undefined,\n body: body ? JSON.stringify(body) : undefined,\n });\n const j = (await r.json().catch(() => ({}))) as Record<string, unknown> & {\n ok?: boolean;\n reason?: string;\n token?: unknown;\n };\n // The host minted for the account this tab shows; keep the pin current.\n const pin = readTabSession();\n if (pin && r.ok && typeof j.token === 'string') {\n writeTabSession({ token: j.token, didOc: pin.didOc });\n }\n return { ok: r.ok && j.ok === true, ...j };\n },\n [authOrigin]\n );\n\n async function release(): Promise<void> {\n setBusy(true);\n setErr(null);\n const j = await call('DELETE').catch(() => ({ ok: false, reason: 'network' }));\n setBusy(false);\n if (!j.ok) return setErr(bondRefusal(j.reason));\n setConfirming(false);\n setBond(null);\n onChange?.(null);\n }\n\n if (loadErr) {\n return (\n <div className={className} data-oc-bond=\"\" style={panelStyle('warn')} role=\"alert\">\n <p style={bodyStyle}>couldn&apos;t read your backing · nothing was changed</p>\n <button\n type=\"button\"\n style={ghostBtnStyle(false)}\n onClick={() => {\n setLoadErr(false);\n setNonce((x) => x + 1);\n }}\n >\n try again\n </button>\n </div>\n );\n }\n if (bond === undefined) {\n return (\n <div className={className} data-oc-bond=\"\" style={panelStyle()}>\n <span style={hintStyle}>reading your backing…</span>\n </div>\n );\n }\n\n return (\n <div className={className} data-oc-bond=\"\" style={{ display: 'grid', gap: 16 }}>\n {bond && !adding ? (\n <div style={panelStyle('success')}>\n <SectionLabel tone=\"success\">§ backed by bitcoin</SectionLabel>\n <p style={{ ...bodyStyle, color: V.foreground }}>\n apps see a coin {bondFloorsText(bond)}.\n </p>\n <p style={bodyStyle}>\n coin {short(bond.outpoint)}, {n(bond.sats)} sats · last found unspent at\n block {n(bond.checked_height)} · only you and orangecheck see this line\n </p>\n {confirming ? (\n <div role=\"group\" aria-label=\"stop backing\" data-oc-bond-confirm=\"\">\n <p style={{ ...bodyStyle, color: V.foreground, margin: 0 }}>\n stop backing? apps that ask for a backed account may stop letting\n you in.\n </p>\n <FormButtons>\n <button\n type=\"button\"\n style={dangerBtnStyle(busy)}\n disabled={busy}\n onClick={() => void release()}\n >\n stop backing\n </button>\n <button\n type=\"button\"\n style={ghostBtnStyle(busy)}\n disabled={busy}\n autoFocus\n onClick={() => {\n setErr(null);\n setConfirming(false);\n }}\n >\n keep\n </button>\n </FormButtons>\n </div>\n ) : (\n <FormButtons>\n <button\n type=\"button\"\n style={ghostBtnStyle(false)}\n onClick={() => {\n setErr(null);\n setAdding(true);\n }}\n >\n use another coin\n </button>\n <button\n type=\"button\"\n style={dangerBtnStyle(false)}\n onClick={() => setConfirming(true)}\n >\n stop backing\n </button>\n </FormButtons>\n )}\n {err ? <ErrorLine>{err}</ErrorLine> : null}\n </div>\n ) : null}\n {!bond && !adding ? (\n <div style={panelStyle()}>\n <SectionLabel>§ not backed</SectionLabel>\n <p style={bodyStyle}>\n your account works everywhere as it is. some apps let in only accounts\n backed by a coin you hold. it stays in your wallet, and apps never see\n which coin.\n </p>\n <button\n type=\"button\"\n style={primaryBtnStyle(false)}\n onClick={() => setAdding(true)}\n >\n back your account\n </button>\n </div>\n ) : null}\n {adding ? (\n <BondForm\n call={call}\n onCancel={() => setAdding(false)}\n onDone={(b) => {\n setBond(b);\n setAdding(false);\n onChange?.(b);\n }}\n />\n ) : null}\n <p style={explainerStyle}>\n Apps see only rough floors, like “at least 100,000 sats, held 30 days or\n more”, and a number unique to that app. Never the coin, your address or your\n balance. An app can block that number; only moving the coin gets past it, and a\n moved coin starts its age again.\n </p>\n </div>\n );\n}\n\ntype Call = (\n method: string,\n body?: unknown\n) => Promise<Record<string, unknown> & { ok: boolean; reason?: string }>;\n\nfunction BondForm({\n call,\n onCancel,\n onDone,\n}: {\n call: Call;\n onCancel: () => void;\n onDone: (bond: OcBondView) => void;\n}): React.ReactElement {\n const [address, setAddress] = React.useState('');\n const [coins, setCoins] = React.useState<Coin[] | null>(null);\n const [chosen, setChosen] = React.useState('');\n const [message, setMessage] = React.useState<string | null>(null);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n const id = React.useId();\n\n const addr = address.trim();\n const canFind = addr.length >= 14;\n\n async function run(fn: () => Promise<void>): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n await fn();\n } catch {\n setErr(bondRefusal('chain_unavailable'));\n } finally {\n setBusy(false);\n }\n }\n\n const findCoins = () =>\n run(async () => {\n const j = await call('POST', { action: 'coins', address: addr });\n if (!j.ok) return setErr(bondRefusal(j.reason));\n const list = (j.coins as Coin[]) ?? [];\n setCoins(list);\n setChosen(list.find(usable)?.outpoint ?? '');\n });\n\n const getMessage = () =>\n run(async () => {\n const j = await call('POST', { action: 'message', address: addr, outpoint: chosen });\n if (!j.ok || typeof j.message !== 'string') return setErr(bondRefusal(j.reason));\n setMessage(j.message);\n });\n\n const submit = (signature: string) =>\n run(async () => {\n const j = await call('POST', { action: 'bond', message, signature });\n if (!j.ok || !j.bond) return setErr(bondRefusal(j.reason));\n onDone(j.bond as OcBondView);\n });\n\n /** Back one step: from signing to the coin list, or from the coins to the address. */\n function back(): void {\n setErr(null);\n if (message) setMessage(null);\n else setCoins(null);\n }\n\n const error = err ? <ErrorLine>{err}</ErrorLine> : null;\n const backButton = (\n <button type=\"button\" style={ghostBtnStyle(busy)} disabled={busy} onClick={back}>\n back\n </button>\n );\n const cancelButton = (\n <button type=\"button\" style={ghostBtnStyle(busy)} disabled={busy} onClick={onCancel}>\n cancel\n </button>\n );\n\n let step: React.ReactNode;\n if (message) {\n step = (\n <>\n <p style={bodyStyle}>\n sign with the wallet that holds <span style={addressStyle}>{addr}</span>.\n signing spends nothing, and the coin stays in your wallet.\n </p>\n <LazyWalletButton\n address={addr}\n message={message}\n showManual\n onSigned={(sig) => void submit(sig)}\n onError={(e) => setErr(e.message ?? 'the wallet refused to sign')}\n />\n {error}\n <FormButtons>\n {backButton}\n {cancelButton}\n </FormButtons>\n </>\n );\n } else if (coins) {\n const eligible = coins.filter((c) => c.sats >= MIN_SATS);\n const dust = coins.length - eligible.length;\n step = (\n <>\n {coins.length === 0 ? (\n <p style={bodyStyle}>\n no confirmed coins at that address · send at least {n(MIN_SATS)} sats\n there and wait for six confirmations, about an hour. the longer a coin\n sits, the more it says.\n </p>\n ) : eligible.length === 0 ? (\n <>\n <p style={{ ...bodyStyle, color: V.foreground }}>{dustText(dust)}</p>\n <p style={bodyStyle}>\n send at least {n(MIN_SATS)} sats there in one payment and wait for six\n confirmations, about an hour. coins aren&apos;t added together.\n </p>\n </>\n ) : (\n <>\n <p id={`${id}-pick`} style={bodyStyle}>\n pick one coin · coins aren&apos;t added together\n </p>\n <div\n role=\"radiogroup\"\n aria-labelledby={`${id}-pick`}\n style={{ display: 'grid', gap: 8 }}\n >\n {eligible.map((c) => {\n const young = c.age_blocks < MIN_CONFIRMATIONS;\n return (\n <label\n key={c.outpoint}\n style={coinStyle(chosen === c.outpoint, young)}\n >\n <input\n type=\"radio\"\n name=\"oc-bond-coin\"\n value={c.outpoint}\n checked={chosen === c.outpoint}\n disabled={young}\n onChange={() => setChosen(c.outpoint)}\n style={{ accentColor: V.primary, margin: '2px 0 0' }}\n />\n <span style={{ display: 'grid', gap: 2, minWidth: 0 }}>\n <span\n style={{\n color: young ? V.muted : V.foreground,\n fontSize: 13,\n }}\n >\n {n(c.sats)} sats, held{' '}\n {Math.floor(c.age_blocks / 144)} days\n </span>\n <span style={smallStyle}>\n {young\n ? 'too recent: needs six confirmations'\n : `apps would see ${bondFloorsText(c)}`}\n </span>\n <span\n style={{\n ...smallStyle,\n fontFamily: SIGNIN_FONT,\n wordBreak: 'break-all',\n }}\n >\n {short(c.outpoint)}\n </span>\n </span>\n </label>\n );\n })}\n </div>\n {dust > 0 ? (\n <p style={{ ...smallStyle, marginTop: 8 }}>{dustText(dust)}</p>\n ) : null}\n </>\n )}\n {error}\n <FormButtons>\n {eligible.length > 0 ? (\n <button\n type=\"button\"\n style={primaryBtnStyle(busy || !chosen)}\n disabled={busy || !chosen}\n onClick={() => void getMessage()}\n >\n continue\n </button>\n ) : null}\n {backButton}\n {cancelButton}\n </FormButtons>\n </>\n );\n } else {\n step = (\n <form\n style={{ margin: 0 }}\n onSubmit={(e) => {\n e.preventDefault();\n if (canFind && !busy) void findCoins();\n }}\n >\n <label htmlFor={`${id}-address`} style={fieldLabelStyle}>\n bitcoin address that holds the coin\n </label>\n <p id={`${id}-advice`} style={{ ...bodyStyle, margin: '0 0 8px' }}>\n use one that holds nothing else of yours: orangecheck learns its address and\n amount. apps never do.\n </p>\n <input\n data-oca-field=\"\"\n id={`${id}-address`}\n aria-describedby={`${id}-advice`}\n style={inputStyle}\n value={address}\n onChange={(e) => setAddress(e.target.value)}\n placeholder=\"bc1q…\"\n spellCheck={false}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n />\n {error}\n <FormButtons>\n <button\n type=\"submit\"\n style={primaryBtnStyle(busy || !canFind)}\n disabled={busy || !canFind}\n >\n find its coins\n </button>\n {cancelButton}\n </FormButtons>\n </form>\n );\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-bond-form=\"\">\n <SectionLabel>§ back your account</SectionLabel>\n {step}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAcA,YAAY,WAAW;AA0MX,SAgNA,UA/MI,KADJ;AAvLZ,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAE1B,IAAM,WAAW;AAgCjB,IAAM,IAAI,CAAC,MAAc,EAAE,eAAe,OAAO;AAG1C,SAAS,eAAe,GAAmB;AAC9C,QAAM,OAAO,EAAE,aAAa,IAAI,YAAY,EAAE,EAAE,UAAU,CAAC,UAAU;AACrE,QAAM,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,UAAU,kBAAkB;AACtE,SAAO,GAAG,IAAI,KAAK,IAAI;AAC3B;AAEA,IAAM,QAAQ,CAAC,aAAqB;AAChC,QAAM,CAAC,OAAO,IAAI,OAAO,EAAE,IAAI,SAAS,MAAM,GAAG;AACjD,SAAO,GAAG,KAAK,MAAM,GAAG,CAAC,CAAC,SAAI,KAAK,MAAM,EAAE,CAAC,IAAI,IAAI;AACxD;AAEA,IAAM,SAAS,CAAC,MAAY,EAAE,QAAQ,YAAY,EAAE,cAAc;AAElE,IAAM,WAAW,CAAC,UACd,GAAG,EAAE,KAAK,CAAC,IAAI,UAAU,IAAI,SAAS,OAAO,UAAU,EAAE,QAAQ,CAAC;AAGtE,IAAM,UAAkC;AAAA,EACpC,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,aACI;AAAA,EACJ,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,uBAAuB;AAAA,EACvB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB,sBAAsB,EAAE,QAAQ,CAAC;AAAA,EACjD,4BAA4B;AAAA,EAC5B,8BACI;AAAA,EAEJ,mBAAmB;AAAA,EACnB,cAAc;AAClB;AAGO,IAAM,cAAc,CAAC,WACvB,UAAU,QAAQ,MAAM,KAAM;AAGnC,IAAM,aAAkC,EAAE,GAAG,WAAW,UAAU,IAAI,QAAQ,EAAE;AAChF,IAAM,iBAAsC;AAAA,EACxC,GAAG;AAAA,EACH,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,QAAQ;AACZ;AACA,IAAM,kBAAuC;AAAA,EACzC,SAAS;AAAA,EACT,OAAO,EAAE;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,QAAQ;AACZ;AACA,IAAM,eAAoC,EAAE,WAAW,aAAa,OAAO,EAAE,WAAW;AAExF,SAAS,UAAU,QAAiB,OAAqC;AACrE,SAAO;AAAA,IACH,YAAY,EAAE;AAAA,IACd,QAAQ,OAAO,QAAQ,WAAW,OAAO,IAAI,SAAS,EAAE,UAAU,EAAE,MAAM;AAAA,IAC1E,SAAS;AAAA,IACT,SAAS;AAAA,IACT,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,QAAQ,QAAQ,gBAAgB;AAAA,EACpC;AACJ;AAGA,eAAsB,YAAY,MAA4D;AAC1F,QAAM,IAAI,MAAM,MAAM,GAAG,MAAM,cAAc,mBAAmB,kBAAkB;AAAA,IAC9E,aAAa;AAAA,EACjB,CAAC;AACD,QAAM,IAAK,MAAM,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC1C,MAAI,CAAC,EAAE,MAAM,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,QAAQ,EAAE,MAAM,EAAE;AACtD,SAAO,EAAE,QAAQ;AACrB;AAEO,SAAS,OAAO;AAAA,EACnB,aAAa;AAAA,EACb;AAAA,EACA;AACJ,GAAoC;AAChC,QAAM,CAAC,MAAM,OAAO,IAAU,eAAwC,MAAS;AAC/E,QAAM,CAAC,SAAS,UAAU,IAAU,eAAS,KAAK;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAS,CAAC;AAC1C,QAAM,CAAC,QAAQ,SAAS,IAAU,eAAS,KAAK;AAChD,QAAM,CAAC,YAAY,aAAa,IAAU,eAAS,KAAK;AACxD,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,KAAK;AAC5C,QAAM,CAAC,KAAK,MAAM,IAAU,eAAwB,IAAI;AAExD,EAAM,gBAAU,MAAM;AAClB,QAAI,QAAQ;AACZ,gBAAY,EAAE,WAAW,CAAC,EAAE;AAAA,MACxB,CAAC,MAAM,SAAS,QAAQ,CAAC;AAAA,MACzB,MAAM,SAAS,WAAW,IAAI;AAAA,IAClC;AACA,WAAO,MAAM;AACT,cAAQ;AAAA,IACZ;AAAA,EACJ,GAAG,CAAC,YAAY,KAAK,CAAC;AAEtB,QAAM,OAAa;AAAA,IACf,OAAO,QAAgB,SAAmB;AACtC,YAAM,IAAI,MAAM,MAAM,GAAG,UAAU,kBAAkB;AAAA,QACjD;AAAA,QACA,aAAa;AAAA,QACb,SAAS,OAAO,EAAE,gBAAgB,mBAAmB,IAAI;AAAA,QACzD,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,MACxC,CAAC;AACD,YAAM,IAAK,MAAM,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAM1C,YAAM,MAAM,eAAe;AAC3B,UAAI,OAAO,EAAE,MAAM,OAAO,EAAE,UAAU,UAAU;AAC5C,wBAAgB,EAAE,OAAO,EAAE,OAAO,OAAO,IAAI,MAAM,CAAC;AAAA,MACxD;AACA,aAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,MAAM,GAAG,EAAE;AAAA,IAC7C;AAAA,IACA,CAAC,UAAU;AAAA,EACf;AAEA,iBAAe,UAAyB;AACpC,YAAQ,IAAI;AACZ,WAAO,IAAI;AACX,UAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,MAAM,OAAO,EAAE,IAAI,OAAO,QAAQ,UAAU,EAAE;AAC7E,YAAQ,KAAK;AACb,QAAI,CAAC,EAAE,GAAI,QAAO,OAAO,YAAY,EAAE,MAAM,CAAC;AAC9C,kBAAc,KAAK;AACnB,YAAQ,IAAI;AACZ,eAAW,IAAI;AAAA,EACnB;AAEA,MAAI,SAAS;AACT,WACI,qBAAC,SAAI,WAAsB,gBAAa,IAAG,OAAO,WAAW,MAAM,GAAG,MAAK,SACvE;AAAA,0BAAC,OAAE,OAAO,WAAW,iEAAqD;AAAA,MAC1E;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,OAAO,cAAc,KAAK;AAAA,UAC1B,SAAS,MAAM;AACX,uBAAW,KAAK;AAChB,qBAAS,CAAC,MAAM,IAAI,CAAC;AAAA,UACzB;AAAA,UACH;AAAA;AAAA,MAED;AAAA,OACJ;AAAA,EAER;AACA,MAAI,SAAS,QAAW;AACpB,WACI,oBAAC,SAAI,WAAsB,gBAAa,IAAG,OAAO,WAAW,GACzD,8BAAC,UAAK,OAAO,WAAW,wCAAqB,GACjD;AAAA,EAER;AAEA,SACI,qBAAC,SAAI,WAAsB,gBAAa,IAAG,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,GACxE;AAAA,YAAQ,CAAC,SACN,qBAAC,SAAI,OAAO,WAAW,SAAS,GAC5B;AAAA,0BAAC,gBAAa,MAAK,WAAU,oCAAmB;AAAA,MAChD,qBAAC,OAAE,OAAO,EAAE,GAAG,WAAW,OAAO,EAAE,WAAW,GAAG;AAAA;AAAA,QAC5B,eAAe,IAAI;AAAA,QAAE;AAAA,SAC1C;AAAA,MACA,qBAAC,OAAE,OAAO,WAAW;AAAA;AAAA,QACX,MAAM,KAAK,QAAQ;AAAA,QAAE;AAAA,QAAG,EAAE,KAAK,IAAI;AAAA,QAAE;AAAA,QACpC,EAAE,KAAK,cAAc;AAAA,QAAE;AAAA,SAClC;AAAA,MACC,aACG,qBAAC,SAAI,MAAK,SAAQ,cAAW,gBAAe,wBAAqB,IAC7D;AAAA,4BAAC,OAAE,OAAO,EAAE,GAAG,WAAW,OAAO,EAAE,YAAY,QAAQ,EAAE,GAAG,uFAG5D;AAAA,QACA,qBAAC,eACG;AAAA;AAAA,YAAC;AAAA;AAAA,cACG,MAAK;AAAA,cACL,OAAO,eAAe,IAAI;AAAA,cAC1B,UAAU;AAAA,cACV,SAAS,MAAM,KAAK,QAAQ;AAAA,cAC/B;AAAA;AAAA,UAED;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACG,MAAK;AAAA,cACL,OAAO,cAAc,IAAI;AAAA,cACzB,UAAU;AAAA,cACV,WAAS;AAAA,cACT,SAAS,MAAM;AACX,uBAAO,IAAI;AACX,8BAAc,KAAK;AAAA,cACvB;AAAA,cACH;AAAA;AAAA,UAED;AAAA,WACJ;AAAA,SACJ,IAEA,qBAAC,eACG;AAAA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,OAAO,cAAc,KAAK;AAAA,YAC1B,SAAS,MAAM;AACX,qBAAO,IAAI;AACX,wBAAU,IAAI;AAAA,YAClB;AAAA,YACH;AAAA;AAAA,QAED;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,OAAO,eAAe,KAAK;AAAA,YAC3B,SAAS,MAAM,cAAc,IAAI;AAAA,YACpC;AAAA;AAAA,QAED;AAAA,SACJ;AAAA,MAEH,MAAM,oBAAC,aAAW,eAAI,IAAe;AAAA,OAC1C,IACA;AAAA,IACH,CAAC,QAAQ,CAAC,SACP,qBAAC,SAAI,OAAO,WAAW,GACnB;AAAA,0BAAC,gBAAa,6BAAY;AAAA,MAC1B,oBAAC,OAAE,OAAO,WAAW,uKAIrB;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,OAAO,gBAAgB,KAAK;AAAA,UAC5B,SAAS,MAAM,UAAU,IAAI;AAAA,UAChC;AAAA;AAAA,MAED;AAAA,OACJ,IACA;AAAA,IACH,SACG;AAAA,MAAC;AAAA;AAAA,QACG;AAAA,QACA,UAAU,MAAM,UAAU,KAAK;AAAA,QAC/B,QAAQ,CAAC,MAAM;AACX,kBAAQ,CAAC;AACT,oBAAU,KAAK;AACf,qBAAW,CAAC;AAAA,QAChB;AAAA;AAAA,IACJ,IACA;AAAA,IACJ,oBAAC,OAAE,OAAO,gBAAgB,8RAK1B;AAAA,KACJ;AAER;AAOA,SAAS,SAAS;AAAA,EACd;AAAA,EACA;AAAA,EACA;AACJ,GAIuB;AACnB,QAAM,CAAC,SAAS,UAAU,IAAU,eAAS,EAAE;AAC/C,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAwB,IAAI;AAC5D,QAAM,CAAC,QAAQ,SAAS,IAAU,eAAS,EAAE;AAC7C,QAAM,CAAC,SAAS,UAAU,IAAU,eAAwB,IAAI;AAChE,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,KAAK;AAC5C,QAAM,CAAC,KAAK,MAAM,IAAU,eAAwB,IAAI;AACxD,QAAM,KAAW,YAAM;AAEvB,QAAM,OAAO,QAAQ,KAAK;AAC1B,QAAM,UAAU,KAAK,UAAU;AAE/B,iBAAe,IAAI,IAAwC;AACvD,YAAQ,IAAI;AACZ,WAAO,IAAI;AACX,QAAI;AACA,YAAM,GAAG;AAAA,IACb,QAAQ;AACJ,aAAO,YAAY,mBAAmB,CAAC;AAAA,IAC3C,UAAE;AACE,cAAQ,KAAK;AAAA,IACjB;AAAA,EACJ;AAEA,QAAM,YAAY,MACd,IAAI,YAAY;AACZ,UAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,QAAQ,SAAS,SAAS,KAAK,CAAC;AAC/D,QAAI,CAAC,EAAE,GAAI,QAAO,OAAO,YAAY,EAAE,MAAM,CAAC;AAC9C,UAAM,OAAQ,EAAE,SAAoB,CAAC;AACrC,aAAS,IAAI;AACb,cAAU,KAAK,KAAK,MAAM,GAAG,YAAY,EAAE;AAAA,EAC/C,CAAC;AAEL,QAAM,aAAa,MACf,IAAI,YAAY;AACZ,UAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,QAAQ,WAAW,SAAS,MAAM,UAAU,OAAO,CAAC;AACnF,QAAI,CAAC,EAAE,MAAM,OAAO,EAAE,YAAY,SAAU,QAAO,OAAO,YAAY,EAAE,MAAM,CAAC;AAC/E,eAAW,EAAE,OAAO;AAAA,EACxB,CAAC;AAEL,QAAM,SAAS,CAAC,cACZ,IAAI,YAAY;AACZ,UAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,QAAQ,QAAQ,SAAS,UAAU,CAAC;AACnE,QAAI,CAAC,EAAE,MAAM,CAAC,EAAE,KAAM,QAAO,OAAO,YAAY,EAAE,MAAM,CAAC;AACzD,WAAO,EAAE,IAAkB;AAAA,EAC/B,CAAC;AAGL,WAAS,OAAa;AAClB,WAAO,IAAI;AACX,QAAI,QAAS,YAAW,IAAI;AAAA,QACvB,UAAS,IAAI;AAAA,EACtB;AAEA,QAAM,QAAQ,MAAM,oBAAC,aAAW,eAAI,IAAe;AACnD,QAAM,aACF,oBAAC,YAAO,MAAK,UAAS,OAAO,cAAc,IAAI,GAAG,UAAU,MAAM,SAAS,MAAM,kBAEjF;AAEJ,QAAM,eACF,oBAAC,YAAO,MAAK,UAAS,OAAO,cAAc,IAAI,GAAG,UAAU,MAAM,SAAS,UAAU,oBAErF;AAGJ,MAAI;AACJ,MAAI,SAAS;AACT,WACI,iCACI;AAAA,2BAAC,OAAE,OAAO,WAAW;AAAA;AAAA,QACe,oBAAC,UAAK,OAAO,cAAe,gBAAK;AAAA,QAAO;AAAA,SAE5E;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACG,SAAS;AAAA,UACT;AAAA,UACA,YAAU;AAAA,UACV,UAAU,CAAC,QAAQ,KAAK,OAAO,GAAG;AAAA,UAClC,SAAS,CAAC,MAAM,OAAO,EAAE,WAAW,4BAA4B;AAAA;AAAA,MACpE;AAAA,MACC;AAAA,MACD,qBAAC,eACI;AAAA;AAAA,QACA;AAAA,SACL;AAAA,OACJ;AAAA,EAER,WAAW,OAAO;AACd,UAAM,WAAW,MAAM,OAAO,CAAC,MAAM,EAAE,QAAQ,QAAQ;AACvD,UAAM,OAAO,MAAM,SAAS,SAAS;AACrC,WACI,iCACK;AAAA,YAAM,WAAW,IACd,qBAAC,OAAE,OAAO,WAAW;AAAA;AAAA,QACmC,EAAE,QAAQ;AAAA,QAAE;AAAA,SAGpE,IACA,SAAS,WAAW,IACpB,iCACI;AAAA,4BAAC,OAAE,OAAO,EAAE,GAAG,WAAW,OAAO,EAAE,WAAW,GAAI,mBAAS,IAAI,GAAE;AAAA,QACjE,qBAAC,OAAE,OAAO,WAAW;AAAA;AAAA,UACF,EAAE,QAAQ;AAAA,UAAE;AAAA,WAE/B;AAAA,SACJ,IAEA,iCACI;AAAA,4BAAC,OAAE,IAAI,GAAG,EAAE,SAAS,OAAO,WAAW,4DAEvC;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,mBAAiB,GAAG,EAAE;AAAA,YACtB,OAAO,EAAE,SAAS,QAAQ,KAAK,EAAE;AAAA,YAEhC,mBAAS,IAAI,CAAC,MAAM;AACjB,oBAAM,QAAQ,EAAE,aAAa;AAC7B,qBACI;AAAA,gBAAC;AAAA;AAAA,kBAEG,OAAO,UAAU,WAAW,EAAE,UAAU,KAAK;AAAA,kBAE7C;AAAA;AAAA,sBAAC;AAAA;AAAA,wBACG,MAAK;AAAA,wBACL,MAAK;AAAA,wBACL,OAAO,EAAE;AAAA,wBACT,SAAS,WAAW,EAAE;AAAA,wBACtB,UAAU;AAAA,wBACV,UAAU,MAAM,UAAU,EAAE,QAAQ;AAAA,wBACpC,OAAO,EAAE,aAAa,EAAE,SAAS,QAAQ,UAAU;AAAA;AAAA,oBACvD;AAAA,oBACA,qBAAC,UAAK,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,UAAU,EAAE,GAChD;AAAA;AAAA,wBAAC;AAAA;AAAA,0BACG,OAAO;AAAA,4BACH,OAAO,QAAQ,EAAE,QAAQ,EAAE;AAAA,4BAC3B,UAAU;AAAA,0BACd;AAAA,0BAEC;AAAA,8BAAE,EAAE,IAAI;AAAA,4BAAE;AAAA,4BAAY;AAAA,4BACtB,KAAK,MAAM,EAAE,aAAa,GAAG;AAAA,4BAAE;AAAA;AAAA;AAAA,sBACpC;AAAA,sBACA,oBAAC,UAAK,OAAO,YACR,kBACK,wCACA,kBAAkB,eAAe,CAAC,CAAC,IAC7C;AAAA,sBACA;AAAA,wBAAC;AAAA;AAAA,0BACG,OAAO;AAAA,4BACH,GAAG;AAAA,4BACH,YAAY;AAAA,4BACZ,WAAW;AAAA,0BACf;AAAA,0BAEC,gBAAM,EAAE,QAAQ;AAAA;AAAA,sBACrB;AAAA,uBACJ;AAAA;AAAA;AAAA,gBApCK,EAAE;AAAA,cAqCX;AAAA,YAER,CAAC;AAAA;AAAA,QACL;AAAA,QACC,OAAO,IACJ,oBAAC,OAAE,OAAO,EAAE,GAAG,YAAY,WAAW,EAAE,GAAI,mBAAS,IAAI,GAAE,IAC3D;AAAA,SACR;AAAA,MAEH;AAAA,MACD,qBAAC,eACI;AAAA,iBAAS,SAAS,IACf;AAAA,UAAC;AAAA;AAAA,YACG,MAAK;AAAA,YACL,OAAO,gBAAgB,QAAQ,CAAC,MAAM;AAAA,YACtC,UAAU,QAAQ,CAAC;AAAA,YACnB,SAAS,MAAM,KAAK,WAAW;AAAA,YAClC;AAAA;AAAA,QAED,IACA;AAAA,QACH;AAAA,QACA;AAAA,SACL;AAAA,OACJ;AAAA,EAER,OAAO;AACH,WACI;AAAA,MAAC;AAAA;AAAA,QACG,OAAO,EAAE,QAAQ,EAAE;AAAA,QACnB,UAAU,CAAC,MAAM;AACb,YAAE,eAAe;AACjB,cAAI,WAAW,CAAC,KAAM,MAAK,UAAU;AAAA,QACzC;AAAA,QAEA;AAAA,8BAAC,WAAM,SAAS,GAAG,EAAE,YAAY,OAAO,iBAAiB,iDAEzD;AAAA,UACA,oBAAC,OAAE,IAAI,GAAG,EAAE,WAAW,OAAO,EAAE,GAAG,WAAW,QAAQ,UAAU,GAAG,iHAGnE;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACG,kBAAe;AAAA,cACf,IAAI,GAAG,EAAE;AAAA,cACT,oBAAkB,GAAG,EAAE;AAAA,cACvB,OAAO;AAAA,cACP,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA,cAC1C,aAAY;AAAA,cACZ,YAAY;AAAA,cACZ,cAAa;AAAA,cACb,gBAAe;AAAA;AAAA,UACnB;AAAA,UACC;AAAA,UACD,qBAAC,eACG;AAAA;AAAA,cAAC;AAAA;AAAA,gBACG,MAAK;AAAA,gBACL,OAAO,gBAAgB,QAAQ,CAAC,OAAO;AAAA,gBACvC,UAAU,QAAQ,CAAC;AAAA,gBACtB;AAAA;AAAA,YAED;AAAA,YACC;AAAA,aACL;AAAA;AAAA;AAAA,IACJ;AAAA,EAER;AAEA,SACI,qBAAC,SAAI,OAAO,WAAW,QAAQ,GAAG,qBAAkB,IAChD;AAAA,wBAAC,gBAAa,oCAAmB;AAAA,IAChC;AAAA,KACL;AAER;","names":[]}
package/dist/index.js CHANGED
@@ -1781,6 +1781,7 @@ function EmailLinkForm({
1781
1781
  /* @__PURE__ */ jsxRuntime.jsx(
1782
1782
  "input",
1783
1783
  {
1784
+ "data-oca-field": "",
1784
1785
  type: "email",
1785
1786
  autoComplete: "email",
1786
1787
  value: email,
@@ -1821,6 +1822,7 @@ function EmailLinkForm({
1821
1822
  /* @__PURE__ */ jsxRuntime.jsx(
1822
1823
  "input",
1823
1824
  {
1825
+ "data-oca-field": "",
1824
1826
  type: "text",
1825
1827
  inputMode: "numeric",
1826
1828
  autoComplete: "one-time-code",
@@ -1930,6 +1932,7 @@ function BtcLinkForm({
1930
1932
  /* @__PURE__ */ jsxRuntime.jsx(
1931
1933
  "input",
1932
1934
  {
1935
+ "data-oca-field": "",
1933
1936
  type: "text",
1934
1937
  value: addr,
1935
1938
  onChange: (e) => setAddr(e.target.value.trim()),
@@ -2869,8 +2872,15 @@ function WalletFlow({
2869
2872
  setSubmitting(false);
2870
2873
  return;
2871
2874
  }
2872
- const signer = (await loadAdapter()).getSigner(id, { address: addr });
2873
- const signature = await signer(challenge.message);
2875
+ const adapter = await loadAdapter();
2876
+ let signature;
2877
+ try {
2878
+ signature = await adapter.getSigner(id, { address: addr })(challenge.message);
2879
+ } catch (err) {
2880
+ setError(adapter.walletFailureText(err));
2881
+ setSubmitting(false);
2882
+ return;
2883
+ }
2874
2884
  step = "submit";
2875
2885
  await submitSignature(challenge.message, challenge.nonce, signature, "bip322");
2876
2886
  } catch (err) {
@@ -2915,6 +2925,7 @@ function WalletFlow({
2915
2925
  /* @__PURE__ */ jsxRuntime.jsx(
2916
2926
  "textarea",
2917
2927
  {
2928
+ "data-oca-field": "",
2918
2929
  id: ids.message,
2919
2930
  readOnly: true,
2920
2931
  value: manual.message,
@@ -2930,6 +2941,7 @@ function WalletFlow({
2930
2941
  /* @__PURE__ */ jsxRuntime.jsx(
2931
2942
  "textarea",
2932
2943
  {
2944
+ "data-oca-field": "",
2933
2945
  id: ids.signature,
2934
2946
  value: pasted,
2935
2947
  onChange: (e) => {
@@ -2978,6 +2990,7 @@ function WalletFlow({
2978
2990
  /* @__PURE__ */ jsxRuntime.jsx(
2979
2991
  "input",
2980
2992
  {
2993
+ "data-oca-field": "",
2981
2994
  id: ids.address,
2982
2995
  type: "text",
2983
2996
  value: address,
@@ -3116,6 +3129,7 @@ function EmailFlow({ authOrigin, add, credentials, onSuccess }) {
3116
3129
  /* @__PURE__ */ jsxRuntime.jsx(
3117
3130
  "input",
3118
3131
  {
3132
+ "data-oca-field": "",
3119
3133
  id: `${uid}-email`,
3120
3134
  type: "email",
3121
3135
  autoComplete: "email",
@@ -3144,6 +3158,7 @@ function EmailFlow({ authOrigin, add, credentials, onSuccess }) {
3144
3158
  /* @__PURE__ */ jsxRuntime.jsx(
3145
3159
  "input",
3146
3160
  {
3161
+ "data-oca-field": "",
3147
3162
  id: `${uid}-code`,
3148
3163
  type: "text",
3149
3164
  inputMode: "numeric",
@@ -3779,6 +3794,7 @@ function BondForm({
3779
3794
  /* @__PURE__ */ jsxRuntime.jsx(
3780
3795
  "input",
3781
3796
  {
3797
+ "data-oca-field": "",
3782
3798
  id: `${id}-address`,
3783
3799
  "aria-describedby": `${id}-advice`,
3784
3800
  style: inputStyle,