@swype-org/react-sdk 0.1.230 → 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,6 +158,322 @@ function useBlinkDepositAmount() {
158
158
  };
159
159
  }
160
160
 
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
+ }
167
+ };
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;
176
+ }
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";
298
+ }
299
+ if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
300
+ return trimmedHostname;
301
+ }
302
+ const parts = trimmedHostname.split(".").filter(Boolean);
303
+ if (parts.length < 2) {
304
+ return trimmedHostname;
305
+ }
306
+ return parts.slice(-2).join(".");
307
+ }
308
+
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);
330
+ });
331
+ }
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);
342
+ }
343
+ return bytes;
344
+ }
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;
356
+ }
357
+ function resolvePasskeyRpId() {
358
+ const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
359
+ if (configuredDomain) {
360
+ return normalizeConfiguredDomain(configuredDomain);
361
+ }
362
+ if (typeof window !== "undefined") {
363
+ return resolveRootDomainFromHostname(window.location.hostname);
364
+ }
365
+ return "localhost";
366
+ }
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
+
161
477
  // src/api.ts
162
478
  var api_exports = {};
163
479
  __export(api_exports, {
@@ -169,6 +485,7 @@ __export(api_exports, {
169
485
  fetchAccount: () => fetchAccount,
170
486
  fetchAccounts: () => fetchAccounts,
171
487
  fetchAuthorizationSession: () => fetchAuthorizationSession,
488
+ fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
172
489
  fetchChains: () => fetchChains,
173
490
  fetchGuestAccount: () => fetchGuestAccount,
174
491
  fetchGuestTransferBalances: () => fetchGuestTransferBalances,
@@ -348,6 +665,13 @@ async function fetchAuthorizationSession(apiBaseUrl, sessionId) {
348
665
  if (!res.ok) await throwApiError(res);
349
666
  return await res.json();
350
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
+ }
351
675
  async function registerPasskey(apiBaseUrl, token, credentialId, publicKey) {
352
676
  const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
353
677
  method: "POST",
@@ -504,17 +828,87 @@ async function setAccountOwner(apiBaseUrl, accessToken, accountId, guestSessionT
504
828
  if (!res.ok) await throwApiError(res);
505
829
  return await res.json();
506
830
  }
507
- async function reportActionCompletion(apiBaseUrl, actionId, result) {
508
- const res = await fetch(
509
- `${apiBaseUrl}/v1/authorization-actions/${actionId}`,
510
- {
511
- method: "PATCH",
512
- headers: { "Content-Type": "application/json" },
513
- body: JSON.stringify({ status: "COMPLETED", result })
831
+ async function reportActionCompletion(apiBaseUrl, actionId, result) {
832
+ const res = await fetch(
833
+ `${apiBaseUrl}/v1/authorization-actions/${actionId}`,
834
+ {
835
+ method: "PATCH",
836
+ headers: { "Content-Type": "application/json" },
837
+ body: JSON.stringify({ status: "COMPLETED", result })
838
+ }
839
+ );
840
+ if (!res.ok) await throwApiError(res);
841
+ return await res.json();
842
+ }
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;
514
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]
515
909
  );
516
- if (!res.ok) await throwApiError(res);
517
- return await res.json();
910
+ react.useEffect(() => () => stopPolling(), [stopPolling]);
911
+ return { transfer, error, isPolling, startPolling, stopPolling };
518
912
  }
519
913
 
520
914
  // node_modules/@wagmi/core/dist/esm/utils/getAction.js
@@ -814,250 +1208,61 @@ async function waitForTransactionReceipt(config, parameters) {
814
1208
  chainId: client.chain.id
815
1209
  };
816
1210
  }
817
-
818
- // src/passkeyRpId.ts
819
- function normalizeConfiguredDomain(value) {
820
- return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
821
- }
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;
833
- }
834
- return parts.slice(-2).join(".");
835
- }
836
1211
  var ERC_6492_MAGIC_SUFFIX = "6492649264926492649264926492649264926492649264926492649264926492";
837
1212
  function normalizeSignature(sig) {
838
1213
  const hex = sig.startsWith("0x") ? sig.slice(2) : sig;
839
1214
  if (hex.length === 130) {
840
1215
  return `0x${hex}`;
841
1216
  }
842
- if (hex.length === 128) {
843
- const r = hex.slice(0, 64);
844
- const yParityAndS = hex.slice(64, 128);
845
- const highByte = parseInt(yParityAndS.slice(0, 2), 16);
846
- const v = (highByte & 128) !== 0 ? 28 : 27;
847
- const sFirstByte = (highByte & 127).toString(16).padStart(2, "0");
848
- const s = sFirstByte + yParityAndS.slice(2);
849
- return `0x${r}${s}${v.toString(16)}`;
850
- }
851
- if (hex.length > 64 && hex.endsWith(ERC_6492_MAGIC_SUFFIX)) {
852
- const { signature: inner } = utils.parseErc6492Signature(
853
- `0x${hex}`
854
- );
855
- return normalizeSignature(inner);
856
- }
857
- if (hex.length > 130) {
858
- try {
859
- const [, innerBytes] = viem.decodeAbiParameters(
860
- [{ type: "uint256" }, { type: "bytes" }],
861
- `0x${hex}`
862
- );
863
- return normalizeSignature(innerBytes);
864
- } catch {
865
- try {
866
- const [wrapper] = viem.decodeAbiParameters(
867
- [{
868
- type: "tuple",
869
- components: [{ type: "uint8" }, { type: "bytes" }]
870
- }],
871
- `0x${hex}`
872
- );
873
- return normalizeSignature(wrapper[1]);
874
- } catch {
875
- return `0x${hex}`;
876
- }
877
- }
878
- }
879
- throw new Error(
880
- `Invalid signature: unable to normalize. Length=${hex.length / 2} bytes. Expected 65, 64, ERC-6492 wrapped, or ABI-encoded SignatureWrapper.`
881
- );
882
- }
883
-
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);
1217
+ if (hex.length === 128) {
1218
+ const r = hex.slice(0, 64);
1219
+ const yParityAndS = hex.slice(64, 128);
1220
+ const highByte = parseInt(yParityAndS.slice(0, 2), 16);
1221
+ const v = (highByte & 128) !== 0 ? 28 : 27;
1222
+ const sFirstByte = (highByte & 127).toString(16).padStart(2, "0");
1223
+ const s = sFirstByte + yParityAndS.slice(2);
1224
+ return `0x${r}${s}${v.toString(16)}`;
1225
+ }
1226
+ if (hex.length > 64 && hex.endsWith(ERC_6492_MAGIC_SUFFIX)) {
1227
+ const { signature: inner } = utils.parseErc6492Signature(
1228
+ `0x${hex}`
1229
+ );
1230
+ return normalizeSignature(inner);
1231
+ }
1232
+ if (hex.length > 130) {
1233
+ try {
1234
+ const [, innerBytes] = viem.decodeAbiParameters(
1235
+ [{ type: "uint256" }, { type: "bytes" }],
1236
+ `0x${hex}`
1237
+ );
1238
+ return normalizeSignature(innerBytes);
1239
+ } catch {
1240
+ try {
1241
+ const [wrapper] = viem.decodeAbiParameters(
1242
+ [{
1243
+ type: "tuple",
1244
+ components: [{ type: "uint8" }, { type: "bytes" }]
1245
+ }],
1246
+ `0x${hex}`
1247
+ );
1248
+ return normalizeSignature(wrapper[1]);
1249
+ } catch {
1250
+ return `0x${hex}`;
1011
1251
  }
1012
- }, POPUP_CLOSED_POLL_MS);
1013
- function cleanup() {
1014
- clearTimeout(timer);
1015
- clearInterval(closedPoll);
1016
1252
  }
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;
1253
+ }
1254
+ throw new Error(
1255
+ `Invalid signature: unable to normalize. Length=${hex.length / 2} bytes. Expected 65, 64, ERC-6492 wrapped, or ABI-encoded SignatureWrapper.`
1256
+ );
1029
1257
  }
1030
1258
 
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
  }
@@ -1068,243 +1273,44 @@ function isUserRejection(msg) {
1068
1273
  const lower = msg.toLowerCase();
1069
1274
  return lower.includes("rejected") || lower.includes("denied");
1070
1275
  }
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;
1100
- }
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";
1110
- }
1111
1276
  async function waitForWalletClient(wagmiConfig2, params = {}) {
1112
- for (let i = 0; i < WALLET_CLIENT_MAX_ATTEMPTS; i++) {
1113
- try {
1114
- const account = getAccount(wagmiConfig2);
1115
- const enrichedParams = account.connector ? { ...params, connector: account.connector } : params;
1116
- return await getWalletClient(wagmiConfig2, enrichedParams);
1117
- } catch {
1118
- if (i === WALLET_CLIENT_MAX_ATTEMPTS - 1) {
1119
- throw new Error("Wallet not ready. Please try again.");
1120
- }
1121
- await new Promise((r) => setTimeout(r, WALLET_CLIENT_POLL_MS));
1122
- }
1123
- }
1124
- throw new Error("Wallet not ready. Please try again.");
1125
- }
1126
- function parseSignTypedDataPayload(typedData) {
1127
- const { domain, types, primaryType, message } = typedData;
1128
- if (!domain || typeof domain !== "object" || Array.isArray(domain)) {
1129
- throw new Error("SIGN_PERMIT2 typedData is missing a valid domain object.");
1130
- }
1131
- if (!types || typeof types !== "object" || Array.isArray(types)) {
1132
- throw new Error("SIGN_PERMIT2 typedData is missing a valid types object.");
1133
- }
1134
- if (typeof primaryType !== "string") {
1135
- throw new Error("SIGN_PERMIT2 typedData is missing primaryType.");
1136
- }
1137
- if (!message || typeof message !== "object" || Array.isArray(message)) {
1138
- throw new Error("SIGN_PERMIT2 typedData is missing a valid message object.");
1139
- }
1140
- return {
1141
- domain,
1142
- types,
1143
- primaryType,
1144
- message
1145
- };
1146
- }
1147
- function getPendingActions(session, completedIds) {
1148
- return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
1149
- }
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
1277
+ for (let i = 0; i < WALLET_CLIENT_MAX_ATTEMPTS; i++) {
1278
+ try {
1279
+ const account = getAccount(wagmiConfig2);
1280
+ const enrichedParams = account.connector ? { ...params, connector: account.connector } : params;
1281
+ return await getWalletClient(wagmiConfig2, enrichedParams);
1282
+ } catch {
1283
+ if (i === WALLET_CLIENT_MAX_ATTEMPTS - 1) {
1284
+ throw new Error("Wallet not ready. Please try again.");
1251
1285
  }
1252
- });
1253
- if (!assertion) return null;
1254
- return toBase64(assertion.rawId);
1255
- } catch {
1256
- return null;
1286
+ await new Promise((r) => setTimeout(r, WALLET_CLIENT_POLL_MS));
1287
+ }
1257
1288
  }
1289
+ throw new Error("Wallet not ready. Please try again.");
1258
1290
  }
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 };
1291
+ function parseSignTypedDataPayload(typedData) {
1292
+ const { domain, types, primaryType, message } = typedData;
1293
+ if (!domain || typeof domain !== "object" || Array.isArray(domain)) {
1294
+ throw new Error("SIGN_PERMIT2 typedData is missing a valid domain object.");
1295
+ }
1296
+ if (!types || typeof types !== "object" || Array.isArray(types)) {
1297
+ throw new Error("SIGN_PERMIT2 typedData is missing a valid types object.");
1298
+ }
1299
+ if (typeof primaryType !== "string") {
1300
+ throw new Error("SIGN_PERMIT2 typedData is missing primaryType.");
1301
+ }
1302
+ if (!message || typeof message !== "object" || Array.isArray(message)) {
1303
+ throw new Error("SIGN_PERMIT2 typedData is missing a valid message object.");
1304
+ }
1305
+ return {
1306
+ domain,
1307
+ types,
1308
+ primaryType,
1309
+ message
1310
+ };
1311
+ }
1312
+ function getPendingActions(session, completedIds) {
1313
+ return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
1308
1314
  }
1309
1315
  async function executeOpenProvider(action, wagmiConfig2, connectors, connectAsync) {
1310
1316
  try {
@@ -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 {
@@ -2275,139 +2417,85 @@ function paymentReducer(state, action) {
2275
2417
  default:
2276
2418
  return state;
2277
2419
  }
2278
- }
2279
-
2280
- // src/auth.ts
2281
- var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2282
- var PHONE_DIGIT_PATTERN = /^\d{7,15}$/;
2283
- function normalizePhoneNumber(rawValue) {
2284
- const trimmed = rawValue.trim();
2285
- if (!trimmed) return null;
2286
- const hasExplicitCountryCode = trimmed.startsWith("+");
2287
- const digits = trimmed.replace(/\D/g, "");
2288
- if (!PHONE_DIGIT_PATTERN.test(digits)) return null;
2289
- return hasExplicitCountryCode ? `+${digits}` : digits;
2290
- }
2291
- function normalizeAuthIdentifier(rawValue) {
2292
- const trimmed = rawValue.trim();
2293
- if (!trimmed) return null;
2294
- if (EMAIL_PATTERN.test(trimmed)) {
2295
- return {
2296
- kind: "email",
2297
- value: trimmed.toLowerCase()
2298
- };
2299
- }
2300
- const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
2301
- if (normalizedPhoneNumber) {
2302
- return {
2303
- kind: "phone",
2304
- value: normalizedPhoneNumber
2305
- };
2306
- }
2307
- return null;
2308
- }
2309
- function maskAuthIdentifier(identifier) {
2310
- if (identifier.kind === "email") {
2311
- const [localPart, domain = ""] = identifier.value.split("@");
2312
- const localPrefix = localPart.slice(0, 2);
2313
- return `${localPrefix}${"*".repeat(Math.max(localPart.length - 2, 0))}@${domain}`;
2314
- }
2315
- const digits = identifier.value.replace(/\D/g, "");
2316
- const visibleSuffix = digits.slice(-4);
2317
- return `***-***-${visibleSuffix}`;
2318
- }
2319
-
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
- // 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";
2420
+ }
2421
+
2422
+ // src/auth.ts
2423
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2424
+ var PHONE_DIGIT_PATTERN = /^\d{7,15}$/;
2425
+ function normalizePhoneNumber(rawValue) {
2426
+ const trimmed = rawValue.trim();
2427
+ if (!trimmed) return null;
2428
+ const hasExplicitCountryCode = trimmed.startsWith("+");
2429
+ const digits = trimmed.replace(/\D/g, "");
2430
+ if (!PHONE_DIGIT_PATTERN.test(digits)) return null;
2431
+ return hasExplicitCountryCode ? `+${digits}` : digits;
2432
+ }
2433
+ function normalizeAuthIdentifier(rawValue) {
2434
+ const trimmed = rawValue.trim();
2435
+ if (!trimmed) return null;
2436
+ if (EMAIL_PATTERN.test(trimmed)) {
2437
+ return {
2438
+ kind: "email",
2439
+ value: trimmed.toLowerCase()
2440
+ };
2400
2441
  }
2401
- if (state.userIntent === "switch-wallet") {
2402
- return "wallet-picker";
2442
+ const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
2443
+ if (normalizedPhoneNumber) {
2444
+ return {
2445
+ kind: "phone",
2446
+ value: normalizedPhoneNumber
2447
+ };
2403
2448
  }
2404
- if (state.activeCredentialId != null && hasActiveWallet(state.accounts) && !state.loadingData) {
2405
- return "deposit";
2449
+ return null;
2450
+ }
2451
+ function maskAuthIdentifier(identifier) {
2452
+ if (identifier.kind === "email") {
2453
+ const [localPart, domain = ""] = identifier.value.split("@");
2454
+ const localPrefix = localPart.slice(0, 2);
2455
+ return `${localPrefix}${"*".repeat(Math.max(localPart.length - 2, 0))}@${domain}`;
2406
2456
  }
2407
- if (state.isGuestFlow) {
2408
- return "wallet-picker";
2457
+ const digits = identifier.value.replace(/\D/g, "");
2458
+ const visibleSuffix = digits.slice(-4);
2459
+ return `***-***-${visibleSuffix}`;
2460
+ }
2461
+
2462
+ // src/resolveScreen.ts
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]
6335
- );
6336
- const depositEligibleAccounts = react.useMemo(
6337
- () => getDepositEligibleAccounts(state.accounts),
6338
- [state.accounts]
6398
+ const pendingConnections = state.accounts.filter(
6399
+ (a) => a.wallets.length > 0 && !a.wallets.some((w) => w.status === "ACTIVE")
6339
6400
  );
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,38 +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 checkGuestAccount = async () => {
8263
- try {
8264
- const result = await fetchGuestAccount(apiBaseUrl, state.guestSessionToken);
8265
- if (cancelled) return;
8266
- if (result && !result.hasPasskey) {
8267
- dispatch({ type: "GUEST_PREAUTH_DETECTED", accountId: result.accountId });
8268
- }
8269
- } catch {
8270
- }
8271
- };
8272
- checkGuestAccount();
8273
- return () => {
8274
- cancelled = true;
8275
- };
8276
- }, [state.transfer, state.guestSessionToken, state.guestPreauthAccountId, apiBaseUrl, dispatch]);
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;
8277
8428
  const settingOwnerRef = react.useRef(false);
8278
8429
  react.useEffect(() => {
8279
8430
  if (!state.guestPreauthAccountId) return;
@@ -8282,6 +8433,8 @@ function usePaymentEffects(deps) {
8282
8433
  if (!authenticated) return;
8283
8434
  if (!state.guestSessionToken) return;
8284
8435
  if (settingOwnerRef.current) return;
8436
+ const hasActive = state.accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
8437
+ if (!hasActive) return;
8285
8438
  settingOwnerRef.current = true;
8286
8439
  let cancelled = false;
8287
8440
  const setOwner = async () => {
@@ -8319,12 +8472,73 @@ function usePaymentEffects(deps) {
8319
8472
  state.activeCredentialId,
8320
8473
  state.activePublicKey,
8321
8474
  state.guestSessionToken,
8475
+ state.accounts,
8322
8476
  authenticated,
8323
8477
  apiBaseUrl,
8324
8478
  dispatch,
8325
8479
  reloadAccounts
8326
8480
  ]);
8327
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
+ }
8328
8542
  function BlinkPayment(props) {
8329
8543
  const resetKey = react.useRef(0);
8330
8544
  const handleBoundaryReset = react.useCallback(() => {
@@ -8346,6 +8560,10 @@ function BlinkPaymentInner({
8346
8560
  const { apiBaseUrl, depositAmount } = useBlinkConfig();
8347
8561
  const { ready, authenticated, logout, getAccessToken } = reactAuth.usePrivy();
8348
8562
  reactAuth.useLoginWithOAuth();
8563
+ const isDesktop = shouldUseWalletConnector({
8564
+ useWalletConnector: useWalletConnectorProp,
8565
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8566
+ });
8349
8567
  const [state, dispatch] = react.useReducer(
8350
8568
  paymentReducer,
8351
8569
  {
@@ -8429,7 +8647,9 @@ function BlinkPaymentInner({
8429
8647
  reauthTokenRef: mobileFlowRefs.reauthTokenRef,
8430
8648
  authenticated,
8431
8649
  merchantAuthorization,
8432
- destination
8650
+ destination,
8651
+ guestSessionToken: state.guestSessionToken,
8652
+ selectedProviderId: state.selectedProviderId
8433
8653
  });
8434
8654
  const oneTapSetup = useOneTapSetupHandlers({
8435
8655
  dispatch,
@@ -8451,13 +8671,12 @@ function BlinkPaymentInner({
8451
8671
  clearMobileFlowState();
8452
8672
  transfer.processingStartedAtRef.current = null;
8453
8673
  transfer.pollingTransferIdRef.current = null;
8454
- oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
8455
8674
  dispatch({
8456
8675
  type: "NEW_PAYMENT",
8457
8676
  depositAmount,
8458
8677
  firstAccountId: state.accounts.length > 0 ? state.accounts[0].id : null
8459
8678
  });
8460
- }, [depositAmount, state.accounts, transfer, oneTapSetup]);
8679
+ }, [depositAmount, state.accounts, transfer]);
8461
8680
  const handleLogout = react.useCallback(async () => {
8462
8681
  try {
8463
8682
  await logout();
@@ -8469,65 +8688,112 @@ function BlinkPaymentInner({
8469
8688
  }
8470
8689
  polling.stopPolling();
8471
8690
  passkey.checkingPasskeyRef.current = false;
8472
- oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
8473
8691
  auth.setAuthInput("");
8474
8692
  auth.setOtpCode("");
8475
8693
  dispatch({ type: "LOGOUT", depositAmount });
8476
- }, [logout, polling, depositAmount, auth, passkey, oneTapSetup]);
8477
- 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({
8478
8701
  state,
8479
8702
  dispatch,
8480
- ready,
8481
8703
  authenticated,
8482
- apiBaseUrl,
8483
- depositAmount,
8484
- onComplete,
8485
- onError,
8486
- polling,
8487
- authExecutor,
8488
- reloadAccounts: transfer.reloadAccounts,
8489
8704
  activeOtpStatus: auth.activeOtpStatus,
8490
8705
  activeOtpErrorMessage: auth.activeOtpErrorMessage,
8491
8706
  otpCode: auth.otpCode,
8492
8707
  handleVerifyLoginCode: auth.handleVerifyLoginCode,
8493
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,
8494
8720
  setOtpCode: auth.setOtpCode,
8721
+ polling,
8495
8722
  mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
8496
8723
  handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
8497
8724
  setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
8498
8725
  reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
8499
8726
  reauthTokenRef: mobileFlowRefs.reauthTokenRef,
8500
- loadingDataRef: mobileFlowRefs.loadingDataRef,
8501
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,
8502
8744
  processingStartedAtRef: transfer.processingStartedAtRef,
8503
- 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({
8504
8764
  pendingSelectSourceAction: sourceSelection.pendingSelectSourceAction,
8505
8765
  selectSourceChoices: sourceSelection.selectSourceChoices,
8506
8766
  selectSourceRecommended: sourceSelection.selectSourceRecommended,
8507
8767
  setSelectSourceChainName: sourceSelection.setSelectSourceChainName,
8508
8768
  setSelectSourceTokenSymbol: sourceSelection.setSelectSourceTokenSymbol,
8509
- initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef,
8510
- oneTapLimitSavedDuringSetupRef: oneTapSetup.oneTapLimitSavedDuringSetupRef,
8511
- handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn
8769
+ initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef
8512
8770
  });
8513
- const autoSelectingRef = react.useRef(false);
8514
- react.useEffect(() => {
8515
- if (!state.isGuestFlow || !state.selectedProviderId || !state.activeCredentialId || !authenticated || autoSelectingRef.current || state.transfer != null) return;
8516
- const hasActive = state.accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
8517
- if (hasActive) return;
8518
- autoSelectingRef.current = true;
8519
- provider.handleSelectProvider(state.selectedProviderId).finally(() => {
8520
- autoSelectingRef.current = false;
8521
- });
8522
- }, [
8523
- state.isGuestFlow,
8524
- state.selectedProviderId,
8525
- state.activeCredentialId,
8526
- state.transfer,
8527
- state.accounts,
8771
+ useOneTapAutoResolveEffect({
8772
+ authExecutor,
8773
+ dispatch,
8774
+ oneTapLimitSavedDuringSetup: state.oneTapLimitSavedDuringSetup,
8775
+ reloadAccounts: transfer.reloadAccounts
8776
+ });
8777
+ useGuestPreauthEffect({
8778
+ state,
8779
+ dispatch,
8528
8780
  authenticated,
8529
- provider
8530
- ]);
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
+ });
8531
8797
  const handlers = react.useMemo(() => ({
8532
8798
  onSendLoginCode: auth.handleSendLoginCode,
8533
8799
  onVerifyLoginCode: auth.handleVerifyLoginCode,
@@ -8550,7 +8816,7 @@ function BlinkPaymentInner({
8550
8816
  onBackFromOpenWallet: () => dispatch({ type: "CLEAR_MOBILE_STATE" }),
8551
8817
  onLogout: handleLogout,
8552
8818
  onNewPayment: handleNewPayment,
8553
- onSetUserIntent: (intent) => dispatch({ type: "SET_USER_INTENT", intent }),
8819
+ onSetPhase: (phase) => dispatch({ type: "SET_USER_INTENT", intent: phase }),
8554
8820
  onSetAuthInput: auth.setAuthInput,
8555
8821
  onSetOtpCode: (code) => {
8556
8822
  auth.setOtpCode(code);
@@ -8565,29 +8831,7 @@ function BlinkPaymentInner({
8565
8831
  onAuthorizeToken: provider.handleAuthorizeToken,
8566
8832
  onSelectGuestToken: guestTransfer.handleSelectGuestToken,
8567
8833
  onLogin: () => dispatch({ type: "REQUEST_LOGIN" }),
8568
- onPreauthorize: async () => {
8569
- if (state.guestPreauthAccountId) {
8570
- dispatch({ type: "REQUEST_LOGIN" });
8571
- return;
8572
- }
8573
- if (!state.guestSessionToken || !state.selectedProviderId) {
8574
- dispatch({ type: "REQUEST_LOGIN" });
8575
- return;
8576
- }
8577
- try {
8578
- const providerName = state.providers.find((p) => p.id === state.selectedProviderId)?.name ?? "Wallet";
8579
- const created = await createGuestAccount(
8580
- apiBaseUrl,
8581
- state.guestSessionToken,
8582
- state.selectedProviderId,
8583
- providerName
8584
- );
8585
- dispatch({ type: "GUEST_PREAUTH_DETECTED", accountId: created.accountId });
8586
- dispatch({ type: "REQUEST_LOGIN" });
8587
- } catch {
8588
- dispatch({ type: "REQUEST_LOGIN" });
8589
- }
8590
- }
8834
+ onPreauthorize: provider.handlePreauthorize
8591
8835
  }), [
8592
8836
  auth,
8593
8837
  passkey,
@@ -8598,51 +8842,52 @@ function BlinkPaymentInner({
8598
8842
  oneTapSetup,
8599
8843
  guestTransfer,
8600
8844
  handleLogout,
8601
- handleNewPayment,
8602
- state.guestPreauthAccountId,
8603
- state.guestSessionToken,
8604
- state.selectedProviderId,
8605
- state.providers,
8606
- apiBaseUrl
8845
+ handleNewPayment
8607
8846
  ]);
8608
8847
  return /* @__PURE__ */ jsxRuntime.jsx(
8609
8848
  StepRenderer,
8610
8849
  {
8611
- state,
8612
- ready,
8613
- authenticated,
8614
- activeOtpStatus: auth.activeOtpStatus,
8615
- pollingTransfer: polling.transfer,
8616
- pollingError: polling.error,
8617
- authExecutorError: authExecutor.error,
8618
- inlineAuthorizationExecuting: authExecutor.executing,
8619
- transferSigningSigning: transferSigning.signing,
8620
- transferSigningError: transferSigning.error,
8621
- pendingSelectSource: authExecutor.pendingSelectSource,
8622
- pendingOneTapSetup: authExecutor.pendingOneTapSetup,
8623
- oneTapLimitAlreadySaved: oneTapSetup.oneTapLimitSavedDuringSetupRef.current,
8624
- pendingConnections: derived.pendingConnections,
8625
- depositEligibleAccounts: derived.depositEligibleAccounts,
8626
- sourceName: derived.sourceName,
8627
- maxSourceBalance: derived.maxSourceBalance,
8628
- tokenCount: derived.tokenCount,
8629
- selectedAccount: derived.selectedAccount,
8630
- selectedSource: derived.selectedSource,
8631
- selectSourceChoices: sourceSelection.selectSourceChoices,
8632
- selectSourceRecommended: sourceSelection.selectSourceRecommended,
8633
- selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance,
8634
- guestTokenEntries: guestTransfer.guestTokenEntries,
8635
- guestLoadingBalances: guestTransfer.loadingBalances,
8636
- guestSettingSender: guestTransfer.settingSender,
8637
- authInput: auth.authInput,
8638
- otpCode: auth.otpCode,
8639
- selectSourceChainName: sourceSelection.selectSourceChainName,
8640
- selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
8641
- savingOneTapLimit: oneTapSetup.savingOneTapLimit,
8642
- merchantName,
8643
- onBack,
8644
- onDismiss,
8645
- 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
+ },
8646
8891
  handlers
8647
8892
  }
8648
8893
  );
@@ -8681,7 +8926,7 @@ exports.findDevicePasskeyViaPopup = findDevicePasskeyViaPopup;
8681
8926
  exports.getTheme = getTheme;
8682
8927
  exports.lightTheme = lightTheme;
8683
8928
  exports.resolvePasskeyRpId = resolvePasskeyRpId;
8684
- exports.resolveScreen = resolveScreen;
8929
+ exports.screenForPhase = screenForPhase;
8685
8930
  exports.useAuthorizationExecutor = useAuthorizationExecutor;
8686
8931
  exports.useBlinkConfig = useBlinkConfig;
8687
8932
  exports.useBlinkDepositAmount = useBlinkDepositAmount;