@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.js
CHANGED
|
@@ -155,6 +155,322 @@ function useBlinkDepositAmount() {
|
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// src/passkey-delegation.ts
|
|
159
|
+
var PasskeyIframeBlockedError = class extends Error {
|
|
160
|
+
constructor(message = "Passkey creation is not supported in this browser context.") {
|
|
161
|
+
super(message);
|
|
162
|
+
this.name = "PasskeyIframeBlockedError";
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
function isInCrossOriginIframe() {
|
|
166
|
+
if (typeof window === "undefined") return false;
|
|
167
|
+
if (window.parent === window) return false;
|
|
168
|
+
try {
|
|
169
|
+
void window.parent.location.origin;
|
|
170
|
+
return false;
|
|
171
|
+
} catch {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function isSafari() {
|
|
176
|
+
if (typeof navigator === "undefined") return false;
|
|
177
|
+
const ua = navigator.userAgent;
|
|
178
|
+
return /Safari/i.test(ua) && !/Chrome|CriOS|Chromium|Edg|OPR|Firefox/i.test(ua);
|
|
179
|
+
}
|
|
180
|
+
var POPUP_RESULT_TIMEOUT_MS = 12e4;
|
|
181
|
+
var POPUP_CLOSED_POLL_MS = 500;
|
|
182
|
+
var POPUP_CLOSED_GRACE_MS = 1e3;
|
|
183
|
+
function createPasskeyViaPopup(options) {
|
|
184
|
+
return new Promise((resolve, reject) => {
|
|
185
|
+
const verificationToken = crypto.randomUUID();
|
|
186
|
+
const payload = { ...options, verificationToken };
|
|
187
|
+
const encoded = btoa(JSON.stringify(payload));
|
|
188
|
+
const popupUrl = `${window.location.origin}/passkey-register#${encoded}`;
|
|
189
|
+
const popup = window.open(popupUrl, "blink-passkey");
|
|
190
|
+
if (!popup) {
|
|
191
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
let settled = false;
|
|
195
|
+
const timer = setTimeout(() => {
|
|
196
|
+
cleanup();
|
|
197
|
+
reject(new Error("Passkey creation timed out. Please try again."));
|
|
198
|
+
}, POPUP_RESULT_TIMEOUT_MS);
|
|
199
|
+
const closedPoll = setInterval(() => {
|
|
200
|
+
if (popup.closed) {
|
|
201
|
+
clearInterval(closedPoll);
|
|
202
|
+
setTimeout(() => {
|
|
203
|
+
if (!settled) {
|
|
204
|
+
settled = true;
|
|
205
|
+
cleanup();
|
|
206
|
+
checkServerForPasskeyByToken(
|
|
207
|
+
options.authToken,
|
|
208
|
+
options.apiBaseUrl,
|
|
209
|
+
verificationToken
|
|
210
|
+
).then((result) => {
|
|
211
|
+
if (result) {
|
|
212
|
+
resolve(result);
|
|
213
|
+
} else {
|
|
214
|
+
reject(new Error("Passkey window was closed before completing."));
|
|
215
|
+
}
|
|
216
|
+
}).catch(() => {
|
|
217
|
+
reject(new Error("Passkey window was closed before completing."));
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}, POPUP_CLOSED_GRACE_MS);
|
|
221
|
+
}
|
|
222
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
223
|
+
function cleanup() {
|
|
224
|
+
clearTimeout(timer);
|
|
225
|
+
clearInterval(closedPoll);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
var VERIFY_POPUP_TIMEOUT_MS = 6e4;
|
|
230
|
+
function findDevicePasskeyViaPopup(options) {
|
|
231
|
+
return new Promise((resolve, reject) => {
|
|
232
|
+
const verificationToken = crypto.randomUUID();
|
|
233
|
+
const payload = {
|
|
234
|
+
...options,
|
|
235
|
+
verificationToken
|
|
236
|
+
};
|
|
237
|
+
const encoded = btoa(JSON.stringify(payload));
|
|
238
|
+
const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
|
|
239
|
+
const popup = window.open(popupUrl, "blink-passkey-verify");
|
|
240
|
+
if (!popup) {
|
|
241
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
let settled = false;
|
|
245
|
+
const timer = setTimeout(() => {
|
|
246
|
+
cleanup();
|
|
247
|
+
resolve(null);
|
|
248
|
+
}, VERIFY_POPUP_TIMEOUT_MS);
|
|
249
|
+
const closedPoll = setInterval(() => {
|
|
250
|
+
if (popup.closed && !settled) {
|
|
251
|
+
clearInterval(closedPoll);
|
|
252
|
+
setTimeout(() => {
|
|
253
|
+
if (!settled) {
|
|
254
|
+
settled = true;
|
|
255
|
+
cleanup();
|
|
256
|
+
checkServerForPasskeyByToken(
|
|
257
|
+
options.authToken,
|
|
258
|
+
options.apiBaseUrl,
|
|
259
|
+
verificationToken
|
|
260
|
+
).then((result) => {
|
|
261
|
+
resolve(result?.credentialId ?? null);
|
|
262
|
+
}).catch(() => {
|
|
263
|
+
resolve(null);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}, POPUP_CLOSED_GRACE_MS);
|
|
267
|
+
}
|
|
268
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
269
|
+
function cleanup() {
|
|
270
|
+
clearTimeout(timer);
|
|
271
|
+
clearInterval(closedPoll);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
async function checkServerForPasskeyByToken(authToken, apiBaseUrl, verificationToken) {
|
|
276
|
+
if (!authToken || !apiBaseUrl) return null;
|
|
277
|
+
const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
|
|
278
|
+
headers: { Authorization: `Bearer ${authToken}` }
|
|
279
|
+
});
|
|
280
|
+
if (!res.ok) return null;
|
|
281
|
+
const body = await res.json();
|
|
282
|
+
const passkeys = body.config.passkeys ?? [];
|
|
283
|
+
const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
|
|
284
|
+
return matched ? { credentialId: matched.credentialId, publicKey: matched.publicKey } : null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/passkeyRpId.ts
|
|
288
|
+
function normalizeConfiguredDomain(value) {
|
|
289
|
+
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
290
|
+
}
|
|
291
|
+
function resolveRootDomainFromHostname(hostname) {
|
|
292
|
+
const trimmedHostname = hostname.trim().toLowerCase();
|
|
293
|
+
if (!trimmedHostname) {
|
|
294
|
+
return "localhost";
|
|
295
|
+
}
|
|
296
|
+
if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
|
|
297
|
+
return trimmedHostname;
|
|
298
|
+
}
|
|
299
|
+
const parts = trimmedHostname.split(".").filter(Boolean);
|
|
300
|
+
if (parts.length < 2) {
|
|
301
|
+
return trimmedHostname;
|
|
302
|
+
}
|
|
303
|
+
return parts.slice(-2).join(".");
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/hooks/passkeyPublic.ts
|
|
307
|
+
function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
|
|
308
|
+
return new Promise((resolve, reject) => {
|
|
309
|
+
if (typeof document === "undefined") {
|
|
310
|
+
resolve();
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (document.hasFocus()) {
|
|
314
|
+
resolve();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const deadline = Date.now() + timeoutMs;
|
|
318
|
+
const timer = setInterval(() => {
|
|
319
|
+
if (document.hasFocus()) {
|
|
320
|
+
clearInterval(timer);
|
|
321
|
+
resolve();
|
|
322
|
+
} else if (Date.now() >= deadline) {
|
|
323
|
+
clearInterval(timer);
|
|
324
|
+
resolve();
|
|
325
|
+
}
|
|
326
|
+
}, intervalMs);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
function toBase64(buffer) {
|
|
330
|
+
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
331
|
+
}
|
|
332
|
+
function base64ToBytes(value) {
|
|
333
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
334
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
335
|
+
const raw = atob(padded);
|
|
336
|
+
const bytes = new Uint8Array(raw.length);
|
|
337
|
+
for (let i = 0; i < raw.length; i++) {
|
|
338
|
+
bytes[i] = raw.charCodeAt(i);
|
|
339
|
+
}
|
|
340
|
+
return bytes;
|
|
341
|
+
}
|
|
342
|
+
function readEnvValue(name) {
|
|
343
|
+
const meta = import.meta;
|
|
344
|
+
const metaValue = meta.env?.[name];
|
|
345
|
+
if (typeof metaValue === "string" && metaValue.trim().length > 0) {
|
|
346
|
+
return metaValue.trim();
|
|
347
|
+
}
|
|
348
|
+
const processValue = globalThis.process?.env?.[name];
|
|
349
|
+
if (typeof processValue === "string" && processValue.trim().length > 0) {
|
|
350
|
+
return processValue.trim();
|
|
351
|
+
}
|
|
352
|
+
return void 0;
|
|
353
|
+
}
|
|
354
|
+
function resolvePasskeyRpId() {
|
|
355
|
+
const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
|
|
356
|
+
if (configuredDomain) {
|
|
357
|
+
return normalizeConfiguredDomain(configuredDomain);
|
|
358
|
+
}
|
|
359
|
+
if (typeof window !== "undefined") {
|
|
360
|
+
return resolveRootDomainFromHostname(window.location.hostname);
|
|
361
|
+
}
|
|
362
|
+
return "localhost";
|
|
363
|
+
}
|
|
364
|
+
async function createPasskeyCredential(params) {
|
|
365
|
+
const challenge = new Uint8Array(32);
|
|
366
|
+
crypto.getRandomValues(challenge);
|
|
367
|
+
const rpId = resolvePasskeyRpId();
|
|
368
|
+
const publicKeyOptions = {
|
|
369
|
+
challenge,
|
|
370
|
+
rp: { name: "Blink", id: rpId },
|
|
371
|
+
user: {
|
|
372
|
+
id: new TextEncoder().encode(params.userId),
|
|
373
|
+
name: params.displayName,
|
|
374
|
+
displayName: params.displayName
|
|
375
|
+
},
|
|
376
|
+
pubKeyCredParams: [
|
|
377
|
+
{ alg: -7, type: "public-key" },
|
|
378
|
+
{ alg: -257, type: "public-key" }
|
|
379
|
+
],
|
|
380
|
+
authenticatorSelection: {
|
|
381
|
+
authenticatorAttachment: "platform",
|
|
382
|
+
residentKey: "preferred",
|
|
383
|
+
userVerification: "required"
|
|
384
|
+
},
|
|
385
|
+
timeout: 6e4
|
|
386
|
+
};
|
|
387
|
+
if (isInCrossOriginIframe()) {
|
|
388
|
+
try {
|
|
389
|
+
await waitForDocumentFocus();
|
|
390
|
+
const credential2 = await navigator.credentials.create({
|
|
391
|
+
publicKey: publicKeyOptions
|
|
392
|
+
});
|
|
393
|
+
if (!credential2) {
|
|
394
|
+
throw new Error("Passkey creation was cancelled.");
|
|
395
|
+
}
|
|
396
|
+
return extractPasskeyResult(credential2);
|
|
397
|
+
} catch (err) {
|
|
398
|
+
if (err instanceof PasskeyIframeBlockedError) throw err;
|
|
399
|
+
if (err instanceof Error && err.message === "Passkey creation was cancelled.") throw err;
|
|
400
|
+
throw new PasskeyIframeBlockedError();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
await waitForDocumentFocus();
|
|
404
|
+
const credential = await navigator.credentials.create({
|
|
405
|
+
publicKey: publicKeyOptions
|
|
406
|
+
});
|
|
407
|
+
if (!credential) {
|
|
408
|
+
throw new Error("Passkey creation was cancelled.");
|
|
409
|
+
}
|
|
410
|
+
return extractPasskeyResult(credential);
|
|
411
|
+
}
|
|
412
|
+
function extractPasskeyResult(credential) {
|
|
413
|
+
const response = credential.response;
|
|
414
|
+
const publicKeyBytes = response.getPublicKey?.();
|
|
415
|
+
return {
|
|
416
|
+
credentialId: toBase64(credential.rawId),
|
|
417
|
+
publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function buildPasskeyPopupOptions(params) {
|
|
421
|
+
const challenge = new Uint8Array(32);
|
|
422
|
+
crypto.getRandomValues(challenge);
|
|
423
|
+
const rpId = resolvePasskeyRpId();
|
|
424
|
+
return {
|
|
425
|
+
challenge: toBase64(challenge),
|
|
426
|
+
rpId,
|
|
427
|
+
rpName: "Blink",
|
|
428
|
+
userId: toBase64(new TextEncoder().encode(params.userId)),
|
|
429
|
+
userName: params.displayName,
|
|
430
|
+
userDisplayName: params.displayName,
|
|
431
|
+
pubKeyCredParams: [
|
|
432
|
+
{ alg: -7, type: "public-key" },
|
|
433
|
+
{ alg: -257, type: "public-key" }
|
|
434
|
+
],
|
|
435
|
+
authenticatorSelection: {
|
|
436
|
+
authenticatorAttachment: "platform",
|
|
437
|
+
residentKey: "preferred",
|
|
438
|
+
userVerification: "required"
|
|
439
|
+
},
|
|
440
|
+
timeout: 6e4,
|
|
441
|
+
authToken: params.authToken,
|
|
442
|
+
apiBaseUrl: params.apiBaseUrl
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
async function deviceHasPasskey(credentialId) {
|
|
446
|
+
const found = await findDevicePasskey([credentialId]);
|
|
447
|
+
return found != null;
|
|
448
|
+
}
|
|
449
|
+
async function findDevicePasskey(credentialIds) {
|
|
450
|
+
if (credentialIds.length === 0) return null;
|
|
451
|
+
try {
|
|
452
|
+
const challenge = new Uint8Array(32);
|
|
453
|
+
crypto.getRandomValues(challenge);
|
|
454
|
+
await waitForDocumentFocus();
|
|
455
|
+
const assertion = await navigator.credentials.get({
|
|
456
|
+
publicKey: {
|
|
457
|
+
challenge,
|
|
458
|
+
rpId: resolvePasskeyRpId(),
|
|
459
|
+
allowCredentials: credentialIds.map((id) => ({
|
|
460
|
+
type: "public-key",
|
|
461
|
+
id: base64ToBytes(id)
|
|
462
|
+
})),
|
|
463
|
+
userVerification: "discouraged",
|
|
464
|
+
timeout: 3e4
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
if (!assertion) return null;
|
|
468
|
+
return toBase64(assertion.rawId);
|
|
469
|
+
} catch {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
158
474
|
// src/api.ts
|
|
159
475
|
var api_exports = {};
|
|
160
476
|
__export(api_exports, {
|
|
@@ -166,6 +482,7 @@ __export(api_exports, {
|
|
|
166
482
|
fetchAccount: () => fetchAccount,
|
|
167
483
|
fetchAccounts: () => fetchAccounts,
|
|
168
484
|
fetchAuthorizationSession: () => fetchAuthorizationSession,
|
|
485
|
+
fetchAuthorizationSessionByToken: () => fetchAuthorizationSessionByToken,
|
|
169
486
|
fetchChains: () => fetchChains,
|
|
170
487
|
fetchGuestAccount: () => fetchGuestAccount,
|
|
171
488
|
fetchGuestTransferBalances: () => fetchGuestTransferBalances,
|
|
@@ -345,6 +662,13 @@ async function fetchAuthorizationSession(apiBaseUrl, sessionId) {
|
|
|
345
662
|
if (!res.ok) await throwApiError(res);
|
|
346
663
|
return await res.json();
|
|
347
664
|
}
|
|
665
|
+
async function fetchAuthorizationSessionByToken(apiBaseUrl, token) {
|
|
666
|
+
const res = await fetch(
|
|
667
|
+
`${apiBaseUrl}/v1/authorization-sessions?token=${encodeURIComponent(token)}`
|
|
668
|
+
);
|
|
669
|
+
if (!res.ok) await throwApiError(res);
|
|
670
|
+
return await res.json();
|
|
671
|
+
}
|
|
348
672
|
async function registerPasskey(apiBaseUrl, token, credentialId, publicKey) {
|
|
349
673
|
const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
|
|
350
674
|
method: "POST",
|
|
@@ -501,17 +825,87 @@ async function setAccountOwner(apiBaseUrl, accessToken, accountId, guestSessionT
|
|
|
501
825
|
if (!res.ok) await throwApiError(res);
|
|
502
826
|
return await res.json();
|
|
503
827
|
}
|
|
504
|
-
async function reportActionCompletion(apiBaseUrl, actionId, result) {
|
|
505
|
-
const res = await fetch(
|
|
506
|
-
`${apiBaseUrl}/v1/authorization-actions/${actionId}`,
|
|
507
|
-
{
|
|
508
|
-
method: "PATCH",
|
|
509
|
-
headers: { "Content-Type": "application/json" },
|
|
510
|
-
body: JSON.stringify({ status: "COMPLETED", result })
|
|
828
|
+
async function reportActionCompletion(apiBaseUrl, actionId, result) {
|
|
829
|
+
const res = await fetch(
|
|
830
|
+
`${apiBaseUrl}/v1/authorization-actions/${actionId}`,
|
|
831
|
+
{
|
|
832
|
+
method: "PATCH",
|
|
833
|
+
headers: { "Content-Type": "application/json" },
|
|
834
|
+
body: JSON.stringify({ status: "COMPLETED", result })
|
|
835
|
+
}
|
|
836
|
+
);
|
|
837
|
+
if (!res.ok) await throwApiError(res);
|
|
838
|
+
return await res.json();
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// src/transferPolling.ts
|
|
842
|
+
async function pollTransferTick(params) {
|
|
843
|
+
const fetchTransfer2 = params.fetchTransfer ?? fetchTransfer;
|
|
844
|
+
const token = await params.getAccessToken();
|
|
845
|
+
if (!token) {
|
|
846
|
+
return { kind: "retry" };
|
|
847
|
+
}
|
|
848
|
+
try {
|
|
849
|
+
const transfer = await fetchTransfer2(params.apiBaseUrl, token, params.transferId);
|
|
850
|
+
return { kind: "success", transfer };
|
|
851
|
+
} catch (err) {
|
|
852
|
+
return {
|
|
853
|
+
kind: "error",
|
|
854
|
+
message: err instanceof Error ? err.message : "Polling error"
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// src/hooks/useTransferPolling.ts
|
|
860
|
+
function useTransferPolling(intervalMs = 3e3) {
|
|
861
|
+
const { apiBaseUrl } = useBlinkConfig();
|
|
862
|
+
const { getAccessToken } = usePrivy();
|
|
863
|
+
const [transfer, setTransfer] = useState(null);
|
|
864
|
+
const [error, setError] = useState(null);
|
|
865
|
+
const [isPolling, setIsPolling] = useState(false);
|
|
866
|
+
const intervalRef = useRef(null);
|
|
867
|
+
const transferIdRef = useRef(null);
|
|
868
|
+
const stopPolling = useCallback(() => {
|
|
869
|
+
if (intervalRef.current) {
|
|
870
|
+
clearInterval(intervalRef.current);
|
|
871
|
+
intervalRef.current = null;
|
|
872
|
+
}
|
|
873
|
+
setIsPolling(false);
|
|
874
|
+
}, []);
|
|
875
|
+
const poll = useCallback(async () => {
|
|
876
|
+
if (!transferIdRef.current) return;
|
|
877
|
+
const result = await pollTransferTick({
|
|
878
|
+
apiBaseUrl,
|
|
879
|
+
transferId: transferIdRef.current,
|
|
880
|
+
getAccessToken
|
|
881
|
+
});
|
|
882
|
+
if (result.kind === "retry") {
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
if (result.kind === "error") {
|
|
886
|
+
setError(result.message);
|
|
887
|
+
stopPolling();
|
|
888
|
+
return;
|
|
511
889
|
}
|
|
890
|
+
setError(null);
|
|
891
|
+
setTransfer(result.transfer);
|
|
892
|
+
if (result.transfer.status === "COMPLETED" || result.transfer.status === "FAILED") {
|
|
893
|
+
stopPolling();
|
|
894
|
+
}
|
|
895
|
+
}, [apiBaseUrl, getAccessToken, stopPolling]);
|
|
896
|
+
const startPolling = useCallback(
|
|
897
|
+
(transferId) => {
|
|
898
|
+
stopPolling();
|
|
899
|
+
transferIdRef.current = transferId;
|
|
900
|
+
setIsPolling(true);
|
|
901
|
+
setError(null);
|
|
902
|
+
poll();
|
|
903
|
+
intervalRef.current = setInterval(poll, intervalMs);
|
|
904
|
+
},
|
|
905
|
+
[poll, intervalMs, stopPolling]
|
|
512
906
|
);
|
|
513
|
-
|
|
514
|
-
return
|
|
907
|
+
useEffect(() => () => stopPolling(), [stopPolling]);
|
|
908
|
+
return { transfer, error, isPolling, startPolling, stopPolling };
|
|
515
909
|
}
|
|
516
910
|
|
|
517
911
|
// node_modules/@wagmi/core/dist/esm/utils/getAction.js
|
|
@@ -811,250 +1205,61 @@ async function waitForTransactionReceipt(config, parameters) {
|
|
|
811
1205
|
chainId: client.chain.id
|
|
812
1206
|
};
|
|
813
1207
|
}
|
|
814
|
-
|
|
815
|
-
// src/passkeyRpId.ts
|
|
816
|
-
function normalizeConfiguredDomain(value) {
|
|
817
|
-
return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
|
|
818
|
-
}
|
|
819
|
-
function resolveRootDomainFromHostname(hostname) {
|
|
820
|
-
const trimmedHostname = hostname.trim().toLowerCase();
|
|
821
|
-
if (!trimmedHostname) {
|
|
822
|
-
return "localhost";
|
|
823
|
-
}
|
|
824
|
-
if (trimmedHostname === "localhost" || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(trimmedHostname)) {
|
|
825
|
-
return trimmedHostname;
|
|
826
|
-
}
|
|
827
|
-
const parts = trimmedHostname.split(".").filter(Boolean);
|
|
828
|
-
if (parts.length < 2) {
|
|
829
|
-
return trimmedHostname;
|
|
830
|
-
}
|
|
831
|
-
return parts.slice(-2).join(".");
|
|
832
|
-
}
|
|
833
1208
|
var ERC_6492_MAGIC_SUFFIX = "6492649264926492649264926492649264926492649264926492649264926492";
|
|
834
1209
|
function normalizeSignature(sig) {
|
|
835
1210
|
const hex = sig.startsWith("0x") ? sig.slice(2) : sig;
|
|
836
1211
|
if (hex.length === 130) {
|
|
837
1212
|
return `0x${hex}`;
|
|
838
1213
|
}
|
|
839
|
-
if (hex.length === 128) {
|
|
840
|
-
const r = hex.slice(0, 64);
|
|
841
|
-
const yParityAndS = hex.slice(64, 128);
|
|
842
|
-
const highByte = parseInt(yParityAndS.slice(0, 2), 16);
|
|
843
|
-
const v = (highByte & 128) !== 0 ? 28 : 27;
|
|
844
|
-
const sFirstByte = (highByte & 127).toString(16).padStart(2, "0");
|
|
845
|
-
const s = sFirstByte + yParityAndS.slice(2);
|
|
846
|
-
return `0x${r}${s}${v.toString(16)}`;
|
|
847
|
-
}
|
|
848
|
-
if (hex.length > 64 && hex.endsWith(ERC_6492_MAGIC_SUFFIX)) {
|
|
849
|
-
const { signature: inner } = parseErc6492Signature(
|
|
850
|
-
`0x${hex}`
|
|
851
|
-
);
|
|
852
|
-
return normalizeSignature(inner);
|
|
853
|
-
}
|
|
854
|
-
if (hex.length > 130) {
|
|
855
|
-
try {
|
|
856
|
-
const [, innerBytes] = decodeAbiParameters(
|
|
857
|
-
[{ type: "uint256" }, { type: "bytes" }],
|
|
858
|
-
`0x${hex}`
|
|
859
|
-
);
|
|
860
|
-
return normalizeSignature(innerBytes);
|
|
861
|
-
} catch {
|
|
862
|
-
try {
|
|
863
|
-
const [wrapper] = decodeAbiParameters(
|
|
864
|
-
[{
|
|
865
|
-
type: "tuple",
|
|
866
|
-
components: [{ type: "uint8" }, { type: "bytes" }]
|
|
867
|
-
}],
|
|
868
|
-
`0x${hex}`
|
|
869
|
-
);
|
|
870
|
-
return normalizeSignature(wrapper[1]);
|
|
871
|
-
} catch {
|
|
872
|
-
return `0x${hex}`;
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
throw new Error(
|
|
877
|
-
`Invalid signature: unable to normalize. Length=${hex.length / 2} bytes. Expected 65, 64, ERC-6492 wrapped, or ABI-encoded SignatureWrapper.`
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
// src/transferPolling.ts
|
|
882
|
-
async function pollTransferTick(params) {
|
|
883
|
-
const fetchTransfer2 = params.fetchTransfer ?? fetchTransfer;
|
|
884
|
-
const token = await params.getAccessToken();
|
|
885
|
-
if (!token) {
|
|
886
|
-
return { kind: "retry" };
|
|
887
|
-
}
|
|
888
|
-
try {
|
|
889
|
-
const transfer = await fetchTransfer2(params.apiBaseUrl, token, params.transferId);
|
|
890
|
-
return { kind: "success", transfer };
|
|
891
|
-
} catch (err) {
|
|
892
|
-
return {
|
|
893
|
-
kind: "error",
|
|
894
|
-
message: err instanceof Error ? err.message : "Polling error"
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
// src/passkey-delegation.ts
|
|
900
|
-
var PasskeyIframeBlockedError = class extends Error {
|
|
901
|
-
constructor(message = "Passkey creation is not supported in this browser context.") {
|
|
902
|
-
super(message);
|
|
903
|
-
this.name = "PasskeyIframeBlockedError";
|
|
904
|
-
}
|
|
905
|
-
};
|
|
906
|
-
function isInCrossOriginIframe() {
|
|
907
|
-
if (typeof window === "undefined") return false;
|
|
908
|
-
if (window.parent === window) return false;
|
|
909
|
-
try {
|
|
910
|
-
void window.parent.location.origin;
|
|
911
|
-
return false;
|
|
912
|
-
} catch {
|
|
913
|
-
return true;
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
function isSafari() {
|
|
917
|
-
if (typeof navigator === "undefined") return false;
|
|
918
|
-
const ua = navigator.userAgent;
|
|
919
|
-
return /Safari/i.test(ua) && !/Chrome|CriOS|Chromium|Edg|OPR|Firefox/i.test(ua);
|
|
920
|
-
}
|
|
921
|
-
var POPUP_RESULT_TIMEOUT_MS = 12e4;
|
|
922
|
-
var POPUP_CLOSED_POLL_MS = 500;
|
|
923
|
-
var POPUP_CLOSED_GRACE_MS = 1e3;
|
|
924
|
-
function createPasskeyViaPopup(options) {
|
|
925
|
-
return new Promise((resolve, reject) => {
|
|
926
|
-
const verificationToken = crypto.randomUUID();
|
|
927
|
-
const payload = { ...options, verificationToken };
|
|
928
|
-
const encoded = btoa(JSON.stringify(payload));
|
|
929
|
-
const popupUrl = `${window.location.origin}/passkey-register#${encoded}`;
|
|
930
|
-
const popup = window.open(popupUrl, "blink-passkey");
|
|
931
|
-
if (!popup) {
|
|
932
|
-
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
let settled = false;
|
|
936
|
-
const timer = setTimeout(() => {
|
|
937
|
-
cleanup();
|
|
938
|
-
reject(new Error("Passkey creation timed out. Please try again."));
|
|
939
|
-
}, POPUP_RESULT_TIMEOUT_MS);
|
|
940
|
-
const closedPoll = setInterval(() => {
|
|
941
|
-
if (popup.closed) {
|
|
942
|
-
clearInterval(closedPoll);
|
|
943
|
-
setTimeout(() => {
|
|
944
|
-
if (!settled) {
|
|
945
|
-
settled = true;
|
|
946
|
-
cleanup();
|
|
947
|
-
checkServerForPasskeyByToken(
|
|
948
|
-
options.authToken,
|
|
949
|
-
options.apiBaseUrl,
|
|
950
|
-
verificationToken
|
|
951
|
-
).then((result) => {
|
|
952
|
-
if (result) {
|
|
953
|
-
resolve(result);
|
|
954
|
-
} else {
|
|
955
|
-
reject(new Error("Passkey window was closed before completing."));
|
|
956
|
-
}
|
|
957
|
-
}).catch(() => {
|
|
958
|
-
reject(new Error("Passkey window was closed before completing."));
|
|
959
|
-
});
|
|
960
|
-
}
|
|
961
|
-
}, POPUP_CLOSED_GRACE_MS);
|
|
962
|
-
}
|
|
963
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
964
|
-
function cleanup() {
|
|
965
|
-
clearTimeout(timer);
|
|
966
|
-
clearInterval(closedPoll);
|
|
967
|
-
}
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
var VERIFY_POPUP_TIMEOUT_MS = 6e4;
|
|
971
|
-
function findDevicePasskeyViaPopup(options) {
|
|
972
|
-
return new Promise((resolve, reject) => {
|
|
973
|
-
const verificationToken = crypto.randomUUID();
|
|
974
|
-
const payload = {
|
|
975
|
-
...options,
|
|
976
|
-
verificationToken
|
|
977
|
-
};
|
|
978
|
-
const encoded = btoa(JSON.stringify(payload));
|
|
979
|
-
const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
|
|
980
|
-
const popup = window.open(popupUrl, "blink-passkey-verify");
|
|
981
|
-
if (!popup) {
|
|
982
|
-
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
983
|
-
return;
|
|
984
|
-
}
|
|
985
|
-
let settled = false;
|
|
986
|
-
const timer = setTimeout(() => {
|
|
987
|
-
cleanup();
|
|
988
|
-
resolve(null);
|
|
989
|
-
}, VERIFY_POPUP_TIMEOUT_MS);
|
|
990
|
-
const closedPoll = setInterval(() => {
|
|
991
|
-
if (popup.closed && !settled) {
|
|
992
|
-
clearInterval(closedPoll);
|
|
993
|
-
setTimeout(() => {
|
|
994
|
-
if (!settled) {
|
|
995
|
-
settled = true;
|
|
996
|
-
cleanup();
|
|
997
|
-
checkServerForPasskeyByToken(
|
|
998
|
-
options.authToken,
|
|
999
|
-
options.apiBaseUrl,
|
|
1000
|
-
verificationToken
|
|
1001
|
-
).then((result) => {
|
|
1002
|
-
resolve(result?.credentialId ?? null);
|
|
1003
|
-
}).catch(() => {
|
|
1004
|
-
resolve(null);
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
}, POPUP_CLOSED_GRACE_MS);
|
|
1214
|
+
if (hex.length === 128) {
|
|
1215
|
+
const r = hex.slice(0, 64);
|
|
1216
|
+
const yParityAndS = hex.slice(64, 128);
|
|
1217
|
+
const highByte = parseInt(yParityAndS.slice(0, 2), 16);
|
|
1218
|
+
const v = (highByte & 128) !== 0 ? 28 : 27;
|
|
1219
|
+
const sFirstByte = (highByte & 127).toString(16).padStart(2, "0");
|
|
1220
|
+
const s = sFirstByte + yParityAndS.slice(2);
|
|
1221
|
+
return `0x${r}${s}${v.toString(16)}`;
|
|
1222
|
+
}
|
|
1223
|
+
if (hex.length > 64 && hex.endsWith(ERC_6492_MAGIC_SUFFIX)) {
|
|
1224
|
+
const { signature: inner } = parseErc6492Signature(
|
|
1225
|
+
`0x${hex}`
|
|
1226
|
+
);
|
|
1227
|
+
return normalizeSignature(inner);
|
|
1228
|
+
}
|
|
1229
|
+
if (hex.length > 130) {
|
|
1230
|
+
try {
|
|
1231
|
+
const [, innerBytes] = decodeAbiParameters(
|
|
1232
|
+
[{ type: "uint256" }, { type: "bytes" }],
|
|
1233
|
+
`0x${hex}`
|
|
1234
|
+
);
|
|
1235
|
+
return normalizeSignature(innerBytes);
|
|
1236
|
+
} catch {
|
|
1237
|
+
try {
|
|
1238
|
+
const [wrapper] = decodeAbiParameters(
|
|
1239
|
+
[{
|
|
1240
|
+
type: "tuple",
|
|
1241
|
+
components: [{ type: "uint8" }, { type: "bytes" }]
|
|
1242
|
+
}],
|
|
1243
|
+
`0x${hex}`
|
|
1244
|
+
);
|
|
1245
|
+
return normalizeSignature(wrapper[1]);
|
|
1246
|
+
} catch {
|
|
1247
|
+
return `0x${hex}`;
|
|
1008
1248
|
}
|
|
1009
|
-
}, POPUP_CLOSED_POLL_MS);
|
|
1010
|
-
function cleanup() {
|
|
1011
|
-
clearTimeout(timer);
|
|
1012
|
-
clearInterval(closedPoll);
|
|
1013
1249
|
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
|
|
1019
|
-
headers: { Authorization: `Bearer ${authToken}` }
|
|
1020
|
-
});
|
|
1021
|
-
if (!res.ok) return null;
|
|
1022
|
-
const body = await res.json();
|
|
1023
|
-
const passkeys = body.config.passkeys ?? [];
|
|
1024
|
-
const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
|
|
1025
|
-
return matched ? { credentialId: matched.credentialId, publicKey: matched.publicKey } : null;
|
|
1250
|
+
}
|
|
1251
|
+
throw new Error(
|
|
1252
|
+
`Invalid signature: unable to normalize. Length=${hex.length / 2} bytes. Expected 65, 64, ERC-6492 wrapped, or ABI-encoded SignatureWrapper.`
|
|
1253
|
+
);
|
|
1026
1254
|
}
|
|
1027
1255
|
|
|
1028
|
-
// src/hooks.ts
|
|
1256
|
+
// src/hooks/authorizationExecutor.ts
|
|
1029
1257
|
var WALLET_CLIENT_MAX_ATTEMPTS = 25;
|
|
1030
1258
|
var WALLET_CLIENT_POLL_MS = 400;
|
|
1031
1259
|
var ACTION_POLL_INTERVAL_MS = 500;
|
|
1032
1260
|
var ACTION_POLL_MAX_RETRIES = 20;
|
|
1033
1261
|
var SIGN_PERMIT2_POLL_MS = 1e3;
|
|
1034
1262
|
var SIGN_PERMIT2_MAX_POLLS = 15;
|
|
1035
|
-
var TRANSFER_SIGN_MAX_POLLS = 60;
|
|
1036
|
-
function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
|
|
1037
|
-
return new Promise((resolve, reject) => {
|
|
1038
|
-
if (typeof document === "undefined") {
|
|
1039
|
-
resolve();
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1042
|
-
if (document.hasFocus()) {
|
|
1043
|
-
resolve();
|
|
1044
|
-
return;
|
|
1045
|
-
}
|
|
1046
|
-
const deadline = Date.now() + timeoutMs;
|
|
1047
|
-
const timer = setInterval(() => {
|
|
1048
|
-
if (document.hasFocus()) {
|
|
1049
|
-
clearInterval(timer);
|
|
1050
|
-
resolve();
|
|
1051
|
-
} else if (Date.now() >= deadline) {
|
|
1052
|
-
clearInterval(timer);
|
|
1053
|
-
resolve();
|
|
1054
|
-
}
|
|
1055
|
-
}, intervalMs);
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
1263
|
function actionSuccess(action, message, data) {
|
|
1059
1264
|
return { actionId: action.id, type: action.type, status: "success", message, data };
|
|
1060
1265
|
}
|
|
@@ -1065,243 +1270,44 @@ function isUserRejection(msg) {
|
|
|
1065
1270
|
const lower = msg.toLowerCase();
|
|
1066
1271
|
return lower.includes("rejected") || lower.includes("denied");
|
|
1067
1272
|
}
|
|
1068
|
-
function hexToBytes(hex) {
|
|
1069
|
-
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1070
|
-
const bytes = clean.match(/.{1,2}/g).map((b) => parseInt(b, 16));
|
|
1071
|
-
return new Uint8Array(bytes);
|
|
1072
|
-
}
|
|
1073
|
-
function toBase64(buffer) {
|
|
1074
|
-
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
1075
|
-
}
|
|
1076
|
-
function base64ToBytes(value) {
|
|
1077
|
-
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
1078
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
1079
|
-
const raw = atob(padded);
|
|
1080
|
-
const bytes = new Uint8Array(raw.length);
|
|
1081
|
-
for (let i = 0; i < raw.length; i++) {
|
|
1082
|
-
bytes[i] = raw.charCodeAt(i);
|
|
1083
|
-
}
|
|
1084
|
-
return bytes;
|
|
1085
|
-
}
|
|
1086
|
-
function readEnvValue(name) {
|
|
1087
|
-
const meta = import.meta;
|
|
1088
|
-
const metaValue = meta.env?.[name];
|
|
1089
|
-
if (typeof metaValue === "string" && metaValue.trim().length > 0) {
|
|
1090
|
-
return metaValue.trim();
|
|
1091
|
-
}
|
|
1092
|
-
const processValue = globalThis.process?.env?.[name];
|
|
1093
|
-
if (typeof processValue === "string" && processValue.trim().length > 0) {
|
|
1094
|
-
return processValue.trim();
|
|
1095
|
-
}
|
|
1096
|
-
return void 0;
|
|
1097
|
-
}
|
|
1098
|
-
function resolvePasskeyRpId() {
|
|
1099
|
-
const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("BLINK_DOMAIN");
|
|
1100
|
-
if (configuredDomain) {
|
|
1101
|
-
return normalizeConfiguredDomain(configuredDomain);
|
|
1102
|
-
}
|
|
1103
|
-
if (typeof window !== "undefined") {
|
|
1104
|
-
return resolveRootDomainFromHostname(window.location.hostname);
|
|
1105
|
-
}
|
|
1106
|
-
return "localhost";
|
|
1107
|
-
}
|
|
1108
1273
|
async function waitForWalletClient(wagmiConfig2, params = {}) {
|
|
1109
|
-
for (let i = 0; i < WALLET_CLIENT_MAX_ATTEMPTS; i++) {
|
|
1110
|
-
try {
|
|
1111
|
-
const account = getAccount(wagmiConfig2);
|
|
1112
|
-
const enrichedParams = account.connector ? { ...params, connector: account.connector } : params;
|
|
1113
|
-
return await getWalletClient(wagmiConfig2, enrichedParams);
|
|
1114
|
-
} catch {
|
|
1115
|
-
if (i === WALLET_CLIENT_MAX_ATTEMPTS - 1) {
|
|
1116
|
-
throw new Error("Wallet not ready. Please try again.");
|
|
1117
|
-
}
|
|
1118
|
-
await new Promise((r) => setTimeout(r, WALLET_CLIENT_POLL_MS));
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
throw new Error("Wallet not ready. Please try again.");
|
|
1122
|
-
}
|
|
1123
|
-
function parseSignTypedDataPayload(typedData) {
|
|
1124
|
-
const { domain, types, primaryType, message } = typedData;
|
|
1125
|
-
if (!domain || typeof domain !== "object" || Array.isArray(domain)) {
|
|
1126
|
-
throw new Error("SIGN_PERMIT2 typedData is missing a valid domain object.");
|
|
1127
|
-
}
|
|
1128
|
-
if (!types || typeof types !== "object" || Array.isArray(types)) {
|
|
1129
|
-
throw new Error("SIGN_PERMIT2 typedData is missing a valid types object.");
|
|
1130
|
-
}
|
|
1131
|
-
if (typeof primaryType !== "string") {
|
|
1132
|
-
throw new Error("SIGN_PERMIT2 typedData is missing primaryType.");
|
|
1133
|
-
}
|
|
1134
|
-
if (!message || typeof message !== "object" || Array.isArray(message)) {
|
|
1135
|
-
throw new Error("SIGN_PERMIT2 typedData is missing a valid message object.");
|
|
1136
|
-
}
|
|
1137
|
-
return {
|
|
1138
|
-
domain,
|
|
1139
|
-
types,
|
|
1140
|
-
primaryType,
|
|
1141
|
-
message
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
function getPendingActions(session, completedIds) {
|
|
1145
|
-
return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
|
|
1146
|
-
}
|
|
1147
|
-
async function createPasskeyCredential(params) {
|
|
1148
|
-
const challenge = new Uint8Array(32);
|
|
1149
|
-
crypto.getRandomValues(challenge);
|
|
1150
|
-
const rpId = resolvePasskeyRpId();
|
|
1151
|
-
const publicKeyOptions = {
|
|
1152
|
-
challenge,
|
|
1153
|
-
rp: { name: "Blink", id: rpId },
|
|
1154
|
-
user: {
|
|
1155
|
-
id: new TextEncoder().encode(params.userId),
|
|
1156
|
-
name: params.displayName,
|
|
1157
|
-
displayName: params.displayName
|
|
1158
|
-
},
|
|
1159
|
-
pubKeyCredParams: [
|
|
1160
|
-
{ alg: -7, type: "public-key" },
|
|
1161
|
-
{ alg: -257, type: "public-key" }
|
|
1162
|
-
],
|
|
1163
|
-
authenticatorSelection: {
|
|
1164
|
-
authenticatorAttachment: "platform",
|
|
1165
|
-
residentKey: "preferred",
|
|
1166
|
-
userVerification: "required"
|
|
1167
|
-
},
|
|
1168
|
-
timeout: 6e4
|
|
1169
|
-
};
|
|
1170
|
-
if (isInCrossOriginIframe()) {
|
|
1171
|
-
try {
|
|
1172
|
-
await waitForDocumentFocus();
|
|
1173
|
-
const credential2 = await navigator.credentials.create({
|
|
1174
|
-
publicKey: publicKeyOptions
|
|
1175
|
-
});
|
|
1176
|
-
if (!credential2) {
|
|
1177
|
-
throw new Error("Passkey creation was cancelled.");
|
|
1178
|
-
}
|
|
1179
|
-
return extractPasskeyResult(credential2);
|
|
1180
|
-
} catch (err) {
|
|
1181
|
-
if (err instanceof PasskeyIframeBlockedError) throw err;
|
|
1182
|
-
if (err instanceof Error && err.message === "Passkey creation was cancelled.") throw err;
|
|
1183
|
-
throw new PasskeyIframeBlockedError();
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
await waitForDocumentFocus();
|
|
1187
|
-
const credential = await navigator.credentials.create({
|
|
1188
|
-
publicKey: publicKeyOptions
|
|
1189
|
-
});
|
|
1190
|
-
if (!credential) {
|
|
1191
|
-
throw new Error("Passkey creation was cancelled.");
|
|
1192
|
-
}
|
|
1193
|
-
return extractPasskeyResult(credential);
|
|
1194
|
-
}
|
|
1195
|
-
function extractPasskeyResult(credential) {
|
|
1196
|
-
const response = credential.response;
|
|
1197
|
-
const publicKeyBytes = response.getPublicKey?.();
|
|
1198
|
-
return {
|
|
1199
|
-
credentialId: toBase64(credential.rawId),
|
|
1200
|
-
publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
|
|
1201
|
-
};
|
|
1202
|
-
}
|
|
1203
|
-
function buildPasskeyPopupOptions(params) {
|
|
1204
|
-
const challenge = new Uint8Array(32);
|
|
1205
|
-
crypto.getRandomValues(challenge);
|
|
1206
|
-
const rpId = resolvePasskeyRpId();
|
|
1207
|
-
return {
|
|
1208
|
-
challenge: toBase64(challenge),
|
|
1209
|
-
rpId,
|
|
1210
|
-
rpName: "Blink",
|
|
1211
|
-
userId: toBase64(new TextEncoder().encode(params.userId)),
|
|
1212
|
-
userName: params.displayName,
|
|
1213
|
-
userDisplayName: params.displayName,
|
|
1214
|
-
pubKeyCredParams: [
|
|
1215
|
-
{ alg: -7, type: "public-key" },
|
|
1216
|
-
{ alg: -257, type: "public-key" }
|
|
1217
|
-
],
|
|
1218
|
-
authenticatorSelection: {
|
|
1219
|
-
authenticatorAttachment: "platform",
|
|
1220
|
-
residentKey: "preferred",
|
|
1221
|
-
userVerification: "required"
|
|
1222
|
-
},
|
|
1223
|
-
timeout: 6e4,
|
|
1224
|
-
authToken: params.authToken,
|
|
1225
|
-
apiBaseUrl: params.apiBaseUrl
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
async function deviceHasPasskey(credentialId) {
|
|
1229
|
-
const found = await findDevicePasskey([credentialId]);
|
|
1230
|
-
return found != null;
|
|
1231
|
-
}
|
|
1232
|
-
async function findDevicePasskey(credentialIds) {
|
|
1233
|
-
if (credentialIds.length === 0) return null;
|
|
1234
|
-
try {
|
|
1235
|
-
const challenge = new Uint8Array(32);
|
|
1236
|
-
crypto.getRandomValues(challenge);
|
|
1237
|
-
await waitForDocumentFocus();
|
|
1238
|
-
const assertion = await navigator.credentials.get({
|
|
1239
|
-
publicKey: {
|
|
1240
|
-
challenge,
|
|
1241
|
-
rpId: resolvePasskeyRpId(),
|
|
1242
|
-
allowCredentials: credentialIds.map((id) => ({
|
|
1243
|
-
type: "public-key",
|
|
1244
|
-
id: base64ToBytes(id)
|
|
1245
|
-
})),
|
|
1246
|
-
userVerification: "discouraged",
|
|
1247
|
-
timeout: 3e4
|
|
1274
|
+
for (let i = 0; i < WALLET_CLIENT_MAX_ATTEMPTS; i++) {
|
|
1275
|
+
try {
|
|
1276
|
+
const account = getAccount(wagmiConfig2);
|
|
1277
|
+
const enrichedParams = account.connector ? { ...params, connector: account.connector } : params;
|
|
1278
|
+
return await getWalletClient(wagmiConfig2, enrichedParams);
|
|
1279
|
+
} catch {
|
|
1280
|
+
if (i === WALLET_CLIENT_MAX_ATTEMPTS - 1) {
|
|
1281
|
+
throw new Error("Wallet not ready. Please try again.");
|
|
1248
1282
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
return toBase64(assertion.rawId);
|
|
1252
|
-
} catch {
|
|
1253
|
-
return null;
|
|
1283
|
+
await new Promise((r) => setTimeout(r, WALLET_CLIENT_POLL_MS));
|
|
1284
|
+
}
|
|
1254
1285
|
}
|
|
1286
|
+
throw new Error("Wallet not ready. Please try again.");
|
|
1255
1287
|
}
|
|
1256
|
-
function
|
|
1257
|
-
const {
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
return;
|
|
1280
|
-
}
|
|
1281
|
-
if (result.kind === "error") {
|
|
1282
|
-
setError(result.message);
|
|
1283
|
-
stopPolling();
|
|
1284
|
-
return;
|
|
1285
|
-
}
|
|
1286
|
-
setError(null);
|
|
1287
|
-
setTransfer(result.transfer);
|
|
1288
|
-
if (result.transfer.status === "COMPLETED" || result.transfer.status === "FAILED") {
|
|
1289
|
-
stopPolling();
|
|
1290
|
-
}
|
|
1291
|
-
}, [apiBaseUrl, getAccessToken, stopPolling]);
|
|
1292
|
-
const startPolling = useCallback(
|
|
1293
|
-
(transferId) => {
|
|
1294
|
-
stopPolling();
|
|
1295
|
-
transferIdRef.current = transferId;
|
|
1296
|
-
setIsPolling(true);
|
|
1297
|
-
setError(null);
|
|
1298
|
-
poll();
|
|
1299
|
-
intervalRef.current = setInterval(poll, intervalMs);
|
|
1300
|
-
},
|
|
1301
|
-
[poll, intervalMs, stopPolling]
|
|
1302
|
-
);
|
|
1303
|
-
useEffect(() => () => stopPolling(), [stopPolling]);
|
|
1304
|
-
return { transfer, error, isPolling, startPolling, stopPolling };
|
|
1288
|
+
function parseSignTypedDataPayload(typedData) {
|
|
1289
|
+
const { domain, types, primaryType, message } = typedData;
|
|
1290
|
+
if (!domain || typeof domain !== "object" || Array.isArray(domain)) {
|
|
1291
|
+
throw new Error("SIGN_PERMIT2 typedData is missing a valid domain object.");
|
|
1292
|
+
}
|
|
1293
|
+
if (!types || typeof types !== "object" || Array.isArray(types)) {
|
|
1294
|
+
throw new Error("SIGN_PERMIT2 typedData is missing a valid types object.");
|
|
1295
|
+
}
|
|
1296
|
+
if (typeof primaryType !== "string") {
|
|
1297
|
+
throw new Error("SIGN_PERMIT2 typedData is missing primaryType.");
|
|
1298
|
+
}
|
|
1299
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) {
|
|
1300
|
+
throw new Error("SIGN_PERMIT2 typedData is missing a valid message object.");
|
|
1301
|
+
}
|
|
1302
|
+
return {
|
|
1303
|
+
domain,
|
|
1304
|
+
types,
|
|
1305
|
+
primaryType,
|
|
1306
|
+
message
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
function getPendingActions(session, completedIds) {
|
|
1310
|
+
return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
|
|
1305
1311
|
}
|
|
1306
1312
|
async function executeOpenProvider(action, wagmiConfig2, connectors, connectAsync) {
|
|
1307
1313
|
try {
|
|
@@ -1699,6 +1705,47 @@ function useAuthorizationExecutor(options) {
|
|
|
1699
1705
|
executeSessionById
|
|
1700
1706
|
};
|
|
1701
1707
|
}
|
|
1708
|
+
var TRANSFER_SIGN_MAX_POLLS = 60;
|
|
1709
|
+
function waitForDocumentFocus2(timeoutMs = 5e3, intervalMs = 100) {
|
|
1710
|
+
return new Promise((resolve, reject) => {
|
|
1711
|
+
if (typeof document === "undefined") {
|
|
1712
|
+
resolve();
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
if (document.hasFocus()) {
|
|
1716
|
+
resolve();
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
const deadline = Date.now() + timeoutMs;
|
|
1720
|
+
const timer = setInterval(() => {
|
|
1721
|
+
if (document.hasFocus()) {
|
|
1722
|
+
clearInterval(timer);
|
|
1723
|
+
resolve();
|
|
1724
|
+
} else if (Date.now() >= deadline) {
|
|
1725
|
+
clearInterval(timer);
|
|
1726
|
+
resolve();
|
|
1727
|
+
}
|
|
1728
|
+
}, intervalMs);
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
function hexToBytes(hex) {
|
|
1732
|
+
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1733
|
+
const bytes = clean.match(/.{1,2}/g).map((b) => parseInt(b, 16));
|
|
1734
|
+
return new Uint8Array(bytes);
|
|
1735
|
+
}
|
|
1736
|
+
function toBase642(buffer) {
|
|
1737
|
+
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
1738
|
+
}
|
|
1739
|
+
function base64ToBytes2(value) {
|
|
1740
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
1741
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
1742
|
+
const raw = atob(padded);
|
|
1743
|
+
const bytes = new Uint8Array(raw.length);
|
|
1744
|
+
for (let i = 0; i < raw.length; i++) {
|
|
1745
|
+
bytes[i] = raw.charCodeAt(i);
|
|
1746
|
+
}
|
|
1747
|
+
return bytes;
|
|
1748
|
+
}
|
|
1702
1749
|
function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
1703
1750
|
const blinkConfig = useOptionalBlinkConfig();
|
|
1704
1751
|
const apiBaseUrl = options?.apiBaseUrl ?? blinkConfig?.apiBaseUrl;
|
|
@@ -1754,9 +1801,9 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
1754
1801
|
let signedUserOp;
|
|
1755
1802
|
const allowCredentials = payload.passkeyCredentialId ? [{
|
|
1756
1803
|
type: "public-key",
|
|
1757
|
-
id:
|
|
1804
|
+
id: base64ToBytes2(payload.passkeyCredentialId)
|
|
1758
1805
|
}] : void 0;
|
|
1759
|
-
await
|
|
1806
|
+
await waitForDocumentFocus2();
|
|
1760
1807
|
const assertion = await navigator.credentials.get({
|
|
1761
1808
|
publicKey: {
|
|
1762
1809
|
challenge: hashBytes,
|
|
@@ -1772,10 +1819,10 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
1772
1819
|
const response = assertion.response;
|
|
1773
1820
|
signedUserOp = {
|
|
1774
1821
|
...payload.userOp,
|
|
1775
|
-
credentialId:
|
|
1776
|
-
signature:
|
|
1777
|
-
authenticatorData:
|
|
1778
|
-
clientDataJSON:
|
|
1822
|
+
credentialId: toBase642(assertion.rawId),
|
|
1823
|
+
signature: toBase642(response.signature),
|
|
1824
|
+
authenticatorData: toBase642(response.authenticatorData),
|
|
1825
|
+
clientDataJSON: toBase642(response.clientDataJSON)
|
|
1779
1826
|
};
|
|
1780
1827
|
return await signTransfer(
|
|
1781
1828
|
apiBaseUrl,
|
|
@@ -1934,6 +1981,87 @@ function buildSelectSourceChoices(options) {
|
|
|
1934
1981
|
})).filter((chain) => chain.tokens.length > 0).sort((a, b) => b.balance - a.balance);
|
|
1935
1982
|
}
|
|
1936
1983
|
|
|
1984
|
+
// src/walletFlow.ts
|
|
1985
|
+
var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
|
1986
|
+
function isMobileUserAgent(userAgent) {
|
|
1987
|
+
if (!userAgent) {
|
|
1988
|
+
return false;
|
|
1989
|
+
}
|
|
1990
|
+
return MOBILE_USER_AGENT_PATTERN.test(userAgent);
|
|
1991
|
+
}
|
|
1992
|
+
function shouldUseWalletConnector(options) {
|
|
1993
|
+
return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// src/paymentResolvePhase.ts
|
|
1997
|
+
function hasActiveWallet(accounts) {
|
|
1998
|
+
return accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
|
|
1999
|
+
}
|
|
2000
|
+
function isTransferInFlight(transfer) {
|
|
2001
|
+
if (!transfer) return false;
|
|
2002
|
+
return ["CREATED", "SENDING", "SENT"].includes(transfer.status);
|
|
2003
|
+
}
|
|
2004
|
+
function resolvePhase(state) {
|
|
2005
|
+
const p = state.phase;
|
|
2006
|
+
if (p.step === "select-source" && state.transfer?.status === "COMPLETED" && state.guestPreauthorizing) {
|
|
2007
|
+
return p;
|
|
2008
|
+
}
|
|
2009
|
+
if (state.transfer?.status === "COMPLETED" && state.guestPreauthorizing) {
|
|
2010
|
+
return { step: "processing", transfer: state.transfer };
|
|
2011
|
+
}
|
|
2012
|
+
if (state.transfer?.status === "COMPLETED") {
|
|
2013
|
+
return { step: "completed", transfer: state.transfer };
|
|
2014
|
+
}
|
|
2015
|
+
if (state.transfer?.status === "FAILED") {
|
|
2016
|
+
return { step: "failed", transfer: state.transfer, error: state.error ?? "Transfer failed." };
|
|
2017
|
+
}
|
|
2018
|
+
if (state.creatingTransfer || isTransferInFlight(state.transfer)) {
|
|
2019
|
+
return { step: "processing", transfer: state.transfer };
|
|
2020
|
+
}
|
|
2021
|
+
if (p.step === "token-picker" || p.step === "one-tap-setup" || p.step === "select-source" || p.step === "confirm-sign" || p.step === "guest-token-picker") {
|
|
2022
|
+
return p;
|
|
2023
|
+
}
|
|
2024
|
+
if (p.step === "wallet-setup") return p;
|
|
2025
|
+
if (state.mobileFlow && state.deeplinkUri) {
|
|
2026
|
+
return {
|
|
2027
|
+
step: "wallet-setup",
|
|
2028
|
+
mobile: { deeplinkUri: state.deeplinkUri, providerId: state.selectedProviderId },
|
|
2029
|
+
accountId: null
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
if (!state.activeCredentialId && !state.passkeyConfigLoaded) {
|
|
2033
|
+
return { step: "initializing" };
|
|
2034
|
+
}
|
|
2035
|
+
if (state.verificationTarget) {
|
|
2036
|
+
return { step: "otp-verify", target: state.verificationTarget };
|
|
2037
|
+
}
|
|
2038
|
+
if (state.loginRequested) {
|
|
2039
|
+
return { step: "login" };
|
|
2040
|
+
}
|
|
2041
|
+
if (state.passkeyConfigLoaded && !state.activeCredentialId) {
|
|
2042
|
+
if (state.knownCredentialIds.length > 0 && state.passkeyPopupNeeded) {
|
|
2043
|
+
return { step: "passkey-verify" };
|
|
2044
|
+
}
|
|
2045
|
+
return { step: "passkey-create", popupFallback: state.passkeyPopupNeeded };
|
|
2046
|
+
}
|
|
2047
|
+
if (state.loadingData && state.activeCredentialId && hasActiveWallet(state.accounts)) {
|
|
2048
|
+
return { step: "data-loading" };
|
|
2049
|
+
}
|
|
2050
|
+
if (state.isGuestFlow && state.selectedProviderId != null && !state.transfer) {
|
|
2051
|
+
return { step: "guest-token-picker" };
|
|
2052
|
+
}
|
|
2053
|
+
if (state.activeCredentialId && !hasActiveWallet(state.accounts) && !state.mobileFlow) {
|
|
2054
|
+
return { step: "wallet-picker", reason: "link" };
|
|
2055
|
+
}
|
|
2056
|
+
if (state.activeCredentialId && hasActiveWallet(state.accounts) && !state.loadingData) {
|
|
2057
|
+
return { step: "deposit" };
|
|
2058
|
+
}
|
|
2059
|
+
if (state.isGuestFlow) {
|
|
2060
|
+
return { step: "wallet-picker", reason: "guest-entry" };
|
|
2061
|
+
}
|
|
2062
|
+
return { step: "wallet-picker", reason: "entry" };
|
|
2063
|
+
}
|
|
2064
|
+
|
|
1937
2065
|
// src/paymentReducer.ts
|
|
1938
2066
|
function deriveSourceTypeAndId(state) {
|
|
1939
2067
|
if (state.selectedWalletId) {
|
|
@@ -1946,6 +2074,7 @@ function deriveSourceTypeAndId(state) {
|
|
|
1946
2074
|
}
|
|
1947
2075
|
function createInitialState(config) {
|
|
1948
2076
|
return {
|
|
2077
|
+
phase: { step: "initializing" },
|
|
1949
2078
|
error: null,
|
|
1950
2079
|
providers: [],
|
|
1951
2080
|
accounts: [],
|
|
@@ -1966,6 +2095,7 @@ function createInitialState(config) {
|
|
|
1966
2095
|
knownCredentialIds: [],
|
|
1967
2096
|
verificationTarget: null,
|
|
1968
2097
|
oneTapLimit: 100,
|
|
2098
|
+
oneTapLimitSavedDuringSetup: false,
|
|
1969
2099
|
mobileFlow: false,
|
|
1970
2100
|
deeplinkUri: null,
|
|
1971
2101
|
increasingLimit: false,
|
|
@@ -1973,12 +2103,17 @@ function createInitialState(config) {
|
|
|
1973
2103
|
guestTransferId: null,
|
|
1974
2104
|
guestSessionToken: null,
|
|
1975
2105
|
guestPreauthAccountId: null,
|
|
2106
|
+
guestPreauthSessionId: null,
|
|
1976
2107
|
activePublicKey: null,
|
|
1977
|
-
|
|
1978
|
-
|
|
2108
|
+
loginRequested: false,
|
|
2109
|
+
guestPreauthorizing: false
|
|
1979
2110
|
};
|
|
1980
2111
|
}
|
|
1981
2112
|
function paymentReducer(state, action) {
|
|
2113
|
+
const next = applyAction(state, action);
|
|
2114
|
+
return { ...next, phase: resolvePhase(next) };
|
|
2115
|
+
}
|
|
2116
|
+
function applyAction(state, action) {
|
|
1982
2117
|
switch (action.type) {
|
|
1983
2118
|
// ── Auth ──────────────────────────────────────────────────────
|
|
1984
2119
|
case "CODE_SENT":
|
|
@@ -2056,23 +2191,20 @@ function paymentReducer(state, action) {
|
|
|
2056
2191
|
selectedProviderId: action.providerId,
|
|
2057
2192
|
selectedAccountId: null,
|
|
2058
2193
|
selectedWalletId: null,
|
|
2059
|
-
selectedTokenSymbol: null
|
|
2060
|
-
userIntent: null
|
|
2194
|
+
selectedTokenSymbol: null
|
|
2061
2195
|
};
|
|
2062
2196
|
case "SELECT_ACCOUNT":
|
|
2063
2197
|
return {
|
|
2064
2198
|
...state,
|
|
2065
2199
|
selectedAccountId: action.accountId,
|
|
2066
2200
|
selectedWalletId: action.walletId,
|
|
2067
|
-
selectedTokenSymbol: null
|
|
2068
|
-
userIntent: null
|
|
2201
|
+
selectedTokenSymbol: null
|
|
2069
2202
|
};
|
|
2070
2203
|
case "SELECT_TOKEN":
|
|
2071
2204
|
return {
|
|
2072
2205
|
...state,
|
|
2073
2206
|
selectedWalletId: action.walletId,
|
|
2074
|
-
selectedTokenSymbol: action.tokenSymbol
|
|
2075
|
-
userIntent: null
|
|
2207
|
+
selectedTokenSymbol: action.tokenSymbol
|
|
2076
2208
|
};
|
|
2077
2209
|
// ── Transfer lifecycle ───────────────────────────────────────
|
|
2078
2210
|
case "PAY_STARTED":
|
|
@@ -2081,8 +2213,7 @@ function paymentReducer(state, action) {
|
|
|
2081
2213
|
error: null,
|
|
2082
2214
|
creatingTransfer: true,
|
|
2083
2215
|
deeplinkUri: null,
|
|
2084
|
-
mobileFlow: false
|
|
2085
|
-
userIntent: null
|
|
2216
|
+
mobileFlow: false
|
|
2086
2217
|
};
|
|
2087
2218
|
case "PAY_ENDED":
|
|
2088
2219
|
return { ...state, creatingTransfer: false };
|
|
@@ -2146,7 +2277,8 @@ function paymentReducer(state, action) {
|
|
|
2146
2277
|
transfer: action.transfer,
|
|
2147
2278
|
error: null,
|
|
2148
2279
|
mobileFlow: false,
|
|
2149
|
-
deeplinkUri: null
|
|
2280
|
+
deeplinkUri: null,
|
|
2281
|
+
phase: { step: "confirm-sign", transfer: action.transfer }
|
|
2150
2282
|
};
|
|
2151
2283
|
case "CLEAR_MOBILE_STATE":
|
|
2152
2284
|
return { ...state, mobileFlow: false, deeplinkUri: null };
|
|
@@ -2216,29 +2348,37 @@ function paymentReducer(state, action) {
|
|
|
2216
2348
|
case "GUEST_PREAUTH_DETECTED":
|
|
2217
2349
|
return {
|
|
2218
2350
|
...state,
|
|
2219
|
-
guestPreauthAccountId: action.accountId
|
|
2351
|
+
guestPreauthAccountId: action.accountId,
|
|
2352
|
+
guestPreauthSessionId: action.sessionId ?? state.guestPreauthSessionId
|
|
2220
2353
|
};
|
|
2354
|
+
case "GUEST_PREAUTH_BEGIN":
|
|
2355
|
+
return { ...state, guestPreauthorizing: true, error: null };
|
|
2356
|
+
case "GUEST_PREAUTH_END":
|
|
2357
|
+
return { ...state, guestPreauthorizing: false };
|
|
2221
2358
|
case "ACCOUNT_OWNER_SET":
|
|
2222
2359
|
return {
|
|
2223
2360
|
...state,
|
|
2224
2361
|
guestPreauthAccountId: null,
|
|
2362
|
+
guestPreauthSessionId: null,
|
|
2225
2363
|
activePublicKey: null,
|
|
2226
2364
|
error: null,
|
|
2227
|
-
|
|
2365
|
+
guestPreauthorizing: false,
|
|
2366
|
+
phase: { step: "one-tap-setup", action: null }
|
|
2228
2367
|
};
|
|
2229
2368
|
// ── User intent & error ──────────────────────────────────────
|
|
2230
2369
|
case "SET_USER_INTENT":
|
|
2231
|
-
return { ...state,
|
|
2370
|
+
return { ...state, phase: action.intent };
|
|
2232
2371
|
case "REQUEST_LOGIN":
|
|
2233
2372
|
return {
|
|
2234
2373
|
...state,
|
|
2235
2374
|
loginRequested: true,
|
|
2236
2375
|
transfer: null,
|
|
2237
|
-
isGuestFlow: false,
|
|
2238
2376
|
creatingTransfer: false
|
|
2239
2377
|
};
|
|
2240
2378
|
case "SET_ERROR":
|
|
2241
2379
|
return { ...state, error: action.error };
|
|
2380
|
+
case "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP":
|
|
2381
|
+
return { ...state, oneTapLimitSavedDuringSetup: action.saved };
|
|
2242
2382
|
// ── Lifecycle ────────────────────────────────────────────────
|
|
2243
2383
|
case "NEW_PAYMENT":
|
|
2244
2384
|
return {
|
|
@@ -2255,9 +2395,11 @@ function paymentReducer(state, action) {
|
|
|
2255
2395
|
guestTransferId: null,
|
|
2256
2396
|
guestSessionToken: null,
|
|
2257
2397
|
guestPreauthAccountId: null,
|
|
2398
|
+
guestPreauthSessionId: null,
|
|
2258
2399
|
activePublicKey: null,
|
|
2259
|
-
|
|
2260
|
-
|
|
2400
|
+
loginRequested: false,
|
|
2401
|
+
oneTapLimitSavedDuringSetup: false,
|
|
2402
|
+
guestPreauthorizing: false
|
|
2261
2403
|
};
|
|
2262
2404
|
case "LOGOUT":
|
|
2263
2405
|
return {
|
|
@@ -2272,139 +2414,85 @@ function paymentReducer(state, action) {
|
|
|
2272
2414
|
default:
|
|
2273
2415
|
return state;
|
|
2274
2416
|
}
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
// src/auth.ts
|
|
2278
|
-
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2279
|
-
var PHONE_DIGIT_PATTERN = /^\d{7,15}$/;
|
|
2280
|
-
function normalizePhoneNumber(rawValue) {
|
|
2281
|
-
const trimmed = rawValue.trim();
|
|
2282
|
-
if (!trimmed) return null;
|
|
2283
|
-
const hasExplicitCountryCode = trimmed.startsWith("+");
|
|
2284
|
-
const digits = trimmed.replace(/\D/g, "");
|
|
2285
|
-
if (!PHONE_DIGIT_PATTERN.test(digits)) return null;
|
|
2286
|
-
return hasExplicitCountryCode ? `+${digits}` : digits;
|
|
2287
|
-
}
|
|
2288
|
-
function normalizeAuthIdentifier(rawValue) {
|
|
2289
|
-
const trimmed = rawValue.trim();
|
|
2290
|
-
if (!trimmed) return null;
|
|
2291
|
-
if (EMAIL_PATTERN.test(trimmed)) {
|
|
2292
|
-
return {
|
|
2293
|
-
kind: "email",
|
|
2294
|
-
value: trimmed.toLowerCase()
|
|
2295
|
-
};
|
|
2296
|
-
}
|
|
2297
|
-
const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
|
|
2298
|
-
if (normalizedPhoneNumber) {
|
|
2299
|
-
return {
|
|
2300
|
-
kind: "phone",
|
|
2301
|
-
value: normalizedPhoneNumber
|
|
2302
|
-
};
|
|
2303
|
-
}
|
|
2304
|
-
return null;
|
|
2305
|
-
}
|
|
2306
|
-
function maskAuthIdentifier(identifier) {
|
|
2307
|
-
if (identifier.kind === "email") {
|
|
2308
|
-
const [localPart, domain = ""] = identifier.value.split("@");
|
|
2309
|
-
const localPrefix = localPart.slice(0, 2);
|
|
2310
|
-
return `${localPrefix}${"*".repeat(Math.max(localPart.length - 2, 0))}@${domain}`;
|
|
2311
|
-
}
|
|
2312
|
-
const digits = identifier.value.replace(/\D/g, "");
|
|
2313
|
-
const visibleSuffix = digits.slice(-4);
|
|
2314
|
-
return `***-***-${visibleSuffix}`;
|
|
2315
|
-
}
|
|
2316
|
-
|
|
2317
|
-
// src/walletFlow.ts
|
|
2318
|
-
var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
|
2319
|
-
function isMobileUserAgent(userAgent) {
|
|
2320
|
-
if (!userAgent) {
|
|
2321
|
-
return false;
|
|
2322
|
-
}
|
|
2323
|
-
return MOBILE_USER_AGENT_PATTERN.test(userAgent);
|
|
2324
|
-
}
|
|
2325
|
-
function shouldUseWalletConnector(options) {
|
|
2326
|
-
return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
// src/resolveScreen.ts
|
|
2330
|
-
function hasActiveWallet(accounts) {
|
|
2331
|
-
return accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
|
|
2332
|
-
}
|
|
2333
|
-
function isTransferTerminal(transfer) {
|
|
2334
|
-
return transfer?.status === "COMPLETED" || transfer?.status === "FAILED";
|
|
2335
|
-
}
|
|
2336
|
-
function isTransferInFlight(transfer) {
|
|
2337
|
-
if (!transfer) return false;
|
|
2338
|
-
return ["CREATED", "SENDING", "SENT"].includes(transfer.status);
|
|
2339
|
-
}
|
|
2340
|
-
function isSetupTransfer(transfer) {
|
|
2341
|
-
if (!transfer) return false;
|
|
2342
|
-
return transfer.sources?.some(
|
|
2343
|
-
(s) => s.wallets && Array.isArray(s.wallets) && s.wallets.length === 0
|
|
2344
|
-
) ?? false;
|
|
2345
|
-
}
|
|
2346
|
-
function resolveScreen(state) {
|
|
2347
|
-
if (!state.privyReady) {
|
|
2348
|
-
return "loading";
|
|
2349
|
-
}
|
|
2350
|
-
if (state.authenticated && !state.activeCredentialId && !state.passkeyConfigLoaded) {
|
|
2351
|
-
return "loading";
|
|
2352
|
-
}
|
|
2353
|
-
if (!state.authenticated && !state.verificationTarget && !state.isGuestFlow && (state.isReturningUser || state.guestPreauthRedirect || state.loginRequested)) {
|
|
2354
|
-
return "login";
|
|
2355
|
-
}
|
|
2356
|
-
if (!state.authenticated && state.verificationTarget != null) {
|
|
2357
|
-
return "otp-verify";
|
|
2358
|
-
}
|
|
2359
|
-
if (state.authenticated && !state.activeCredentialId && state.passkeyConfigLoaded && (state.knownCredentialIds.length === 0 || !state.passkeyPopupNeeded)) {
|
|
2360
|
-
return "create-passkey";
|
|
2361
|
-
}
|
|
2362
|
-
if (state.authenticated && !state.activeCredentialId && state.passkeyConfigLoaded && state.knownCredentialIds.length > 0 && state.passkeyPopupNeeded) {
|
|
2363
|
-
return "verify-passkey";
|
|
2364
|
-
}
|
|
2365
|
-
if (isTransferTerminal(state.transfer)) {
|
|
2366
|
-
return "success";
|
|
2367
|
-
}
|
|
2368
|
-
if (state.creatingTransfer || state.transfer != null && isTransferInFlight(state.transfer)) {
|
|
2369
|
-
return "processing";
|
|
2370
|
-
}
|
|
2371
|
-
if (state.transfer?.status === "AUTHORIZED" && !state.isDesktop && !isSetupTransfer(state.transfer)) {
|
|
2372
|
-
return "confirm-sign";
|
|
2373
|
-
}
|
|
2374
|
-
if (state.pendingSelectSource != null) {
|
|
2375
|
-
return state.isDesktop ? "setup" : "select-source";
|
|
2376
|
-
}
|
|
2377
|
-
if (state.pendingOneTapSetup != null && !state.oneTapLimitAlreadySaved) {
|
|
2378
|
-
return "setup";
|
|
2379
|
-
}
|
|
2380
|
-
if (state.mobileFlow || state.inlineAuthorizationExecuting) {
|
|
2381
|
-
return state.isDesktop ? "setup-status" : "open-wallet";
|
|
2382
|
-
}
|
|
2383
|
-
if (state.isGuestFlow && state.selectedProviderId != null && !state.transfer && !state.guestSettingSender) {
|
|
2384
|
-
return "guest-token-picker";
|
|
2385
|
-
}
|
|
2386
|
-
if (state.activeCredentialId && !hasActiveWallet(state.accounts) && !state.mobileFlow || !state.authenticated && !state.isReturningUser && !state.isGuestFlow) {
|
|
2387
|
-
return "wallet-picker";
|
|
2388
|
-
}
|
|
2389
|
-
if (state.loadingData && state.activeCredentialId != null && hasActiveWallet(state.accounts)) {
|
|
2390
|
-
return "loading";
|
|
2391
|
-
}
|
|
2392
|
-
if (state.userIntent === "pick-token" && state.selectedAccount != null) {
|
|
2393
|
-
return "token-picker";
|
|
2394
|
-
}
|
|
2395
|
-
if (state.userIntent === "configure-one-tap") {
|
|
2396
|
-
return "setup";
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
// src/auth.ts
|
|
2420
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2421
|
+
var PHONE_DIGIT_PATTERN = /^\d{7,15}$/;
|
|
2422
|
+
function normalizePhoneNumber(rawValue) {
|
|
2423
|
+
const trimmed = rawValue.trim();
|
|
2424
|
+
if (!trimmed) return null;
|
|
2425
|
+
const hasExplicitCountryCode = trimmed.startsWith("+");
|
|
2426
|
+
const digits = trimmed.replace(/\D/g, "");
|
|
2427
|
+
if (!PHONE_DIGIT_PATTERN.test(digits)) return null;
|
|
2428
|
+
return hasExplicitCountryCode ? `+${digits}` : digits;
|
|
2429
|
+
}
|
|
2430
|
+
function normalizeAuthIdentifier(rawValue) {
|
|
2431
|
+
const trimmed = rawValue.trim();
|
|
2432
|
+
if (!trimmed) return null;
|
|
2433
|
+
if (EMAIL_PATTERN.test(trimmed)) {
|
|
2434
|
+
return {
|
|
2435
|
+
kind: "email",
|
|
2436
|
+
value: trimmed.toLowerCase()
|
|
2437
|
+
};
|
|
2397
2438
|
}
|
|
2398
|
-
|
|
2399
|
-
|
|
2439
|
+
const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
|
|
2440
|
+
if (normalizedPhoneNumber) {
|
|
2441
|
+
return {
|
|
2442
|
+
kind: "phone",
|
|
2443
|
+
value: normalizedPhoneNumber
|
|
2444
|
+
};
|
|
2400
2445
|
}
|
|
2401
|
-
|
|
2402
|
-
|
|
2446
|
+
return null;
|
|
2447
|
+
}
|
|
2448
|
+
function maskAuthIdentifier(identifier) {
|
|
2449
|
+
if (identifier.kind === "email") {
|
|
2450
|
+
const [localPart, domain = ""] = identifier.value.split("@");
|
|
2451
|
+
const localPrefix = localPart.slice(0, 2);
|
|
2452
|
+
return `${localPrefix}${"*".repeat(Math.max(localPart.length - 2, 0))}@${domain}`;
|
|
2403
2453
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2454
|
+
const digits = identifier.value.replace(/\D/g, "");
|
|
2455
|
+
const visibleSuffix = digits.slice(-4);
|
|
2456
|
+
return `***-***-${visibleSuffix}`;
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
// src/resolveScreen.ts
|
|
2460
|
+
function screenForPhase(phase) {
|
|
2461
|
+
switch (phase.step) {
|
|
2462
|
+
case "initializing":
|
|
2463
|
+
case "data-loading":
|
|
2464
|
+
return "loading";
|
|
2465
|
+
case "login":
|
|
2466
|
+
return "login";
|
|
2467
|
+
case "otp-verify":
|
|
2468
|
+
return "otp-verify";
|
|
2469
|
+
case "passkey-create":
|
|
2470
|
+
return "create-passkey";
|
|
2471
|
+
case "passkey-verify":
|
|
2472
|
+
return "verify-passkey";
|
|
2473
|
+
case "wallet-picker":
|
|
2474
|
+
return "wallet-picker";
|
|
2475
|
+
case "wallet-setup":
|
|
2476
|
+
return phase.mobile ? "open-wallet" : "setup-status";
|
|
2477
|
+
case "select-source":
|
|
2478
|
+
if (phase.skipOneTapLimit) return "select-source";
|
|
2479
|
+
return phase.isDesktop ? "setup" : "select-source";
|
|
2480
|
+
case "one-tap-setup":
|
|
2481
|
+
return "setup";
|
|
2482
|
+
case "guest-token-picker":
|
|
2483
|
+
return "guest-token-picker";
|
|
2484
|
+
case "token-picker":
|
|
2485
|
+
return "token-picker";
|
|
2486
|
+
case "deposit":
|
|
2487
|
+
return "deposit";
|
|
2488
|
+
case "processing":
|
|
2489
|
+
return "processing";
|
|
2490
|
+
case "confirm-sign":
|
|
2491
|
+
return "confirm-sign";
|
|
2492
|
+
case "completed":
|
|
2493
|
+
case "failed":
|
|
2494
|
+
return "success";
|
|
2406
2495
|
}
|
|
2407
|
-
return "wallet-picker";
|
|
2408
2496
|
}
|
|
2409
2497
|
var MUTED = "#7fa4b0";
|
|
2410
2498
|
var LOGO_SIZE = 48;
|
|
@@ -5887,85 +5975,66 @@ var DEPOSIT_SCREENS = /* @__PURE__ */ new Set([
|
|
|
5887
5975
|
"processing",
|
|
5888
5976
|
"success"
|
|
5889
5977
|
]);
|
|
5890
|
-
function getFlowPhase(screen,
|
|
5978
|
+
function getFlowPhase(screen, phase) {
|
|
5891
5979
|
if (LINK_SCREENS.has(screen)) return "link";
|
|
5892
5980
|
if (DEPOSIT_SCREENS.has(screen)) return "deposit";
|
|
5893
5981
|
if (screen === "token-picker" || screen === "select-source" || screen === "guest-token-picker") {
|
|
5894
|
-
return
|
|
5982
|
+
return phase.step === "one-tap-setup" ? "link" : "deposit";
|
|
5895
5983
|
}
|
|
5896
5984
|
return null;
|
|
5897
5985
|
}
|
|
5898
5986
|
function StepRenderer(props) {
|
|
5899
|
-
const
|
|
5900
|
-
|
|
5901
|
-
});
|
|
5902
|
-
const isReturningUser = props.state.activeCredentialId != null || typeof window !== "undefined" && window.localStorage.getItem("blink_active_credential") != null;
|
|
5903
|
-
const screenState = {
|
|
5904
|
-
privyReady: props.ready,
|
|
5905
|
-
authenticated: props.authenticated,
|
|
5906
|
-
verificationTarget: props.state.verificationTarget,
|
|
5907
|
-
activeCredentialId: props.state.activeCredentialId,
|
|
5908
|
-
knownCredentialIds: props.state.knownCredentialIds,
|
|
5909
|
-
passkeyConfigLoaded: props.state.passkeyConfigLoaded,
|
|
5910
|
-
passkeyPopupNeeded: props.state.passkeyPopupNeeded,
|
|
5911
|
-
accounts: props.state.accounts,
|
|
5912
|
-
isGuestFlow: props.state.isGuestFlow,
|
|
5913
|
-
selectedProviderId: props.state.selectedProviderId,
|
|
5914
|
-
mobileFlow: props.state.mobileFlow,
|
|
5915
|
-
inlineAuthorizationExecuting: props.inlineAuthorizationExecuting,
|
|
5916
|
-
creatingTransfer: props.state.creatingTransfer,
|
|
5917
|
-
transfer: props.state.transfer,
|
|
5918
|
-
pendingSelectSource: props.pendingSelectSource,
|
|
5919
|
-
pendingOneTapSetup: props.pendingOneTapSetup,
|
|
5920
|
-
oneTapLimitAlreadySaved: props.oneTapLimitAlreadySaved,
|
|
5921
|
-
loadingData: props.state.loadingData,
|
|
5922
|
-
isDesktop,
|
|
5923
|
-
isReturningUser,
|
|
5924
|
-
guestPreauthRedirect: props.state.guestPreauthAccountId != null,
|
|
5925
|
-
loginRequested: props.state.loginRequested,
|
|
5926
|
-
userIntent: props.state.userIntent,
|
|
5927
|
-
selectedAccount: props.selectedAccount,
|
|
5928
|
-
guestSettingSender: props.guestSettingSender
|
|
5929
|
-
};
|
|
5930
|
-
const screen = resolveScreen(screenState);
|
|
5931
|
-
const phase = getFlowPhase(screen, props.state.userIntent);
|
|
5932
|
-
return /* @__PURE__ */ jsx(FlowPhaseProvider, { phase, children: /* @__PURE__ */ jsx(StepRendererContent, { ...props, screen, isDesktop }) });
|
|
5987
|
+
const screen = screenForPhase(props.flow.state.phase);
|
|
5988
|
+
const flowPhase = getFlowPhase(screen, props.flow.state.phase);
|
|
5989
|
+
return /* @__PURE__ */ jsx(FlowPhaseProvider, { phase: flowPhase, children: /* @__PURE__ */ jsx(StepRendererContent, { ...props, screen }) });
|
|
5933
5990
|
}
|
|
5934
5991
|
function StepRendererContent({
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
pollingError,
|
|
5940
|
-
authExecutorError,
|
|
5941
|
-
transferSigningSigning,
|
|
5942
|
-
transferSigningError,
|
|
5943
|
-
pendingConnections,
|
|
5944
|
-
depositEligibleAccounts,
|
|
5945
|
-
sourceName,
|
|
5946
|
-
maxSourceBalance,
|
|
5947
|
-
tokenCount,
|
|
5948
|
-
selectedAccount,
|
|
5949
|
-
selectedSource,
|
|
5950
|
-
selectSourceChoices,
|
|
5951
|
-
selectSourceRecommended,
|
|
5952
|
-
selectSourceAvailableBalance,
|
|
5953
|
-
guestTokenEntries,
|
|
5954
|
-
guestLoadingBalances,
|
|
5955
|
-
guestSettingSender,
|
|
5956
|
-
authInput,
|
|
5957
|
-
otpCode,
|
|
5958
|
-
selectSourceChainName,
|
|
5959
|
-
selectSourceTokenSymbol,
|
|
5960
|
-
savingOneTapLimit,
|
|
5961
|
-
merchantName,
|
|
5962
|
-
onBack,
|
|
5963
|
-
onDismiss,
|
|
5964
|
-
depositAmount,
|
|
5992
|
+
flow,
|
|
5993
|
+
remote,
|
|
5994
|
+
derived,
|
|
5995
|
+
forms,
|
|
5965
5996
|
handlers,
|
|
5966
|
-
screen
|
|
5967
|
-
isDesktop
|
|
5997
|
+
screen
|
|
5968
5998
|
}) {
|
|
5999
|
+
const {
|
|
6000
|
+
state,
|
|
6001
|
+
authenticated,
|
|
6002
|
+
activeOtpStatus,
|
|
6003
|
+
isDesktop,
|
|
6004
|
+
merchantName,
|
|
6005
|
+
onBack,
|
|
6006
|
+
onDismiss,
|
|
6007
|
+
depositAmount
|
|
6008
|
+
} = flow;
|
|
6009
|
+
const {
|
|
6010
|
+
pollingTransfer,
|
|
6011
|
+
pollingError,
|
|
6012
|
+
authExecutorError,
|
|
6013
|
+
transferSigningSigning,
|
|
6014
|
+
transferSigningError
|
|
6015
|
+
} = remote;
|
|
6016
|
+
const {
|
|
6017
|
+
pendingConnections,
|
|
6018
|
+
depositEligibleAccounts,
|
|
6019
|
+
sourceName,
|
|
6020
|
+
maxSourceBalance,
|
|
6021
|
+
tokenCount,
|
|
6022
|
+
selectedAccount,
|
|
6023
|
+
selectedSource,
|
|
6024
|
+
selectSourceChoices,
|
|
6025
|
+
selectSourceRecommended,
|
|
6026
|
+
selectSourceAvailableBalance
|
|
6027
|
+
} = derived;
|
|
6028
|
+
const {
|
|
6029
|
+
guestTokenEntries,
|
|
6030
|
+
guestLoadingBalances,
|
|
6031
|
+
guestSettingSender,
|
|
6032
|
+
authInput,
|
|
6033
|
+
otpCode,
|
|
6034
|
+
selectSourceChainName,
|
|
6035
|
+
selectSourceTokenSymbol,
|
|
6036
|
+
savingOneTapLimit
|
|
6037
|
+
} = forms;
|
|
5969
6038
|
const selectedWallet = selectedAccount?.wallets.find((w) => w.id === state.selectedWalletId);
|
|
5970
6039
|
const selectedSourceLabel = selectedSource && selectedWallet ? `${selectedSource.token.symbol} on ${selectedWallet.chain.name}` : void 0;
|
|
5971
6040
|
switch (screen) {
|
|
@@ -6038,7 +6107,7 @@ function StepRendererContent({
|
|
|
6038
6107
|
onPrepareProvider: handlers.onPrepareProvider,
|
|
6039
6108
|
onSelectProvider: handlers.onSelectProvider,
|
|
6040
6109
|
onContinueConnection: handlers.onContinueConnection,
|
|
6041
|
-
onBack: isEntryPoint ? onBack : () => handlers.
|
|
6110
|
+
onBack: isEntryPoint ? onBack : () => handlers.onSetPhase({ step: "deposit" }),
|
|
6042
6111
|
onLogout: authenticated ? handlers.onLogout : void 0,
|
|
6043
6112
|
onLogin: handlers.onLogin,
|
|
6044
6113
|
showLoginOption: isEntryPoint
|
|
@@ -6069,7 +6138,7 @@ function StepRendererContent({
|
|
|
6069
6138
|
limit: state.oneTapLimit,
|
|
6070
6139
|
tokensApproved: 0,
|
|
6071
6140
|
merchantName,
|
|
6072
|
-
onContinue: () => handlers.
|
|
6141
|
+
onContinue: () => handlers.onSetPhase({ step: "one-tap-setup", action: null }),
|
|
6073
6142
|
onLogout: handlers.onLogout,
|
|
6074
6143
|
error: state.error || authExecutorError
|
|
6075
6144
|
}
|
|
@@ -6088,7 +6157,7 @@ function StepRendererContent({
|
|
|
6088
6157
|
tokenCount: effectiveTokenCount,
|
|
6089
6158
|
sourceName,
|
|
6090
6159
|
onSetupOneTap: handlers.onSetupOneTap,
|
|
6091
|
-
onBack: () => handlers.
|
|
6160
|
+
onBack: () => handlers.onSetPhase({ step: "deposit" }),
|
|
6092
6161
|
onLogout: handlers.onLogout,
|
|
6093
6162
|
onAdvanced: handlers.onSelectToken,
|
|
6094
6163
|
selectedSourceLabel: effectiveSourceLabel,
|
|
@@ -6124,7 +6193,7 @@ function StepRendererContent({
|
|
|
6124
6193
|
processing: state.creatingTransfer,
|
|
6125
6194
|
error: state.error,
|
|
6126
6195
|
onDeposit: handlers.onPay,
|
|
6127
|
-
onSwitchWallet: () => handlers.
|
|
6196
|
+
onSwitchWallet: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
|
|
6128
6197
|
onBack: onBack ?? (() => handlers.onLogout()),
|
|
6129
6198
|
onLogout: handlers.onLogout,
|
|
6130
6199
|
onIncreaseLimit: handlers.onIncreaseLimit,
|
|
@@ -6133,7 +6202,7 @@ function StepRendererContent({
|
|
|
6133
6202
|
selectedAccountId: state.selectedAccountId,
|
|
6134
6203
|
onSelectAccount: handlers.onSelectAccount,
|
|
6135
6204
|
onAuthorizeAccount: handlers.onContinueConnection,
|
|
6136
|
-
onAddProvider: () => handlers.
|
|
6205
|
+
onAddProvider: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
|
|
6137
6206
|
onSelectToken: handlers.onSelectToken,
|
|
6138
6207
|
selectedSourceLabel,
|
|
6139
6208
|
selectedTokenSymbol: selectedSource?.token.symbol
|
|
@@ -6151,7 +6220,7 @@ function StepRendererContent({
|
|
|
6151
6220
|
chains: state.chains,
|
|
6152
6221
|
onSelectAuthorized: handlers.onSelectAuthorizedToken,
|
|
6153
6222
|
onAuthorizeToken: handlers.onAuthorizeToken,
|
|
6154
|
-
onBack: () => handlers.
|
|
6223
|
+
onBack: () => handlers.onSetPhase({ step: "deposit" }),
|
|
6155
6224
|
onLogout: handlers.onLogout,
|
|
6156
6225
|
depositAmount: depositAmount ?? void 0,
|
|
6157
6226
|
selectedTokenSymbol: selectedSource?.token.symbol,
|
|
@@ -6169,7 +6238,7 @@ function StepRendererContent({
|
|
|
6169
6238
|
depositAmount: depositAmount ?? void 0,
|
|
6170
6239
|
error: state.error,
|
|
6171
6240
|
onSelect: handlers.onSelectGuestToken,
|
|
6172
|
-
onBack: () => handlers.
|
|
6241
|
+
onBack: () => handlers.onSetPhase({ step: "wallet-picker", reason: "guest-entry" })
|
|
6173
6242
|
}
|
|
6174
6243
|
);
|
|
6175
6244
|
case "processing": {
|
|
@@ -6308,56 +6377,41 @@ var buttonStyle3 = {
|
|
|
6308
6377
|
fontFamily: "inherit",
|
|
6309
6378
|
cursor: "pointer"
|
|
6310
6379
|
};
|
|
6311
|
-
function
|
|
6380
|
+
function selectedSourceForWallet(selectedWallet, selectedTokenSymbol) {
|
|
6381
|
+
if (!selectedWallet) return null;
|
|
6382
|
+
if (selectedTokenSymbol) {
|
|
6383
|
+
return selectedWallet.sources.find((s) => s.token.symbol === selectedTokenSymbol) ?? null;
|
|
6384
|
+
}
|
|
6385
|
+
return selectedWallet.sources.find((s) => s.token.status === "AUTHORIZED") ?? selectedWallet.sources[0] ?? null;
|
|
6386
|
+
}
|
|
6387
|
+
function computeDerivedState(state) {
|
|
6312
6388
|
const { sourceType, sourceId } = deriveSourceTypeAndId(state);
|
|
6313
6389
|
const selectedAccount = state.accounts.find((a) => a.id === state.selectedAccountId);
|
|
6314
6390
|
const selectedWallet = selectedAccount?.wallets.find(
|
|
6315
6391
|
(w) => w.id === state.selectedWalletId
|
|
6316
6392
|
);
|
|
6317
|
-
const selectedSource =
|
|
6318
|
-
if (!selectedWallet) return null;
|
|
6319
|
-
if (state.selectedTokenSymbol) {
|
|
6320
|
-
return selectedWallet.sources.find(
|
|
6321
|
-
(s) => s.token.symbol === state.selectedTokenSymbol
|
|
6322
|
-
) ?? null;
|
|
6323
|
-
}
|
|
6324
|
-
return selectedWallet.sources.find((s) => s.token.status === "AUTHORIZED") ?? selectedWallet.sources[0] ?? null;
|
|
6325
|
-
}, [selectedWallet, state.selectedTokenSymbol]);
|
|
6393
|
+
const selectedSource = selectedSourceForWallet(selectedWallet, state.selectedTokenSymbol);
|
|
6326
6394
|
const sourceName = selectedAccount?.name ?? selectedWallet?.chain.name ?? "Wallet";
|
|
6327
|
-
const pendingConnections =
|
|
6328
|
-
() =>
|
|
6329
|
-
(a) => a.wallets.length > 0 && !a.wallets.some((w) => w.status === "ACTIVE")
|
|
6330
|
-
),
|
|
6331
|
-
[state.accounts]
|
|
6332
|
-
);
|
|
6333
|
-
const depositEligibleAccounts = useMemo(
|
|
6334
|
-
() => getDepositEligibleAccounts(state.accounts),
|
|
6335
|
-
[state.accounts]
|
|
6395
|
+
const pendingConnections = state.accounts.filter(
|
|
6396
|
+
(a) => a.wallets.length > 0 && !a.wallets.some((w) => w.status === "ACTIVE")
|
|
6336
6397
|
);
|
|
6337
|
-
const
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
}
|
|
6398
|
+
const depositEligibleAccounts = getDepositEligibleAccounts(state.accounts);
|
|
6399
|
+
let maxSourceBalance = 0;
|
|
6400
|
+
for (const acct of state.accounts) {
|
|
6401
|
+
for (const wallet of acct.wallets) {
|
|
6402
|
+
for (const source of wallet.sources) {
|
|
6403
|
+
if (source.balance.available.amount > maxSourceBalance) {
|
|
6404
|
+
maxSourceBalance = source.balance.available.amount;
|
|
6345
6405
|
}
|
|
6346
6406
|
}
|
|
6347
6407
|
}
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
const
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
for (const wallet of acct.wallets) {
|
|
6354
|
-
count += wallet.sources.filter(
|
|
6355
|
-
(s) => s.balance.available.amount > 0
|
|
6356
|
-
).length;
|
|
6357
|
-
}
|
|
6408
|
+
}
|
|
6409
|
+
let tokenCount = 0;
|
|
6410
|
+
for (const acct of state.accounts) {
|
|
6411
|
+
for (const wallet of acct.wallets) {
|
|
6412
|
+
tokenCount += wallet.sources.filter((s) => s.balance.available.amount > 0).length;
|
|
6358
6413
|
}
|
|
6359
|
-
|
|
6360
|
-
}, [state.accounts]);
|
|
6414
|
+
}
|
|
6361
6415
|
return {
|
|
6362
6416
|
sourceType,
|
|
6363
6417
|
sourceId,
|
|
@@ -6371,6 +6425,9 @@ function useDerivedState(state) {
|
|
|
6371
6425
|
tokenCount
|
|
6372
6426
|
};
|
|
6373
6427
|
}
|
|
6428
|
+
function useDerivedState(state) {
|
|
6429
|
+
return useMemo(() => computeDerivedState(state), [state]);
|
|
6430
|
+
}
|
|
6374
6431
|
function useAuthHandlers(dispatch, verificationTarget) {
|
|
6375
6432
|
const {
|
|
6376
6433
|
sendCode: sendEmailCode,
|
|
@@ -6936,7 +6993,9 @@ function useProviderHandlers(deps) {
|
|
|
6936
6993
|
reauthTokenRef,
|
|
6937
6994
|
authenticated,
|
|
6938
6995
|
merchantAuthorization,
|
|
6939
|
-
destination
|
|
6996
|
+
destination,
|
|
6997
|
+
guestSessionToken,
|
|
6998
|
+
selectedProviderId
|
|
6940
6999
|
} = deps;
|
|
6941
7000
|
const wagmiConfig2 = useConfig();
|
|
6942
7001
|
const { connectAsync, connectors } = useConnect();
|
|
@@ -7249,7 +7308,7 @@ function useProviderHandlers(deps) {
|
|
|
7249
7308
|
reauthTokenRef
|
|
7250
7309
|
]);
|
|
7251
7310
|
const handleNavigateToTokenPicker = useCallback(() => {
|
|
7252
|
-
dispatch({ type: "SET_USER_INTENT", intent: "
|
|
7311
|
+
dispatch({ type: "SET_USER_INTENT", intent: { step: "token-picker" } });
|
|
7253
7312
|
}, [dispatch]);
|
|
7254
7313
|
const handleSelectAuthorizedToken = useCallback((walletId, tokenSymbol) => {
|
|
7255
7314
|
dispatch({ type: "SELECT_TOKEN", walletId, tokenSymbol });
|
|
@@ -7332,6 +7391,76 @@ function useProviderHandlers(deps) {
|
|
|
7332
7391
|
reauthSessionIdRef,
|
|
7333
7392
|
reauthTokenRef
|
|
7334
7393
|
]);
|
|
7394
|
+
const handlePreauthorize = useCallback(async () => {
|
|
7395
|
+
if (!guestSessionToken || !selectedProviderId) {
|
|
7396
|
+
dispatch({
|
|
7397
|
+
type: "SET_ERROR",
|
|
7398
|
+
error: "Missing guest session or wallet provider. Try again from the payment screen."
|
|
7399
|
+
});
|
|
7400
|
+
return;
|
|
7401
|
+
}
|
|
7402
|
+
const isMobile = !shouldUseWalletConnector({
|
|
7403
|
+
useWalletConnector: useWalletConnectorProp,
|
|
7404
|
+
userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
|
|
7405
|
+
});
|
|
7406
|
+
const providerName = providers.find((p) => p.id === selectedProviderId)?.name ?? "Wallet";
|
|
7407
|
+
if (!isMobile) {
|
|
7408
|
+
dispatch({ type: "GUEST_PREAUTH_BEGIN" });
|
|
7409
|
+
}
|
|
7410
|
+
try {
|
|
7411
|
+
const created = await createGuestAccount(
|
|
7412
|
+
apiBaseUrl,
|
|
7413
|
+
guestSessionToken,
|
|
7414
|
+
selectedProviderId,
|
|
7415
|
+
providerName
|
|
7416
|
+
);
|
|
7417
|
+
const session = await fetchAuthorizationSessionByToken(
|
|
7418
|
+
apiBaseUrl,
|
|
7419
|
+
created.sessionToken
|
|
7420
|
+
);
|
|
7421
|
+
if (isMobile) {
|
|
7422
|
+
handlingMobileReturnRef.current = false;
|
|
7423
|
+
mobileSetupFlowRef.current = true;
|
|
7424
|
+
setupAccountIdRef.current = created.accountId;
|
|
7425
|
+
persistMobileFlowState({
|
|
7426
|
+
accountId: created.accountId,
|
|
7427
|
+
sessionId: session.id,
|
|
7428
|
+
deeplinkUri: created.sessionUri,
|
|
7429
|
+
providerId: selectedProviderId,
|
|
7430
|
+
isSetup: true,
|
|
7431
|
+
guestSessionToken
|
|
7432
|
+
});
|
|
7433
|
+
triggerDeeplink(created.sessionUri);
|
|
7434
|
+
dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: created.sessionUri });
|
|
7435
|
+
}
|
|
7436
|
+
dispatch({
|
|
7437
|
+
type: "GUEST_PREAUTH_DETECTED",
|
|
7438
|
+
accountId: created.accountId,
|
|
7439
|
+
sessionId: session.id
|
|
7440
|
+
});
|
|
7441
|
+
} catch (err) {
|
|
7442
|
+
captureException(err);
|
|
7443
|
+
if (!isMobile) {
|
|
7444
|
+
dispatch({ type: "GUEST_PREAUTH_END" });
|
|
7445
|
+
}
|
|
7446
|
+
dispatch({
|
|
7447
|
+
type: "SET_ERROR",
|
|
7448
|
+
error: err instanceof Error ? err.message : "Failed to start preauthorization"
|
|
7449
|
+
});
|
|
7450
|
+
onError?.(err instanceof Error ? err.message : "Failed to start preauthorization");
|
|
7451
|
+
}
|
|
7452
|
+
}, [
|
|
7453
|
+
guestSessionToken,
|
|
7454
|
+
selectedProviderId,
|
|
7455
|
+
providers,
|
|
7456
|
+
apiBaseUrl,
|
|
7457
|
+
dispatch,
|
|
7458
|
+
onError,
|
|
7459
|
+
useWalletConnectorProp,
|
|
7460
|
+
mobileSetupFlowRef,
|
|
7461
|
+
handlingMobileReturnRef,
|
|
7462
|
+
setupAccountIdRef
|
|
7463
|
+
]);
|
|
7335
7464
|
return {
|
|
7336
7465
|
handlePrepareProvider,
|
|
7337
7466
|
handleSelectProvider,
|
|
@@ -7340,7 +7469,8 @@ function useProviderHandlers(deps) {
|
|
|
7340
7469
|
handleIncreaseLimit,
|
|
7341
7470
|
handleNavigateToTokenPicker,
|
|
7342
7471
|
handleSelectAuthorizedToken,
|
|
7343
|
-
handleAuthorizeToken
|
|
7472
|
+
handleAuthorizeToken,
|
|
7473
|
+
handlePreauthorize
|
|
7344
7474
|
};
|
|
7345
7475
|
}
|
|
7346
7476
|
|
|
@@ -7561,7 +7691,6 @@ function useOneTapSetupHandlers(deps) {
|
|
|
7561
7691
|
selectSourceTokenSymbol
|
|
7562
7692
|
} = deps;
|
|
7563
7693
|
const [savingOneTapLimit, setSavingOneTapLimit] = useState(false);
|
|
7564
|
-
const oneTapLimitSavedDuringSetupRef = useRef(false);
|
|
7565
7694
|
const handleSetupOneTap = useCallback(async (limit) => {
|
|
7566
7695
|
setSavingOneTapLimit(true);
|
|
7567
7696
|
try {
|
|
@@ -7582,12 +7711,12 @@ function useOneTapSetupHandlers(deps) {
|
|
|
7582
7711
|
chainName = recommended?.chainName ?? choices[0]?.chainName ?? "Base";
|
|
7583
7712
|
tokenSymbol = recommended?.tokenSymbol ?? choices[0]?.tokens[0]?.tokenSymbol ?? "USDC";
|
|
7584
7713
|
}
|
|
7585
|
-
|
|
7714
|
+
dispatch({ type: "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP", saved: true });
|
|
7586
7715
|
authExecutor.resolveSelectSource({ chainName, tokenSymbol });
|
|
7587
7716
|
} else if (authExecutor.pendingOneTapSetup) {
|
|
7588
7717
|
authExecutor.resolveOneTapSetup();
|
|
7589
7718
|
}
|
|
7590
|
-
dispatch({ type: "SET_USER_INTENT", intent:
|
|
7719
|
+
dispatch({ type: "SET_USER_INTENT", intent: { step: "deposit" } });
|
|
7591
7720
|
} catch (err) {
|
|
7592
7721
|
captureException(err);
|
|
7593
7722
|
dispatch({
|
|
@@ -7600,124 +7729,63 @@ function useOneTapSetupHandlers(deps) {
|
|
|
7600
7729
|
}, [getAccessToken, apiBaseUrl, authExecutor, dispatch, selectSourceChainName, selectSourceTokenSymbol]);
|
|
7601
7730
|
return {
|
|
7602
7731
|
handleSetupOneTap,
|
|
7603
|
-
savingOneTapLimit
|
|
7604
|
-
oneTapLimitSavedDuringSetupRef
|
|
7732
|
+
savingOneTapLimit
|
|
7605
7733
|
};
|
|
7606
7734
|
}
|
|
7607
|
-
|
|
7608
|
-
// src/dataLoading.ts
|
|
7609
|
-
function resolveDataLoadAction({
|
|
7610
|
-
authenticated,
|
|
7611
|
-
accountsCount,
|
|
7612
|
-
hasActiveCredential,
|
|
7613
|
-
loading
|
|
7614
|
-
}) {
|
|
7615
|
-
if (!authenticated || accountsCount > 0 || !hasActiveCredential) {
|
|
7616
|
-
return "reset";
|
|
7617
|
-
}
|
|
7618
|
-
if (loading) {
|
|
7619
|
-
return "wait";
|
|
7620
|
-
}
|
|
7621
|
-
return "load";
|
|
7622
|
-
}
|
|
7623
|
-
|
|
7624
|
-
// src/processingStatus.ts
|
|
7625
|
-
var PROCESSING_TIMEOUT_MS = 18e4;
|
|
7626
|
-
function resolvePreferredTransfer(polledTransfer, localTransfer) {
|
|
7627
|
-
return polledTransfer ?? localTransfer;
|
|
7628
|
-
}
|
|
7629
|
-
function getTransferStatus(polledTransfer, localTransfer) {
|
|
7630
|
-
const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
|
|
7631
|
-
return transfer?.status ?? "UNKNOWN";
|
|
7632
|
-
}
|
|
7633
|
-
function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
|
|
7634
|
-
if (!processingStartedAtMs) return false;
|
|
7635
|
-
return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
|
|
7636
|
-
}
|
|
7637
|
-
var STATUS_DISPLAY_LABELS = {
|
|
7638
|
-
CREATED: "created",
|
|
7639
|
-
AUTHORIZED: "authorized",
|
|
7640
|
-
SENDING: "sending",
|
|
7641
|
-
SENT: "confirming delivery",
|
|
7642
|
-
COMPLETED: "completed",
|
|
7643
|
-
FAILED: "failed"
|
|
7644
|
-
};
|
|
7645
|
-
function getStatusDisplayLabel(status) {
|
|
7646
|
-
return STATUS_DISPLAY_LABELS[status] ?? status;
|
|
7647
|
-
}
|
|
7648
|
-
function buildProcessingTimeoutMessage(status) {
|
|
7649
|
-
const label = getStatusDisplayLabel(status);
|
|
7650
|
-
return `Payment is taking longer than expected (status: ${label}). Please try again.`;
|
|
7651
|
-
}
|
|
7652
|
-
|
|
7653
|
-
// src/hooks/usePaymentEffects.ts
|
|
7654
|
-
function usePaymentEffects(deps) {
|
|
7735
|
+
function useOtpEffects(deps) {
|
|
7655
7736
|
const {
|
|
7656
7737
|
state,
|
|
7657
7738
|
dispatch,
|
|
7658
|
-
ready,
|
|
7659
7739
|
authenticated,
|
|
7660
|
-
apiBaseUrl,
|
|
7661
|
-
depositAmount,
|
|
7662
|
-
onComplete,
|
|
7663
|
-
onError,
|
|
7664
|
-
polling,
|
|
7665
|
-
authExecutor,
|
|
7666
|
-
reloadAccounts,
|
|
7667
7740
|
activeOtpStatus,
|
|
7668
7741
|
activeOtpErrorMessage,
|
|
7669
7742
|
otpCode,
|
|
7670
|
-
handleVerifyLoginCode
|
|
7743
|
+
handleVerifyLoginCode
|
|
7744
|
+
} = deps;
|
|
7745
|
+
useEffect(() => {
|
|
7746
|
+
if (authenticated || !state.verificationTarget) return;
|
|
7747
|
+
if (activeOtpErrorMessage) dispatch({ type: "SET_ERROR", error: activeOtpErrorMessage });
|
|
7748
|
+
}, [activeOtpErrorMessage, authenticated, state.verificationTarget, dispatch]);
|
|
7749
|
+
useEffect(() => {
|
|
7750
|
+
if (state.verificationTarget && !authenticated && /^\d{6}$/.test(otpCode.trim()) && activeOtpStatus === "awaiting-code-input") {
|
|
7751
|
+
handleVerifyLoginCode();
|
|
7752
|
+
}
|
|
7753
|
+
}, [otpCode, state.verificationTarget, authenticated, activeOtpStatus, handleVerifyLoginCode]);
|
|
7754
|
+
}
|
|
7755
|
+
function usePasskeyCheckEffect(deps) {
|
|
7756
|
+
const {
|
|
7757
|
+
dispatch,
|
|
7758
|
+
ready,
|
|
7759
|
+
authenticated,
|
|
7760
|
+
apiBaseUrl,
|
|
7761
|
+
activeCredentialId,
|
|
7762
|
+
passkeyConfigLoaded,
|
|
7763
|
+
checkingPasskeyRef,
|
|
7671
7764
|
setAuthInput,
|
|
7672
7765
|
setOtpCode,
|
|
7766
|
+
polling,
|
|
7673
7767
|
mobileSetupFlowRef,
|
|
7674
7768
|
handlingMobileReturnRef,
|
|
7675
7769
|
setupAccountIdRef,
|
|
7676
7770
|
reauthSessionIdRef,
|
|
7677
7771
|
reauthTokenRef,
|
|
7678
|
-
|
|
7679
|
-
pollingTransferIdRef,
|
|
7680
|
-
processingStartedAtRef,
|
|
7681
|
-
checkingPasskeyRef,
|
|
7682
|
-
pendingSelectSourceAction,
|
|
7683
|
-
selectSourceChoices,
|
|
7684
|
-
selectSourceRecommended,
|
|
7685
|
-
setSelectSourceChainName,
|
|
7686
|
-
setSelectSourceTokenSymbol,
|
|
7687
|
-
initializedSelectSourceActionRef,
|
|
7688
|
-
oneTapLimitSavedDuringSetupRef,
|
|
7689
|
-
handleAuthorizedMobileReturn
|
|
7772
|
+
pollingTransferIdRef
|
|
7690
7773
|
} = deps;
|
|
7691
7774
|
const { getAccessToken } = usePrivy();
|
|
7692
|
-
const onCompleteRef = useRef(onComplete);
|
|
7693
|
-
onCompleteRef.current = onComplete;
|
|
7775
|
+
const onCompleteRef = useRef(deps.onComplete);
|
|
7776
|
+
onCompleteRef.current = deps.onComplete;
|
|
7694
7777
|
const getAccessTokenRef = useRef(getAccessToken);
|
|
7695
7778
|
getAccessTokenRef.current = getAccessToken;
|
|
7696
7779
|
const pollingRef = useRef(polling);
|
|
7697
7780
|
pollingRef.current = polling;
|
|
7698
|
-
const handleAuthorizedMobileReturnRef = useRef(handleAuthorizedMobileReturn);
|
|
7699
|
-
handleAuthorizedMobileReturnRef.current = handleAuthorizedMobileReturn;
|
|
7700
|
-
const lastAccountFetchRef = useRef(0);
|
|
7701
|
-
useEffect(() => {
|
|
7702
|
-
if (depositAmount != null) {
|
|
7703
|
-
dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
|
|
7704
|
-
}
|
|
7705
|
-
}, [depositAmount, dispatch]);
|
|
7706
|
-
useEffect(() => {
|
|
7707
|
-
if (authenticated || !state.verificationTarget) return;
|
|
7708
|
-
if (activeOtpErrorMessage) dispatch({ type: "SET_ERROR", error: activeOtpErrorMessage });
|
|
7709
|
-
}, [activeOtpErrorMessage, authenticated, state.verificationTarget, dispatch]);
|
|
7710
|
-
useEffect(() => {
|
|
7711
|
-
if (state.verificationTarget && !authenticated && /^\d{6}$/.test(otpCode.trim()) && activeOtpStatus === "awaiting-code-input") {
|
|
7712
|
-
handleVerifyLoginCode();
|
|
7713
|
-
}
|
|
7714
|
-
}, [otpCode, state.verificationTarget, authenticated, activeOtpStatus, handleVerifyLoginCode]);
|
|
7781
|
+
const handleAuthorizedMobileReturnRef = useRef(deps.handleAuthorizedMobileReturn);
|
|
7782
|
+
handleAuthorizedMobileReturnRef.current = deps.handleAuthorizedMobileReturn;
|
|
7715
7783
|
useEffect(() => {
|
|
7716
7784
|
if (!ready || !authenticated) {
|
|
7717
7785
|
checkingPasskeyRef.current = false;
|
|
7718
7786
|
return;
|
|
7719
7787
|
}
|
|
7720
|
-
if (
|
|
7788
|
+
if (passkeyConfigLoaded || activeCredentialId) return;
|
|
7721
7789
|
if (checkingPasskeyRef.current) return;
|
|
7722
7790
|
checkingPasskeyRef.current = true;
|
|
7723
7791
|
let cancelled = false;
|
|
@@ -7815,11 +7883,7 @@ function usePaymentEffects(deps) {
|
|
|
7815
7883
|
return;
|
|
7816
7884
|
}
|
|
7817
7885
|
if (existingTransfer.status === "AUTHORIZED") {
|
|
7818
|
-
|
|
7819
|
-
await handleAuthorizedMobileReturnRef.current(existingTransfer, true);
|
|
7820
|
-
} else {
|
|
7821
|
-
await handleAuthorizedMobileReturnRef.current(existingTransfer, false);
|
|
7822
|
-
}
|
|
7886
|
+
await handleAuthorizedMobileReturnRef.current(existingTransfer, !!persisted.isSetup);
|
|
7823
7887
|
return;
|
|
7824
7888
|
}
|
|
7825
7889
|
if (persisted.isSetup) {
|
|
@@ -7870,11 +7934,9 @@ function usePaymentEffects(deps) {
|
|
|
7870
7934
|
knownIds: allPasskeys.map((p) => p.credentialId),
|
|
7871
7935
|
oneTapLimit: config.defaultAllowance ?? void 0
|
|
7872
7936
|
});
|
|
7873
|
-
if (allPasskeys.length === 0)
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
if (state.activeCredentialId && allPasskeys.some((p) => p.credentialId === state.activeCredentialId)) {
|
|
7877
|
-
await restoreState(state.activeCredentialId, token);
|
|
7937
|
+
if (allPasskeys.length === 0) return;
|
|
7938
|
+
if (activeCredentialId && allPasskeys.some((p) => p.credentialId === activeCredentialId)) {
|
|
7939
|
+
await restoreState(activeCredentialId, token);
|
|
7878
7940
|
return;
|
|
7879
7941
|
}
|
|
7880
7942
|
if (cancelled) return;
|
|
@@ -7906,7 +7968,39 @@ function usePaymentEffects(deps) {
|
|
|
7906
7968
|
cancelled = true;
|
|
7907
7969
|
checkingPasskeyRef.current = false;
|
|
7908
7970
|
};
|
|
7909
|
-
}, [ready, authenticated, apiBaseUrl,
|
|
7971
|
+
}, [ready, authenticated, apiBaseUrl, activeCredentialId, passkeyConfigLoaded]);
|
|
7972
|
+
}
|
|
7973
|
+
|
|
7974
|
+
// src/dataLoading.ts
|
|
7975
|
+
function resolveDataLoadAction({
|
|
7976
|
+
authenticated,
|
|
7977
|
+
accountsCount,
|
|
7978
|
+
hasActiveCredential,
|
|
7979
|
+
loading
|
|
7980
|
+
}) {
|
|
7981
|
+
if (!authenticated || accountsCount > 0 || !hasActiveCredential) {
|
|
7982
|
+
return "reset";
|
|
7983
|
+
}
|
|
7984
|
+
if (loading) {
|
|
7985
|
+
return "wait";
|
|
7986
|
+
}
|
|
7987
|
+
return "load";
|
|
7988
|
+
}
|
|
7989
|
+
|
|
7990
|
+
// src/hooks/useDataLoadEffect.ts
|
|
7991
|
+
function useDataLoadEffect(deps) {
|
|
7992
|
+
const {
|
|
7993
|
+
state,
|
|
7994
|
+
dispatch,
|
|
7995
|
+
authenticated,
|
|
7996
|
+
apiBaseUrl,
|
|
7997
|
+
depositAmount,
|
|
7998
|
+
loadingDataRef
|
|
7999
|
+
} = deps;
|
|
8000
|
+
const { getAccessToken } = usePrivy();
|
|
8001
|
+
const getAccessTokenRef = useRef(getAccessToken);
|
|
8002
|
+
getAccessTokenRef.current = getAccessToken;
|
|
8003
|
+
const lastAccountFetchRef = useRef(0);
|
|
7910
8004
|
useEffect(() => {
|
|
7911
8005
|
const loadAction = resolveDataLoadAction({
|
|
7912
8006
|
authenticated,
|
|
@@ -8006,6 +8100,61 @@ function usePaymentEffects(deps) {
|
|
|
8006
8100
|
cancelled = true;
|
|
8007
8101
|
};
|
|
8008
8102
|
}, [authenticated, state.providers.length, state.activeCredentialId, apiBaseUrl]);
|
|
8103
|
+
useEffect(() => {
|
|
8104
|
+
if (state.accounts.length > 0 && state.activeCredentialId && !state.loadingData && !state.transfer && authenticated && Date.now() - lastAccountFetchRef.current > 15e3) {
|
|
8105
|
+
lastAccountFetchRef.current = Date.now();
|
|
8106
|
+
deps.reloadAccounts();
|
|
8107
|
+
}
|
|
8108
|
+
}, [
|
|
8109
|
+
state.accounts.length,
|
|
8110
|
+
state.activeCredentialId,
|
|
8111
|
+
state.loadingData,
|
|
8112
|
+
state.transfer,
|
|
8113
|
+
authenticated,
|
|
8114
|
+
deps
|
|
8115
|
+
]);
|
|
8116
|
+
}
|
|
8117
|
+
|
|
8118
|
+
// src/processingStatus.ts
|
|
8119
|
+
var PROCESSING_TIMEOUT_MS = 18e4;
|
|
8120
|
+
function resolvePreferredTransfer(polledTransfer, localTransfer) {
|
|
8121
|
+
return polledTransfer ?? localTransfer;
|
|
8122
|
+
}
|
|
8123
|
+
function getTransferStatus(polledTransfer, localTransfer) {
|
|
8124
|
+
const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
|
|
8125
|
+
return transfer?.status ?? "UNKNOWN";
|
|
8126
|
+
}
|
|
8127
|
+
function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
|
|
8128
|
+
if (!processingStartedAtMs) return false;
|
|
8129
|
+
return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
|
|
8130
|
+
}
|
|
8131
|
+
var STATUS_DISPLAY_LABELS = {
|
|
8132
|
+
CREATED: "created",
|
|
8133
|
+
AUTHORIZED: "authorized",
|
|
8134
|
+
SENDING: "sending",
|
|
8135
|
+
SENT: "confirming delivery",
|
|
8136
|
+
COMPLETED: "completed",
|
|
8137
|
+
FAILED: "failed"
|
|
8138
|
+
};
|
|
8139
|
+
function getStatusDisplayLabel(status) {
|
|
8140
|
+
return STATUS_DISPLAY_LABELS[status] ?? status;
|
|
8141
|
+
}
|
|
8142
|
+
function buildProcessingTimeoutMessage(status) {
|
|
8143
|
+
const label = getStatusDisplayLabel(status);
|
|
8144
|
+
return `Payment is taking longer than expected (status: ${label}). Please try again.`;
|
|
8145
|
+
}
|
|
8146
|
+
|
|
8147
|
+
// src/hooks/useProcessingEffect.ts
|
|
8148
|
+
function useProcessingEffect(deps) {
|
|
8149
|
+
const {
|
|
8150
|
+
state,
|
|
8151
|
+
dispatch,
|
|
8152
|
+
polling,
|
|
8153
|
+
processingStartedAtRef,
|
|
8154
|
+
onComplete,
|
|
8155
|
+
onError,
|
|
8156
|
+
reloadAccounts
|
|
8157
|
+
} = deps;
|
|
8009
8158
|
useEffect(() => {
|
|
8010
8159
|
if (!polling.transfer) return;
|
|
8011
8160
|
if (polling.transfer.status === "COMPLETED") {
|
|
@@ -8018,19 +8167,6 @@ function usePaymentEffects(deps) {
|
|
|
8018
8167
|
dispatch({ type: "TRANSFER_FAILED", transfer: polling.transfer, error: "Transfer failed." });
|
|
8019
8168
|
}
|
|
8020
8169
|
}, [polling.transfer, onComplete, dispatch, reloadAccounts]);
|
|
8021
|
-
useEffect(() => {
|
|
8022
|
-
if (state.accounts.length > 0 && state.activeCredentialId && !state.loadingData && !state.transfer && authenticated && Date.now() - lastAccountFetchRef.current > 15e3) {
|
|
8023
|
-
lastAccountFetchRef.current = Date.now();
|
|
8024
|
-
reloadAccounts();
|
|
8025
|
-
}
|
|
8026
|
-
}, [
|
|
8027
|
-
state.accounts.length,
|
|
8028
|
-
state.activeCredentialId,
|
|
8029
|
-
state.loadingData,
|
|
8030
|
-
state.transfer,
|
|
8031
|
-
authenticated,
|
|
8032
|
-
reloadAccounts
|
|
8033
|
-
]);
|
|
8034
8170
|
useEffect(() => {
|
|
8035
8171
|
const isProcessing = state.creatingTransfer || state.transfer != null && ["CREATED", "SENDING", "SENT"].includes(state.transfer.status);
|
|
8036
8172
|
if (!isProcessing) {
|
|
@@ -8066,6 +8202,26 @@ function usePaymentEffects(deps) {
|
|
|
8066
8202
|
dispatch,
|
|
8067
8203
|
processingStartedAtRef
|
|
8068
8204
|
]);
|
|
8205
|
+
}
|
|
8206
|
+
function useMobilePollingEffect(deps) {
|
|
8207
|
+
const {
|
|
8208
|
+
state,
|
|
8209
|
+
dispatch,
|
|
8210
|
+
polling,
|
|
8211
|
+
mobileSetupFlowRef,
|
|
8212
|
+
handlingMobileReturnRef,
|
|
8213
|
+
setupAccountIdRef,
|
|
8214
|
+
reauthSessionIdRef,
|
|
8215
|
+
reauthTokenRef,
|
|
8216
|
+
pollingTransferIdRef,
|
|
8217
|
+
reloadAccounts,
|
|
8218
|
+
apiBaseUrl
|
|
8219
|
+
} = deps;
|
|
8220
|
+
const { getAccessToken } = usePrivy();
|
|
8221
|
+
const getAccessTokenRef = useRef(getAccessToken);
|
|
8222
|
+
getAccessTokenRef.current = getAccessToken;
|
|
8223
|
+
const handleAuthorizedMobileReturnRef = useRef(deps.handleAuthorizedMobileReturn);
|
|
8224
|
+
handleAuthorizedMobileReturnRef.current = deps.handleAuthorizedMobileReturn;
|
|
8069
8225
|
useEffect(() => {
|
|
8070
8226
|
if (!state.mobileFlow) {
|
|
8071
8227
|
handlingMobileReturnRef.current = false;
|
|
@@ -8074,8 +8230,8 @@ function usePaymentEffects(deps) {
|
|
|
8074
8230
|
if (handlingMobileReturnRef.current) return;
|
|
8075
8231
|
const polledTransfer = polling.transfer;
|
|
8076
8232
|
if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
|
|
8077
|
-
void
|
|
8078
|
-
}, [state.mobileFlow, polling.transfer,
|
|
8233
|
+
void handleAuthorizedMobileReturnRef.current(polledTransfer, mobileSetupFlowRef.current);
|
|
8234
|
+
}, [state.mobileFlow, polling.transfer, handlingMobileReturnRef, mobileSetupFlowRef]);
|
|
8079
8235
|
useEffect(() => {
|
|
8080
8236
|
if (!state.mobileFlow || !mobileSetupFlowRef.current) return;
|
|
8081
8237
|
if (!state.activeCredentialId || !setupAccountIdRef.current) return;
|
|
@@ -8151,14 +8307,10 @@ function usePaymentEffects(deps) {
|
|
|
8151
8307
|
poll();
|
|
8152
8308
|
const intervalId = window.setInterval(poll, POLL_INTERVAL_MS);
|
|
8153
8309
|
const handleVisibility = () => {
|
|
8154
|
-
if (document.visibilityState === "visible" && !cancelled)
|
|
8155
|
-
poll();
|
|
8156
|
-
}
|
|
8310
|
+
if (document.visibilityState === "visible" && !cancelled) poll();
|
|
8157
8311
|
};
|
|
8158
8312
|
const handlePageShow = (e) => {
|
|
8159
|
-
if (e.persisted && !cancelled)
|
|
8160
|
-
poll();
|
|
8161
|
-
}
|
|
8313
|
+
if (e.persisted && !cancelled) poll();
|
|
8162
8314
|
};
|
|
8163
8315
|
document.addEventListener("visibilitychange", handleVisibility);
|
|
8164
8316
|
window.addEventListener("pageshow", handlePageShow);
|
|
@@ -8209,6 +8361,16 @@ function usePaymentEffects(deps) {
|
|
|
8209
8361
|
handlingMobileReturnRef,
|
|
8210
8362
|
pollingTransferIdRef
|
|
8211
8363
|
]);
|
|
8364
|
+
}
|
|
8365
|
+
function useSelectSourceEffect(deps) {
|
|
8366
|
+
const {
|
|
8367
|
+
pendingSelectSourceAction,
|
|
8368
|
+
selectSourceChoices,
|
|
8369
|
+
selectSourceRecommended,
|
|
8370
|
+
setSelectSourceChainName,
|
|
8371
|
+
setSelectSourceTokenSymbol,
|
|
8372
|
+
initializedSelectSourceActionRef
|
|
8373
|
+
} = deps;
|
|
8212
8374
|
useEffect(() => {
|
|
8213
8375
|
if (!pendingSelectSourceAction) {
|
|
8214
8376
|
initializedSelectSourceActionRef.current = null;
|
|
@@ -8239,38 +8401,27 @@ function usePaymentEffects(deps) {
|
|
|
8239
8401
|
setSelectSourceTokenSymbol,
|
|
8240
8402
|
initializedSelectSourceActionRef
|
|
8241
8403
|
]);
|
|
8404
|
+
}
|
|
8405
|
+
function useOneTapAutoResolveEffect(deps) {
|
|
8406
|
+
const { authExecutor, dispatch, oneTapLimitSavedDuringSetup, reloadAccounts } = deps;
|
|
8242
8407
|
const pendingOneTapSetupAction = authExecutor.pendingOneTapSetup;
|
|
8243
8408
|
useEffect(() => {
|
|
8244
|
-
if (pendingOneTapSetupAction &&
|
|
8245
|
-
|
|
8409
|
+
if (pendingOneTapSetupAction && oneTapLimitSavedDuringSetup) {
|
|
8410
|
+
dispatch({ type: "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP", saved: false });
|
|
8246
8411
|
authExecutor.resolveOneTapSetup();
|
|
8247
8412
|
}
|
|
8248
|
-
}, [pendingOneTapSetupAction, authExecutor,
|
|
8413
|
+
}, [pendingOneTapSetupAction, authExecutor, dispatch, oneTapLimitSavedDuringSetup]);
|
|
8249
8414
|
useEffect(() => {
|
|
8250
|
-
if (pendingOneTapSetupAction && !
|
|
8415
|
+
if (pendingOneTapSetupAction && !oneTapLimitSavedDuringSetup) {
|
|
8251
8416
|
reloadAccounts();
|
|
8252
8417
|
}
|
|
8253
|
-
}, [pendingOneTapSetupAction, reloadAccounts,
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
try {
|
|
8261
|
-
const result = await fetchGuestAccount(apiBaseUrl, state.guestSessionToken);
|
|
8262
|
-
if (cancelled) return;
|
|
8263
|
-
if (result && !result.hasPasskey) {
|
|
8264
|
-
dispatch({ type: "GUEST_PREAUTH_DETECTED", accountId: result.accountId });
|
|
8265
|
-
}
|
|
8266
|
-
} catch {
|
|
8267
|
-
}
|
|
8268
|
-
};
|
|
8269
|
-
checkGuestAccount();
|
|
8270
|
-
return () => {
|
|
8271
|
-
cancelled = true;
|
|
8272
|
-
};
|
|
8273
|
-
}, [state.transfer, state.guestSessionToken, state.guestPreauthAccountId, apiBaseUrl, dispatch]);
|
|
8418
|
+
}, [pendingOneTapSetupAction, reloadAccounts, oneTapLimitSavedDuringSetup]);
|
|
8419
|
+
}
|
|
8420
|
+
function useGuestPreauthEffect(deps) {
|
|
8421
|
+
const { state, dispatch, authenticated, apiBaseUrl, reloadAccounts } = deps;
|
|
8422
|
+
const { getAccessToken } = usePrivy();
|
|
8423
|
+
const getAccessTokenRef = useRef(getAccessToken);
|
|
8424
|
+
getAccessTokenRef.current = getAccessToken;
|
|
8274
8425
|
const settingOwnerRef = useRef(false);
|
|
8275
8426
|
useEffect(() => {
|
|
8276
8427
|
if (!state.guestPreauthAccountId) return;
|
|
@@ -8279,6 +8430,8 @@ function usePaymentEffects(deps) {
|
|
|
8279
8430
|
if (!authenticated) return;
|
|
8280
8431
|
if (!state.guestSessionToken) return;
|
|
8281
8432
|
if (settingOwnerRef.current) return;
|
|
8433
|
+
const hasActive = state.accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
|
|
8434
|
+
if (!hasActive) return;
|
|
8282
8435
|
settingOwnerRef.current = true;
|
|
8283
8436
|
let cancelled = false;
|
|
8284
8437
|
const setOwner = async () => {
|
|
@@ -8316,12 +8469,73 @@ function usePaymentEffects(deps) {
|
|
|
8316
8469
|
state.activeCredentialId,
|
|
8317
8470
|
state.activePublicKey,
|
|
8318
8471
|
state.guestSessionToken,
|
|
8472
|
+
state.accounts,
|
|
8319
8473
|
authenticated,
|
|
8320
8474
|
apiBaseUrl,
|
|
8321
8475
|
dispatch,
|
|
8322
8476
|
reloadAccounts
|
|
8323
8477
|
]);
|
|
8324
8478
|
}
|
|
8479
|
+
function useGuestDesktopPreauthSessionEffect(deps) {
|
|
8480
|
+
const { state, authExecutor, reloadAccounts, dispatch, desktopGuestPreauth } = deps;
|
|
8481
|
+
const preauthExecutingRef = useRef(false);
|
|
8482
|
+
useEffect(() => {
|
|
8483
|
+
if (!desktopGuestPreauth) return;
|
|
8484
|
+
if (!state.guestPreauthorizing) return;
|
|
8485
|
+
if (!state.guestPreauthSessionId || preauthExecutingRef.current) return;
|
|
8486
|
+
preauthExecutingRef.current = true;
|
|
8487
|
+
const runPreauthSession = async () => {
|
|
8488
|
+
try {
|
|
8489
|
+
await authExecutor.executeSessionById(state.guestPreauthSessionId);
|
|
8490
|
+
await reloadAccounts();
|
|
8491
|
+
} catch {
|
|
8492
|
+
} finally {
|
|
8493
|
+
preauthExecutingRef.current = false;
|
|
8494
|
+
dispatch({ type: "GUEST_PREAUTH_END" });
|
|
8495
|
+
}
|
|
8496
|
+
};
|
|
8497
|
+
void runPreauthSession();
|
|
8498
|
+
}, [
|
|
8499
|
+
desktopGuestPreauth,
|
|
8500
|
+
state.guestPreauthorizing,
|
|
8501
|
+
state.guestPreauthSessionId,
|
|
8502
|
+
authExecutor,
|
|
8503
|
+
reloadAccounts,
|
|
8504
|
+
dispatch
|
|
8505
|
+
]);
|
|
8506
|
+
}
|
|
8507
|
+
function useGuestPreauthPhaseSyncEffect(deps) {
|
|
8508
|
+
const { state, dispatch, authExecutor, isDesktop } = deps;
|
|
8509
|
+
useEffect(() => {
|
|
8510
|
+
if (!state.guestPreauthorizing || !isDesktop) return;
|
|
8511
|
+
const pending = authExecutor.pendingSelectSource;
|
|
8512
|
+
if (pending) {
|
|
8513
|
+
const intent = {
|
|
8514
|
+
step: "select-source",
|
|
8515
|
+
action: pending,
|
|
8516
|
+
isDesktop,
|
|
8517
|
+
skipOneTapLimit: true
|
|
8518
|
+
};
|
|
8519
|
+
if (state.phase.step === "select-source") {
|
|
8520
|
+
const ph = state.phase;
|
|
8521
|
+
if (ph.skipOneTapLimit && ph.action.id === pending.id) {
|
|
8522
|
+
return;
|
|
8523
|
+
}
|
|
8524
|
+
}
|
|
8525
|
+
dispatch({ type: "SET_USER_INTENT", intent });
|
|
8526
|
+
return;
|
|
8527
|
+
}
|
|
8528
|
+
if (state.phase.step === "select-source" && state.phase.skipOneTapLimit === true) {
|
|
8529
|
+
dispatch({ type: "SET_USER_INTENT", intent: { step: "one-tap-setup", action: null } });
|
|
8530
|
+
}
|
|
8531
|
+
}, [
|
|
8532
|
+
state.guestPreauthorizing,
|
|
8533
|
+
state.phase,
|
|
8534
|
+
isDesktop,
|
|
8535
|
+
authExecutor.pendingSelectSource,
|
|
8536
|
+
dispatch
|
|
8537
|
+
]);
|
|
8538
|
+
}
|
|
8325
8539
|
function BlinkPayment(props) {
|
|
8326
8540
|
const resetKey = useRef(0);
|
|
8327
8541
|
const handleBoundaryReset = useCallback(() => {
|
|
@@ -8343,6 +8557,10 @@ function BlinkPaymentInner({
|
|
|
8343
8557
|
const { apiBaseUrl, depositAmount } = useBlinkConfig();
|
|
8344
8558
|
const { ready, authenticated, logout, getAccessToken } = usePrivy();
|
|
8345
8559
|
useLoginWithOAuth();
|
|
8560
|
+
const isDesktop = shouldUseWalletConnector({
|
|
8561
|
+
useWalletConnector: useWalletConnectorProp,
|
|
8562
|
+
userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
|
|
8563
|
+
});
|
|
8346
8564
|
const [state, dispatch] = useReducer(
|
|
8347
8565
|
paymentReducer,
|
|
8348
8566
|
{
|
|
@@ -8426,7 +8644,9 @@ function BlinkPaymentInner({
|
|
|
8426
8644
|
reauthTokenRef: mobileFlowRefs.reauthTokenRef,
|
|
8427
8645
|
authenticated,
|
|
8428
8646
|
merchantAuthorization,
|
|
8429
|
-
destination
|
|
8647
|
+
destination,
|
|
8648
|
+
guestSessionToken: state.guestSessionToken,
|
|
8649
|
+
selectedProviderId: state.selectedProviderId
|
|
8430
8650
|
});
|
|
8431
8651
|
const oneTapSetup = useOneTapSetupHandlers({
|
|
8432
8652
|
dispatch,
|
|
@@ -8448,13 +8668,12 @@ function BlinkPaymentInner({
|
|
|
8448
8668
|
clearMobileFlowState();
|
|
8449
8669
|
transfer.processingStartedAtRef.current = null;
|
|
8450
8670
|
transfer.pollingTransferIdRef.current = null;
|
|
8451
|
-
oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
|
|
8452
8671
|
dispatch({
|
|
8453
8672
|
type: "NEW_PAYMENT",
|
|
8454
8673
|
depositAmount,
|
|
8455
8674
|
firstAccountId: state.accounts.length > 0 ? state.accounts[0].id : null
|
|
8456
8675
|
});
|
|
8457
|
-
}, [depositAmount, state.accounts, transfer
|
|
8676
|
+
}, [depositAmount, state.accounts, transfer]);
|
|
8458
8677
|
const handleLogout = useCallback(async () => {
|
|
8459
8678
|
try {
|
|
8460
8679
|
await logout();
|
|
@@ -8466,65 +8685,112 @@ function BlinkPaymentInner({
|
|
|
8466
8685
|
}
|
|
8467
8686
|
polling.stopPolling();
|
|
8468
8687
|
passkey.checkingPasskeyRef.current = false;
|
|
8469
|
-
oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
|
|
8470
8688
|
auth.setAuthInput("");
|
|
8471
8689
|
auth.setOtpCode("");
|
|
8472
8690
|
dispatch({ type: "LOGOUT", depositAmount });
|
|
8473
|
-
}, [logout, polling, depositAmount, auth, passkey
|
|
8474
|
-
|
|
8691
|
+
}, [logout, polling, depositAmount, auth, passkey]);
|
|
8692
|
+
useEffect(() => {
|
|
8693
|
+
if (depositAmount != null) {
|
|
8694
|
+
dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
|
|
8695
|
+
}
|
|
8696
|
+
}, [depositAmount, dispatch]);
|
|
8697
|
+
useOtpEffects({
|
|
8475
8698
|
state,
|
|
8476
8699
|
dispatch,
|
|
8477
|
-
ready,
|
|
8478
8700
|
authenticated,
|
|
8479
|
-
apiBaseUrl,
|
|
8480
|
-
depositAmount,
|
|
8481
|
-
onComplete,
|
|
8482
|
-
onError,
|
|
8483
|
-
polling,
|
|
8484
|
-
authExecutor,
|
|
8485
|
-
reloadAccounts: transfer.reloadAccounts,
|
|
8486
8701
|
activeOtpStatus: auth.activeOtpStatus,
|
|
8487
8702
|
activeOtpErrorMessage: auth.activeOtpErrorMessage,
|
|
8488
8703
|
otpCode: auth.otpCode,
|
|
8489
8704
|
handleVerifyLoginCode: auth.handleVerifyLoginCode,
|
|
8490
8705
|
setAuthInput: auth.setAuthInput,
|
|
8706
|
+
setOtpCode: auth.setOtpCode
|
|
8707
|
+
});
|
|
8708
|
+
usePasskeyCheckEffect({
|
|
8709
|
+
dispatch,
|
|
8710
|
+
ready,
|
|
8711
|
+
authenticated,
|
|
8712
|
+
apiBaseUrl,
|
|
8713
|
+
activeCredentialId: state.activeCredentialId,
|
|
8714
|
+
passkeyConfigLoaded: state.passkeyConfigLoaded,
|
|
8715
|
+
checkingPasskeyRef: passkey.checkingPasskeyRef,
|
|
8716
|
+
setAuthInput: auth.setAuthInput,
|
|
8491
8717
|
setOtpCode: auth.setOtpCode,
|
|
8718
|
+
polling,
|
|
8492
8719
|
mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
|
|
8493
8720
|
handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
|
|
8494
8721
|
setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
|
|
8495
8722
|
reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
|
|
8496
8723
|
reauthTokenRef: mobileFlowRefs.reauthTokenRef,
|
|
8497
|
-
loadingDataRef: mobileFlowRefs.loadingDataRef,
|
|
8498
8724
|
pollingTransferIdRef: transfer.pollingTransferIdRef,
|
|
8725
|
+
handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn,
|
|
8726
|
+
onComplete
|
|
8727
|
+
});
|
|
8728
|
+
useDataLoadEffect({
|
|
8729
|
+
state,
|
|
8730
|
+
dispatch,
|
|
8731
|
+
authenticated,
|
|
8732
|
+
apiBaseUrl,
|
|
8733
|
+
depositAmount,
|
|
8734
|
+
loadingDataRef: mobileFlowRefs.loadingDataRef,
|
|
8735
|
+
reloadAccounts: transfer.reloadAccounts
|
|
8736
|
+
});
|
|
8737
|
+
useProcessingEffect({
|
|
8738
|
+
state,
|
|
8739
|
+
dispatch,
|
|
8740
|
+
polling,
|
|
8499
8741
|
processingStartedAtRef: transfer.processingStartedAtRef,
|
|
8500
|
-
|
|
8742
|
+
onComplete,
|
|
8743
|
+
onError,
|
|
8744
|
+
reloadAccounts: transfer.reloadAccounts
|
|
8745
|
+
});
|
|
8746
|
+
useMobilePollingEffect({
|
|
8747
|
+
state,
|
|
8748
|
+
dispatch,
|
|
8749
|
+
polling,
|
|
8750
|
+
mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
|
|
8751
|
+
handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
|
|
8752
|
+
setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
|
|
8753
|
+
reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
|
|
8754
|
+
reauthTokenRef: mobileFlowRefs.reauthTokenRef,
|
|
8755
|
+
pollingTransferIdRef: transfer.pollingTransferIdRef,
|
|
8756
|
+
reloadAccounts: transfer.reloadAccounts,
|
|
8757
|
+
handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn,
|
|
8758
|
+
apiBaseUrl
|
|
8759
|
+
});
|
|
8760
|
+
useSelectSourceEffect({
|
|
8501
8761
|
pendingSelectSourceAction: sourceSelection.pendingSelectSourceAction,
|
|
8502
8762
|
selectSourceChoices: sourceSelection.selectSourceChoices,
|
|
8503
8763
|
selectSourceRecommended: sourceSelection.selectSourceRecommended,
|
|
8504
8764
|
setSelectSourceChainName: sourceSelection.setSelectSourceChainName,
|
|
8505
8765
|
setSelectSourceTokenSymbol: sourceSelection.setSelectSourceTokenSymbol,
|
|
8506
|
-
initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef
|
|
8507
|
-
oneTapLimitSavedDuringSetupRef: oneTapSetup.oneTapLimitSavedDuringSetupRef,
|
|
8508
|
-
handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn
|
|
8766
|
+
initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef
|
|
8509
8767
|
});
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
}, [
|
|
8520
|
-
state.isGuestFlow,
|
|
8521
|
-
state.selectedProviderId,
|
|
8522
|
-
state.activeCredentialId,
|
|
8523
|
-
state.transfer,
|
|
8524
|
-
state.accounts,
|
|
8768
|
+
useOneTapAutoResolveEffect({
|
|
8769
|
+
authExecutor,
|
|
8770
|
+
dispatch,
|
|
8771
|
+
oneTapLimitSavedDuringSetup: state.oneTapLimitSavedDuringSetup,
|
|
8772
|
+
reloadAccounts: transfer.reloadAccounts
|
|
8773
|
+
});
|
|
8774
|
+
useGuestPreauthEffect({
|
|
8775
|
+
state,
|
|
8776
|
+
dispatch,
|
|
8525
8777
|
authenticated,
|
|
8526
|
-
|
|
8527
|
-
|
|
8778
|
+
apiBaseUrl,
|
|
8779
|
+
reloadAccounts: transfer.reloadAccounts
|
|
8780
|
+
});
|
|
8781
|
+
useGuestPreauthPhaseSyncEffect({
|
|
8782
|
+
state,
|
|
8783
|
+
dispatch,
|
|
8784
|
+
authExecutor,
|
|
8785
|
+
isDesktop
|
|
8786
|
+
});
|
|
8787
|
+
useGuestDesktopPreauthSessionEffect({
|
|
8788
|
+
state,
|
|
8789
|
+
authExecutor,
|
|
8790
|
+
reloadAccounts: transfer.reloadAccounts,
|
|
8791
|
+
dispatch,
|
|
8792
|
+
desktopGuestPreauth: isDesktop
|
|
8793
|
+
});
|
|
8528
8794
|
const handlers = useMemo(() => ({
|
|
8529
8795
|
onSendLoginCode: auth.handleSendLoginCode,
|
|
8530
8796
|
onVerifyLoginCode: auth.handleVerifyLoginCode,
|
|
@@ -8547,7 +8813,7 @@ function BlinkPaymentInner({
|
|
|
8547
8813
|
onBackFromOpenWallet: () => dispatch({ type: "CLEAR_MOBILE_STATE" }),
|
|
8548
8814
|
onLogout: handleLogout,
|
|
8549
8815
|
onNewPayment: handleNewPayment,
|
|
8550
|
-
|
|
8816
|
+
onSetPhase: (phase) => dispatch({ type: "SET_USER_INTENT", intent: phase }),
|
|
8551
8817
|
onSetAuthInput: auth.setAuthInput,
|
|
8552
8818
|
onSetOtpCode: (code) => {
|
|
8553
8819
|
auth.setOtpCode(code);
|
|
@@ -8562,29 +8828,7 @@ function BlinkPaymentInner({
|
|
|
8562
8828
|
onAuthorizeToken: provider.handleAuthorizeToken,
|
|
8563
8829
|
onSelectGuestToken: guestTransfer.handleSelectGuestToken,
|
|
8564
8830
|
onLogin: () => dispatch({ type: "REQUEST_LOGIN" }),
|
|
8565
|
-
onPreauthorize:
|
|
8566
|
-
if (state.guestPreauthAccountId) {
|
|
8567
|
-
dispatch({ type: "REQUEST_LOGIN" });
|
|
8568
|
-
return;
|
|
8569
|
-
}
|
|
8570
|
-
if (!state.guestSessionToken || !state.selectedProviderId) {
|
|
8571
|
-
dispatch({ type: "REQUEST_LOGIN" });
|
|
8572
|
-
return;
|
|
8573
|
-
}
|
|
8574
|
-
try {
|
|
8575
|
-
const providerName = state.providers.find((p) => p.id === state.selectedProviderId)?.name ?? "Wallet";
|
|
8576
|
-
const created = await createGuestAccount(
|
|
8577
|
-
apiBaseUrl,
|
|
8578
|
-
state.guestSessionToken,
|
|
8579
|
-
state.selectedProviderId,
|
|
8580
|
-
providerName
|
|
8581
|
-
);
|
|
8582
|
-
dispatch({ type: "GUEST_PREAUTH_DETECTED", accountId: created.accountId });
|
|
8583
|
-
dispatch({ type: "REQUEST_LOGIN" });
|
|
8584
|
-
} catch {
|
|
8585
|
-
dispatch({ type: "REQUEST_LOGIN" });
|
|
8586
|
-
}
|
|
8587
|
-
}
|
|
8831
|
+
onPreauthorize: provider.handlePreauthorize
|
|
8588
8832
|
}), [
|
|
8589
8833
|
auth,
|
|
8590
8834
|
passkey,
|
|
@@ -8595,56 +8839,57 @@ function BlinkPaymentInner({
|
|
|
8595
8839
|
oneTapSetup,
|
|
8596
8840
|
guestTransfer,
|
|
8597
8841
|
handleLogout,
|
|
8598
|
-
handleNewPayment
|
|
8599
|
-
state.guestPreauthAccountId,
|
|
8600
|
-
state.guestSessionToken,
|
|
8601
|
-
state.selectedProviderId,
|
|
8602
|
-
state.providers,
|
|
8603
|
-
apiBaseUrl
|
|
8842
|
+
handleNewPayment
|
|
8604
8843
|
]);
|
|
8605
8844
|
return /* @__PURE__ */ jsx(
|
|
8606
8845
|
StepRenderer,
|
|
8607
8846
|
{
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
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
|
-
|
|
8847
|
+
flow: {
|
|
8848
|
+
state,
|
|
8849
|
+
authenticated,
|
|
8850
|
+
activeOtpStatus: auth.activeOtpStatus,
|
|
8851
|
+
isDesktop,
|
|
8852
|
+
merchantName,
|
|
8853
|
+
onBack,
|
|
8854
|
+
onDismiss,
|
|
8855
|
+
depositAmount
|
|
8856
|
+
},
|
|
8857
|
+
remote: {
|
|
8858
|
+
pollingTransfer: polling.transfer,
|
|
8859
|
+
pollingError: polling.error,
|
|
8860
|
+
authExecutorError: authExecutor.error,
|
|
8861
|
+
transferSigningSigning: transferSigning.signing,
|
|
8862
|
+
transferSigningError: transferSigning.error,
|
|
8863
|
+
pendingSelectSource: authExecutor.pendingSelectSource,
|
|
8864
|
+
pendingOneTapSetup: authExecutor.pendingOneTapSetup
|
|
8865
|
+
},
|
|
8866
|
+
derived: {
|
|
8867
|
+
pendingConnections: derived.pendingConnections,
|
|
8868
|
+
depositEligibleAccounts: derived.depositEligibleAccounts,
|
|
8869
|
+
sourceName: derived.sourceName,
|
|
8870
|
+
maxSourceBalance: derived.maxSourceBalance,
|
|
8871
|
+
tokenCount: derived.tokenCount,
|
|
8872
|
+
selectedAccount: derived.selectedAccount,
|
|
8873
|
+
selectedSource: derived.selectedSource,
|
|
8874
|
+
selectSourceChoices: sourceSelection.selectSourceChoices,
|
|
8875
|
+
selectSourceRecommended: sourceSelection.selectSourceRecommended,
|
|
8876
|
+
selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance
|
|
8877
|
+
},
|
|
8878
|
+
forms: {
|
|
8879
|
+
authInput: auth.authInput,
|
|
8880
|
+
otpCode: auth.otpCode,
|
|
8881
|
+
selectSourceChainName: sourceSelection.selectSourceChainName,
|
|
8882
|
+
selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
|
|
8883
|
+
savingOneTapLimit: oneTapSetup.savingOneTapLimit,
|
|
8884
|
+
guestTokenEntries: guestTransfer.guestTokenEntries,
|
|
8885
|
+
guestLoadingBalances: guestTransfer.loadingBalances,
|
|
8886
|
+
guestSettingSender: guestTransfer.settingSender
|
|
8887
|
+
},
|
|
8643
8888
|
handlers
|
|
8644
8889
|
}
|
|
8645
8890
|
);
|
|
8646
8891
|
}
|
|
8647
8892
|
|
|
8648
|
-
export { AdvancedSourceScreen, BLINK_LOGO, BLINK_MASCOT, BlinkLoadingScreen, BlinkPayment, BlinkProvider, FlowPhaseProvider, IconCircle, InfoBanner, OutlineButton, PasskeyIframeBlockedError, PasskeyScreen, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, Spinner, StepList, TokenPickerScreen, api_exports as blinkApi, buildPasskeyPopupOptions, createPasskeyCredential, createPasskeyViaPopup, darkTheme, deviceHasPasskey, findDevicePasskey, findDevicePasskeyViaPopup, getTheme, lightTheme, resolvePasskeyRpId,
|
|
8893
|
+
export { AdvancedSourceScreen, BLINK_LOGO, BLINK_MASCOT, BlinkLoadingScreen, BlinkPayment, BlinkProvider, FlowPhaseProvider, IconCircle, InfoBanner, OutlineButton, PasskeyIframeBlockedError, PasskeyScreen, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, Spinner, StepList, TokenPickerScreen, api_exports as blinkApi, buildPasskeyPopupOptions, createPasskeyCredential, createPasskeyViaPopup, darkTheme, deviceHasPasskey, findDevicePasskey, findDevicePasskeyViaPopup, getTheme, lightTheme, resolvePasskeyRpId, screenForPhase, useAuthorizationExecutor, useBlinkConfig, useBlinkDepositAmount, useTransferPolling, useTransferSigning };
|
|
8649
8894
|
//# sourceMappingURL=index.js.map
|
|
8650
8895
|
//# sourceMappingURL=index.js.map
|