@swype-org/react-sdk 0.1.229 → 0.1.232

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.
package/dist/index.cjs CHANGED
@@ -158,339 +158,358 @@ function useBlinkDepositAmount() {
158
158
  };
159
159
  }
160
160
 
161
- // node_modules/@wagmi/core/dist/esm/utils/getAction.js
162
- function getAction(client, actionFn, name) {
163
- const action_implicit = client[actionFn.name];
164
- if (typeof action_implicit === "function")
165
- return action_implicit;
166
- const action_explicit = client[name];
167
- if (typeof action_explicit === "function")
168
- return action_explicit;
169
- return (params) => actionFn(client, params);
170
- }
171
-
172
- // node_modules/@wagmi/core/dist/esm/version.js
173
- var version = "2.22.1";
174
-
175
- // node_modules/@wagmi/core/dist/esm/utils/getVersion.js
176
- var getVersion = () => `@wagmi/core@${version}`;
177
-
178
- // node_modules/@wagmi/core/dist/esm/errors/base.js
179
- var __classPrivateFieldGet = function(receiver, state, kind, f) {
180
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
181
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
161
+ // src/passkey-delegation.ts
162
+ var PasskeyIframeBlockedError = class extends Error {
163
+ constructor(message = "Passkey creation is not supported in this browser context.") {
164
+ super(message);
165
+ this.name = "PasskeyIframeBlockedError";
166
+ }
182
167
  };
183
- var _BaseError_instances;
184
- var _BaseError_walk;
185
- var BaseError = class _BaseError extends Error {
186
- get docsBaseUrl() {
187
- return "https://wagmi.sh/core";
168
+ function isInCrossOriginIframe() {
169
+ if (typeof window === "undefined") return false;
170
+ if (window.parent === window) return false;
171
+ try {
172
+ void window.parent.location.origin;
173
+ return false;
174
+ } catch {
175
+ return true;
188
176
  }
189
- get version() {
190
- return getVersion();
177
+ }
178
+ function isSafari() {
179
+ if (typeof navigator === "undefined") return false;
180
+ const ua = navigator.userAgent;
181
+ return /Safari/i.test(ua) && !/Chrome|CriOS|Chromium|Edg|OPR|Firefox/i.test(ua);
182
+ }
183
+ var POPUP_RESULT_TIMEOUT_MS = 12e4;
184
+ var POPUP_CLOSED_POLL_MS = 500;
185
+ var POPUP_CLOSED_GRACE_MS = 1e3;
186
+ function createPasskeyViaPopup(options) {
187
+ return new Promise((resolve, reject) => {
188
+ const verificationToken = crypto.randomUUID();
189
+ const payload = { ...options, verificationToken };
190
+ const encoded = btoa(JSON.stringify(payload));
191
+ const popupUrl = `${window.location.origin}/passkey-register#${encoded}`;
192
+ const popup = window.open(popupUrl, "blink-passkey");
193
+ if (!popup) {
194
+ reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
195
+ return;
196
+ }
197
+ let settled = false;
198
+ const timer = setTimeout(() => {
199
+ cleanup();
200
+ reject(new Error("Passkey creation timed out. Please try again."));
201
+ }, POPUP_RESULT_TIMEOUT_MS);
202
+ const closedPoll = setInterval(() => {
203
+ if (popup.closed) {
204
+ clearInterval(closedPoll);
205
+ setTimeout(() => {
206
+ if (!settled) {
207
+ settled = true;
208
+ cleanup();
209
+ checkServerForPasskeyByToken(
210
+ options.authToken,
211
+ options.apiBaseUrl,
212
+ verificationToken
213
+ ).then((result) => {
214
+ if (result) {
215
+ resolve(result);
216
+ } else {
217
+ reject(new Error("Passkey window was closed before completing."));
218
+ }
219
+ }).catch(() => {
220
+ reject(new Error("Passkey window was closed before completing."));
221
+ });
222
+ }
223
+ }, POPUP_CLOSED_GRACE_MS);
224
+ }
225
+ }, POPUP_CLOSED_POLL_MS);
226
+ function cleanup() {
227
+ clearTimeout(timer);
228
+ clearInterval(closedPoll);
229
+ }
230
+ });
231
+ }
232
+ var VERIFY_POPUP_TIMEOUT_MS = 6e4;
233
+ function findDevicePasskeyViaPopup(options) {
234
+ return new Promise((resolve, reject) => {
235
+ const verificationToken = crypto.randomUUID();
236
+ const payload = {
237
+ ...options,
238
+ verificationToken
239
+ };
240
+ const encoded = btoa(JSON.stringify(payload));
241
+ const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
242
+ const popup = window.open(popupUrl, "blink-passkey-verify");
243
+ if (!popup) {
244
+ reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
245
+ return;
246
+ }
247
+ let settled = false;
248
+ const timer = setTimeout(() => {
249
+ cleanup();
250
+ resolve(null);
251
+ }, VERIFY_POPUP_TIMEOUT_MS);
252
+ const closedPoll = setInterval(() => {
253
+ if (popup.closed && !settled) {
254
+ clearInterval(closedPoll);
255
+ setTimeout(() => {
256
+ if (!settled) {
257
+ settled = true;
258
+ cleanup();
259
+ checkServerForPasskeyByToken(
260
+ options.authToken,
261
+ options.apiBaseUrl,
262
+ verificationToken
263
+ ).then((result) => {
264
+ resolve(result?.credentialId ?? null);
265
+ }).catch(() => {
266
+ resolve(null);
267
+ });
268
+ }
269
+ }, POPUP_CLOSED_GRACE_MS);
270
+ }
271
+ }, POPUP_CLOSED_POLL_MS);
272
+ function cleanup() {
273
+ clearTimeout(timer);
274
+ clearInterval(closedPoll);
275
+ }
276
+ });
277
+ }
278
+ async function checkServerForPasskeyByToken(authToken, apiBaseUrl, verificationToken) {
279
+ if (!authToken || !apiBaseUrl) return null;
280
+ const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
281
+ headers: { Authorization: `Bearer ${authToken}` }
282
+ });
283
+ if (!res.ok) return null;
284
+ const body = await res.json();
285
+ const passkeys = body.config.passkeys ?? [];
286
+ const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
287
+ return matched ? { credentialId: matched.credentialId, publicKey: matched.publicKey } : null;
288
+ }
289
+
290
+ // src/passkeyRpId.ts
291
+ function normalizeConfiguredDomain(value) {
292
+ return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
293
+ }
294
+ function resolveRootDomainFromHostname(hostname) {
295
+ const trimmedHostname = hostname.trim().toLowerCase();
296
+ if (!trimmedHostname) {
297
+ return "localhost";
191
298
  }
192
- constructor(shortMessage, options = {}) {
193
- super();
194
- _BaseError_instances.add(this);
195
- Object.defineProperty(this, "details", {
196
- enumerable: true,
197
- configurable: true,
198
- writable: true,
199
- value: void 0
200
- });
201
- Object.defineProperty(this, "docsPath", {
202
- enumerable: true,
203
- configurable: true,
204
- writable: true,
205
- value: void 0
206
- });
207
- Object.defineProperty(this, "metaMessages", {
208
- enumerable: true,
209
- configurable: true,
210
- writable: true,
211
- value: void 0
212
- });
213
- Object.defineProperty(this, "shortMessage", {
214
- enumerable: true,
215
- configurable: true,
216
- writable: true,
217
- value: void 0
218
- });
219
- Object.defineProperty(this, "name", {
220
- enumerable: true,
221
- configurable: true,
222
- writable: true,
223
- value: "WagmiCoreError"
224
- });
225
- const details = options.cause instanceof _BaseError ? options.cause.details : options.cause?.message ? options.cause.message : options.details;
226
- const docsPath = options.cause instanceof _BaseError ? options.cause.docsPath || options.docsPath : options.docsPath;
227
- this.message = [
228
- shortMessage || "An error occurred.",
229
- "",
230
- ...options.metaMessages ? [...options.metaMessages, ""] : [],
231
- ...docsPath ? [
232
- `Docs: ${this.docsBaseUrl}${docsPath}.html${options.docsSlug ? `#${options.docsSlug}` : ""}`
233
- ] : [],
234
- ...details ? [`Details: ${details}`] : [],
235
- `Version: ${this.version}`
236
- ].join("\n");
237
- if (options.cause)
238
- this.cause = options.cause;
239
- this.details = details;
240
- this.docsPath = docsPath;
241
- this.metaMessages = options.metaMessages;
242
- this.shortMessage = shortMessage;
299
+ if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
300
+ return trimmedHostname;
243
301
  }
244
- walk(fn) {
245
- return __classPrivateFieldGet(this, _BaseError_instances, "m", _BaseError_walk).call(this, this, fn);
302
+ const parts = trimmedHostname.split(".").filter(Boolean);
303
+ if (parts.length < 2) {
304
+ return trimmedHostname;
246
305
  }
247
- };
248
- _BaseError_instances = /* @__PURE__ */ new WeakSet(), _BaseError_walk = function _BaseError_walk2(err, fn) {
249
- if (fn?.(err))
250
- return err;
251
- if (err.cause)
252
- return __classPrivateFieldGet(this, _BaseError_instances, "m", _BaseError_walk2).call(this, err.cause, fn);
253
- return err;
254
- };
306
+ return parts.slice(-2).join(".");
307
+ }
255
308
 
256
- // node_modules/@wagmi/core/dist/esm/errors/config.js
257
- var ConnectorNotConnectedError = class extends BaseError {
258
- constructor() {
259
- super("Connector not connected.");
260
- Object.defineProperty(this, "name", {
261
- enumerable: true,
262
- configurable: true,
263
- writable: true,
264
- value: "ConnectorNotConnectedError"
265
- });
266
- }
267
- };
268
- var ConnectorAccountNotFoundError = class extends BaseError {
269
- constructor({ address, connector }) {
270
- super(`Account "${address}" not found for connector "${connector.name}".`);
271
- Object.defineProperty(this, "name", {
272
- enumerable: true,
273
- configurable: true,
274
- writable: true,
275
- value: "ConnectorAccountNotFoundError"
276
- });
277
- }
278
- };
279
- var ConnectorChainMismatchError = class extends BaseError {
280
- constructor({ connectionChainId, connectorChainId }) {
281
- super(`The current chain of the connector (id: ${connectorChainId}) does not match the connection's chain (id: ${connectionChainId}).`, {
282
- metaMessages: [
283
- `Current Chain ID: ${connectorChainId}`,
284
- `Expected Chain ID: ${connectionChainId}`
285
- ]
286
- });
287
- Object.defineProperty(this, "name", {
288
- enumerable: true,
289
- configurable: true,
290
- writable: true,
291
- value: "ConnectorChainMismatchError"
292
- });
293
- }
294
- };
295
- var ConnectorUnavailableReconnectingError = class extends BaseError {
296
- constructor({ connector }) {
297
- super(`Connector "${connector.name}" unavailable while reconnecting.`, {
298
- details: [
299
- "During the reconnection step, the only connector methods guaranteed to be available are: `id`, `name`, `type`, `uid`.",
300
- "All other methods are not guaranteed to be available until reconnection completes and connectors are fully restored.",
301
- "This error commonly occurs for connectors that asynchronously inject after reconnection has already started."
302
- ].join(" ")
303
- });
304
- Object.defineProperty(this, "name", {
305
- enumerable: true,
306
- configurable: true,
307
- writable: true,
308
- value: "ConnectorUnavailableReconnectingError"
309
- });
310
- }
311
- };
312
- async function getConnectorClient(config, parameters = {}) {
313
- const { assertChainId = true } = parameters;
314
- let connection;
315
- if (parameters.connector) {
316
- const { connector: connector2 } = parameters;
317
- if (config.state.status === "reconnecting" && !connector2.getAccounts && !connector2.getChainId)
318
- throw new ConnectorUnavailableReconnectingError({ connector: connector2 });
319
- const [accounts, chainId2] = await Promise.all([
320
- connector2.getAccounts().catch((e) => {
321
- if (parameters.account === null)
322
- return [];
323
- throw e;
324
- }),
325
- connector2.getChainId()
326
- ]);
327
- connection = {
328
- accounts,
329
- chainId: chainId2,
330
- connector: connector2
331
- };
332
- } else
333
- connection = config.state.connections.get(config.state.current);
334
- if (!connection)
335
- throw new ConnectorNotConnectedError();
336
- const chainId = parameters.chainId ?? connection.chainId;
337
- const connectorChainId = await connection.connector.getChainId();
338
- if (assertChainId && connectorChainId !== chainId)
339
- throw new ConnectorChainMismatchError({
340
- connectionChainId: chainId,
341
- connectorChainId
342
- });
343
- const connector = connection.connector;
344
- if (connector.getClient)
345
- return connector.getClient({ chainId });
346
- const account = utils.parseAccount(parameters.account ?? connection.accounts[0]);
347
- if (account)
348
- account.address = utils.getAddress(account.address);
349
- if (parameters.account && !connection.accounts.some((x) => x.toLowerCase() === account.address.toLowerCase()))
350
- throw new ConnectorAccountNotFoundError({
351
- address: account.address,
352
- connector
353
- });
354
- const chain = config.chains.find((chain2) => chain2.id === chainId);
355
- const provider = await connection.connector.getProvider({ chainId });
356
- return viem.createClient({
357
- account,
358
- chain,
359
- name: "Connector Client",
360
- transport: (opts) => viem.custom(provider)({ ...opts, retryCount: 0 })
309
+ // src/hooks/passkeyPublic.ts
310
+ function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
311
+ return new Promise((resolve, reject) => {
312
+ if (typeof document === "undefined") {
313
+ resolve();
314
+ return;
315
+ }
316
+ if (document.hasFocus()) {
317
+ resolve();
318
+ return;
319
+ }
320
+ const deadline = Date.now() + timeoutMs;
321
+ const timer = setInterval(() => {
322
+ if (document.hasFocus()) {
323
+ clearInterval(timer);
324
+ resolve();
325
+ } else if (Date.now() >= deadline) {
326
+ clearInterval(timer);
327
+ resolve();
328
+ }
329
+ }, intervalMs);
361
330
  });
362
331
  }
363
-
364
- // node_modules/@wagmi/core/dist/esm/actions/getAccount.js
365
- function getAccount(config) {
366
- const uid = config.state.current;
367
- const connection = config.state.connections.get(uid);
368
- const addresses = connection?.accounts;
369
- const address = addresses?.[0];
370
- const chain = config.chains.find((chain2) => chain2.id === connection?.chainId);
371
- const status = config.state.status;
372
- switch (status) {
373
- case "connected":
374
- return {
375
- address,
376
- addresses,
377
- chain,
378
- chainId: connection?.chainId,
379
- connector: connection?.connector,
380
- isConnected: true,
381
- isConnecting: false,
382
- isDisconnected: false,
383
- isReconnecting: false,
384
- status
385
- };
386
- case "reconnecting":
387
- return {
388
- address,
389
- addresses,
390
- chain,
391
- chainId: connection?.chainId,
392
- connector: connection?.connector,
393
- isConnected: !!address,
394
- isConnecting: false,
395
- isDisconnected: false,
396
- isReconnecting: true,
397
- status
398
- };
399
- case "connecting":
400
- return {
401
- address,
402
- addresses,
403
- chain,
404
- chainId: connection?.chainId,
405
- connector: connection?.connector,
406
- isConnected: false,
407
- isConnecting: true,
408
- isDisconnected: false,
409
- isReconnecting: false,
410
- status
411
- };
412
- case "disconnected":
413
- return {
414
- address: void 0,
415
- addresses: void 0,
416
- chain: void 0,
417
- chainId: void 0,
418
- connector: void 0,
419
- isConnected: false,
420
- isConnecting: false,
421
- isDisconnected: true,
422
- isReconnecting: false,
423
- status
424
- };
332
+ function toBase64(buffer) {
333
+ return btoa(String.fromCharCode(...new Uint8Array(buffer)));
334
+ }
335
+ function base64ToBytes(value) {
336
+ const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
337
+ const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
338
+ const raw = atob(padded);
339
+ const bytes = new Uint8Array(raw.length);
340
+ for (let i = 0; i < raw.length; i++) {
341
+ bytes[i] = raw.charCodeAt(i);
425
342
  }
343
+ return bytes;
426
344
  }
427
- async function getWalletClient(config, parameters = {}) {
428
- const client = await getConnectorClient(config, parameters);
429
- return client.extend(viem.walletActions);
345
+ function readEnvValue(name) {
346
+ const meta = ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) });
347
+ const metaValue = meta.env?.[name];
348
+ if (typeof metaValue === "string" && metaValue.trim().length > 0) {
349
+ return metaValue.trim();
350
+ }
351
+ const processValue = globalThis.process?.env?.[name];
352
+ if (typeof processValue === "string" && processValue.trim().length > 0) {
353
+ return processValue.trim();
354
+ }
355
+ return void 0;
430
356
  }
431
- async function waitForTransactionReceipt(config, parameters) {
432
- const { chainId, timeout = 0, ...rest } = parameters;
433
- const client = config.getClient({ chainId });
434
- const action = getAction(client, actions.waitForTransactionReceipt, "waitForTransactionReceipt");
435
- const receipt = await action({ ...rest, timeout });
436
- if (receipt.status === "reverted") {
437
- const action_getTransaction = getAction(client, actions.getTransaction, "getTransaction");
438
- const { from: account, ...txn } = await action_getTransaction({
439
- hash: receipt.transactionHash
440
- });
441
- const action_call = getAction(client, actions.call, "call");
442
- const code = await action_call({
443
- ...txn,
444
- account,
445
- data: txn.input,
446
- gasPrice: txn.type !== "eip1559" ? txn.gasPrice : void 0,
447
- maxFeePerGas: txn.type === "eip1559" ? txn.maxFeePerGas : void 0,
448
- maxPriorityFeePerGas: txn.type === "eip1559" ? txn.maxPriorityFeePerGas : void 0
449
- });
450
- const reason = code?.data ? viem.hexToString(`0x${code.data.substring(138)}`) : "unknown reason";
451
- throw new Error(reason);
357
+ function resolvePasskeyRpId() {
358
+ const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
359
+ if (configuredDomain) {
360
+ return normalizeConfiguredDomain(configuredDomain);
452
361
  }
453
- return {
454
- ...receipt,
455
- chainId: client.chain.id
456
- };
362
+ if (typeof window !== "undefined") {
363
+ return resolveRootDomainFromHostname(window.location.hostname);
364
+ }
365
+ return "localhost";
457
366
  }
458
-
459
- // src/api.ts
460
- var api_exports = {};
461
- __export(api_exports, {
462
- createAccount: () => createAccount,
463
- createAccountAuthorizationSession: () => createAccountAuthorizationSession,
464
- createGuestAccount: () => createGuestAccount,
465
- createGuestTransfer: () => createGuestTransfer,
466
- createTransfer: () => createTransfer,
467
- fetchAccount: () => fetchAccount,
468
- fetchAccounts: () => fetchAccounts,
469
- fetchAuthorizationSession: () => fetchAuthorizationSession,
470
- fetchChains: () => fetchChains,
471
- fetchGuestAccount: () => fetchGuestAccount,
472
- fetchGuestTransferBalances: () => fetchGuestTransferBalances,
473
- fetchMerchantPublicKey: () => fetchMerchantPublicKey,
474
- fetchProviders: () => fetchProviders,
475
- fetchTransfer: () => fetchTransfer,
476
- fetchUserConfig: () => fetchUserConfig,
477
- getGuestTransfer: () => getGuestTransfer,
478
- getTransferByGuestToken: () => getTransferByGuestToken,
479
- registerPasskey: () => registerPasskey,
480
- reportActionCompletion: () => reportActionCompletion,
481
- reportPasskeyActivity: () => reportPasskeyActivity,
482
- setAccountOwner: () => setAccountOwner,
483
- setTransferSender: () => setTransferSender,
484
- signGuestTransfer: () => signGuestTransfer,
485
- signTransfer: () => signTransfer,
486
- updateUserConfig: () => updateUserConfig,
487
- updateUserConfigBySession: () => updateUserConfigBySession
488
- });
489
- async function throwApiError(res) {
490
- const body = await res.json().catch(() => null);
491
- const detail = body?.error ?? body;
492
- const msg = detail?.message ?? res.statusText;
493
- const code = detail?.code ?? String(res.status);
367
+ async function createPasskeyCredential(params) {
368
+ const challenge = new Uint8Array(32);
369
+ crypto.getRandomValues(challenge);
370
+ const rpId = resolvePasskeyRpId();
371
+ const publicKeyOptions = {
372
+ challenge,
373
+ rp: { name: "Blink", id: rpId },
374
+ user: {
375
+ id: new TextEncoder().encode(params.userId),
376
+ name: params.displayName,
377
+ displayName: params.displayName
378
+ },
379
+ pubKeyCredParams: [
380
+ { alg: -7, type: "public-key" },
381
+ { alg: -257, type: "public-key" }
382
+ ],
383
+ authenticatorSelection: {
384
+ authenticatorAttachment: "platform",
385
+ residentKey: "preferred",
386
+ userVerification: "required"
387
+ },
388
+ timeout: 6e4
389
+ };
390
+ if (isInCrossOriginIframe()) {
391
+ try {
392
+ await waitForDocumentFocus();
393
+ const credential2 = await navigator.credentials.create({
394
+ publicKey: publicKeyOptions
395
+ });
396
+ if (!credential2) {
397
+ throw new Error("Passkey creation was cancelled.");
398
+ }
399
+ return extractPasskeyResult(credential2);
400
+ } catch (err) {
401
+ if (err instanceof PasskeyIframeBlockedError) throw err;
402
+ if (err instanceof Error && err.message === "Passkey creation was cancelled.") throw err;
403
+ throw new PasskeyIframeBlockedError();
404
+ }
405
+ }
406
+ await waitForDocumentFocus();
407
+ const credential = await navigator.credentials.create({
408
+ publicKey: publicKeyOptions
409
+ });
410
+ if (!credential) {
411
+ throw new Error("Passkey creation was cancelled.");
412
+ }
413
+ return extractPasskeyResult(credential);
414
+ }
415
+ function extractPasskeyResult(credential) {
416
+ const response = credential.response;
417
+ const publicKeyBytes = response.getPublicKey?.();
418
+ return {
419
+ credentialId: toBase64(credential.rawId),
420
+ publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
421
+ };
422
+ }
423
+ function buildPasskeyPopupOptions(params) {
424
+ const challenge = new Uint8Array(32);
425
+ crypto.getRandomValues(challenge);
426
+ const rpId = resolvePasskeyRpId();
427
+ return {
428
+ challenge: toBase64(challenge),
429
+ rpId,
430
+ rpName: "Blink",
431
+ userId: toBase64(new TextEncoder().encode(params.userId)),
432
+ userName: params.displayName,
433
+ userDisplayName: params.displayName,
434
+ pubKeyCredParams: [
435
+ { alg: -7, type: "public-key" },
436
+ { alg: -257, type: "public-key" }
437
+ ],
438
+ authenticatorSelection: {
439
+ authenticatorAttachment: "platform",
440
+ residentKey: "preferred",
441
+ userVerification: "required"
442
+ },
443
+ timeout: 6e4,
444
+ authToken: params.authToken,
445
+ apiBaseUrl: params.apiBaseUrl
446
+ };
447
+ }
448
+ async function deviceHasPasskey(credentialId) {
449
+ const found = await findDevicePasskey([credentialId]);
450
+ return found != null;
451
+ }
452
+ async function findDevicePasskey(credentialIds) {
453
+ if (credentialIds.length === 0) return null;
454
+ try {
455
+ const challenge = new Uint8Array(32);
456
+ crypto.getRandomValues(challenge);
457
+ await waitForDocumentFocus();
458
+ const assertion = await navigator.credentials.get({
459
+ publicKey: {
460
+ challenge,
461
+ rpId: resolvePasskeyRpId(),
462
+ allowCredentials: credentialIds.map((id) => ({
463
+ type: "public-key",
464
+ id: base64ToBytes(id)
465
+ })),
466
+ userVerification: "discouraged",
467
+ timeout: 3e4
468
+ }
469
+ });
470
+ if (!assertion) return null;
471
+ return toBase64(assertion.rawId);
472
+ } catch {
473
+ return null;
474
+ }
475
+ }
476
+
477
+ // src/api.ts
478
+ var api_exports = {};
479
+ __export(api_exports, {
480
+ createAccount: () => createAccount,
481
+ createAccountAuthorizationSession: () => createAccountAuthorizationSession,
482
+ createGuestAccount: () => createGuestAccount,
483
+ createGuestTransfer: () => createGuestTransfer,
484
+ createTransfer: () => createTransfer,
485
+ fetchAccount: () => fetchAccount,
486
+ fetchAccounts: () => fetchAccounts,
487
+ fetchAuthorizationSession: () => fetchAuthorizationSession,
488
+ fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
489
+ fetchChains: () => fetchChains,
490
+ fetchGuestAccount: () => fetchGuestAccount,
491
+ fetchGuestTransferBalances: () => fetchGuestTransferBalances,
492
+ fetchMerchantPublicKey: () => fetchMerchantPublicKey,
493
+ fetchProviders: () => fetchProviders,
494
+ fetchTransfer: () => fetchTransfer,
495
+ fetchUserConfig: () => fetchUserConfig,
496
+ getGuestTransfer: () => getGuestTransfer,
497
+ getTransferByGuestToken: () => getTransferByGuestToken,
498
+ registerPasskey: () => registerPasskey,
499
+ reportActionCompletion: () => reportActionCompletion,
500
+ reportPasskeyActivity: () => reportPasskeyActivity,
501
+ setAccountOwner: () => setAccountOwner,
502
+ setTransferSender: () => setTransferSender,
503
+ signGuestTransfer: () => signGuestTransfer,
504
+ signTransfer: () => signTransfer,
505
+ updateUserConfig: () => updateUserConfig,
506
+ updateUserConfigBySession: () => updateUserConfigBySession
507
+ });
508
+ async function throwApiError(res) {
509
+ const body = await res.json().catch(() => null);
510
+ const detail = body?.error ?? body;
511
+ const msg = detail?.message ?? res.statusText;
512
+ const code = detail?.code ?? String(res.status);
494
513
  throw new Error(`${res.status} \u2014 ${code}: ${msg}`);
495
514
  }
496
515
  async function fetchProviders(apiBaseUrl, token) {
@@ -646,6 +665,13 @@ async function fetchAuthorizationSession(apiBaseUrl, sessionId) {
646
665
  if (!res.ok) await throwApiError(res);
647
666
  return await res.json();
648
667
  }
668
+ async function fetchAuthorizationSessionByToken(apiBaseUrl, token) {
669
+ const res = await fetch(
670
+ `${apiBaseUrl}/v1/authorization-sessions?token=${encodeURIComponent(token)}`
671
+ );
672
+ if (!res.ok) await throwApiError(res);
673
+ return await res.json();
674
+ }
649
675
  async function registerPasskey(apiBaseUrl, token, credentialId, publicKey) {
650
676
  const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
651
677
  method: "POST",
@@ -814,24 +840,373 @@ async function reportActionCompletion(apiBaseUrl, actionId, result) {
814
840
  if (!res.ok) await throwApiError(res);
815
841
  return await res.json();
816
842
  }
817
-
818
- // src/passkeyRpId.ts
819
- function normalizeConfiguredDomain(value) {
820
- return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
843
+
844
+ // src/transferPolling.ts
845
+ async function pollTransferTick(params) {
846
+ const fetchTransfer2 = params.fetchTransfer ?? fetchTransfer;
847
+ const token = await params.getAccessToken();
848
+ if (!token) {
849
+ return { kind: "retry" };
850
+ }
851
+ try {
852
+ const transfer = await fetchTransfer2(params.apiBaseUrl, token, params.transferId);
853
+ return { kind: "success", transfer };
854
+ } catch (err) {
855
+ return {
856
+ kind: "error",
857
+ message: err instanceof Error ? err.message : "Polling error"
858
+ };
859
+ }
860
+ }
861
+
862
+ // src/hooks/useTransferPolling.ts
863
+ function useTransferPolling(intervalMs = 3e3) {
864
+ const { apiBaseUrl } = useBlinkConfig();
865
+ const { getAccessToken } = reactAuth.usePrivy();
866
+ const [transfer, setTransfer] = react.useState(null);
867
+ const [error, setError] = react.useState(null);
868
+ const [isPolling, setIsPolling] = react.useState(false);
869
+ const intervalRef = react.useRef(null);
870
+ const transferIdRef = react.useRef(null);
871
+ const stopPolling = react.useCallback(() => {
872
+ if (intervalRef.current) {
873
+ clearInterval(intervalRef.current);
874
+ intervalRef.current = null;
875
+ }
876
+ setIsPolling(false);
877
+ }, []);
878
+ const poll = react.useCallback(async () => {
879
+ if (!transferIdRef.current) return;
880
+ const result = await pollTransferTick({
881
+ apiBaseUrl,
882
+ transferId: transferIdRef.current,
883
+ getAccessToken
884
+ });
885
+ if (result.kind === "retry") {
886
+ return;
887
+ }
888
+ if (result.kind === "error") {
889
+ setError(result.message);
890
+ stopPolling();
891
+ return;
892
+ }
893
+ setError(null);
894
+ setTransfer(result.transfer);
895
+ if (result.transfer.status === "COMPLETED" || result.transfer.status === "FAILED") {
896
+ stopPolling();
897
+ }
898
+ }, [apiBaseUrl, getAccessToken, stopPolling]);
899
+ const startPolling = react.useCallback(
900
+ (transferId) => {
901
+ stopPolling();
902
+ transferIdRef.current = transferId;
903
+ setIsPolling(true);
904
+ setError(null);
905
+ poll();
906
+ intervalRef.current = setInterval(poll, intervalMs);
907
+ },
908
+ [poll, intervalMs, stopPolling]
909
+ );
910
+ react.useEffect(() => () => stopPolling(), [stopPolling]);
911
+ return { transfer, error, isPolling, startPolling, stopPolling };
912
+ }
913
+
914
+ // node_modules/@wagmi/core/dist/esm/utils/getAction.js
915
+ function getAction(client, actionFn, name) {
916
+ const action_implicit = client[actionFn.name];
917
+ if (typeof action_implicit === "function")
918
+ return action_implicit;
919
+ const action_explicit = client[name];
920
+ if (typeof action_explicit === "function")
921
+ return action_explicit;
922
+ return (params) => actionFn(client, params);
923
+ }
924
+
925
+ // node_modules/@wagmi/core/dist/esm/version.js
926
+ var version = "2.22.1";
927
+
928
+ // node_modules/@wagmi/core/dist/esm/utils/getVersion.js
929
+ var getVersion = () => `@wagmi/core@${version}`;
930
+
931
+ // node_modules/@wagmi/core/dist/esm/errors/base.js
932
+ var __classPrivateFieldGet = function(receiver, state, kind, f) {
933
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
934
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
935
+ };
936
+ var _BaseError_instances;
937
+ var _BaseError_walk;
938
+ var BaseError = class _BaseError extends Error {
939
+ get docsBaseUrl() {
940
+ return "https://wagmi.sh/core";
941
+ }
942
+ get version() {
943
+ return getVersion();
944
+ }
945
+ constructor(shortMessage, options = {}) {
946
+ super();
947
+ _BaseError_instances.add(this);
948
+ Object.defineProperty(this, "details", {
949
+ enumerable: true,
950
+ configurable: true,
951
+ writable: true,
952
+ value: void 0
953
+ });
954
+ Object.defineProperty(this, "docsPath", {
955
+ enumerable: true,
956
+ configurable: true,
957
+ writable: true,
958
+ value: void 0
959
+ });
960
+ Object.defineProperty(this, "metaMessages", {
961
+ enumerable: true,
962
+ configurable: true,
963
+ writable: true,
964
+ value: void 0
965
+ });
966
+ Object.defineProperty(this, "shortMessage", {
967
+ enumerable: true,
968
+ configurable: true,
969
+ writable: true,
970
+ value: void 0
971
+ });
972
+ Object.defineProperty(this, "name", {
973
+ enumerable: true,
974
+ configurable: true,
975
+ writable: true,
976
+ value: "WagmiCoreError"
977
+ });
978
+ const details = options.cause instanceof _BaseError ? options.cause.details : options.cause?.message ? options.cause.message : options.details;
979
+ const docsPath = options.cause instanceof _BaseError ? options.cause.docsPath || options.docsPath : options.docsPath;
980
+ this.message = [
981
+ shortMessage || "An error occurred.",
982
+ "",
983
+ ...options.metaMessages ? [...options.metaMessages, ""] : [],
984
+ ...docsPath ? [
985
+ `Docs: ${this.docsBaseUrl}${docsPath}.html${options.docsSlug ? `#${options.docsSlug}` : ""}`
986
+ ] : [],
987
+ ...details ? [`Details: ${details}`] : [],
988
+ `Version: ${this.version}`
989
+ ].join("\n");
990
+ if (options.cause)
991
+ this.cause = options.cause;
992
+ this.details = details;
993
+ this.docsPath = docsPath;
994
+ this.metaMessages = options.metaMessages;
995
+ this.shortMessage = shortMessage;
996
+ }
997
+ walk(fn) {
998
+ return __classPrivateFieldGet(this, _BaseError_instances, "m", _BaseError_walk).call(this, this, fn);
999
+ }
1000
+ };
1001
+ _BaseError_instances = /* @__PURE__ */ new WeakSet(), _BaseError_walk = function _BaseError_walk2(err, fn) {
1002
+ if (fn?.(err))
1003
+ return err;
1004
+ if (err.cause)
1005
+ return __classPrivateFieldGet(this, _BaseError_instances, "m", _BaseError_walk2).call(this, err.cause, fn);
1006
+ return err;
1007
+ };
1008
+
1009
+ // node_modules/@wagmi/core/dist/esm/errors/config.js
1010
+ var ConnectorNotConnectedError = class extends BaseError {
1011
+ constructor() {
1012
+ super("Connector not connected.");
1013
+ Object.defineProperty(this, "name", {
1014
+ enumerable: true,
1015
+ configurable: true,
1016
+ writable: true,
1017
+ value: "ConnectorNotConnectedError"
1018
+ });
1019
+ }
1020
+ };
1021
+ var ConnectorAccountNotFoundError = class extends BaseError {
1022
+ constructor({ address, connector }) {
1023
+ super(`Account "${address}" not found for connector "${connector.name}".`);
1024
+ Object.defineProperty(this, "name", {
1025
+ enumerable: true,
1026
+ configurable: true,
1027
+ writable: true,
1028
+ value: "ConnectorAccountNotFoundError"
1029
+ });
1030
+ }
1031
+ };
1032
+ var ConnectorChainMismatchError = class extends BaseError {
1033
+ constructor({ connectionChainId, connectorChainId }) {
1034
+ super(`The current chain of the connector (id: ${connectorChainId}) does not match the connection's chain (id: ${connectionChainId}).`, {
1035
+ metaMessages: [
1036
+ `Current Chain ID: ${connectorChainId}`,
1037
+ `Expected Chain ID: ${connectionChainId}`
1038
+ ]
1039
+ });
1040
+ Object.defineProperty(this, "name", {
1041
+ enumerable: true,
1042
+ configurable: true,
1043
+ writable: true,
1044
+ value: "ConnectorChainMismatchError"
1045
+ });
1046
+ }
1047
+ };
1048
+ var ConnectorUnavailableReconnectingError = class extends BaseError {
1049
+ constructor({ connector }) {
1050
+ super(`Connector "${connector.name}" unavailable while reconnecting.`, {
1051
+ details: [
1052
+ "During the reconnection step, the only connector methods guaranteed to be available are: `id`, `name`, `type`, `uid`.",
1053
+ "All other methods are not guaranteed to be available until reconnection completes and connectors are fully restored.",
1054
+ "This error commonly occurs for connectors that asynchronously inject after reconnection has already started."
1055
+ ].join(" ")
1056
+ });
1057
+ Object.defineProperty(this, "name", {
1058
+ enumerable: true,
1059
+ configurable: true,
1060
+ writable: true,
1061
+ value: "ConnectorUnavailableReconnectingError"
1062
+ });
1063
+ }
1064
+ };
1065
+ async function getConnectorClient(config, parameters = {}) {
1066
+ const { assertChainId = true } = parameters;
1067
+ let connection;
1068
+ if (parameters.connector) {
1069
+ const { connector: connector2 } = parameters;
1070
+ if (config.state.status === "reconnecting" && !connector2.getAccounts && !connector2.getChainId)
1071
+ throw new ConnectorUnavailableReconnectingError({ connector: connector2 });
1072
+ const [accounts, chainId2] = await Promise.all([
1073
+ connector2.getAccounts().catch((e) => {
1074
+ if (parameters.account === null)
1075
+ return [];
1076
+ throw e;
1077
+ }),
1078
+ connector2.getChainId()
1079
+ ]);
1080
+ connection = {
1081
+ accounts,
1082
+ chainId: chainId2,
1083
+ connector: connector2
1084
+ };
1085
+ } else
1086
+ connection = config.state.connections.get(config.state.current);
1087
+ if (!connection)
1088
+ throw new ConnectorNotConnectedError();
1089
+ const chainId = parameters.chainId ?? connection.chainId;
1090
+ const connectorChainId = await connection.connector.getChainId();
1091
+ if (assertChainId && connectorChainId !== chainId)
1092
+ throw new ConnectorChainMismatchError({
1093
+ connectionChainId: chainId,
1094
+ connectorChainId
1095
+ });
1096
+ const connector = connection.connector;
1097
+ if (connector.getClient)
1098
+ return connector.getClient({ chainId });
1099
+ const account = utils.parseAccount(parameters.account ?? connection.accounts[0]);
1100
+ if (account)
1101
+ account.address = utils.getAddress(account.address);
1102
+ if (parameters.account && !connection.accounts.some((x) => x.toLowerCase() === account.address.toLowerCase()))
1103
+ throw new ConnectorAccountNotFoundError({
1104
+ address: account.address,
1105
+ connector
1106
+ });
1107
+ const chain = config.chains.find((chain2) => chain2.id === chainId);
1108
+ const provider = await connection.connector.getProvider({ chainId });
1109
+ return viem.createClient({
1110
+ account,
1111
+ chain,
1112
+ name: "Connector Client",
1113
+ transport: (opts) => viem.custom(provider)({ ...opts, retryCount: 0 })
1114
+ });
1115
+ }
1116
+
1117
+ // node_modules/@wagmi/core/dist/esm/actions/getAccount.js
1118
+ function getAccount(config) {
1119
+ const uid = config.state.current;
1120
+ const connection = config.state.connections.get(uid);
1121
+ const addresses = connection?.accounts;
1122
+ const address = addresses?.[0];
1123
+ const chain = config.chains.find((chain2) => chain2.id === connection?.chainId);
1124
+ const status = config.state.status;
1125
+ switch (status) {
1126
+ case "connected":
1127
+ return {
1128
+ address,
1129
+ addresses,
1130
+ chain,
1131
+ chainId: connection?.chainId,
1132
+ connector: connection?.connector,
1133
+ isConnected: true,
1134
+ isConnecting: false,
1135
+ isDisconnected: false,
1136
+ isReconnecting: false,
1137
+ status
1138
+ };
1139
+ case "reconnecting":
1140
+ return {
1141
+ address,
1142
+ addresses,
1143
+ chain,
1144
+ chainId: connection?.chainId,
1145
+ connector: connection?.connector,
1146
+ isConnected: !!address,
1147
+ isConnecting: false,
1148
+ isDisconnected: false,
1149
+ isReconnecting: true,
1150
+ status
1151
+ };
1152
+ case "connecting":
1153
+ return {
1154
+ address,
1155
+ addresses,
1156
+ chain,
1157
+ chainId: connection?.chainId,
1158
+ connector: connection?.connector,
1159
+ isConnected: false,
1160
+ isConnecting: true,
1161
+ isDisconnected: false,
1162
+ isReconnecting: false,
1163
+ status
1164
+ };
1165
+ case "disconnected":
1166
+ return {
1167
+ address: void 0,
1168
+ addresses: void 0,
1169
+ chain: void 0,
1170
+ chainId: void 0,
1171
+ connector: void 0,
1172
+ isConnected: false,
1173
+ isConnecting: false,
1174
+ isDisconnected: true,
1175
+ isReconnecting: false,
1176
+ status
1177
+ };
1178
+ }
1179
+ }
1180
+ async function getWalletClient(config, parameters = {}) {
1181
+ const client = await getConnectorClient(config, parameters);
1182
+ return client.extend(viem.walletActions);
821
1183
  }
822
- function resolveRootDomainFromHostname(hostname) {
823
- const trimmedHostname = hostname.trim().toLowerCase();
824
- if (!trimmedHostname) {
825
- return "localhost";
826
- }
827
- if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
828
- return trimmedHostname;
829
- }
830
- const parts = trimmedHostname.split(".").filter(Boolean);
831
- if (parts.length < 2) {
832
- return trimmedHostname;
1184
+ async function waitForTransactionReceipt(config, parameters) {
1185
+ const { chainId, timeout = 0, ...rest } = parameters;
1186
+ const client = config.getClient({ chainId });
1187
+ const action = getAction(client, actions.waitForTransactionReceipt, "waitForTransactionReceipt");
1188
+ const receipt = await action({ ...rest, timeout });
1189
+ if (receipt.status === "reverted") {
1190
+ const action_getTransaction = getAction(client, actions.getTransaction, "getTransaction");
1191
+ const { from: account, ...txn } = await action_getTransaction({
1192
+ hash: receipt.transactionHash
1193
+ });
1194
+ const action_call = getAction(client, actions.call, "call");
1195
+ const code = await action_call({
1196
+ ...txn,
1197
+ account,
1198
+ data: txn.input,
1199
+ gasPrice: txn.type !== "eip1559" ? txn.gasPrice : void 0,
1200
+ maxFeePerGas: txn.type === "eip1559" ? txn.maxFeePerGas : void 0,
1201
+ maxPriorityFeePerGas: txn.type === "eip1559" ? txn.maxPriorityFeePerGas : void 0
1202
+ });
1203
+ const reason = code?.data ? viem.hexToString(`0x${code.data.substring(138)}`) : "unknown reason";
1204
+ throw new Error(reason);
833
1205
  }
834
- return parts.slice(-2).join(".");
1206
+ return {
1207
+ ...receipt,
1208
+ chainId: client.chain.id
1209
+ };
835
1210
  }
836
1211
  var ERC_6492_MAGIC_SUFFIX = "6492649264926492649264926492649264926492649264926492649264926492";
837
1212
  function normalizeSignature(sig) {
@@ -881,232 +1256,22 @@ function normalizeSignature(sig) {
881
1256
  );
882
1257
  }
883
1258
 
884
- // src/transferPolling.ts
885
- async function pollTransferTick(params) {
886
- const fetchTransfer2 = params.fetchTransfer ?? fetchTransfer;
887
- const token = await params.getAccessToken();
888
- if (!token) {
889
- return { kind: "retry" };
890
- }
891
- try {
892
- const transfer = await fetchTransfer2(params.apiBaseUrl, token, params.transferId);
893
- return { kind: "success", transfer };
894
- } catch (err) {
895
- return {
896
- kind: "error",
897
- message: err instanceof Error ? err.message : "Polling error"
898
- };
899
- }
900
- }
901
-
902
- // src/passkey-delegation.ts
903
- var PasskeyIframeBlockedError = class extends Error {
904
- constructor(message = "Passkey creation is not supported in this browser context.") {
905
- super(message);
906
- this.name = "PasskeyIframeBlockedError";
907
- }
908
- };
909
- function isInCrossOriginIframe() {
910
- if (typeof window === "undefined") return false;
911
- if (window.parent === window) return false;
912
- try {
913
- void window.parent.location.origin;
914
- return false;
915
- } catch {
916
- return true;
917
- }
918
- }
919
- function isSafari() {
920
- if (typeof navigator === "undefined") return false;
921
- const ua = navigator.userAgent;
922
- return /Safari/i.test(ua) && !/Chrome|CriOS|Chromium|Edg|OPR|Firefox/i.test(ua);
923
- }
924
- var POPUP_RESULT_TIMEOUT_MS = 12e4;
925
- var POPUP_CLOSED_POLL_MS = 500;
926
- var POPUP_CLOSED_GRACE_MS = 1e3;
927
- function createPasskeyViaPopup(options) {
928
- return new Promise((resolve, reject) => {
929
- const verificationToken = crypto.randomUUID();
930
- const payload = { ...options, verificationToken };
931
- const encoded = btoa(JSON.stringify(payload));
932
- const popupUrl = `${window.location.origin}/passkey-register#${encoded}`;
933
- const popup = window.open(popupUrl, "blink-passkey");
934
- if (!popup) {
935
- reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
936
- return;
937
- }
938
- let settled = false;
939
- const timer = setTimeout(() => {
940
- cleanup();
941
- reject(new Error("Passkey creation timed out. Please try again."));
942
- }, POPUP_RESULT_TIMEOUT_MS);
943
- const closedPoll = setInterval(() => {
944
- if (popup.closed) {
945
- clearInterval(closedPoll);
946
- setTimeout(() => {
947
- if (!settled) {
948
- settled = true;
949
- cleanup();
950
- checkServerForPasskeyByToken(
951
- options.authToken,
952
- options.apiBaseUrl,
953
- verificationToken
954
- ).then((result) => {
955
- if (result) {
956
- resolve(result);
957
- } else {
958
- reject(new Error("Passkey window was closed before completing."));
959
- }
960
- }).catch(() => {
961
- reject(new Error("Passkey window was closed before completing."));
962
- });
963
- }
964
- }, POPUP_CLOSED_GRACE_MS);
965
- }
966
- }, POPUP_CLOSED_POLL_MS);
967
- function cleanup() {
968
- clearTimeout(timer);
969
- clearInterval(closedPoll);
970
- }
971
- });
972
- }
973
- var VERIFY_POPUP_TIMEOUT_MS = 6e4;
974
- function findDevicePasskeyViaPopup(options) {
975
- return new Promise((resolve, reject) => {
976
- const verificationToken = crypto.randomUUID();
977
- const payload = {
978
- ...options,
979
- verificationToken
980
- };
981
- const encoded = btoa(JSON.stringify(payload));
982
- const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
983
- const popup = window.open(popupUrl, "blink-passkey-verify");
984
- if (!popup) {
985
- reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
986
- return;
987
- }
988
- let settled = false;
989
- const timer = setTimeout(() => {
990
- cleanup();
991
- resolve(null);
992
- }, VERIFY_POPUP_TIMEOUT_MS);
993
- const closedPoll = setInterval(() => {
994
- if (popup.closed && !settled) {
995
- clearInterval(closedPoll);
996
- setTimeout(() => {
997
- if (!settled) {
998
- settled = true;
999
- cleanup();
1000
- checkServerForPasskeyByToken(
1001
- options.authToken,
1002
- options.apiBaseUrl,
1003
- verificationToken
1004
- ).then((result) => {
1005
- resolve(result?.credentialId ?? null);
1006
- }).catch(() => {
1007
- resolve(null);
1008
- });
1009
- }
1010
- }, POPUP_CLOSED_GRACE_MS);
1011
- }
1012
- }, POPUP_CLOSED_POLL_MS);
1013
- function cleanup() {
1014
- clearTimeout(timer);
1015
- clearInterval(closedPoll);
1016
- }
1017
- });
1018
- }
1019
- async function checkServerForPasskeyByToken(authToken, apiBaseUrl, verificationToken) {
1020
- if (!authToken || !apiBaseUrl) return null;
1021
- const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
1022
- headers: { Authorization: `Bearer ${authToken}` }
1023
- });
1024
- if (!res.ok) return null;
1025
- const body = await res.json();
1026
- const passkeys = body.config.passkeys ?? [];
1027
- const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
1028
- return matched ? { credentialId: matched.credentialId, publicKey: matched.publicKey } : null;
1029
- }
1030
-
1031
- // src/hooks.ts
1259
+ // src/hooks/authorizationExecutor.ts
1032
1260
  var WALLET_CLIENT_MAX_ATTEMPTS = 25;
1033
1261
  var WALLET_CLIENT_POLL_MS = 400;
1034
1262
  var ACTION_POLL_INTERVAL_MS = 500;
1035
1263
  var ACTION_POLL_MAX_RETRIES = 20;
1036
1264
  var SIGN_PERMIT2_POLL_MS = 1e3;
1037
1265
  var SIGN_PERMIT2_MAX_POLLS = 15;
1038
- var TRANSFER_SIGN_MAX_POLLS = 60;
1039
- function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
1040
- return new Promise((resolve, reject) => {
1041
- if (typeof document === "undefined") {
1042
- resolve();
1043
- return;
1044
- }
1045
- if (document.hasFocus()) {
1046
- resolve();
1047
- return;
1048
- }
1049
- const deadline = Date.now() + timeoutMs;
1050
- const timer = setInterval(() => {
1051
- if (document.hasFocus()) {
1052
- clearInterval(timer);
1053
- resolve();
1054
- } else if (Date.now() >= deadline) {
1055
- clearInterval(timer);
1056
- resolve();
1057
- }
1058
- }, intervalMs);
1059
- });
1060
- }
1061
1266
  function actionSuccess(action, message, data) {
1062
1267
  return { actionId: action.id, type: action.type, status: "success", message, data };
1063
1268
  }
1064
- function actionError(action, message) {
1065
- return { actionId: action.id, type: action.type, status: "error", message };
1066
- }
1067
- function isUserRejection(msg) {
1068
- const lower = msg.toLowerCase();
1069
- return lower.includes("rejected") || lower.includes("denied");
1070
- }
1071
- function hexToBytes(hex) {
1072
- const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1073
- const bytes = clean.match(/.{1,2}/g).map((b) => parseInt(b, 16));
1074
- return new Uint8Array(bytes);
1075
- }
1076
- function toBase64(buffer) {
1077
- return btoa(String.fromCharCode(...new Uint8Array(buffer)));
1078
- }
1079
- function base64ToBytes(value) {
1080
- const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
1081
- const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
1082
- const raw = atob(padded);
1083
- const bytes = new Uint8Array(raw.length);
1084
- for (let i = 0; i < raw.length; i++) {
1085
- bytes[i] = raw.charCodeAt(i);
1086
- }
1087
- return bytes;
1088
- }
1089
- function readEnvValue(name) {
1090
- const meta = ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) });
1091
- const metaValue = meta.env?.[name];
1092
- if (typeof metaValue === "string" && metaValue.trim().length > 0) {
1093
- return metaValue.trim();
1094
- }
1095
- const processValue = globalThis.process?.env?.[name];
1096
- if (typeof processValue === "string" && processValue.trim().length > 0) {
1097
- return processValue.trim();
1098
- }
1099
- return void 0;
1269
+ function actionError(action, message) {
1270
+ return { actionId: action.id, type: action.type, status: "error", message };
1100
1271
  }
1101
- function resolvePasskeyRpId() {
1102
- const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
1103
- if (configuredDomain) {
1104
- return normalizeConfiguredDomain(configuredDomain);
1105
- }
1106
- if (typeof window !== "undefined") {
1107
- return resolveRootDomainFromHostname(window.location.hostname);
1108
- }
1109
- return "localhost";
1272
+ function isUserRejection(msg) {
1273
+ const lower = msg.toLowerCase();
1274
+ return lower.includes("rejected") || lower.includes("denied");
1110
1275
  }
1111
1276
  async function waitForWalletClient(wagmiConfig2, params = {}) {
1112
1277
  for (let i = 0; i < WALLET_CLIENT_MAX_ATTEMPTS; i++) {
@@ -1147,165 +1312,6 @@ function parseSignTypedDataPayload(typedData) {
1147
1312
  function getPendingActions(session, completedIds) {
1148
1313
  return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
1149
1314
  }
1150
- async function createPasskeyCredential(params) {
1151
- const challenge = new Uint8Array(32);
1152
- crypto.getRandomValues(challenge);
1153
- const rpId = resolvePasskeyRpId();
1154
- const publicKeyOptions = {
1155
- challenge,
1156
- rp: { name: "Blink", id: rpId },
1157
- user: {
1158
- id: new TextEncoder().encode(params.userId),
1159
- name: params.displayName,
1160
- displayName: params.displayName
1161
- },
1162
- pubKeyCredParams: [
1163
- { alg: -7, type: "public-key" },
1164
- { alg: -257, type: "public-key" }
1165
- ],
1166
- authenticatorSelection: {
1167
- authenticatorAttachment: "platform",
1168
- residentKey: "preferred",
1169
- userVerification: "required"
1170
- },
1171
- timeout: 6e4
1172
- };
1173
- if (isInCrossOriginIframe()) {
1174
- try {
1175
- await waitForDocumentFocus();
1176
- const credential2 = await navigator.credentials.create({
1177
- publicKey: publicKeyOptions
1178
- });
1179
- if (!credential2) {
1180
- throw new Error("Passkey creation was cancelled.");
1181
- }
1182
- return extractPasskeyResult(credential2);
1183
- } catch (err) {
1184
- if (err instanceof PasskeyIframeBlockedError) throw err;
1185
- if (err instanceof Error && err.message === "Passkey creation was cancelled.") throw err;
1186
- throw new PasskeyIframeBlockedError();
1187
- }
1188
- }
1189
- await waitForDocumentFocus();
1190
- const credential = await navigator.credentials.create({
1191
- publicKey: publicKeyOptions
1192
- });
1193
- if (!credential) {
1194
- throw new Error("Passkey creation was cancelled.");
1195
- }
1196
- return extractPasskeyResult(credential);
1197
- }
1198
- function extractPasskeyResult(credential) {
1199
- const response = credential.response;
1200
- const publicKeyBytes = response.getPublicKey?.();
1201
- return {
1202
- credentialId: toBase64(credential.rawId),
1203
- publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
1204
- };
1205
- }
1206
- function buildPasskeyPopupOptions(params) {
1207
- const challenge = new Uint8Array(32);
1208
- crypto.getRandomValues(challenge);
1209
- const rpId = resolvePasskeyRpId();
1210
- return {
1211
- challenge: toBase64(challenge),
1212
- rpId,
1213
- rpName: "Blink",
1214
- userId: toBase64(new TextEncoder().encode(params.userId)),
1215
- userName: params.displayName,
1216
- userDisplayName: params.displayName,
1217
- pubKeyCredParams: [
1218
- { alg: -7, type: "public-key" },
1219
- { alg: -257, type: "public-key" }
1220
- ],
1221
- authenticatorSelection: {
1222
- authenticatorAttachment: "platform",
1223
- residentKey: "preferred",
1224
- userVerification: "required"
1225
- },
1226
- timeout: 6e4,
1227
- authToken: params.authToken,
1228
- apiBaseUrl: params.apiBaseUrl
1229
- };
1230
- }
1231
- async function deviceHasPasskey(credentialId) {
1232
- const found = await findDevicePasskey([credentialId]);
1233
- return found != null;
1234
- }
1235
- async function findDevicePasskey(credentialIds) {
1236
- if (credentialIds.length === 0) return null;
1237
- try {
1238
- const challenge = new Uint8Array(32);
1239
- crypto.getRandomValues(challenge);
1240
- await waitForDocumentFocus();
1241
- const assertion = await navigator.credentials.get({
1242
- publicKey: {
1243
- challenge,
1244
- rpId: resolvePasskeyRpId(),
1245
- allowCredentials: credentialIds.map((id) => ({
1246
- type: "public-key",
1247
- id: base64ToBytes(id)
1248
- })),
1249
- userVerification: "discouraged",
1250
- timeout: 3e4
1251
- }
1252
- });
1253
- if (!assertion) return null;
1254
- return toBase64(assertion.rawId);
1255
- } catch {
1256
- return null;
1257
- }
1258
- }
1259
- function useTransferPolling(intervalMs = 3e3) {
1260
- const { apiBaseUrl } = useBlinkConfig();
1261
- const { getAccessToken } = reactAuth.usePrivy();
1262
- const [transfer, setTransfer] = react.useState(null);
1263
- const [error, setError] = react.useState(null);
1264
- const [isPolling, setIsPolling] = react.useState(false);
1265
- const intervalRef = react.useRef(null);
1266
- const transferIdRef = react.useRef(null);
1267
- const stopPolling = react.useCallback(() => {
1268
- if (intervalRef.current) {
1269
- clearInterval(intervalRef.current);
1270
- intervalRef.current = null;
1271
- }
1272
- setIsPolling(false);
1273
- }, []);
1274
- const poll = react.useCallback(async () => {
1275
- if (!transferIdRef.current) return;
1276
- const result = await pollTransferTick({
1277
- apiBaseUrl,
1278
- transferId: transferIdRef.current,
1279
- getAccessToken
1280
- });
1281
- if (result.kind === "retry") {
1282
- return;
1283
- }
1284
- if (result.kind === "error") {
1285
- setError(result.message);
1286
- stopPolling();
1287
- return;
1288
- }
1289
- setError(null);
1290
- setTransfer(result.transfer);
1291
- if (result.transfer.status === "COMPLETED" || result.transfer.status === "FAILED") {
1292
- stopPolling();
1293
- }
1294
- }, [apiBaseUrl, getAccessToken, stopPolling]);
1295
- const startPolling = react.useCallback(
1296
- (transferId) => {
1297
- stopPolling();
1298
- transferIdRef.current = transferId;
1299
- setIsPolling(true);
1300
- setError(null);
1301
- poll();
1302
- intervalRef.current = setInterval(poll, intervalMs);
1303
- },
1304
- [poll, intervalMs, stopPolling]
1305
- );
1306
- react.useEffect(() => () => stopPolling(), [stopPolling]);
1307
- return { transfer, error, isPolling, startPolling, stopPolling };
1308
- }
1309
1315
  async function executeOpenProvider(action, wagmiConfig2, connectors, connectAsync) {
1310
1316
  try {
1311
1317
  const account = getAccount(wagmiConfig2);
@@ -1702,6 +1708,47 @@ function useAuthorizationExecutor(options) {
1702
1708
  executeSessionById
1703
1709
  };
1704
1710
  }
1711
+ var TRANSFER_SIGN_MAX_POLLS = 60;
1712
+ function waitForDocumentFocus2(timeoutMs = 5e3, intervalMs = 100) {
1713
+ return new Promise((resolve, reject) => {
1714
+ if (typeof document === "undefined") {
1715
+ resolve();
1716
+ return;
1717
+ }
1718
+ if (document.hasFocus()) {
1719
+ resolve();
1720
+ return;
1721
+ }
1722
+ const deadline = Date.now() + timeoutMs;
1723
+ const timer = setInterval(() => {
1724
+ if (document.hasFocus()) {
1725
+ clearInterval(timer);
1726
+ resolve();
1727
+ } else if (Date.now() >= deadline) {
1728
+ clearInterval(timer);
1729
+ resolve();
1730
+ }
1731
+ }, intervalMs);
1732
+ });
1733
+ }
1734
+ function hexToBytes(hex) {
1735
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1736
+ const bytes = clean.match(/.{1,2}/g).map((b) => parseInt(b, 16));
1737
+ return new Uint8Array(bytes);
1738
+ }
1739
+ function toBase642(buffer) {
1740
+ return btoa(String.fromCharCode(...new Uint8Array(buffer)));
1741
+ }
1742
+ function base64ToBytes2(value) {
1743
+ const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
1744
+ const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
1745
+ const raw = atob(padded);
1746
+ const bytes = new Uint8Array(raw.length);
1747
+ for (let i = 0; i < raw.length; i++) {
1748
+ bytes[i] = raw.charCodeAt(i);
1749
+ }
1750
+ return bytes;
1751
+ }
1705
1752
  function useTransferSigning(pollIntervalMs = 2e3, options) {
1706
1753
  const blinkConfig = useOptionalBlinkConfig();
1707
1754
  const apiBaseUrl = options?.apiBaseUrl ?? blinkConfig?.apiBaseUrl;
@@ -1757,9 +1804,9 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
1757
1804
  let signedUserOp;
1758
1805
  const allowCredentials = payload.passkeyCredentialId ? [{
1759
1806
  type: "public-key",
1760
- id: base64ToBytes(payload.passkeyCredentialId)
1807
+ id: base64ToBytes2(payload.passkeyCredentialId)
1761
1808
  }] : void 0;
1762
- await waitForDocumentFocus();
1809
+ await waitForDocumentFocus2();
1763
1810
  const assertion = await navigator.credentials.get({
1764
1811
  publicKey: {
1765
1812
  challenge: hashBytes,
@@ -1775,10 +1822,10 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
1775
1822
  const response = assertion.response;
1776
1823
  signedUserOp = {
1777
1824
  ...payload.userOp,
1778
- credentialId: toBase64(assertion.rawId),
1779
- signature: toBase64(response.signature),
1780
- authenticatorData: toBase64(response.authenticatorData),
1781
- clientDataJSON: toBase64(response.clientDataJSON)
1825
+ credentialId: toBase642(assertion.rawId),
1826
+ signature: toBase642(response.signature),
1827
+ authenticatorData: toBase642(response.authenticatorData),
1828
+ clientDataJSON: toBase642(response.clientDataJSON)
1782
1829
  };
1783
1830
  return await signTransfer(
1784
1831
  apiBaseUrl,
@@ -1937,6 +1984,87 @@ function buildSelectSourceChoices(options) {
1937
1984
  })).filter((chain) => chain.tokens.length > 0).sort((a, b) => b.balance - a.balance);
1938
1985
  }
1939
1986
 
1987
+ // src/walletFlow.ts
1988
+ var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
1989
+ function isMobileUserAgent(userAgent) {
1990
+ if (!userAgent) {
1991
+ return false;
1992
+ }
1993
+ return MOBILE_USER_AGENT_PATTERN.test(userAgent);
1994
+ }
1995
+ function shouldUseWalletConnector(options) {
1996
+ return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
1997
+ }
1998
+
1999
+ // src/paymentResolvePhase.ts
2000
+ function hasActiveWallet(accounts) {
2001
+ return accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
2002
+ }
2003
+ function isTransferInFlight(transfer) {
2004
+ if (!transfer) return false;
2005
+ return ["CREATED", "SENDING", "SENT"].includes(transfer.status);
2006
+ }
2007
+ function resolvePhase(state) {
2008
+ const p = state.phase;
2009
+ if (p.step === "select-source" && state.transfer?.status === "COMPLETED" && state.guestPreauthorizing) {
2010
+ return p;
2011
+ }
2012
+ if (state.transfer?.status === "COMPLETED" && state.guestPreauthorizing) {
2013
+ return { step: "processing", transfer: state.transfer };
2014
+ }
2015
+ if (state.transfer?.status === "COMPLETED") {
2016
+ return { step: "completed", transfer: state.transfer };
2017
+ }
2018
+ if (state.transfer?.status === "FAILED") {
2019
+ return { step: "failed", transfer: state.transfer, error: state.error ?? "Transfer failed." };
2020
+ }
2021
+ if (state.creatingTransfer || isTransferInFlight(state.transfer)) {
2022
+ return { step: "processing", transfer: state.transfer };
2023
+ }
2024
+ if (p.step === "token-picker" || p.step === "one-tap-setup" || p.step === "select-source" || p.step === "confirm-sign" || p.step === "guest-token-picker") {
2025
+ return p;
2026
+ }
2027
+ if (p.step === "wallet-setup") return p;
2028
+ if (state.mobileFlow && state.deeplinkUri) {
2029
+ return {
2030
+ step: "wallet-setup",
2031
+ mobile: { deeplinkUri: state.deeplinkUri, providerId: state.selectedProviderId },
2032
+ accountId: null
2033
+ };
2034
+ }
2035
+ if (!state.activeCredentialId && !state.passkeyConfigLoaded) {
2036
+ return { step: "initializing" };
2037
+ }
2038
+ if (state.verificationTarget) {
2039
+ return { step: "otp-verify", target: state.verificationTarget };
2040
+ }
2041
+ if (state.loginRequested) {
2042
+ return { step: "login" };
2043
+ }
2044
+ if (state.passkeyConfigLoaded && !state.activeCredentialId) {
2045
+ if (state.knownCredentialIds.length > 0 && state.passkeyPopupNeeded) {
2046
+ return { step: "passkey-verify" };
2047
+ }
2048
+ return { step: "passkey-create", popupFallback: state.passkeyPopupNeeded };
2049
+ }
2050
+ if (state.loadingData && state.activeCredentialId && hasActiveWallet(state.accounts)) {
2051
+ return { step: "data-loading" };
2052
+ }
2053
+ if (state.isGuestFlow && state.selectedProviderId != null && !state.transfer) {
2054
+ return { step: "guest-token-picker" };
2055
+ }
2056
+ if (state.activeCredentialId && !hasActiveWallet(state.accounts) && !state.mobileFlow) {
2057
+ return { step: "wallet-picker", reason: "link" };
2058
+ }
2059
+ if (state.activeCredentialId && hasActiveWallet(state.accounts) && !state.loadingData) {
2060
+ return { step: "deposit" };
2061
+ }
2062
+ if (state.isGuestFlow) {
2063
+ return { step: "wallet-picker", reason: "guest-entry" };
2064
+ }
2065
+ return { step: "wallet-picker", reason: "entry" };
2066
+ }
2067
+
1940
2068
  // src/paymentReducer.ts
1941
2069
  function deriveSourceTypeAndId(state) {
1942
2070
  if (state.selectedWalletId) {
@@ -1949,6 +2077,7 @@ function deriveSourceTypeAndId(state) {
1949
2077
  }
1950
2078
  function createInitialState(config) {
1951
2079
  return {
2080
+ phase: { step: "initializing" },
1952
2081
  error: null,
1953
2082
  providers: [],
1954
2083
  accounts: [],
@@ -1969,6 +2098,7 @@ function createInitialState(config) {
1969
2098
  knownCredentialIds: [],
1970
2099
  verificationTarget: null,
1971
2100
  oneTapLimit: 100,
2101
+ oneTapLimitSavedDuringSetup: false,
1972
2102
  mobileFlow: false,
1973
2103
  deeplinkUri: null,
1974
2104
  increasingLimit: false,
@@ -1976,12 +2106,17 @@ function createInitialState(config) {
1976
2106
  guestTransferId: null,
1977
2107
  guestSessionToken: null,
1978
2108
  guestPreauthAccountId: null,
2109
+ guestPreauthSessionId: null,
1979
2110
  activePublicKey: null,
1980
- userIntent: null,
1981
- loginRequested: false
2111
+ loginRequested: false,
2112
+ guestPreauthorizing: false
1982
2113
  };
1983
2114
  }
1984
2115
  function paymentReducer(state, action) {
2116
+ const next = applyAction(state, action);
2117
+ return { ...next, phase: resolvePhase(next) };
2118
+ }
2119
+ function applyAction(state, action) {
1985
2120
  switch (action.type) {
1986
2121
  // ── Auth ──────────────────────────────────────────────────────
1987
2122
  case "CODE_SENT":
@@ -2059,23 +2194,20 @@ function paymentReducer(state, action) {
2059
2194
  selectedProviderId: action.providerId,
2060
2195
  selectedAccountId: null,
2061
2196
  selectedWalletId: null,
2062
- selectedTokenSymbol: null,
2063
- userIntent: null
2197
+ selectedTokenSymbol: null
2064
2198
  };
2065
2199
  case "SELECT_ACCOUNT":
2066
2200
  return {
2067
2201
  ...state,
2068
2202
  selectedAccountId: action.accountId,
2069
2203
  selectedWalletId: action.walletId,
2070
- selectedTokenSymbol: null,
2071
- userIntent: null
2204
+ selectedTokenSymbol: null
2072
2205
  };
2073
2206
  case "SELECT_TOKEN":
2074
2207
  return {
2075
2208
  ...state,
2076
2209
  selectedWalletId: action.walletId,
2077
- selectedTokenSymbol: action.tokenSymbol,
2078
- userIntent: null
2210
+ selectedTokenSymbol: action.tokenSymbol
2079
2211
  };
2080
2212
  // ── Transfer lifecycle ───────────────────────────────────────
2081
2213
  case "PAY_STARTED":
@@ -2084,8 +2216,7 @@ function paymentReducer(state, action) {
2084
2216
  error: null,
2085
2217
  creatingTransfer: true,
2086
2218
  deeplinkUri: null,
2087
- mobileFlow: false,
2088
- userIntent: null
2219
+ mobileFlow: false
2089
2220
  };
2090
2221
  case "PAY_ENDED":
2091
2222
  return { ...state, creatingTransfer: false };
@@ -2149,7 +2280,8 @@ function paymentReducer(state, action) {
2149
2280
  transfer: action.transfer,
2150
2281
  error: null,
2151
2282
  mobileFlow: false,
2152
- deeplinkUri: null
2283
+ deeplinkUri: null,
2284
+ phase: { step: "confirm-sign", transfer: action.transfer }
2153
2285
  };
2154
2286
  case "CLEAR_MOBILE_STATE":
2155
2287
  return { ...state, mobileFlow: false, deeplinkUri: null };
@@ -2219,29 +2351,37 @@ function paymentReducer(state, action) {
2219
2351
  case "GUEST_PREAUTH_DETECTED":
2220
2352
  return {
2221
2353
  ...state,
2222
- guestPreauthAccountId: action.accountId
2354
+ guestPreauthAccountId: action.accountId,
2355
+ guestPreauthSessionId: action.sessionId ?? state.guestPreauthSessionId
2223
2356
  };
2357
+ case "GUEST_PREAUTH_BEGIN":
2358
+ return { ...state, guestPreauthorizing: true, error: null };
2359
+ case "GUEST_PREAUTH_END":
2360
+ return { ...state, guestPreauthorizing: false };
2224
2361
  case "ACCOUNT_OWNER_SET":
2225
2362
  return {
2226
2363
  ...state,
2227
2364
  guestPreauthAccountId: null,
2365
+ guestPreauthSessionId: null,
2228
2366
  activePublicKey: null,
2229
2367
  error: null,
2230
- userIntent: "configure-one-tap"
2368
+ guestPreauthorizing: false,
2369
+ phase: { step: "one-tap-setup", action: null }
2231
2370
  };
2232
2371
  // ── User intent & error ──────────────────────────────────────
2233
2372
  case "SET_USER_INTENT":
2234
- return { ...state, userIntent: action.intent };
2373
+ return { ...state, phase: action.intent };
2235
2374
  case "REQUEST_LOGIN":
2236
2375
  return {
2237
2376
  ...state,
2238
2377
  loginRequested: true,
2239
2378
  transfer: null,
2240
- isGuestFlow: false,
2241
2379
  creatingTransfer: false
2242
2380
  };
2243
2381
  case "SET_ERROR":
2244
2382
  return { ...state, error: action.error };
2383
+ case "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP":
2384
+ return { ...state, oneTapLimitSavedDuringSetup: action.saved };
2245
2385
  // ── Lifecycle ────────────────────────────────────────────────
2246
2386
  case "NEW_PAYMENT":
2247
2387
  return {
@@ -2258,9 +2398,11 @@ function paymentReducer(state, action) {
2258
2398
  guestTransferId: null,
2259
2399
  guestSessionToken: null,
2260
2400
  guestPreauthAccountId: null,
2401
+ guestPreauthSessionId: null,
2261
2402
  activePublicKey: null,
2262
- userIntent: null,
2263
- loginRequested: false
2403
+ loginRequested: false,
2404
+ oneTapLimitSavedDuringSetup: false,
2405
+ guestPreauthorizing: false
2264
2406
  };
2265
2407
  case "LOGOUT":
2266
2408
  return {
@@ -2317,97 +2459,43 @@ function maskAuthIdentifier(identifier) {
2317
2459
  return `***-***-${visibleSuffix}`;
2318
2460
  }
2319
2461
 
2320
- // src/walletFlow.ts
2321
- var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
2322
- function isMobileUserAgent(userAgent) {
2323
- if (!userAgent) {
2324
- return false;
2325
- }
2326
- return MOBILE_USER_AGENT_PATTERN.test(userAgent);
2327
- }
2328
- function shouldUseWalletConnector(options) {
2329
- return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
2330
- }
2331
-
2332
2462
  // src/resolveScreen.ts
2333
- function hasActiveWallet(accounts) {
2334
- return accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
2335
- }
2336
- function isTransferTerminal(transfer) {
2337
- return transfer?.status === "COMPLETED" || transfer?.status === "FAILED";
2338
- }
2339
- function isTransferInFlight(transfer) {
2340
- if (!transfer) return false;
2341
- return ["CREATED", "SENDING", "SENT"].includes(transfer.status);
2342
- }
2343
- function isSetupTransfer(transfer) {
2344
- if (!transfer) return false;
2345
- return transfer.sources?.some(
2346
- (s) => s.wallets && Array.isArray(s.wallets) && s.wallets.length === 0
2347
- ) ?? false;
2348
- }
2349
- function resolveScreen(state) {
2350
- if (!state.privyReady) {
2351
- return "loading";
2352
- }
2353
- if (state.authenticated && !state.activeCredentialId && !state.passkeyConfigLoaded) {
2354
- return "loading";
2355
- }
2356
- if (!state.authenticated && !state.verificationTarget && !state.isGuestFlow && (state.isReturningUser || state.guestPreauthRedirect || state.loginRequested)) {
2357
- return "login";
2358
- }
2359
- if (!state.authenticated && state.verificationTarget != null) {
2360
- return "otp-verify";
2361
- }
2362
- if (state.authenticated && !state.activeCredentialId && state.passkeyConfigLoaded && (state.knownCredentialIds.length === 0 || !state.passkeyPopupNeeded)) {
2363
- return "create-passkey";
2364
- }
2365
- if (state.authenticated && !state.activeCredentialId && state.passkeyConfigLoaded && state.knownCredentialIds.length > 0 && state.passkeyPopupNeeded) {
2366
- return "verify-passkey";
2367
- }
2368
- if (isTransferTerminal(state.transfer)) {
2369
- return "success";
2370
- }
2371
- if (state.creatingTransfer || state.transfer != null && isTransferInFlight(state.transfer)) {
2372
- return "processing";
2373
- }
2374
- if (state.transfer?.status === "AUTHORIZED" && !state.isDesktop && !isSetupTransfer(state.transfer)) {
2375
- return "confirm-sign";
2376
- }
2377
- if (state.pendingSelectSource != null) {
2378
- return state.isDesktop ? "setup" : "select-source";
2379
- }
2380
- if (state.pendingOneTapSetup != null && !state.oneTapLimitAlreadySaved) {
2381
- return "setup";
2382
- }
2383
- if (state.mobileFlow || state.inlineAuthorizationExecuting) {
2384
- return state.isDesktop ? "setup-status" : "open-wallet";
2385
- }
2386
- if (state.isGuestFlow && state.selectedProviderId != null && !state.transfer && !state.guestSettingSender) {
2387
- return "guest-token-picker";
2388
- }
2389
- if (state.activeCredentialId && !hasActiveWallet(state.accounts) && !state.mobileFlow || !state.authenticated && !state.isReturningUser && !state.isGuestFlow) {
2390
- return "wallet-picker";
2391
- }
2392
- if (state.loadingData && state.activeCredentialId != null && hasActiveWallet(state.accounts)) {
2393
- return "loading";
2394
- }
2395
- if (state.userIntent === "pick-token" && state.selectedAccount != null) {
2396
- return "token-picker";
2397
- }
2398
- if (state.userIntent === "configure-one-tap") {
2399
- return "setup";
2400
- }
2401
- if (state.userIntent === "switch-wallet") {
2402
- return "wallet-picker";
2403
- }
2404
- if (state.activeCredentialId != null && hasActiveWallet(state.accounts) && !state.loadingData) {
2405
- return "deposit";
2406
- }
2407
- if (state.isGuestFlow) {
2408
- return "wallet-picker";
2463
+ function screenForPhase(phase) {
2464
+ switch (phase.step) {
2465
+ case "initializing":
2466
+ case "data-loading":
2467
+ return "loading";
2468
+ case "login":
2469
+ return "login";
2470
+ case "otp-verify":
2471
+ return "otp-verify";
2472
+ case "passkey-create":
2473
+ return "create-passkey";
2474
+ case "passkey-verify":
2475
+ return "verify-passkey";
2476
+ case "wallet-picker":
2477
+ return "wallet-picker";
2478
+ case "wallet-setup":
2479
+ return phase.mobile ? "open-wallet" : "setup-status";
2480
+ case "select-source":
2481
+ if (phase.skipOneTapLimit) return "select-source";
2482
+ return phase.isDesktop ? "setup" : "select-source";
2483
+ case "one-tap-setup":
2484
+ return "setup";
2485
+ case "guest-token-picker":
2486
+ return "guest-token-picker";
2487
+ case "token-picker":
2488
+ return "token-picker";
2489
+ case "deposit":
2490
+ return "deposit";
2491
+ case "processing":
2492
+ return "processing";
2493
+ case "confirm-sign":
2494
+ return "confirm-sign";
2495
+ case "completed":
2496
+ case "failed":
2497
+ return "success";
2409
2498
  }
2410
- return "wallet-picker";
2411
2499
  }
2412
2500
  var MUTED = "#7fa4b0";
2413
2501
  var LOGO_SIZE = 48;
@@ -5890,85 +5978,66 @@ var DEPOSIT_SCREENS = /* @__PURE__ */ new Set([
5890
5978
  "processing",
5891
5979
  "success"
5892
5980
  ]);
5893
- function getFlowPhase(screen, userIntent) {
5981
+ function getFlowPhase(screen, phase) {
5894
5982
  if (LINK_SCREENS.has(screen)) return "link";
5895
5983
  if (DEPOSIT_SCREENS.has(screen)) return "deposit";
5896
5984
  if (screen === "token-picker" || screen === "select-source" || screen === "guest-token-picker") {
5897
- return userIntent === "configure-one-tap" ? "link" : "deposit";
5985
+ return phase.step === "one-tap-setup" ? "link" : "deposit";
5898
5986
  }
5899
5987
  return null;
5900
5988
  }
5901
5989
  function StepRenderer(props) {
5902
- const isDesktop = shouldUseWalletConnector({
5903
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
5904
- });
5905
- const isReturningUser = props.state.activeCredentialId != null || typeof window !== "undefined" && window.localStorage.getItem("blink_active_credential") != null;
5906
- const screenState = {
5907
- privyReady: props.ready,
5908
- authenticated: props.authenticated,
5909
- verificationTarget: props.state.verificationTarget,
5910
- activeCredentialId: props.state.activeCredentialId,
5911
- knownCredentialIds: props.state.knownCredentialIds,
5912
- passkeyConfigLoaded: props.state.passkeyConfigLoaded,
5913
- passkeyPopupNeeded: props.state.passkeyPopupNeeded,
5914
- accounts: props.state.accounts,
5915
- isGuestFlow: props.state.isGuestFlow,
5916
- selectedProviderId: props.state.selectedProviderId,
5917
- mobileFlow: props.state.mobileFlow,
5918
- inlineAuthorizationExecuting: props.inlineAuthorizationExecuting,
5919
- creatingTransfer: props.state.creatingTransfer,
5920
- transfer: props.state.transfer,
5921
- pendingSelectSource: props.pendingSelectSource,
5922
- pendingOneTapSetup: props.pendingOneTapSetup,
5923
- oneTapLimitAlreadySaved: props.oneTapLimitAlreadySaved,
5924
- loadingData: props.state.loadingData,
5925
- isDesktop,
5926
- isReturningUser,
5927
- guestPreauthRedirect: props.state.guestPreauthAccountId != null,
5928
- loginRequested: props.state.loginRequested,
5929
- userIntent: props.state.userIntent,
5930
- selectedAccount: props.selectedAccount,
5931
- guestSettingSender: props.guestSettingSender
5932
- };
5933
- const screen = resolveScreen(screenState);
5934
- const phase = getFlowPhase(screen, props.state.userIntent);
5935
- return /* @__PURE__ */ jsxRuntime.jsx(FlowPhaseProvider, { phase, children: /* @__PURE__ */ jsxRuntime.jsx(StepRendererContent, { ...props, screen, isDesktop }) });
5990
+ const screen = screenForPhase(props.flow.state.phase);
5991
+ const flowPhase = getFlowPhase(screen, props.flow.state.phase);
5992
+ return /* @__PURE__ */ jsxRuntime.jsx(FlowPhaseProvider, { phase: flowPhase, children: /* @__PURE__ */ jsxRuntime.jsx(StepRendererContent, { ...props, screen }) });
5936
5993
  }
5937
5994
  function StepRendererContent({
5938
- state,
5939
- authenticated,
5940
- activeOtpStatus,
5941
- pollingTransfer,
5942
- pollingError,
5943
- authExecutorError,
5944
- transferSigningSigning,
5945
- transferSigningError,
5946
- pendingConnections,
5947
- depositEligibleAccounts,
5948
- sourceName,
5949
- maxSourceBalance,
5950
- tokenCount,
5951
- selectedAccount,
5952
- selectedSource,
5953
- selectSourceChoices,
5954
- selectSourceRecommended,
5955
- selectSourceAvailableBalance,
5956
- guestTokenEntries,
5957
- guestLoadingBalances,
5958
- guestSettingSender,
5959
- authInput,
5960
- otpCode,
5961
- selectSourceChainName,
5962
- selectSourceTokenSymbol,
5963
- savingOneTapLimit,
5964
- merchantName,
5965
- onBack,
5966
- onDismiss,
5967
- depositAmount,
5995
+ flow,
5996
+ remote,
5997
+ derived,
5998
+ forms,
5968
5999
  handlers,
5969
- screen,
5970
- isDesktop
6000
+ screen
5971
6001
  }) {
6002
+ const {
6003
+ state,
6004
+ authenticated,
6005
+ activeOtpStatus,
6006
+ isDesktop,
6007
+ merchantName,
6008
+ onBack,
6009
+ onDismiss,
6010
+ depositAmount
6011
+ } = flow;
6012
+ const {
6013
+ pollingTransfer,
6014
+ pollingError,
6015
+ authExecutorError,
6016
+ transferSigningSigning,
6017
+ transferSigningError
6018
+ } = remote;
6019
+ const {
6020
+ pendingConnections,
6021
+ depositEligibleAccounts,
6022
+ sourceName,
6023
+ maxSourceBalance,
6024
+ tokenCount,
6025
+ selectedAccount,
6026
+ selectedSource,
6027
+ selectSourceChoices,
6028
+ selectSourceRecommended,
6029
+ selectSourceAvailableBalance
6030
+ } = derived;
6031
+ const {
6032
+ guestTokenEntries,
6033
+ guestLoadingBalances,
6034
+ guestSettingSender,
6035
+ authInput,
6036
+ otpCode,
6037
+ selectSourceChainName,
6038
+ selectSourceTokenSymbol,
6039
+ savingOneTapLimit
6040
+ } = forms;
5972
6041
  const selectedWallet = selectedAccount?.wallets.find((w) => w.id === state.selectedWalletId);
5973
6042
  const selectedSourceLabel = selectedSource && selectedWallet ? `${selectedSource.token.symbol} on ${selectedWallet.chain.name}` : void 0;
5974
6043
  switch (screen) {
@@ -6041,7 +6110,7 @@ function StepRendererContent({
6041
6110
  onPrepareProvider: handlers.onPrepareProvider,
6042
6111
  onSelectProvider: handlers.onSelectProvider,
6043
6112
  onContinueConnection: handlers.onContinueConnection,
6044
- onBack: isEntryPoint ? onBack : () => handlers.onSetUserIntent(null),
6113
+ onBack: isEntryPoint ? onBack : () => handlers.onSetPhase({ step: "deposit" }),
6045
6114
  onLogout: authenticated ? handlers.onLogout : void 0,
6046
6115
  onLogin: handlers.onLogin,
6047
6116
  showLoginOption: isEntryPoint
@@ -6072,7 +6141,7 @@ function StepRendererContent({
6072
6141
  limit: state.oneTapLimit,
6073
6142
  tokensApproved: 0,
6074
6143
  merchantName,
6075
- onContinue: () => handlers.onSetUserIntent("configure-one-tap"),
6144
+ onContinue: () => handlers.onSetPhase({ step: "one-tap-setup", action: null }),
6076
6145
  onLogout: handlers.onLogout,
6077
6146
  error: state.error || authExecutorError
6078
6147
  }
@@ -6091,7 +6160,7 @@ function StepRendererContent({
6091
6160
  tokenCount: effectiveTokenCount,
6092
6161
  sourceName,
6093
6162
  onSetupOneTap: handlers.onSetupOneTap,
6094
- onBack: () => handlers.onSetUserIntent(null),
6163
+ onBack: () => handlers.onSetPhase({ step: "deposit" }),
6095
6164
  onLogout: handlers.onLogout,
6096
6165
  onAdvanced: handlers.onSelectToken,
6097
6166
  selectedSourceLabel: effectiveSourceLabel,
@@ -6127,7 +6196,7 @@ function StepRendererContent({
6127
6196
  processing: state.creatingTransfer,
6128
6197
  error: state.error,
6129
6198
  onDeposit: handlers.onPay,
6130
- onSwitchWallet: () => handlers.onSetUserIntent("switch-wallet"),
6199
+ onSwitchWallet: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
6131
6200
  onBack: onBack ?? (() => handlers.onLogout()),
6132
6201
  onLogout: handlers.onLogout,
6133
6202
  onIncreaseLimit: handlers.onIncreaseLimit,
@@ -6136,7 +6205,7 @@ function StepRendererContent({
6136
6205
  selectedAccountId: state.selectedAccountId,
6137
6206
  onSelectAccount: handlers.onSelectAccount,
6138
6207
  onAuthorizeAccount: handlers.onContinueConnection,
6139
- onAddProvider: () => handlers.onSetUserIntent("switch-wallet"),
6208
+ onAddProvider: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
6140
6209
  onSelectToken: handlers.onSelectToken,
6141
6210
  selectedSourceLabel,
6142
6211
  selectedTokenSymbol: selectedSource?.token.symbol
@@ -6154,7 +6223,7 @@ function StepRendererContent({
6154
6223
  chains: state.chains,
6155
6224
  onSelectAuthorized: handlers.onSelectAuthorizedToken,
6156
6225
  onAuthorizeToken: handlers.onAuthorizeToken,
6157
- onBack: () => handlers.onSetUserIntent(null),
6226
+ onBack: () => handlers.onSetPhase({ step: "deposit" }),
6158
6227
  onLogout: handlers.onLogout,
6159
6228
  depositAmount: depositAmount ?? void 0,
6160
6229
  selectedTokenSymbol: selectedSource?.token.symbol,
@@ -6172,7 +6241,7 @@ function StepRendererContent({
6172
6241
  depositAmount: depositAmount ?? void 0,
6173
6242
  error: state.error,
6174
6243
  onSelect: handlers.onSelectGuestToken,
6175
- onBack: () => handlers.onSetUserIntent(null)
6244
+ onBack: () => handlers.onSetPhase({ step: "wallet-picker", reason: "guest-entry" })
6176
6245
  }
6177
6246
  );
6178
6247
  case "processing": {
@@ -6311,56 +6380,41 @@ var buttonStyle3 = {
6311
6380
  fontFamily: "inherit",
6312
6381
  cursor: "pointer"
6313
6382
  };
6314
- function useDerivedState(state) {
6383
+ function selectedSourceForWallet(selectedWallet, selectedTokenSymbol) {
6384
+ if (!selectedWallet) return null;
6385
+ if (selectedTokenSymbol) {
6386
+ return selectedWallet.sources.find((s) => s.token.symbol === selectedTokenSymbol) ?? null;
6387
+ }
6388
+ return selectedWallet.sources.find((s) => s.token.status === "AUTHORIZED") ?? selectedWallet.sources[0] ?? null;
6389
+ }
6390
+ function computeDerivedState(state) {
6315
6391
  const { sourceType, sourceId } = deriveSourceTypeAndId(state);
6316
6392
  const selectedAccount = state.accounts.find((a) => a.id === state.selectedAccountId);
6317
6393
  const selectedWallet = selectedAccount?.wallets.find(
6318
6394
  (w) => w.id === state.selectedWalletId
6319
6395
  );
6320
- const selectedSource = react.useMemo(() => {
6321
- if (!selectedWallet) return null;
6322
- if (state.selectedTokenSymbol) {
6323
- return selectedWallet.sources.find(
6324
- (s) => s.token.symbol === state.selectedTokenSymbol
6325
- ) ?? null;
6326
- }
6327
- return selectedWallet.sources.find((s) => s.token.status === "AUTHORIZED") ?? selectedWallet.sources[0] ?? null;
6328
- }, [selectedWallet, state.selectedTokenSymbol]);
6396
+ const selectedSource = selectedSourceForWallet(selectedWallet, state.selectedTokenSymbol);
6329
6397
  const sourceName = selectedAccount?.name ?? selectedWallet?.chain.name ?? "Wallet";
6330
- const pendingConnections = react.useMemo(
6331
- () => state.accounts.filter(
6332
- (a) => a.wallets.length > 0 && !a.wallets.some((w) => w.status === "ACTIVE")
6333
- ),
6334
- [state.accounts]
6398
+ const pendingConnections = state.accounts.filter(
6399
+ (a) => a.wallets.length > 0 && !a.wallets.some((w) => w.status === "ACTIVE")
6335
6400
  );
6336
- const depositEligibleAccounts = react.useMemo(
6337
- () => getDepositEligibleAccounts(state.accounts),
6338
- [state.accounts]
6339
- );
6340
- const maxSourceBalance = react.useMemo(() => {
6341
- let max = 0;
6342
- for (const acct of state.accounts) {
6343
- for (const wallet of acct.wallets) {
6344
- for (const source of wallet.sources) {
6345
- if (source.balance.available.amount > max) {
6346
- max = source.balance.available.amount;
6347
- }
6401
+ const depositEligibleAccounts = getDepositEligibleAccounts(state.accounts);
6402
+ let maxSourceBalance = 0;
6403
+ for (const acct of state.accounts) {
6404
+ for (const wallet of acct.wallets) {
6405
+ for (const source of wallet.sources) {
6406
+ if (source.balance.available.amount > maxSourceBalance) {
6407
+ maxSourceBalance = source.balance.available.amount;
6348
6408
  }
6349
6409
  }
6350
6410
  }
6351
- return max;
6352
- }, [state.accounts]);
6353
- const tokenCount = react.useMemo(() => {
6354
- let count = 0;
6355
- for (const acct of state.accounts) {
6356
- for (const wallet of acct.wallets) {
6357
- count += wallet.sources.filter(
6358
- (s) => s.balance.available.amount > 0
6359
- ).length;
6360
- }
6411
+ }
6412
+ let tokenCount = 0;
6413
+ for (const acct of state.accounts) {
6414
+ for (const wallet of acct.wallets) {
6415
+ tokenCount += wallet.sources.filter((s) => s.balance.available.amount > 0).length;
6361
6416
  }
6362
- return count;
6363
- }, [state.accounts]);
6417
+ }
6364
6418
  return {
6365
6419
  sourceType,
6366
6420
  sourceId,
@@ -6374,6 +6428,9 @@ function useDerivedState(state) {
6374
6428
  tokenCount
6375
6429
  };
6376
6430
  }
6431
+ function useDerivedState(state) {
6432
+ return react.useMemo(() => computeDerivedState(state), [state]);
6433
+ }
6377
6434
  function useAuthHandlers(dispatch, verificationTarget) {
6378
6435
  const {
6379
6436
  sendCode: sendEmailCode,
@@ -6939,7 +6996,9 @@ function useProviderHandlers(deps) {
6939
6996
  reauthTokenRef,
6940
6997
  authenticated,
6941
6998
  merchantAuthorization,
6942
- destination
6999
+ destination,
7000
+ guestSessionToken,
7001
+ selectedProviderId
6943
7002
  } = deps;
6944
7003
  const wagmiConfig2 = wagmi.useConfig();
6945
7004
  const { connectAsync, connectors } = wagmi.useConnect();
@@ -7252,7 +7311,7 @@ function useProviderHandlers(deps) {
7252
7311
  reauthTokenRef
7253
7312
  ]);
7254
7313
  const handleNavigateToTokenPicker = react.useCallback(() => {
7255
- dispatch({ type: "SET_USER_INTENT", intent: "pick-token" });
7314
+ dispatch({ type: "SET_USER_INTENT", intent: { step: "token-picker" } });
7256
7315
  }, [dispatch]);
7257
7316
  const handleSelectAuthorizedToken = react.useCallback((walletId, tokenSymbol) => {
7258
7317
  dispatch({ type: "SELECT_TOKEN", walletId, tokenSymbol });
@@ -7335,6 +7394,76 @@ function useProviderHandlers(deps) {
7335
7394
  reauthSessionIdRef,
7336
7395
  reauthTokenRef
7337
7396
  ]);
7397
+ const handlePreauthorize = react.useCallback(async () => {
7398
+ if (!guestSessionToken || !selectedProviderId) {
7399
+ dispatch({
7400
+ type: "SET_ERROR",
7401
+ error: "Missing guest session or wallet provider. Try again from the payment screen."
7402
+ });
7403
+ return;
7404
+ }
7405
+ const isMobile = !shouldUseWalletConnector({
7406
+ useWalletConnector: useWalletConnectorProp,
7407
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
7408
+ });
7409
+ const providerName = providers.find((p) => p.id === selectedProviderId)?.name ?? "Wallet";
7410
+ if (!isMobile) {
7411
+ dispatch({ type: "GUEST_PREAUTH_BEGIN" });
7412
+ }
7413
+ try {
7414
+ const created = await createGuestAccount(
7415
+ apiBaseUrl,
7416
+ guestSessionToken,
7417
+ selectedProviderId,
7418
+ providerName
7419
+ );
7420
+ const session = await fetchAuthorizationSessionByToken(
7421
+ apiBaseUrl,
7422
+ created.sessionToken
7423
+ );
7424
+ if (isMobile) {
7425
+ handlingMobileReturnRef.current = false;
7426
+ mobileSetupFlowRef.current = true;
7427
+ setupAccountIdRef.current = created.accountId;
7428
+ persistMobileFlowState({
7429
+ accountId: created.accountId,
7430
+ sessionId: session.id,
7431
+ deeplinkUri: created.sessionUri,
7432
+ providerId: selectedProviderId,
7433
+ isSetup: true,
7434
+ guestSessionToken
7435
+ });
7436
+ triggerDeeplink(created.sessionUri);
7437
+ dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: created.sessionUri });
7438
+ }
7439
+ dispatch({
7440
+ type: "GUEST_PREAUTH_DETECTED",
7441
+ accountId: created.accountId,
7442
+ sessionId: session.id
7443
+ });
7444
+ } catch (err) {
7445
+ captureException(err);
7446
+ if (!isMobile) {
7447
+ dispatch({ type: "GUEST_PREAUTH_END" });
7448
+ }
7449
+ dispatch({
7450
+ type: "SET_ERROR",
7451
+ error: err instanceof Error ? err.message : "Failed to start preauthorization"
7452
+ });
7453
+ onError?.(err instanceof Error ? err.message : "Failed to start preauthorization");
7454
+ }
7455
+ }, [
7456
+ guestSessionToken,
7457
+ selectedProviderId,
7458
+ providers,
7459
+ apiBaseUrl,
7460
+ dispatch,
7461
+ onError,
7462
+ useWalletConnectorProp,
7463
+ mobileSetupFlowRef,
7464
+ handlingMobileReturnRef,
7465
+ setupAccountIdRef
7466
+ ]);
7338
7467
  return {
7339
7468
  handlePrepareProvider,
7340
7469
  handleSelectProvider,
@@ -7343,7 +7472,8 @@ function useProviderHandlers(deps) {
7343
7472
  handleIncreaseLimit,
7344
7473
  handleNavigateToTokenPicker,
7345
7474
  handleSelectAuthorizedToken,
7346
- handleAuthorizeToken
7475
+ handleAuthorizeToken,
7476
+ handlePreauthorize
7347
7477
  };
7348
7478
  }
7349
7479
 
@@ -7564,7 +7694,6 @@ function useOneTapSetupHandlers(deps) {
7564
7694
  selectSourceTokenSymbol
7565
7695
  } = deps;
7566
7696
  const [savingOneTapLimit, setSavingOneTapLimit] = react.useState(false);
7567
- const oneTapLimitSavedDuringSetupRef = react.useRef(false);
7568
7697
  const handleSetupOneTap = react.useCallback(async (limit) => {
7569
7698
  setSavingOneTapLimit(true);
7570
7699
  try {
@@ -7585,12 +7714,12 @@ function useOneTapSetupHandlers(deps) {
7585
7714
  chainName = recommended?.chainName ?? choices[0]?.chainName ?? "Base";
7586
7715
  tokenSymbol = recommended?.tokenSymbol ?? choices[0]?.tokens[0]?.tokenSymbol ?? "USDC";
7587
7716
  }
7588
- oneTapLimitSavedDuringSetupRef.current = true;
7717
+ dispatch({ type: "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP", saved: true });
7589
7718
  authExecutor.resolveSelectSource({ chainName, tokenSymbol });
7590
7719
  } else if (authExecutor.pendingOneTapSetup) {
7591
7720
  authExecutor.resolveOneTapSetup();
7592
7721
  }
7593
- dispatch({ type: "SET_USER_INTENT", intent: null });
7722
+ dispatch({ type: "SET_USER_INTENT", intent: { step: "deposit" } });
7594
7723
  } catch (err) {
7595
7724
  captureException(err);
7596
7725
  dispatch({
@@ -7603,124 +7732,63 @@ function useOneTapSetupHandlers(deps) {
7603
7732
  }, [getAccessToken, apiBaseUrl, authExecutor, dispatch, selectSourceChainName, selectSourceTokenSymbol]);
7604
7733
  return {
7605
7734
  handleSetupOneTap,
7606
- savingOneTapLimit,
7607
- oneTapLimitSavedDuringSetupRef
7735
+ savingOneTapLimit
7608
7736
  };
7609
7737
  }
7610
-
7611
- // src/dataLoading.ts
7612
- function resolveDataLoadAction({
7613
- authenticated,
7614
- accountsCount,
7615
- hasActiveCredential,
7616
- loading
7617
- }) {
7618
- if (!authenticated || accountsCount > 0 || !hasActiveCredential) {
7619
- return "reset";
7620
- }
7621
- if (loading) {
7622
- return "wait";
7623
- }
7624
- return "load";
7625
- }
7626
-
7627
- // src/processingStatus.ts
7628
- var PROCESSING_TIMEOUT_MS = 18e4;
7629
- function resolvePreferredTransfer(polledTransfer, localTransfer) {
7630
- return polledTransfer ?? localTransfer;
7631
- }
7632
- function getTransferStatus(polledTransfer, localTransfer) {
7633
- const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
7634
- return transfer?.status ?? "UNKNOWN";
7635
- }
7636
- function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
7637
- if (!processingStartedAtMs) return false;
7638
- return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
7639
- }
7640
- var STATUS_DISPLAY_LABELS = {
7641
- CREATED: "created",
7642
- AUTHORIZED: "authorized",
7643
- SENDING: "sending",
7644
- SENT: "confirming delivery",
7645
- COMPLETED: "completed",
7646
- FAILED: "failed"
7647
- };
7648
- function getStatusDisplayLabel(status) {
7649
- return STATUS_DISPLAY_LABELS[status] ?? status;
7650
- }
7651
- function buildProcessingTimeoutMessage(status) {
7652
- const label = getStatusDisplayLabel(status);
7653
- return `Payment is taking longer than expected (status: ${label}). Please try again.`;
7654
- }
7655
-
7656
- // src/hooks/usePaymentEffects.ts
7657
- function usePaymentEffects(deps) {
7738
+ function useOtpEffects(deps) {
7658
7739
  const {
7659
7740
  state,
7660
7741
  dispatch,
7661
- ready,
7662
7742
  authenticated,
7663
- apiBaseUrl,
7664
- depositAmount,
7665
- onComplete,
7666
- onError,
7667
- polling,
7668
- authExecutor,
7669
- reloadAccounts,
7670
7743
  activeOtpStatus,
7671
7744
  activeOtpErrorMessage,
7672
7745
  otpCode,
7673
- handleVerifyLoginCode,
7746
+ handleVerifyLoginCode
7747
+ } = deps;
7748
+ react.useEffect(() => {
7749
+ if (authenticated || !state.verificationTarget) return;
7750
+ if (activeOtpErrorMessage) dispatch({ type: "SET_ERROR", error: activeOtpErrorMessage });
7751
+ }, [activeOtpErrorMessage, authenticated, state.verificationTarget, dispatch]);
7752
+ react.useEffect(() => {
7753
+ if (state.verificationTarget && !authenticated && /^\d{6}$/.test(otpCode.trim()) && activeOtpStatus === "awaiting-code-input") {
7754
+ handleVerifyLoginCode();
7755
+ }
7756
+ }, [otpCode, state.verificationTarget, authenticated, activeOtpStatus, handleVerifyLoginCode]);
7757
+ }
7758
+ function usePasskeyCheckEffect(deps) {
7759
+ const {
7760
+ dispatch,
7761
+ ready,
7762
+ authenticated,
7763
+ apiBaseUrl,
7764
+ activeCredentialId,
7765
+ passkeyConfigLoaded,
7766
+ checkingPasskeyRef,
7674
7767
  setAuthInput,
7675
7768
  setOtpCode,
7769
+ polling,
7676
7770
  mobileSetupFlowRef,
7677
7771
  handlingMobileReturnRef,
7678
7772
  setupAccountIdRef,
7679
7773
  reauthSessionIdRef,
7680
7774
  reauthTokenRef,
7681
- loadingDataRef,
7682
- pollingTransferIdRef,
7683
- processingStartedAtRef,
7684
- checkingPasskeyRef,
7685
- pendingSelectSourceAction,
7686
- selectSourceChoices,
7687
- selectSourceRecommended,
7688
- setSelectSourceChainName,
7689
- setSelectSourceTokenSymbol,
7690
- initializedSelectSourceActionRef,
7691
- oneTapLimitSavedDuringSetupRef,
7692
- handleAuthorizedMobileReturn
7775
+ pollingTransferIdRef
7693
7776
  } = deps;
7694
7777
  const { getAccessToken } = reactAuth.usePrivy();
7695
- const onCompleteRef = react.useRef(onComplete);
7696
- onCompleteRef.current = onComplete;
7778
+ const onCompleteRef = react.useRef(deps.onComplete);
7779
+ onCompleteRef.current = deps.onComplete;
7697
7780
  const getAccessTokenRef = react.useRef(getAccessToken);
7698
7781
  getAccessTokenRef.current = getAccessToken;
7699
7782
  const pollingRef = react.useRef(polling);
7700
7783
  pollingRef.current = polling;
7701
- const handleAuthorizedMobileReturnRef = react.useRef(handleAuthorizedMobileReturn);
7702
- handleAuthorizedMobileReturnRef.current = handleAuthorizedMobileReturn;
7703
- const lastAccountFetchRef = react.useRef(0);
7704
- react.useEffect(() => {
7705
- if (depositAmount != null) {
7706
- dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
7707
- }
7708
- }, [depositAmount, dispatch]);
7709
- react.useEffect(() => {
7710
- if (authenticated || !state.verificationTarget) return;
7711
- if (activeOtpErrorMessage) dispatch({ type: "SET_ERROR", error: activeOtpErrorMessage });
7712
- }, [activeOtpErrorMessage, authenticated, state.verificationTarget, dispatch]);
7713
- react.useEffect(() => {
7714
- if (state.verificationTarget && !authenticated && /^\d{6}$/.test(otpCode.trim()) && activeOtpStatus === "awaiting-code-input") {
7715
- handleVerifyLoginCode();
7716
- }
7717
- }, [otpCode, state.verificationTarget, authenticated, activeOtpStatus, handleVerifyLoginCode]);
7784
+ const handleAuthorizedMobileReturnRef = react.useRef(deps.handleAuthorizedMobileReturn);
7785
+ handleAuthorizedMobileReturnRef.current = deps.handleAuthorizedMobileReturn;
7718
7786
  react.useEffect(() => {
7719
7787
  if (!ready || !authenticated) {
7720
7788
  checkingPasskeyRef.current = false;
7721
7789
  return;
7722
7790
  }
7723
- if (state.passkeyConfigLoaded || state.activeCredentialId) return;
7791
+ if (passkeyConfigLoaded || activeCredentialId) return;
7724
7792
  if (checkingPasskeyRef.current) return;
7725
7793
  checkingPasskeyRef.current = true;
7726
7794
  let cancelled = false;
@@ -7818,11 +7886,7 @@ function usePaymentEffects(deps) {
7818
7886
  return;
7819
7887
  }
7820
7888
  if (existingTransfer.status === "AUTHORIZED") {
7821
- if (persisted.isSetup) {
7822
- await handleAuthorizedMobileReturnRef.current(existingTransfer, true);
7823
- } else {
7824
- await handleAuthorizedMobileReturnRef.current(existingTransfer, false);
7825
- }
7889
+ await handleAuthorizedMobileReturnRef.current(existingTransfer, !!persisted.isSetup);
7826
7890
  return;
7827
7891
  }
7828
7892
  if (persisted.isSetup) {
@@ -7873,11 +7937,9 @@ function usePaymentEffects(deps) {
7873
7937
  knownIds: allPasskeys.map((p) => p.credentialId),
7874
7938
  oneTapLimit: config.defaultAllowance ?? void 0
7875
7939
  });
7876
- if (allPasskeys.length === 0) {
7877
- return;
7878
- }
7879
- if (state.activeCredentialId && allPasskeys.some((p) => p.credentialId === state.activeCredentialId)) {
7880
- await restoreState(state.activeCredentialId, token);
7940
+ if (allPasskeys.length === 0) return;
7941
+ if (activeCredentialId && allPasskeys.some((p) => p.credentialId === activeCredentialId)) {
7942
+ await restoreState(activeCredentialId, token);
7881
7943
  return;
7882
7944
  }
7883
7945
  if (cancelled) return;
@@ -7909,7 +7971,39 @@ function usePaymentEffects(deps) {
7909
7971
  cancelled = true;
7910
7972
  checkingPasskeyRef.current = false;
7911
7973
  };
7912
- }, [ready, authenticated, apiBaseUrl, state.activeCredentialId, state.passkeyConfigLoaded]);
7974
+ }, [ready, authenticated, apiBaseUrl, activeCredentialId, passkeyConfigLoaded]);
7975
+ }
7976
+
7977
+ // src/dataLoading.ts
7978
+ function resolveDataLoadAction({
7979
+ authenticated,
7980
+ accountsCount,
7981
+ hasActiveCredential,
7982
+ loading
7983
+ }) {
7984
+ if (!authenticated || accountsCount > 0 || !hasActiveCredential) {
7985
+ return "reset";
7986
+ }
7987
+ if (loading) {
7988
+ return "wait";
7989
+ }
7990
+ return "load";
7991
+ }
7992
+
7993
+ // src/hooks/useDataLoadEffect.ts
7994
+ function useDataLoadEffect(deps) {
7995
+ const {
7996
+ state,
7997
+ dispatch,
7998
+ authenticated,
7999
+ apiBaseUrl,
8000
+ depositAmount,
8001
+ loadingDataRef
8002
+ } = deps;
8003
+ const { getAccessToken } = reactAuth.usePrivy();
8004
+ const getAccessTokenRef = react.useRef(getAccessToken);
8005
+ getAccessTokenRef.current = getAccessToken;
8006
+ const lastAccountFetchRef = react.useRef(0);
7913
8007
  react.useEffect(() => {
7914
8008
  const loadAction = resolveDataLoadAction({
7915
8009
  authenticated,
@@ -8009,6 +8103,61 @@ function usePaymentEffects(deps) {
8009
8103
  cancelled = true;
8010
8104
  };
8011
8105
  }, [authenticated, state.providers.length, state.activeCredentialId, apiBaseUrl]);
8106
+ react.useEffect(() => {
8107
+ if (state.accounts.length > 0 && state.activeCredentialId && !state.loadingData && !state.transfer && authenticated && Date.now() - lastAccountFetchRef.current > 15e3) {
8108
+ lastAccountFetchRef.current = Date.now();
8109
+ deps.reloadAccounts();
8110
+ }
8111
+ }, [
8112
+ state.accounts.length,
8113
+ state.activeCredentialId,
8114
+ state.loadingData,
8115
+ state.transfer,
8116
+ authenticated,
8117
+ deps
8118
+ ]);
8119
+ }
8120
+
8121
+ // src/processingStatus.ts
8122
+ var PROCESSING_TIMEOUT_MS = 18e4;
8123
+ function resolvePreferredTransfer(polledTransfer, localTransfer) {
8124
+ return polledTransfer ?? localTransfer;
8125
+ }
8126
+ function getTransferStatus(polledTransfer, localTransfer) {
8127
+ const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
8128
+ return transfer?.status ?? "UNKNOWN";
8129
+ }
8130
+ function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
8131
+ if (!processingStartedAtMs) return false;
8132
+ return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
8133
+ }
8134
+ var STATUS_DISPLAY_LABELS = {
8135
+ CREATED: "created",
8136
+ AUTHORIZED: "authorized",
8137
+ SENDING: "sending",
8138
+ SENT: "confirming delivery",
8139
+ COMPLETED: "completed",
8140
+ FAILED: "failed"
8141
+ };
8142
+ function getStatusDisplayLabel(status) {
8143
+ return STATUS_DISPLAY_LABELS[status] ?? status;
8144
+ }
8145
+ function buildProcessingTimeoutMessage(status) {
8146
+ const label = getStatusDisplayLabel(status);
8147
+ return `Payment is taking longer than expected (status: ${label}). Please try again.`;
8148
+ }
8149
+
8150
+ // src/hooks/useProcessingEffect.ts
8151
+ function useProcessingEffect(deps) {
8152
+ const {
8153
+ state,
8154
+ dispatch,
8155
+ polling,
8156
+ processingStartedAtRef,
8157
+ onComplete,
8158
+ onError,
8159
+ reloadAccounts
8160
+ } = deps;
8012
8161
  react.useEffect(() => {
8013
8162
  if (!polling.transfer) return;
8014
8163
  if (polling.transfer.status === "COMPLETED") {
@@ -8021,19 +8170,6 @@ function usePaymentEffects(deps) {
8021
8170
  dispatch({ type: "TRANSFER_FAILED", transfer: polling.transfer, error: "Transfer failed." });
8022
8171
  }
8023
8172
  }, [polling.transfer, onComplete, dispatch, reloadAccounts]);
8024
- react.useEffect(() => {
8025
- if (state.accounts.length > 0 && state.activeCredentialId && !state.loadingData && !state.transfer && authenticated && Date.now() - lastAccountFetchRef.current > 15e3) {
8026
- lastAccountFetchRef.current = Date.now();
8027
- reloadAccounts();
8028
- }
8029
- }, [
8030
- state.accounts.length,
8031
- state.activeCredentialId,
8032
- state.loadingData,
8033
- state.transfer,
8034
- authenticated,
8035
- reloadAccounts
8036
- ]);
8037
8173
  react.useEffect(() => {
8038
8174
  const isProcessing = state.creatingTransfer || state.transfer != null && ["CREATED", "SENDING", "SENT"].includes(state.transfer.status);
8039
8175
  if (!isProcessing) {
@@ -8069,6 +8205,26 @@ function usePaymentEffects(deps) {
8069
8205
  dispatch,
8070
8206
  processingStartedAtRef
8071
8207
  ]);
8208
+ }
8209
+ function useMobilePollingEffect(deps) {
8210
+ const {
8211
+ state,
8212
+ dispatch,
8213
+ polling,
8214
+ mobileSetupFlowRef,
8215
+ handlingMobileReturnRef,
8216
+ setupAccountIdRef,
8217
+ reauthSessionIdRef,
8218
+ reauthTokenRef,
8219
+ pollingTransferIdRef,
8220
+ reloadAccounts,
8221
+ apiBaseUrl
8222
+ } = deps;
8223
+ const { getAccessToken } = reactAuth.usePrivy();
8224
+ const getAccessTokenRef = react.useRef(getAccessToken);
8225
+ getAccessTokenRef.current = getAccessToken;
8226
+ const handleAuthorizedMobileReturnRef = react.useRef(deps.handleAuthorizedMobileReturn);
8227
+ handleAuthorizedMobileReturnRef.current = deps.handleAuthorizedMobileReturn;
8072
8228
  react.useEffect(() => {
8073
8229
  if (!state.mobileFlow) {
8074
8230
  handlingMobileReturnRef.current = false;
@@ -8077,8 +8233,8 @@ function usePaymentEffects(deps) {
8077
8233
  if (handlingMobileReturnRef.current) return;
8078
8234
  const polledTransfer = polling.transfer;
8079
8235
  if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
8080
- void handleAuthorizedMobileReturn(polledTransfer, mobileSetupFlowRef.current);
8081
- }, [state.mobileFlow, polling.transfer, handleAuthorizedMobileReturn, handlingMobileReturnRef, mobileSetupFlowRef]);
8236
+ void handleAuthorizedMobileReturnRef.current(polledTransfer, mobileSetupFlowRef.current);
8237
+ }, [state.mobileFlow, polling.transfer, handlingMobileReturnRef, mobileSetupFlowRef]);
8082
8238
  react.useEffect(() => {
8083
8239
  if (!state.mobileFlow || !mobileSetupFlowRef.current) return;
8084
8240
  if (!state.activeCredentialId || !setupAccountIdRef.current) return;
@@ -8154,14 +8310,10 @@ function usePaymentEffects(deps) {
8154
8310
  poll();
8155
8311
  const intervalId = window.setInterval(poll, POLL_INTERVAL_MS);
8156
8312
  const handleVisibility = () => {
8157
- if (document.visibilityState === "visible" && !cancelled) {
8158
- poll();
8159
- }
8313
+ if (document.visibilityState === "visible" && !cancelled) poll();
8160
8314
  };
8161
8315
  const handlePageShow = (e) => {
8162
- if (e.persisted && !cancelled) {
8163
- poll();
8164
- }
8316
+ if (e.persisted && !cancelled) poll();
8165
8317
  };
8166
8318
  document.addEventListener("visibilitychange", handleVisibility);
8167
8319
  window.addEventListener("pageshow", handlePageShow);
@@ -8212,6 +8364,16 @@ function usePaymentEffects(deps) {
8212
8364
  handlingMobileReturnRef,
8213
8365
  pollingTransferIdRef
8214
8366
  ]);
8367
+ }
8368
+ function useSelectSourceEffect(deps) {
8369
+ const {
8370
+ pendingSelectSourceAction,
8371
+ selectSourceChoices,
8372
+ selectSourceRecommended,
8373
+ setSelectSourceChainName,
8374
+ setSelectSourceTokenSymbol,
8375
+ initializedSelectSourceActionRef
8376
+ } = deps;
8215
8377
  react.useEffect(() => {
8216
8378
  if (!pendingSelectSourceAction) {
8217
8379
  initializedSelectSourceActionRef.current = null;
@@ -8242,57 +8404,27 @@ function usePaymentEffects(deps) {
8242
8404
  setSelectSourceTokenSymbol,
8243
8405
  initializedSelectSourceActionRef
8244
8406
  ]);
8407
+ }
8408
+ function useOneTapAutoResolveEffect(deps) {
8409
+ const { authExecutor, dispatch, oneTapLimitSavedDuringSetup, reloadAccounts } = deps;
8245
8410
  const pendingOneTapSetupAction = authExecutor.pendingOneTapSetup;
8246
8411
  react.useEffect(() => {
8247
- if (pendingOneTapSetupAction && oneTapLimitSavedDuringSetupRef.current) {
8248
- oneTapLimitSavedDuringSetupRef.current = false;
8412
+ if (pendingOneTapSetupAction && oneTapLimitSavedDuringSetup) {
8413
+ dispatch({ type: "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP", saved: false });
8249
8414
  authExecutor.resolveOneTapSetup();
8250
8415
  }
8251
- }, [pendingOneTapSetupAction, authExecutor, oneTapLimitSavedDuringSetupRef]);
8416
+ }, [pendingOneTapSetupAction, authExecutor, dispatch, oneTapLimitSavedDuringSetup]);
8252
8417
  react.useEffect(() => {
8253
- if (pendingOneTapSetupAction && !oneTapLimitSavedDuringSetupRef.current) {
8418
+ if (pendingOneTapSetupAction && !oneTapLimitSavedDuringSetup) {
8254
8419
  reloadAccounts();
8255
8420
  }
8256
- }, [pendingOneTapSetupAction, reloadAccounts, oneTapLimitSavedDuringSetupRef]);
8257
- react.useEffect(() => {
8258
- if (!state.guestSessionToken) return;
8259
- if (state.guestPreauthAccountId) return;
8260
- if (!state.transfer || state.transfer.status !== "COMPLETED") return;
8261
- let cancelled = false;
8262
- const ensureGuestAccount = async () => {
8263
- try {
8264
- let result = await fetchGuestAccount(apiBaseUrl, state.guestSessionToken);
8265
- if (cancelled) return;
8266
- if (!result && state.selectedProviderId) {
8267
- const providerName = state.providers.find((p) => p.id === state.selectedProviderId)?.name ?? "Wallet";
8268
- const created = await createGuestAccount(
8269
- apiBaseUrl,
8270
- state.guestSessionToken,
8271
- state.selectedProviderId,
8272
- providerName
8273
- );
8274
- if (cancelled) return;
8275
- result = { accountId: created.accountId, hasPasskey: false, walletAddress: null };
8276
- }
8277
- if (result && !result.hasPasskey) {
8278
- dispatch({ type: "GUEST_PREAUTH_DETECTED", accountId: result.accountId });
8279
- }
8280
- } catch {
8281
- }
8282
- };
8283
- ensureGuestAccount();
8284
- return () => {
8285
- cancelled = true;
8286
- };
8287
- }, [
8288
- state.transfer,
8289
- state.guestSessionToken,
8290
- state.guestPreauthAccountId,
8291
- state.selectedProviderId,
8292
- state.providers,
8293
- apiBaseUrl,
8294
- dispatch
8295
- ]);
8421
+ }, [pendingOneTapSetupAction, reloadAccounts, oneTapLimitSavedDuringSetup]);
8422
+ }
8423
+ function useGuestPreauthEffect(deps) {
8424
+ const { state, dispatch, authenticated, apiBaseUrl, reloadAccounts } = deps;
8425
+ const { getAccessToken } = reactAuth.usePrivy();
8426
+ const getAccessTokenRef = react.useRef(getAccessToken);
8427
+ getAccessTokenRef.current = getAccessToken;
8296
8428
  const settingOwnerRef = react.useRef(false);
8297
8429
  react.useEffect(() => {
8298
8430
  if (!state.guestPreauthAccountId) return;
@@ -8301,6 +8433,8 @@ function usePaymentEffects(deps) {
8301
8433
  if (!authenticated) return;
8302
8434
  if (!state.guestSessionToken) return;
8303
8435
  if (settingOwnerRef.current) return;
8436
+ const hasActive = state.accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
8437
+ if (!hasActive) return;
8304
8438
  settingOwnerRef.current = true;
8305
8439
  let cancelled = false;
8306
8440
  const setOwner = async () => {
@@ -8338,12 +8472,73 @@ function usePaymentEffects(deps) {
8338
8472
  state.activeCredentialId,
8339
8473
  state.activePublicKey,
8340
8474
  state.guestSessionToken,
8475
+ state.accounts,
8341
8476
  authenticated,
8342
8477
  apiBaseUrl,
8343
8478
  dispatch,
8344
8479
  reloadAccounts
8345
8480
  ]);
8346
8481
  }
8482
+ function useGuestDesktopPreauthSessionEffect(deps) {
8483
+ const { state, authExecutor, reloadAccounts, dispatch, desktopGuestPreauth } = deps;
8484
+ const preauthExecutingRef = react.useRef(false);
8485
+ react.useEffect(() => {
8486
+ if (!desktopGuestPreauth) return;
8487
+ if (!state.guestPreauthorizing) return;
8488
+ if (!state.guestPreauthSessionId || preauthExecutingRef.current) return;
8489
+ preauthExecutingRef.current = true;
8490
+ const runPreauthSession = async () => {
8491
+ try {
8492
+ await authExecutor.executeSessionById(state.guestPreauthSessionId);
8493
+ await reloadAccounts();
8494
+ } catch {
8495
+ } finally {
8496
+ preauthExecutingRef.current = false;
8497
+ dispatch({ type: "GUEST_PREAUTH_END" });
8498
+ }
8499
+ };
8500
+ void runPreauthSession();
8501
+ }, [
8502
+ desktopGuestPreauth,
8503
+ state.guestPreauthorizing,
8504
+ state.guestPreauthSessionId,
8505
+ authExecutor,
8506
+ reloadAccounts,
8507
+ dispatch
8508
+ ]);
8509
+ }
8510
+ function useGuestPreauthPhaseSyncEffect(deps) {
8511
+ const { state, dispatch, authExecutor, isDesktop } = deps;
8512
+ react.useEffect(() => {
8513
+ if (!state.guestPreauthorizing || !isDesktop) return;
8514
+ const pending = authExecutor.pendingSelectSource;
8515
+ if (pending) {
8516
+ const intent = {
8517
+ step: "select-source",
8518
+ action: pending,
8519
+ isDesktop,
8520
+ skipOneTapLimit: true
8521
+ };
8522
+ if (state.phase.step === "select-source") {
8523
+ const ph = state.phase;
8524
+ if (ph.skipOneTapLimit && ph.action.id === pending.id) {
8525
+ return;
8526
+ }
8527
+ }
8528
+ dispatch({ type: "SET_USER_INTENT", intent });
8529
+ return;
8530
+ }
8531
+ if (state.phase.step === "select-source" && state.phase.skipOneTapLimit === true) {
8532
+ dispatch({ type: "SET_USER_INTENT", intent: { step: "one-tap-setup", action: null } });
8533
+ }
8534
+ }, [
8535
+ state.guestPreauthorizing,
8536
+ state.phase,
8537
+ isDesktop,
8538
+ authExecutor.pendingSelectSource,
8539
+ dispatch
8540
+ ]);
8541
+ }
8347
8542
  function BlinkPayment(props) {
8348
8543
  const resetKey = react.useRef(0);
8349
8544
  const handleBoundaryReset = react.useCallback(() => {
@@ -8365,6 +8560,10 @@ function BlinkPaymentInner({
8365
8560
  const { apiBaseUrl, depositAmount } = useBlinkConfig();
8366
8561
  const { ready, authenticated, logout, getAccessToken } = reactAuth.usePrivy();
8367
8562
  reactAuth.useLoginWithOAuth();
8563
+ const isDesktop = shouldUseWalletConnector({
8564
+ useWalletConnector: useWalletConnectorProp,
8565
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8566
+ });
8368
8567
  const [state, dispatch] = react.useReducer(
8369
8568
  paymentReducer,
8370
8569
  {
@@ -8448,7 +8647,9 @@ function BlinkPaymentInner({
8448
8647
  reauthTokenRef: mobileFlowRefs.reauthTokenRef,
8449
8648
  authenticated,
8450
8649
  merchantAuthorization,
8451
- destination
8650
+ destination,
8651
+ guestSessionToken: state.guestSessionToken,
8652
+ selectedProviderId: state.selectedProviderId
8452
8653
  });
8453
8654
  const oneTapSetup = useOneTapSetupHandlers({
8454
8655
  dispatch,
@@ -8470,13 +8671,12 @@ function BlinkPaymentInner({
8470
8671
  clearMobileFlowState();
8471
8672
  transfer.processingStartedAtRef.current = null;
8472
8673
  transfer.pollingTransferIdRef.current = null;
8473
- oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
8474
8674
  dispatch({
8475
8675
  type: "NEW_PAYMENT",
8476
8676
  depositAmount,
8477
8677
  firstAccountId: state.accounts.length > 0 ? state.accounts[0].id : null
8478
8678
  });
8479
- }, [depositAmount, state.accounts, transfer, oneTapSetup]);
8679
+ }, [depositAmount, state.accounts, transfer]);
8480
8680
  const handleLogout = react.useCallback(async () => {
8481
8681
  try {
8482
8682
  await logout();
@@ -8488,65 +8688,112 @@ function BlinkPaymentInner({
8488
8688
  }
8489
8689
  polling.stopPolling();
8490
8690
  passkey.checkingPasskeyRef.current = false;
8491
- oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
8492
8691
  auth.setAuthInput("");
8493
8692
  auth.setOtpCode("");
8494
8693
  dispatch({ type: "LOGOUT", depositAmount });
8495
- }, [logout, polling, depositAmount, auth, passkey, oneTapSetup]);
8496
- usePaymentEffects({
8694
+ }, [logout, polling, depositAmount, auth, passkey]);
8695
+ react.useEffect(() => {
8696
+ if (depositAmount != null) {
8697
+ dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
8698
+ }
8699
+ }, [depositAmount, dispatch]);
8700
+ useOtpEffects({
8497
8701
  state,
8498
8702
  dispatch,
8499
- ready,
8500
8703
  authenticated,
8501
- apiBaseUrl,
8502
- depositAmount,
8503
- onComplete,
8504
- onError,
8505
- polling,
8506
- authExecutor,
8507
- reloadAccounts: transfer.reloadAccounts,
8508
8704
  activeOtpStatus: auth.activeOtpStatus,
8509
8705
  activeOtpErrorMessage: auth.activeOtpErrorMessage,
8510
8706
  otpCode: auth.otpCode,
8511
8707
  handleVerifyLoginCode: auth.handleVerifyLoginCode,
8512
8708
  setAuthInput: auth.setAuthInput,
8709
+ setOtpCode: auth.setOtpCode
8710
+ });
8711
+ usePasskeyCheckEffect({
8712
+ dispatch,
8713
+ ready,
8714
+ authenticated,
8715
+ apiBaseUrl,
8716
+ activeCredentialId: state.activeCredentialId,
8717
+ passkeyConfigLoaded: state.passkeyConfigLoaded,
8718
+ checkingPasskeyRef: passkey.checkingPasskeyRef,
8719
+ setAuthInput: auth.setAuthInput,
8513
8720
  setOtpCode: auth.setOtpCode,
8721
+ polling,
8514
8722
  mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
8515
8723
  handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
8516
8724
  setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
8517
8725
  reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
8518
8726
  reauthTokenRef: mobileFlowRefs.reauthTokenRef,
8519
- loadingDataRef: mobileFlowRefs.loadingDataRef,
8520
8727
  pollingTransferIdRef: transfer.pollingTransferIdRef,
8728
+ handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn,
8729
+ onComplete
8730
+ });
8731
+ useDataLoadEffect({
8732
+ state,
8733
+ dispatch,
8734
+ authenticated,
8735
+ apiBaseUrl,
8736
+ depositAmount,
8737
+ loadingDataRef: mobileFlowRefs.loadingDataRef,
8738
+ reloadAccounts: transfer.reloadAccounts
8739
+ });
8740
+ useProcessingEffect({
8741
+ state,
8742
+ dispatch,
8743
+ polling,
8521
8744
  processingStartedAtRef: transfer.processingStartedAtRef,
8522
- checkingPasskeyRef: passkey.checkingPasskeyRef,
8745
+ onComplete,
8746
+ onError,
8747
+ reloadAccounts: transfer.reloadAccounts
8748
+ });
8749
+ useMobilePollingEffect({
8750
+ state,
8751
+ dispatch,
8752
+ polling,
8753
+ mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
8754
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
8755
+ setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
8756
+ reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
8757
+ reauthTokenRef: mobileFlowRefs.reauthTokenRef,
8758
+ pollingTransferIdRef: transfer.pollingTransferIdRef,
8759
+ reloadAccounts: transfer.reloadAccounts,
8760
+ handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn,
8761
+ apiBaseUrl
8762
+ });
8763
+ useSelectSourceEffect({
8523
8764
  pendingSelectSourceAction: sourceSelection.pendingSelectSourceAction,
8524
8765
  selectSourceChoices: sourceSelection.selectSourceChoices,
8525
8766
  selectSourceRecommended: sourceSelection.selectSourceRecommended,
8526
8767
  setSelectSourceChainName: sourceSelection.setSelectSourceChainName,
8527
8768
  setSelectSourceTokenSymbol: sourceSelection.setSelectSourceTokenSymbol,
8528
- initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef,
8529
- oneTapLimitSavedDuringSetupRef: oneTapSetup.oneTapLimitSavedDuringSetupRef,
8530
- handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn
8769
+ initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef
8531
8770
  });
8532
- const autoSelectingRef = react.useRef(false);
8533
- react.useEffect(() => {
8534
- if (!state.isGuestFlow || !state.selectedProviderId || !state.activeCredentialId || !authenticated || autoSelectingRef.current || state.transfer != null) return;
8535
- const hasActive = state.accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
8536
- if (hasActive) return;
8537
- autoSelectingRef.current = true;
8538
- provider.handleSelectProvider(state.selectedProviderId).finally(() => {
8539
- autoSelectingRef.current = false;
8540
- });
8541
- }, [
8542
- state.isGuestFlow,
8543
- state.selectedProviderId,
8544
- state.activeCredentialId,
8545
- state.transfer,
8546
- state.accounts,
8771
+ useOneTapAutoResolveEffect({
8772
+ authExecutor,
8773
+ dispatch,
8774
+ oneTapLimitSavedDuringSetup: state.oneTapLimitSavedDuringSetup,
8775
+ reloadAccounts: transfer.reloadAccounts
8776
+ });
8777
+ useGuestPreauthEffect({
8778
+ state,
8779
+ dispatch,
8547
8780
  authenticated,
8548
- provider
8549
- ]);
8781
+ apiBaseUrl,
8782
+ reloadAccounts: transfer.reloadAccounts
8783
+ });
8784
+ useGuestPreauthPhaseSyncEffect({
8785
+ state,
8786
+ dispatch,
8787
+ authExecutor,
8788
+ isDesktop
8789
+ });
8790
+ useGuestDesktopPreauthSessionEffect({
8791
+ state,
8792
+ authExecutor,
8793
+ reloadAccounts: transfer.reloadAccounts,
8794
+ dispatch,
8795
+ desktopGuestPreauth: isDesktop
8796
+ });
8550
8797
  const handlers = react.useMemo(() => ({
8551
8798
  onSendLoginCode: auth.handleSendLoginCode,
8552
8799
  onVerifyLoginCode: auth.handleVerifyLoginCode,
@@ -8569,7 +8816,7 @@ function BlinkPaymentInner({
8569
8816
  onBackFromOpenWallet: () => dispatch({ type: "CLEAR_MOBILE_STATE" }),
8570
8817
  onLogout: handleLogout,
8571
8818
  onNewPayment: handleNewPayment,
8572
- onSetUserIntent: (intent) => dispatch({ type: "SET_USER_INTENT", intent }),
8819
+ onSetPhase: (phase) => dispatch({ type: "SET_USER_INTENT", intent: phase }),
8573
8820
  onSetAuthInput: auth.setAuthInput,
8574
8821
  onSetOtpCode: (code) => {
8575
8822
  auth.setOtpCode(code);
@@ -8584,7 +8831,7 @@ function BlinkPaymentInner({
8584
8831
  onAuthorizeToken: provider.handleAuthorizeToken,
8585
8832
  onSelectGuestToken: guestTransfer.handleSelectGuestToken,
8586
8833
  onLogin: () => dispatch({ type: "REQUEST_LOGIN" }),
8587
- onPreauthorize: () => dispatch({ type: "REQUEST_LOGIN" })
8834
+ onPreauthorize: provider.handlePreauthorize
8588
8835
  }), [
8589
8836
  auth,
8590
8837
  passkey,
@@ -8600,41 +8847,47 @@ function BlinkPaymentInner({
8600
8847
  return /* @__PURE__ */ jsxRuntime.jsx(
8601
8848
  StepRenderer,
8602
8849
  {
8603
- state,
8604
- ready,
8605
- authenticated,
8606
- activeOtpStatus: auth.activeOtpStatus,
8607
- pollingTransfer: polling.transfer,
8608
- pollingError: polling.error,
8609
- authExecutorError: authExecutor.error,
8610
- inlineAuthorizationExecuting: authExecutor.executing,
8611
- transferSigningSigning: transferSigning.signing,
8612
- transferSigningError: transferSigning.error,
8613
- pendingSelectSource: authExecutor.pendingSelectSource,
8614
- pendingOneTapSetup: authExecutor.pendingOneTapSetup,
8615
- oneTapLimitAlreadySaved: oneTapSetup.oneTapLimitSavedDuringSetupRef.current,
8616
- pendingConnections: derived.pendingConnections,
8617
- depositEligibleAccounts: derived.depositEligibleAccounts,
8618
- sourceName: derived.sourceName,
8619
- maxSourceBalance: derived.maxSourceBalance,
8620
- tokenCount: derived.tokenCount,
8621
- selectedAccount: derived.selectedAccount,
8622
- selectedSource: derived.selectedSource,
8623
- selectSourceChoices: sourceSelection.selectSourceChoices,
8624
- selectSourceRecommended: sourceSelection.selectSourceRecommended,
8625
- selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance,
8626
- guestTokenEntries: guestTransfer.guestTokenEntries,
8627
- guestLoadingBalances: guestTransfer.loadingBalances,
8628
- guestSettingSender: guestTransfer.settingSender,
8629
- authInput: auth.authInput,
8630
- otpCode: auth.otpCode,
8631
- selectSourceChainName: sourceSelection.selectSourceChainName,
8632
- selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
8633
- savingOneTapLimit: oneTapSetup.savingOneTapLimit,
8634
- merchantName,
8635
- onBack,
8636
- onDismiss,
8637
- depositAmount,
8850
+ flow: {
8851
+ state,
8852
+ authenticated,
8853
+ activeOtpStatus: auth.activeOtpStatus,
8854
+ isDesktop,
8855
+ merchantName,
8856
+ onBack,
8857
+ onDismiss,
8858
+ depositAmount
8859
+ },
8860
+ remote: {
8861
+ pollingTransfer: polling.transfer,
8862
+ pollingError: polling.error,
8863
+ authExecutorError: authExecutor.error,
8864
+ transferSigningSigning: transferSigning.signing,
8865
+ transferSigningError: transferSigning.error,
8866
+ pendingSelectSource: authExecutor.pendingSelectSource,
8867
+ pendingOneTapSetup: authExecutor.pendingOneTapSetup
8868
+ },
8869
+ derived: {
8870
+ pendingConnections: derived.pendingConnections,
8871
+ depositEligibleAccounts: derived.depositEligibleAccounts,
8872
+ sourceName: derived.sourceName,
8873
+ maxSourceBalance: derived.maxSourceBalance,
8874
+ tokenCount: derived.tokenCount,
8875
+ selectedAccount: derived.selectedAccount,
8876
+ selectedSource: derived.selectedSource,
8877
+ selectSourceChoices: sourceSelection.selectSourceChoices,
8878
+ selectSourceRecommended: sourceSelection.selectSourceRecommended,
8879
+ selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance
8880
+ },
8881
+ forms: {
8882
+ authInput: auth.authInput,
8883
+ otpCode: auth.otpCode,
8884
+ selectSourceChainName: sourceSelection.selectSourceChainName,
8885
+ selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
8886
+ savingOneTapLimit: oneTapSetup.savingOneTapLimit,
8887
+ guestTokenEntries: guestTransfer.guestTokenEntries,
8888
+ guestLoadingBalances: guestTransfer.loadingBalances,
8889
+ guestSettingSender: guestTransfer.settingSender
8890
+ },
8638
8891
  handlers
8639
8892
  }
8640
8893
  );
@@ -8673,7 +8926,7 @@ exports.findDevicePasskeyViaPopup = findDevicePasskeyViaPopup;
8673
8926
  exports.getTheme = getTheme;
8674
8927
  exports.lightTheme = lightTheme;
8675
8928
  exports.resolvePasskeyRpId = resolvePasskeyRpId;
8676
- exports.resolveScreen = resolveScreen;
8929
+ exports.screenForPhase = screenForPhase;
8677
8930
  exports.useAuthorizationExecutor = useAuthorizationExecutor;
8678
8931
  exports.useBlinkConfig = useBlinkConfig;
8679
8932
  exports.useBlinkDepositAmount = useBlinkDepositAmount;