import { D as DepositStatus, a as DepositResult, b as DepositError, c as DepositConfig, d as DepositRequest, V as VerificationStatus, e as VerificationResult, f as VerificationError, g as VerificationConfig, h as VerifyHumanOptions } from './types-uAsx5xcj.js'; export { C as CheckoutConfig, i as CheckoutError, j as CheckoutErrorCode, k as CheckoutStatus, l as DepositDestination, m as DepositErrorCode, S as SignerFunction, n as SignerRequest, o as SignerResponse, T as TransferSummary, p as VerificationErrorCode, q as VerificationSignerFunction, r as VerificationSignerRequest, s as VerificationSignerResponse, t as getDisplayMessage, u as getVerificationDisplayMessage } from './types-uAsx5xcj.js'; declare const DEFAULT_WEBVIEW_BASE_URL = "https://pay.blink.cash"; declare const SANDBOX_WEBVIEW_BASE_URL = "https://pay-sandbox.blink.cash"; type EventMap$1 = { complete: (result: DepositResult) => void; error: (error: DepositError) => void; close: () => void; 'status-change': (status: DepositStatus) => void; /** * Embedded presentation only: the hosted flow's rendered height changed and * the inline iframe has already been resized to match. Hosts that size their * own panel around the iframe follow it from here. * * `heightPx` fires on every frame of the flow's own height transition — * apply it directly, with NO CSS transition on the host side, or two * independent animations of the same quantity fight each other and the panel * keeps moving after the content has settled. * * `preferredWidthPx` / `minWidthPx` are constants describing the flow's * design envelope, so a host that wants its panel to fit the flow in both * axes can size the width once rather than guessing. */ resize: (info: { heightPx: number; preferredWidthPx?: number; minWidthPx?: number; }) => void; }; type EventName$1 = keyof EventMap$1; /** * Merchant-facing deposit SDK. * * Opens a hosted payment flow in a modal iframe, communicates with the * merchant's signer backend, and resolves when the user completes payment. * * The iframe has `publickey-credentials-get` and `publickey-credentials-create` * permissions (with explicit origin) so WebAuthn passkey ceremonies work * cross-origin, including in Safari. * * ```ts * const deposit = new Deposit({ signer: '/api/sign-payment' }); * const result = await deposit.requestDeposit({ amount: 50, chainId: 8453, address: '0x...', token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' }); * ``` */ declare class Deposit { private readonly config; private iframe; private hostedOrigin; private messageListener; private requestId; private destroyed; private _status; private _result; private _error; private flowTimer; private lastSignerResponse; private warmIframe; private warmIframeReady; private warmIframeReadyCancel; /** Resolved embedded-vs-overlay decision; see {@link Deposit.isEmbedded}. */ private embeddedResolved; /** * Settles the in-flight `requestDeposit()` as a dismissal. Non-null exactly * while a flow is in progress, so {@link Deposit.close} can reject the promise * the caller is awaiting instead of tearing the frame down under it. */ private dismissActiveFlow; private listeners; /** Current phase of the deposit flow. */ get status(): DepositStatus; /** Last successful {@link DepositResult}, available when `status === 'completed'`. */ get result(): DepositResult | null; /** Last {@link DepositError}, available when `status === 'error'`. */ get error(): DepositError | null; /** Whether a deposit flow is currently in progress (signer loading or iframe open). */ get isActive(): boolean; /** * How this instance will actually present: `'embedded'` renders inline in * `containerElement`, `'overlay'` covers the page. * * Not simply an echo of the config — a host that asked for `'embedded'` gets * `'overlay'` on mobile, where a phone-sized method panel cannot hold the * flow (see {@link Deposit.isEmbedded}). Read it when laying out the panel: * on `'overlay'` no `resize` event ever fires, so a panel sized from those * reports would sit empty behind the overlay — collapse or skip it. * * Live until the SDK builds its first (warm-up) frame and fixed from then on, * so re-read it on resize rather than caching it at mount. */ get presentation(): 'overlay' | 'embedded'; constructor(config: DepositConfig); /** * Warm up the hosted payment flow in a hidden iframe so `requestDeposit` * opens an already-loaded page (JS bundle, auth session restore, and — * when {@link DepositConfig.merchantId} is set — merchant config prefetch * all happen in the background). * * Runs automatically after construction unless `preload: false`; call it * manually to control the timing (e.g. on deposit-button hover). No-op * when a warm iframe already exists or a deposit flow is active. */ preload(): void; private schedulePreload; /** * Whether the flow renders inline in the host's element rather than as an * overlay. * * `presentation: 'embedded'` is a request, not a guarantee: **on mobile the * flow presents as the normal overlay** even for an embedded host. An * aggregator's method panel is a narrow, short column on a phone, and the * flow inside it is a payment journey with a keypad, wallet lists and a QR * code — it needs the screen. The overlay is exactly what a phone user gets * from every other Blink integration, and the host's own dialog stays behind * it. Desktop keeps the inline card, where the panel has room. * * **The decision lives with the frame.** Read live until a frame is built, * then committed for good ({@link Deposit.createPresentedIframe}) — `preload()` * warms a frame from these same predicates and the flow that later adopts it * must agree, because a frame warmed at `layout=embed` handed to the overlay * presenter (or the reverse) is a blank modal. Committing at frame creation * rather than at construction matters: a host that constructs `Deposit` before * layout settles — a widget mounting inside a transition, a background or * prerendered tab — reports a zero-width viewport, and `(max-width: 640px)` * matches at zero, so caching then would lock a desktop user into the overlay * over a viewport that never existed. The warm-up already waits for `load` * plus an idle callback, so by the time it commits the measurement is real. */ private isEmbedded; /** * The host's height budget for the inline iframe. Explicit config wins; * otherwise most of the host viewport, which keeps a tall flow from * outgrowing the page it is embedded in. */ private resolveEmbedMaxHeightPx; /** Build the iframe for the configured presentation. */ private createPresentedIframe; /** * Where the overlay mounts. `containerElement` means two different things by * presentation: an overlay mount point (`presentation: 'overlay'`) or the * aggregator's inline panel slot (`'embedded'`). When an embedded flow * presents as an overlay on mobile, that slot must NOT be the mount point — * `position: fixed` resolves against the nearest transformed/filtered/ * contained ancestor, and an aggregator's animated dialog is exactly that, so * the "full-screen" overlay would end up positioned inside their panel. * Falls through to `document.body`. */ private overlayMountTarget; private buildPreloadUrl; /** * Detach the warm iframe for use by a starting flow. Returns null (and * cleans up) when there is none or it was closed in the meantime. */ private takeWarmIframe; private discardWarmIframe; /** * Open the hosted payment flow for the given deposit. * * The returned Promise resolves when the user completes the payment and * rejects with a {@link DepositError} on failure. */ requestDeposit(request: DepositRequest): Promise; /** Register an event listener. */ on(event: K, handler: EventMap$1[K]): this; /** Remove a previously registered event listener. */ off(event: K, handler: EventMap$1[K]): this; /** No-op — retained for API compatibility with the popup-based SDK. */ focus(): void; /** * Close the deposit iframe without waiting for completion — the host's own * back button or dialog chrome. * * This **settles a flow in progress** by rejecting its `requestDeposit()` * promise with `DEPOSIT_DISMISSED`, exactly as the flow's own close control * does. It has to: `cleanup()` destroys the frame through `destroy()`, which * drops the handle's close callback on purpose, so without this the caller's * `await` never returns. An aggregator whose back button awaited that promise * to restore its method list was left showing an empty panel — the flow gone, * its own list still not rendered, and no error to explain why. * * The rejection code is the same `DEPOSIT_DISMISSED` the in-flow control * produces, so a host writes one dismissal branch rather than one per * affordance. What it deliberately does not do is report an error: no `error` * event, and `status` lands on 'idle' as it always has, because the host * initiated this. With no flow in progress it is a plain teardown, unchanged. */ close(): void; /** Tear down the instance and release all resources. */ destroy(): void; private log; /** * Appends `enableFullWidget=false` to the hosted-flow URL when the merchant * integration disables the full widget. Absence of the param means enabled * (the default) — the hosted flow ANDs this with the merchant's backend * config, so the full widget renders only when both allow it. */ private applyFullWidgetParam; /** * Appends `appearance=dark` (or `appearance=system`) to the hosted-flow URL. * An explicit `appearance: { theme }` config always wins — including * `theme: 'light'`, which pins light regardless of the merchant page. When * the merchant passes no theme, the default follows the merchant page's own * declared color scheme (see {@link detectMerchantColorScheme}) so the flow * doesn't render a light modal on a page that says it is dark. Absence of * the param means light. The hosted flow resolves `system` against the * user's `prefers-color-scheme`. Applied alongside the full-widget param on * every hosted/preload URL. */ private applyAppearanceParam; /** * Appends the merchant's brand colors as `brand=p-0f62fe.bg-ffffff…`. * * On the URL rather than a message because the hosted flow's loading shell * paints from its entry chunk, hundreds of ms before React mounts: a palette * that arrived by `postMessage` would show Blink's own card color first and * then become the merchant's. Unreserved characters only, so the param costs * ~50 bytes of the URL budget rather than triple that in percent-encoding. * * Validated here purely so the merchant sees the complaint in their OWN * console — the hosted flow re-validates everything it decodes, since the URL * is host-controlled input that ends up in a stylesheet. Silence would be the * worst outcome: a dropped color that nobody is told about looks like the SDK * ignoring the config. */ private applyBrandParam; /** * Normalizes the display-only {@link DepositRequest.balance} for the wire, * or returns `undefined` to omit it everywhere — the `blink:signed-payload` * sibling field and the legacy fallback URL param alike. Additive wire * discipline: an unset or rejected balance leaves both channels * byte-for-byte what they always were, and old webviews ignore the field * by construction. * * Validated here purely so the merchant sees the complaint in their OWN * console (same rationale as {@link applyBrandParam}); the hosted flow * re-validates whatever arrives, since both channels are host-controlled * input. A rejected balance never blocks the deposit — the flow proceeds * without the subtitle. */ private normalizeRequestBalance; /** * Normalizes {@link DepositRequest.minimumDeposit} for the wire, or returns * `undefined` to omit it everywhere. Same additive discipline and same * console-complaint rationale as {@link normalizeRequestBalance}. * * A rejected minimum never blocks the deposit — the flow proceeds on * Blink's own per-route floor, which is the safe direction to fail: the * merchant loses their higher floor, nobody is locked out of depositing. */ private normalizeRequestMinimumDeposit; /** * Fluid is the default; `layout: 'fixed'` opts back into the legacy * container. Embedded is a separate presentation and never fluid — the * constructor rejects the combination outright. */ private isFluidLayout; /** * Marks the hosted-flow URL as fluid-layout: the iframe spans the full * viewport and the hosted flow renders the card/sheet chrome itself. * Only applied to preload URLs — the URL-param fallback path exists * precisely because the webview never signalled ready, so it cannot be * assumed to understand fluid layout and gets the fixed container. */ private applyLayoutParam; /** * Seeds the merchant page's large-viewport height and safe-area bottom * inset onto the fluid preload URL so the hosted flow can size the sheet * before its JS boots. `blink:viewport` messages keep the values fresh * afterwards (rotation, window resize) — see iframe.ts. */ private applyViewportParams; private setStatus; private runSignerFlow; /** * Resolves `{ ready: true, ... }` when the iframe posts * `blink:iframe-ready` (with `fluidCapable` reflecting its advertised * capabilities), or `{ ready: false, fluidCapable: false }` on timeout. * `cancel()` settles the promise as not-ready immediately and releases * the listener + timer — used to reap the generous warm-up wait once it no * longer matters (warm iframe consumed or discarded). */ private waitForIframeReady; private startMessageListener; private cleanup; private emit; } /** @deprecated Use {@link Deposit} instead. */ declare const Checkout: typeof Deposit; type EventMap = { complete: (result: VerificationResult) => void; error: (error: VerificationError) => void; close: () => void; 'status-change': (status: VerificationStatus) => void; }; type EventName = keyof EventMap; /** * Merchant-facing human verification. * * Proves that the merchant's signed-in user is a real, unique human by * sending them through a palm scan on a Blink-hosted page, and resolves with * a **hint** when it is done. No Blink account, passkey or deposit is * involved: the user is signed in to the merchant only, and the merchant's * backend names them in a signed grant before the scan starts. * * ```ts * const verification = new Verification({ signer: '/api/sign-verification' }); * const { status } = await verification.verifyHuman(); // 'verified' — a HINT * await fetch('/api/me/verification/refresh'); // your server asks Blink — the answer * ``` * * **Unlock on your server's answer, never on this promise.** A user controls * their own browser and can forge anything it resolves with; they cannot * forge your backend's `GET /v1/identity/human-verifications`. */ declare class Verification { private readonly config; private iframe; private hostedOrigin; private messageListener; private requestId; private destroyed; private _status; private _result; private _error; private flowTimer; private lingerTimer; /** Settles a pending ready-wait early so its timer and listener are released on cleanup. */ private cancelReadyWait; private dismissActiveFlow; private listeners; /** Current phase of the flow. */ get status(): VerificationStatus; /** Last result, available when `status === 'completed'`. A hint; see the class docs. */ get result(): VerificationResult | null; /** Last error, available when `status === 'error'`. */ get error(): VerificationError | null; /** Whether a verification flow is currently in progress. */ get isActive(): boolean; constructor(config: VerificationConfig); /** * Open the hosted verification flow for the merchant's signed-in user. * * Resolves with `{ sessionId, status: 'verified' }` when the palm scan * bound this user, and rejects with a {@link VerificationError} otherwise. * The signer is asked with `{ kind: 'human-verification' }`; it must verify * its own user's session and sign a grant naming that user. */ verifyHuman(options?: VerifyHumanOptions): Promise; /** Register an event listener. */ on(event: K, handler: EventMap[K]): this; /** Remove a previously registered event listener. */ off(event: K, handler: EventMap[K]): this; /** * Close the flow without waiting for completion. Rejects a flow in * progress with `VERIFICATION_DISMISSED`, quietly (no `error` event). */ close(): void; /** * Tear down the instance and release all resources. A flow in progress is * settled as a quiet dismissal, so a caller awaiting it is never left * hanging by an unmount. */ destroy(): void; private log; private setStatus; /** * The hosted page URL. `preload=true` is what makes the page's inline * bridge script fire `blink:iframe-ready` on HTML parse and buffer the * grant until React mounts — the same handshake the deposit flow uses, * reused here so the grant never rides on the URL. */ private buildHostedUrl; private runFlow; private waitForIframeReady; private startMessageListener; private cleanup; private emit; } /** * A device identifier collected on the merchant's page, in the shape * `POST /v1/identity/users` accepts under `thirdPartyIdentifiers[]`. * * The browser only COLLECTS these. Blink verifies each one server-side by * fetching `eventId` from the provider with its secret key and checking the * event names the same `id`, so a merchant backend should forward them as-is * and never treat them as trusted on their own. */ interface ThirdPartyIdentifier { provider: 'fingerprint'; /** The provider's identifier for the device (the Fingerprint visitor id). */ id: string; /** The provider's reference for this observation (the Fingerprint event id). */ eventId: string; } /** Options for {@link getIdentifiers}. */ interface GetIdentifiersOptions { /** Server-issued Blink assessment challenge, bound to this account and origin. */ assessmentId?: string; /** * Fingerprint public key to identify with. Defaults to Blink's key, which is * the one the hosted flow already uses; override only to point the * merchant page at a separate Fingerprint environment. */ apiKey?: string; /** Fingerprint region. Defaults to `'us'`. */ region?: 'us' | 'eu' | 'ap'; /** * Activated Fingerprint custom endpoint on the merchant's domain (HTTPS). * Used for both agent loading and identification, without falling back to * Fingerprint's public domains. Omit to use the provider defaults. */ endpoint?: string; /** * Upper bound on the whole identification. The agent can stall rather than * fail (a blackholed request never rejects), so a bound is the only way to * guarantee the promise settles. Defaults to 10 seconds. */ timeoutMs?: number; } /** The slice of the Fingerprint agent this module depends on. */ interface FingerprintAgentLike { get(options?: { linkedId: string; }): Promise<{ visitor_id?: string; event_id: string; }>; } interface FingerprintAgentStartOptions { apiKey: string; region: 'us' | 'eu' | 'ap'; endpoint?: string; } /** Loads and starts the Fingerprint agent. Injected so tests never touch the network. */ type FingerprintAgentLoader = (options: FingerprintAgentStartOptions) => Promise; /** * Builds a `getIdentifiers` bound to a specific agent loader. The exported * {@link getIdentifiers} uses the real agent; tests build one with a fake. */ declare function createGetIdentifiers(loadAgent: FingerprintAgentLoader): (options?: GetIdentifiersOptions) => Promise; /** * Collects the third-party identifiers Blink Trust accepts on * `POST /v1/identity/users`, from the merchant's own page. * * Runs one Fingerprint identification as a first party on the merchant * origin (the hosted flow's identification happens on a Blink origin and is * a third party to the merchant's page) and returns * `[{ provider: 'fingerprint', id, eventId }]`. * * Send the result to your backend, add the identity fields your auth * provider vouches for (email, phone), and forward to Blink. Rejects with a * `DepositError` of code `IDENTIFIERS_UNAVAILABLE` rather than resolving an * empty array, so "no signal" is distinguishable from "nothing collected". */ declare const getIdentifiers: (options?: GetIdentifiersOptions) => Promise; /** Current wire-protocol version. Bump on breaking changes. */ declare const BRIDGE_PROTOCOL_VERSION = 1; interface EIP6963ProviderInfo { uuid: string; name: string; icon: string; rdns: string; } interface EIP1193RequestArgs { method: string; params?: unknown[] | Record; } interface EIP1193Provider { request(args: EIP1193RequestArgs): Promise; on?: (event: string, listener: (...args: unknown[]) => void) => unknown; removeListener?: (event: string, listener: (...args: unknown[]) => void) => unknown; } interface EIP6963ProviderDetail { info: EIP6963ProviderInfo; provider: EIP1193Provider; } /** Iframe → Parent: handshake, sent once on mount. */ interface BridgeHelloMessage { type: 'blink:bridge-hello'; protocolVersion: number; } /** Parent → Iframe: list of installed wallets the iframe can borrow. */ interface WalletsAdvertisedMessage { type: 'blink:wallets-advertised'; protocolVersion: number; wallets: AdvertisedWallet[]; } interface AdvertisedWallet { uuid: string; rdns: string; name: string; icon: string; } /** Iframe → Parent: invoke a JSON-RPC method on a top-frame wallet. */ interface RpcRequestMessage { type: 'blink:rpc-request'; protocolVersion: number; uuid: string; id: string; method: string; params?: unknown[] | Record; } /** Parent → Iframe: result of a previous `RpcRequestMessage`. */ interface RpcResponseMessage { type: 'blink:rpc-response'; protocolVersion: number; id: string; result?: unknown; error?: { code: number; message: string; data?: unknown; }; } /** Parent → Iframe: forwarded EIP-1193 event from a top-frame wallet. */ interface RpcEventMessage { type: 'blink:rpc-event'; protocolVersion: number; uuid: string; event: string; args: unknown[]; } /** * Iframe → Parent: ask "which advertised wallet has any of these flags * (e.g. `isUniswapWallet`) set as a truthy property on its provider?". * Used to disambiguate Reown registry entries that lack `rdns` (e.g. * Uniswap, where Reown only ships `injected[].injectedId: * 'isUniswapWallet'`). The parent reads `provider[flag]` defensively * for each advertised provider and returns the first hit, or null. * * The parent answers each request once; the response is matched by * `id`. Cost is local property reads on already-discovered providers — * no network and no cross-frame I/O beyond the postMessage round-trip. */ interface ResolveByFlagRequestMessage { type: 'blink:resolve-by-flag'; protocolVersion: number; /** Request id, mirrored on the response so the iframe can route it. */ id: string; /** Flag names to test in order; first truthy property wins. */ flags: string[]; } /** Parent → Iframe: result of a previous `ResolveByFlagRequestMessage`. */ interface ResolveByFlagResponseMessage { type: 'blink:resolve-by-flag-response'; protocolVersion: number; id: string; /** `null` when no advertised provider had any flag set. */ match: ResolveByFlagMatch | null; } interface ResolveByFlagMatch { /** EIP-6963 `rdns` of the matched provider. */ rdns: string; /** EIP-6963 `uuid` of the matched provider. */ uuid: string; /** Which flag matched first (lets the caller log/telemeter). */ flag: string; } type BridgeMessage = BridgeHelloMessage | WalletsAdvertisedMessage | RpcRequestMessage | RpcResponseMessage | RpcEventMessage | ResolveByFlagRequestMessage | ResolveByFlagResponseMessage; declare const ALLOWED_RPC_METHODS: Set; declare function parseBridgeMessage(data: unknown): BridgeMessage | null; interface RegisteredProvider { info: EIP6963ProviderInfo; provider: EIP1193Provider; } interface WalletDiscovererHandle { /** Snapshot of currently-registered providers. */ list(): RegisteredProvider[]; /** Look up a single provider by EIP-6963 uuid. */ get(uuid: string): EIP1193Provider | undefined; /** Subscribe to registry changes. Returns an unsubscribe callback. */ subscribe(listener: (snapshot: RegisteredProvider[]) => void): () => void; /** * Re-dispatch `eip6963:requestProvider`. Wallets that wired up after * page load (or after the initial sweep) will re-announce in response. */ requestProviders(): void; /** Tear down listeners. After this, `list()` returns `[]`. */ destroy(): void; } /** * Start discovering EIP-6963 providers in the current (top-level) frame. * * Safe to call multiple times — each handle owns its own listeners and * registry; calling `destroy()` releases both. In practice, `createIframe` * spins one handle per modal open. */ declare function createWalletDiscoverer(): WalletDiscovererHandle; interface RpcHostHandle { /** Tear down listeners and unsubscribe from all bridged providers. */ detach(): void; } interface RpcHostOptions { /** Cross-origin iframe window we're talking to. */ iframeWindow: Window; /** Origin of the iframe — origin-pin all postMessage send/receive. */ iframeOrigin: string; /** Discoverer providing the EIP-6963 provider registry. */ discoverer: WalletDiscovererHandle; /** Optional debug logger; defaults to a no-op. */ log?: (message: string, data?: Record) => void; } /** * Attach an RPC host to the given iframe. Listens on `window` for * messages from the iframe and routes allowlisted JSON-RPC calls to the * matching top-frame EIP-1193 provider. */ declare function attachRpcHost(options: RpcHostOptions): RpcHostHandle; declare const VERSION = "0.3.38"; export { ALLOWED_RPC_METHODS, type AdvertisedWallet, BRIDGE_PROTOCOL_VERSION, type EIP1193Provider as BridgeEIP1193Provider, type BridgeHelloMessage, type BridgeMessage, Checkout, DEFAULT_WEBVIEW_BASE_URL, Deposit, DepositConfig, DepositError, DepositRequest, DepositResult, DepositStatus, type EIP6963ProviderDetail, type EIP6963ProviderInfo, type FingerprintAgentLike, type FingerprintAgentLoader, type FingerprintAgentStartOptions, type GetIdentifiersOptions, type RegisteredProvider, type RpcEventMessage, type RpcHostHandle, type RpcHostOptions, type RpcRequestMessage, type RpcResponseMessage, SANDBOX_WEBVIEW_BASE_URL, type ThirdPartyIdentifier, VERSION, Verification, VerificationConfig, VerificationError, VerificationResult, VerificationStatus, VerifyHumanOptions, type WalletDiscovererHandle, type WalletsAdvertisedMessage, attachRpcHost, createGetIdentifiers, createWalletDiscoverer, getIdentifiers, parseBridgeMessage };