@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 +1237 -992
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -42
- package/dist/index.d.ts +62 -42
- package/dist/index.js +1237 -992
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
517
|
-
return
|
|
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
|
-
|
|
1020
|
-
|
|
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
|
-
|
|
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
|
|
1260
|
-
const {
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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:
|
|
1807
|
+
id: base64ToBytes2(payload.passkeyCredentialId)
|
|
1761
1808
|
}] : void 0;
|
|
1762
|
-
await
|
|
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:
|
|
1779
|
-
signature:
|
|
1780
|
-
authenticatorData:
|
|
1781
|
-
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
|
-
|
|
1981
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
2263
|
-
|
|
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
|
-
|
|
2402
|
-
|
|
2442
|
+
const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
|
|
2443
|
+
if (normalizedPhoneNumber) {
|
|
2444
|
+
return {
|
|
2445
|
+
kind: "phone",
|
|
2446
|
+
value: normalizedPhoneNumber
|
|
2447
|
+
};
|
|
2403
2448
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
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
|
-
|
|
2408
|
-
|
|
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,
|
|
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
|
|
5985
|
+
return phase.step === "one-tap-setup" ? "link" : "deposit";
|
|
5898
5986
|
}
|
|
5899
5987
|
return null;
|
|
5900
5988
|
}
|
|
5901
5989
|
function StepRenderer(props) {
|
|
5902
|
-
const
|
|
5903
|
-
|
|
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
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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 =
|
|
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 =
|
|
6331
|
-
() =>
|
|
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
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
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
|
-
|
|
6352
|
-
|
|
6353
|
-
const
|
|
6354
|
-
|
|
6355
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
8081
|
-
}, [state.mobileFlow, polling.transfer,
|
|
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 &&
|
|
8248
|
-
|
|
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,
|
|
8416
|
+
}, [pendingOneTapSetupAction, authExecutor, dispatch, oneTapLimitSavedDuringSetup]);
|
|
8252
8417
|
react.useEffect(() => {
|
|
8253
|
-
if (pendingOneTapSetupAction && !
|
|
8418
|
+
if (pendingOneTapSetupAction && !oneTapLimitSavedDuringSetup) {
|
|
8254
8419
|
reloadAccounts();
|
|
8255
8420
|
}
|
|
8256
|
-
}, [pendingOneTapSetupAction, reloadAccounts,
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
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
|
|
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
|
|
8477
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
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.
|
|
8929
|
+
exports.screenForPhase = screenForPhase;
|
|
8685
8930
|
exports.useAuthorizationExecutor = useAuthorizationExecutor;
|
|
8686
8931
|
exports.useBlinkConfig = useBlinkConfig;
|
|
8687
8932
|
exports.useBlinkDepositAmount = useBlinkDepositAmount;
|