@swype-org/react-sdk 0.1.230 → 0.1.237
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 +1322 -973
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -45
- package/dist/index.d.ts +65 -45
- package/dist/index.js +1322 -973
- 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;
|
|
889
|
+
}
|
|
890
|
+
setError(null);
|
|
891
|
+
setTransfer(result.transfer);
|
|
892
|
+
if (result.transfer.status === "COMPLETED" || result.transfer.status === "FAILED") {
|
|
893
|
+
stopPolling();
|
|
511
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
|
-
if (hex.length === 130) {
|
|
837
|
-
return `0x${hex}`;
|
|
838
|
-
}
|
|
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);
|
|
1211
|
+
if (hex.length === 130) {
|
|
1212
|
+
return `0x${hex}`;
|
|
1213
|
+
}
|
|
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
1274
|
for (let i = 0; i < WALLET_CLIENT_MAX_ATTEMPTS; i++) {
|
|
1110
1275
|
try {
|
|
1111
1276
|
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
|
|
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,95 @@ 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 (state.mobileFlow && state.deeplinkUri) {
|
|
2025
|
+
return {
|
|
2026
|
+
step: "wallet-setup",
|
|
2027
|
+
mobile: { deeplinkUri: state.deeplinkUri, providerId: state.selectedProviderId },
|
|
2028
|
+
accountId: null
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
if (p.step === "wallet-setup" && p.mobile == null) {
|
|
2032
|
+
return p;
|
|
2033
|
+
}
|
|
2034
|
+
if (state.isGuestFlow && state.selectedProviderId != null && !state.transfer) {
|
|
2035
|
+
return { step: "guest-token-picker" };
|
|
2036
|
+
}
|
|
2037
|
+
if (p.step === "wallet-picker" && !state.creatingTransfer && !(state.mobileFlow && state.deeplinkUri)) {
|
|
2038
|
+
return p;
|
|
2039
|
+
}
|
|
2040
|
+
if (!state.privyReady) {
|
|
2041
|
+
return { step: "initializing" };
|
|
2042
|
+
}
|
|
2043
|
+
if (state.privyAuthenticated && !state.activeCredentialId && !state.passkeyConfigLoaded) {
|
|
2044
|
+
return { step: "initializing" };
|
|
2045
|
+
}
|
|
2046
|
+
if (state.verificationTarget) {
|
|
2047
|
+
return { step: "otp-verify", target: state.verificationTarget };
|
|
2048
|
+
}
|
|
2049
|
+
if (state.loginRequested) {
|
|
2050
|
+
return { step: "login" };
|
|
2051
|
+
}
|
|
2052
|
+
if (state.passkeyConfigLoaded && !state.activeCredentialId) {
|
|
2053
|
+
if (state.knownCredentialIds.length > 0 && state.passkeyPopupNeeded) {
|
|
2054
|
+
return { step: "passkey-verify" };
|
|
2055
|
+
}
|
|
2056
|
+
return { step: "passkey-create", popupFallback: state.passkeyPopupNeeded };
|
|
2057
|
+
}
|
|
2058
|
+
if (state.loadingData && state.activeCredentialId && hasActiveWallet(state.accounts)) {
|
|
2059
|
+
return { step: "data-loading" };
|
|
2060
|
+
}
|
|
2061
|
+
if (state.activeCredentialId && !hasActiveWallet(state.accounts) && !state.mobileFlow) {
|
|
2062
|
+
return { step: "wallet-picker", reason: "link" };
|
|
2063
|
+
}
|
|
2064
|
+
if (state.activeCredentialId && hasActiveWallet(state.accounts) && !state.loadingData) {
|
|
2065
|
+
return { step: "deposit" };
|
|
2066
|
+
}
|
|
2067
|
+
if (state.isGuestFlow) {
|
|
2068
|
+
return { step: "wallet-picker", reason: "guest-entry" };
|
|
2069
|
+
}
|
|
2070
|
+
return { step: "wallet-picker", reason: "entry" };
|
|
2071
|
+
}
|
|
2072
|
+
|
|
1937
2073
|
// src/paymentReducer.ts
|
|
1938
2074
|
function deriveSourceTypeAndId(state) {
|
|
1939
2075
|
if (state.selectedWalletId) {
|
|
@@ -1946,6 +2082,7 @@ function deriveSourceTypeAndId(state) {
|
|
|
1946
2082
|
}
|
|
1947
2083
|
function createInitialState(config) {
|
|
1948
2084
|
return {
|
|
2085
|
+
phase: { step: "initializing" },
|
|
1949
2086
|
error: null,
|
|
1950
2087
|
providers: [],
|
|
1951
2088
|
accounts: [],
|
|
@@ -1966,6 +2103,7 @@ function createInitialState(config) {
|
|
|
1966
2103
|
knownCredentialIds: [],
|
|
1967
2104
|
verificationTarget: null,
|
|
1968
2105
|
oneTapLimit: 100,
|
|
2106
|
+
oneTapLimitSavedDuringSetup: false,
|
|
1969
2107
|
mobileFlow: false,
|
|
1970
2108
|
deeplinkUri: null,
|
|
1971
2109
|
increasingLimit: false,
|
|
@@ -1973,12 +2111,19 @@ function createInitialState(config) {
|
|
|
1973
2111
|
guestTransferId: null,
|
|
1974
2112
|
guestSessionToken: null,
|
|
1975
2113
|
guestPreauthAccountId: null,
|
|
2114
|
+
guestPreauthSessionId: null,
|
|
1976
2115
|
activePublicKey: null,
|
|
1977
|
-
|
|
1978
|
-
|
|
2116
|
+
loginRequested: false,
|
|
2117
|
+
guestPreauthorizing: false,
|
|
2118
|
+
privyReady: false,
|
|
2119
|
+
privyAuthenticated: false
|
|
1979
2120
|
};
|
|
1980
2121
|
}
|
|
1981
2122
|
function paymentReducer(state, action) {
|
|
2123
|
+
const next = applyAction(state, action);
|
|
2124
|
+
return { ...next, phase: resolvePhase(next) };
|
|
2125
|
+
}
|
|
2126
|
+
function applyAction(state, action) {
|
|
1982
2127
|
switch (action.type) {
|
|
1983
2128
|
// ── Auth ──────────────────────────────────────────────────────
|
|
1984
2129
|
case "CODE_SENT":
|
|
@@ -2056,23 +2201,20 @@ function paymentReducer(state, action) {
|
|
|
2056
2201
|
selectedProviderId: action.providerId,
|
|
2057
2202
|
selectedAccountId: null,
|
|
2058
2203
|
selectedWalletId: null,
|
|
2059
|
-
selectedTokenSymbol: null
|
|
2060
|
-
userIntent: null
|
|
2204
|
+
selectedTokenSymbol: null
|
|
2061
2205
|
};
|
|
2062
2206
|
case "SELECT_ACCOUNT":
|
|
2063
2207
|
return {
|
|
2064
2208
|
...state,
|
|
2065
2209
|
selectedAccountId: action.accountId,
|
|
2066
2210
|
selectedWalletId: action.walletId,
|
|
2067
|
-
selectedTokenSymbol: null
|
|
2068
|
-
userIntent: null
|
|
2211
|
+
selectedTokenSymbol: null
|
|
2069
2212
|
};
|
|
2070
2213
|
case "SELECT_TOKEN":
|
|
2071
2214
|
return {
|
|
2072
2215
|
...state,
|
|
2073
2216
|
selectedWalletId: action.walletId,
|
|
2074
|
-
selectedTokenSymbol: action.tokenSymbol
|
|
2075
|
-
userIntent: null
|
|
2217
|
+
selectedTokenSymbol: action.tokenSymbol
|
|
2076
2218
|
};
|
|
2077
2219
|
// ── Transfer lifecycle ───────────────────────────────────────
|
|
2078
2220
|
case "PAY_STARTED":
|
|
@@ -2081,8 +2223,7 @@ function paymentReducer(state, action) {
|
|
|
2081
2223
|
error: null,
|
|
2082
2224
|
creatingTransfer: true,
|
|
2083
2225
|
deeplinkUri: null,
|
|
2084
|
-
mobileFlow: false
|
|
2085
|
-
userIntent: null
|
|
2226
|
+
mobileFlow: false
|
|
2086
2227
|
};
|
|
2087
2228
|
case "PAY_ENDED":
|
|
2088
2229
|
return { ...state, creatingTransfer: false };
|
|
@@ -2146,7 +2287,8 @@ function paymentReducer(state, action) {
|
|
|
2146
2287
|
transfer: action.transfer,
|
|
2147
2288
|
error: null,
|
|
2148
2289
|
mobileFlow: false,
|
|
2149
|
-
deeplinkUri: null
|
|
2290
|
+
deeplinkUri: null,
|
|
2291
|
+
phase: { step: "confirm-sign", transfer: action.transfer }
|
|
2150
2292
|
};
|
|
2151
2293
|
case "CLEAR_MOBILE_STATE":
|
|
2152
2294
|
return { ...state, mobileFlow: false, deeplinkUri: null };
|
|
@@ -2204,6 +2346,17 @@ function paymentReducer(state, action) {
|
|
|
2204
2346
|
selectedWalletId: null,
|
|
2205
2347
|
selectedTokenSymbol: null
|
|
2206
2348
|
};
|
|
2349
|
+
case "GUEST_BACK_FROM_TOKEN_PICKER":
|
|
2350
|
+
return {
|
|
2351
|
+
...state,
|
|
2352
|
+
selectedProviderId: null,
|
|
2353
|
+
guestTransferId: null,
|
|
2354
|
+
guestSessionToken: null,
|
|
2355
|
+
selectedAccountId: null,
|
|
2356
|
+
selectedWalletId: null,
|
|
2357
|
+
selectedTokenSymbol: null,
|
|
2358
|
+
error: null
|
|
2359
|
+
};
|
|
2207
2360
|
case "GUEST_TRANSFER_COMPLETED":
|
|
2208
2361
|
return {
|
|
2209
2362
|
...state,
|
|
@@ -2216,29 +2369,40 @@ function paymentReducer(state, action) {
|
|
|
2216
2369
|
case "GUEST_PREAUTH_DETECTED":
|
|
2217
2370
|
return {
|
|
2218
2371
|
...state,
|
|
2219
|
-
guestPreauthAccountId: action.accountId
|
|
2372
|
+
guestPreauthAccountId: action.accountId,
|
|
2373
|
+
guestPreauthSessionId: action.sessionId ?? state.guestPreauthSessionId,
|
|
2374
|
+
selectedAccountId: action.accountId,
|
|
2375
|
+
selectedWalletId: null,
|
|
2376
|
+
selectedTokenSymbol: null
|
|
2220
2377
|
};
|
|
2378
|
+
case "GUEST_PREAUTH_BEGIN":
|
|
2379
|
+
return { ...state, guestPreauthorizing: true, error: null };
|
|
2380
|
+
case "GUEST_PREAUTH_END":
|
|
2381
|
+
return { ...state, guestPreauthorizing: false };
|
|
2221
2382
|
case "ACCOUNT_OWNER_SET":
|
|
2222
2383
|
return {
|
|
2223
2384
|
...state,
|
|
2224
2385
|
guestPreauthAccountId: null,
|
|
2386
|
+
guestPreauthSessionId: null,
|
|
2225
2387
|
activePublicKey: null,
|
|
2226
2388
|
error: null,
|
|
2227
|
-
|
|
2389
|
+
guestPreauthorizing: false,
|
|
2390
|
+
phase: { step: "one-tap-setup", action: null }
|
|
2228
2391
|
};
|
|
2229
2392
|
// ── User intent & error ──────────────────────────────────────
|
|
2230
2393
|
case "SET_USER_INTENT":
|
|
2231
|
-
return { ...state,
|
|
2394
|
+
return { ...state, phase: action.intent };
|
|
2232
2395
|
case "REQUEST_LOGIN":
|
|
2233
2396
|
return {
|
|
2234
2397
|
...state,
|
|
2235
2398
|
loginRequested: true,
|
|
2236
2399
|
transfer: null,
|
|
2237
|
-
isGuestFlow: false,
|
|
2238
2400
|
creatingTransfer: false
|
|
2239
2401
|
};
|
|
2240
2402
|
case "SET_ERROR":
|
|
2241
2403
|
return { ...state, error: action.error };
|
|
2404
|
+
case "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP":
|
|
2405
|
+
return { ...state, oneTapLimitSavedDuringSetup: action.saved };
|
|
2242
2406
|
// ── Lifecycle ────────────────────────────────────────────────
|
|
2243
2407
|
case "NEW_PAYMENT":
|
|
2244
2408
|
return {
|
|
@@ -2255,9 +2419,11 @@ function paymentReducer(state, action) {
|
|
|
2255
2419
|
guestTransferId: null,
|
|
2256
2420
|
guestSessionToken: null,
|
|
2257
2421
|
guestPreauthAccountId: null,
|
|
2422
|
+
guestPreauthSessionId: null,
|
|
2258
2423
|
activePublicKey: null,
|
|
2259
|
-
|
|
2260
|
-
|
|
2424
|
+
loginRequested: false,
|
|
2425
|
+
oneTapLimitSavedDuringSetup: false,
|
|
2426
|
+
guestPreauthorizing: false
|
|
2261
2427
|
};
|
|
2262
2428
|
case "LOGOUT":
|
|
2263
2429
|
return {
|
|
@@ -2265,7 +2431,15 @@ function paymentReducer(state, action) {
|
|
|
2265
2431
|
depositAmount: action.depositAmount,
|
|
2266
2432
|
passkeyPopupNeeded: state.passkeyPopupNeeded,
|
|
2267
2433
|
activeCredentialId: null
|
|
2268
|
-
})
|
|
2434
|
+
}),
|
|
2435
|
+
privyReady: action.privyReady,
|
|
2436
|
+
privyAuthenticated: false
|
|
2437
|
+
};
|
|
2438
|
+
case "SYNC_PRIVY_SESSION":
|
|
2439
|
+
return {
|
|
2440
|
+
...state,
|
|
2441
|
+
privyReady: action.ready,
|
|
2442
|
+
privyAuthenticated: action.authenticated
|
|
2269
2443
|
};
|
|
2270
2444
|
case "SYNC_AMOUNT":
|
|
2271
2445
|
return { ...state, amount: action.amount };
|
|
@@ -2314,97 +2488,42 @@ function maskAuthIdentifier(identifier) {
|
|
|
2314
2488
|
return `***-***-${visibleSuffix}`;
|
|
2315
2489
|
}
|
|
2316
2490
|
|
|
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
2491
|
// src/resolveScreen.ts
|
|
2330
|
-
function
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
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";
|
|
2397
|
-
}
|
|
2398
|
-
if (state.userIntent === "switch-wallet") {
|
|
2399
|
-
return "wallet-picker";
|
|
2400
|
-
}
|
|
2401
|
-
if (state.activeCredentialId != null && hasActiveWallet(state.accounts) && !state.loadingData) {
|
|
2402
|
-
return "deposit";
|
|
2403
|
-
}
|
|
2404
|
-
if (state.isGuestFlow) {
|
|
2405
|
-
return "wallet-picker";
|
|
2492
|
+
function screenForPhase(phase) {
|
|
2493
|
+
switch (phase.step) {
|
|
2494
|
+
case "initializing":
|
|
2495
|
+
case "data-loading":
|
|
2496
|
+
return "loading";
|
|
2497
|
+
case "login":
|
|
2498
|
+
return "login";
|
|
2499
|
+
case "otp-verify":
|
|
2500
|
+
return "otp-verify";
|
|
2501
|
+
case "passkey-create":
|
|
2502
|
+
return "create-passkey";
|
|
2503
|
+
case "passkey-verify":
|
|
2504
|
+
return "verify-passkey";
|
|
2505
|
+
case "wallet-picker":
|
|
2506
|
+
return "wallet-picker";
|
|
2507
|
+
case "wallet-setup":
|
|
2508
|
+
return phase.mobile ? "open-wallet" : "setup-status";
|
|
2509
|
+
case "select-source":
|
|
2510
|
+
return phase.isDesktop ? "setup" : "select-source";
|
|
2511
|
+
case "one-tap-setup":
|
|
2512
|
+
return "setup";
|
|
2513
|
+
case "guest-token-picker":
|
|
2514
|
+
return "guest-token-picker";
|
|
2515
|
+
case "token-picker":
|
|
2516
|
+
return "token-picker";
|
|
2517
|
+
case "deposit":
|
|
2518
|
+
return "deposit";
|
|
2519
|
+
case "processing":
|
|
2520
|
+
return "processing";
|
|
2521
|
+
case "confirm-sign":
|
|
2522
|
+
return "confirm-sign";
|
|
2523
|
+
case "completed":
|
|
2524
|
+
case "failed":
|
|
2525
|
+
return "success";
|
|
2406
2526
|
}
|
|
2407
|
-
return "wallet-picker";
|
|
2408
2527
|
}
|
|
2409
2528
|
var MUTED = "#7fa4b0";
|
|
2410
2529
|
var LOGO_SIZE = 48;
|
|
@@ -5887,85 +6006,66 @@ var DEPOSIT_SCREENS = /* @__PURE__ */ new Set([
|
|
|
5887
6006
|
"processing",
|
|
5888
6007
|
"success"
|
|
5889
6008
|
]);
|
|
5890
|
-
function getFlowPhase(screen,
|
|
6009
|
+
function getFlowPhase(screen, phase) {
|
|
5891
6010
|
if (LINK_SCREENS.has(screen)) return "link";
|
|
5892
6011
|
if (DEPOSIT_SCREENS.has(screen)) return "deposit";
|
|
5893
6012
|
if (screen === "token-picker" || screen === "select-source" || screen === "guest-token-picker") {
|
|
5894
|
-
return
|
|
6013
|
+
return phase.step === "one-tap-setup" ? "link" : "deposit";
|
|
5895
6014
|
}
|
|
5896
6015
|
return null;
|
|
5897
6016
|
}
|
|
5898
6017
|
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 }) });
|
|
6018
|
+
const screen = screenForPhase(props.flow.state.phase);
|
|
6019
|
+
const flowPhase = getFlowPhase(screen, props.flow.state.phase);
|
|
6020
|
+
return /* @__PURE__ */ jsx(FlowPhaseProvider, { phase: flowPhase, children: /* @__PURE__ */ jsx(StepRendererContent, { ...props, screen }) });
|
|
5933
6021
|
}
|
|
5934
6022
|
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,
|
|
6023
|
+
flow,
|
|
6024
|
+
remote,
|
|
6025
|
+
derived,
|
|
6026
|
+
forms,
|
|
5965
6027
|
handlers,
|
|
5966
|
-
screen
|
|
5967
|
-
isDesktop
|
|
6028
|
+
screen
|
|
5968
6029
|
}) {
|
|
6030
|
+
const {
|
|
6031
|
+
state,
|
|
6032
|
+
authenticated,
|
|
6033
|
+
activeOtpStatus,
|
|
6034
|
+
isDesktop,
|
|
6035
|
+
merchantName,
|
|
6036
|
+
onBack,
|
|
6037
|
+
onDismiss,
|
|
6038
|
+
depositAmount
|
|
6039
|
+
} = flow;
|
|
6040
|
+
const {
|
|
6041
|
+
pollingTransfer,
|
|
6042
|
+
pollingError,
|
|
6043
|
+
authExecutorError,
|
|
6044
|
+
transferSigningSigning,
|
|
6045
|
+
transferSigningError
|
|
6046
|
+
} = remote;
|
|
6047
|
+
const {
|
|
6048
|
+
pendingConnections,
|
|
6049
|
+
depositEligibleAccounts,
|
|
6050
|
+
sourceName,
|
|
6051
|
+
maxSourceBalance,
|
|
6052
|
+
tokenCount,
|
|
6053
|
+
selectedAccount,
|
|
6054
|
+
selectedSource,
|
|
6055
|
+
selectSourceChoices,
|
|
6056
|
+
selectSourceRecommended,
|
|
6057
|
+
selectSourceAvailableBalance
|
|
6058
|
+
} = derived;
|
|
6059
|
+
const {
|
|
6060
|
+
guestTokenEntries,
|
|
6061
|
+
guestLoadingBalances,
|
|
6062
|
+
guestSettingSender,
|
|
6063
|
+
authInput,
|
|
6064
|
+
otpCode,
|
|
6065
|
+
selectSourceChainName,
|
|
6066
|
+
selectSourceTokenSymbol,
|
|
6067
|
+
savingOneTapLimit
|
|
6068
|
+
} = forms;
|
|
5969
6069
|
const selectedWallet = selectedAccount?.wallets.find((w) => w.id === state.selectedWalletId);
|
|
5970
6070
|
const selectedSourceLabel = selectedSource && selectedWallet ? `${selectedSource.token.symbol} on ${selectedWallet.chain.name}` : void 0;
|
|
5971
6071
|
switch (screen) {
|
|
@@ -6038,7 +6138,7 @@ function StepRendererContent({
|
|
|
6038
6138
|
onPrepareProvider: handlers.onPrepareProvider,
|
|
6039
6139
|
onSelectProvider: handlers.onSelectProvider,
|
|
6040
6140
|
onContinueConnection: handlers.onContinueConnection,
|
|
6041
|
-
onBack: isEntryPoint ? onBack : () => handlers.
|
|
6141
|
+
onBack: isEntryPoint ? onBack : () => handlers.onSetPhase({ step: "deposit" }),
|
|
6042
6142
|
onLogout: authenticated ? handlers.onLogout : void 0,
|
|
6043
6143
|
onLogin: handlers.onLogin,
|
|
6044
6144
|
showLoginOption: isEntryPoint
|
|
@@ -6069,7 +6169,7 @@ function StepRendererContent({
|
|
|
6069
6169
|
limit: state.oneTapLimit,
|
|
6070
6170
|
tokensApproved: 0,
|
|
6071
6171
|
merchantName,
|
|
6072
|
-
onContinue: () => handlers.
|
|
6172
|
+
onContinue: () => handlers.onSetPhase({ step: "one-tap-setup", action: null }),
|
|
6073
6173
|
onLogout: handlers.onLogout,
|
|
6074
6174
|
error: state.error || authExecutorError
|
|
6075
6175
|
}
|
|
@@ -6088,7 +6188,7 @@ function StepRendererContent({
|
|
|
6088
6188
|
tokenCount: effectiveTokenCount,
|
|
6089
6189
|
sourceName,
|
|
6090
6190
|
onSetupOneTap: handlers.onSetupOneTap,
|
|
6091
|
-
onBack: () => handlers.
|
|
6191
|
+
onBack: () => handlers.onSetPhase({ step: "deposit" }),
|
|
6092
6192
|
onLogout: handlers.onLogout,
|
|
6093
6193
|
onAdvanced: handlers.onSelectToken,
|
|
6094
6194
|
selectedSourceLabel: effectiveSourceLabel,
|
|
@@ -6124,7 +6224,7 @@ function StepRendererContent({
|
|
|
6124
6224
|
processing: state.creatingTransfer,
|
|
6125
6225
|
error: state.error,
|
|
6126
6226
|
onDeposit: handlers.onPay,
|
|
6127
|
-
onSwitchWallet: () => handlers.
|
|
6227
|
+
onSwitchWallet: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
|
|
6128
6228
|
onBack: onBack ?? (() => handlers.onLogout()),
|
|
6129
6229
|
onLogout: handlers.onLogout,
|
|
6130
6230
|
onIncreaseLimit: handlers.onIncreaseLimit,
|
|
@@ -6133,7 +6233,7 @@ function StepRendererContent({
|
|
|
6133
6233
|
selectedAccountId: state.selectedAccountId,
|
|
6134
6234
|
onSelectAccount: handlers.onSelectAccount,
|
|
6135
6235
|
onAuthorizeAccount: handlers.onContinueConnection,
|
|
6136
|
-
onAddProvider: () => handlers.
|
|
6236
|
+
onAddProvider: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
|
|
6137
6237
|
onSelectToken: handlers.onSelectToken,
|
|
6138
6238
|
selectedSourceLabel,
|
|
6139
6239
|
selectedTokenSymbol: selectedSource?.token.symbol
|
|
@@ -6151,7 +6251,7 @@ function StepRendererContent({
|
|
|
6151
6251
|
chains: state.chains,
|
|
6152
6252
|
onSelectAuthorized: handlers.onSelectAuthorizedToken,
|
|
6153
6253
|
onAuthorizeToken: handlers.onAuthorizeToken,
|
|
6154
|
-
onBack: () => handlers.
|
|
6254
|
+
onBack: () => handlers.onSetPhase({ step: "deposit" }),
|
|
6155
6255
|
onLogout: handlers.onLogout,
|
|
6156
6256
|
depositAmount: depositAmount ?? void 0,
|
|
6157
6257
|
selectedTokenSymbol: selectedSource?.token.symbol,
|
|
@@ -6169,7 +6269,7 @@ function StepRendererContent({
|
|
|
6169
6269
|
depositAmount: depositAmount ?? void 0,
|
|
6170
6270
|
error: state.error,
|
|
6171
6271
|
onSelect: handlers.onSelectGuestToken,
|
|
6172
|
-
onBack:
|
|
6272
|
+
onBack: handlers.onGuestBackFromTokenPicker
|
|
6173
6273
|
}
|
|
6174
6274
|
);
|
|
6175
6275
|
case "processing": {
|
|
@@ -6308,56 +6408,41 @@ var buttonStyle3 = {
|
|
|
6308
6408
|
fontFamily: "inherit",
|
|
6309
6409
|
cursor: "pointer"
|
|
6310
6410
|
};
|
|
6311
|
-
function
|
|
6411
|
+
function selectedSourceForWallet(selectedWallet, selectedTokenSymbol) {
|
|
6412
|
+
if (!selectedWallet) return null;
|
|
6413
|
+
if (selectedTokenSymbol) {
|
|
6414
|
+
return selectedWallet.sources.find((s) => s.token.symbol === selectedTokenSymbol) ?? null;
|
|
6415
|
+
}
|
|
6416
|
+
return selectedWallet.sources.find((s) => s.token.status === "AUTHORIZED") ?? selectedWallet.sources[0] ?? null;
|
|
6417
|
+
}
|
|
6418
|
+
function computeDerivedState(state) {
|
|
6312
6419
|
const { sourceType, sourceId } = deriveSourceTypeAndId(state);
|
|
6313
6420
|
const selectedAccount = state.accounts.find((a) => a.id === state.selectedAccountId);
|
|
6314
6421
|
const selectedWallet = selectedAccount?.wallets.find(
|
|
6315
6422
|
(w) => w.id === state.selectedWalletId
|
|
6316
6423
|
);
|
|
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]);
|
|
6424
|
+
const selectedSource = selectedSourceForWallet(selectedWallet, state.selectedTokenSymbol);
|
|
6326
6425
|
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]
|
|
6426
|
+
const pendingConnections = state.accounts.filter(
|
|
6427
|
+
(a) => a.wallets.length > 0 && !a.wallets.some((w) => w.status === "ACTIVE")
|
|
6336
6428
|
);
|
|
6337
|
-
const
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
}
|
|
6429
|
+
const depositEligibleAccounts = getDepositEligibleAccounts(state.accounts);
|
|
6430
|
+
let maxSourceBalance = 0;
|
|
6431
|
+
for (const acct of state.accounts) {
|
|
6432
|
+
for (const wallet of acct.wallets) {
|
|
6433
|
+
for (const source of wallet.sources) {
|
|
6434
|
+
if (source.balance.available.amount > maxSourceBalance) {
|
|
6435
|
+
maxSourceBalance = source.balance.available.amount;
|
|
6345
6436
|
}
|
|
6346
6437
|
}
|
|
6347
6438
|
}
|
|
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
|
-
}
|
|
6439
|
+
}
|
|
6440
|
+
let tokenCount = 0;
|
|
6441
|
+
for (const acct of state.accounts) {
|
|
6442
|
+
for (const wallet of acct.wallets) {
|
|
6443
|
+
tokenCount += wallet.sources.filter((s) => s.balance.available.amount > 0).length;
|
|
6358
6444
|
}
|
|
6359
|
-
|
|
6360
|
-
}, [state.accounts]);
|
|
6445
|
+
}
|
|
6361
6446
|
return {
|
|
6362
6447
|
sourceType,
|
|
6363
6448
|
sourceId,
|
|
@@ -6371,6 +6456,9 @@ function useDerivedState(state) {
|
|
|
6371
6456
|
tokenCount
|
|
6372
6457
|
};
|
|
6373
6458
|
}
|
|
6459
|
+
function useDerivedState(state) {
|
|
6460
|
+
return useMemo(() => computeDerivedState(state), [state]);
|
|
6461
|
+
}
|
|
6374
6462
|
function useAuthHandlers(dispatch, verificationTarget) {
|
|
6375
6463
|
const {
|
|
6376
6464
|
sendCode: sendEmailCode,
|
|
@@ -6463,9 +6551,18 @@ function usePasskeyHandlers(dispatch, apiBaseUrl, accounts, knownCredentialIds,
|
|
|
6463
6551
|
const { user, getAccessToken } = usePrivy();
|
|
6464
6552
|
const checkingPasskeyRef = useRef(false);
|
|
6465
6553
|
const activateExistingCredential = useCallback(async (credentialId) => {
|
|
6466
|
-
dispatch({ type: "PASSKEY_ACTIVATED", credentialId });
|
|
6467
|
-
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
|
|
6468
6554
|
const token = await getAccessToken();
|
|
6555
|
+
let publicKey;
|
|
6556
|
+
if (token) {
|
|
6557
|
+
try {
|
|
6558
|
+
const { config } = await fetchUserConfig(apiBaseUrl, token);
|
|
6559
|
+
const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
|
|
6560
|
+
publicKey = allPasskeys.find((p) => p.credentialId === credentialId)?.publicKey;
|
|
6561
|
+
} catch {
|
|
6562
|
+
}
|
|
6563
|
+
}
|
|
6564
|
+
dispatch({ type: "PASSKEY_ACTIVATED", credentialId, publicKey });
|
|
6565
|
+
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
|
|
6469
6566
|
if (token) {
|
|
6470
6567
|
reportPasskeyActivity(apiBaseUrl, token, credentialId).catch(() => {
|
|
6471
6568
|
});
|
|
@@ -6538,9 +6635,13 @@ function usePasskeyHandlers(dispatch, apiBaseUrl, accounts, knownCredentialIds,
|
|
|
6538
6635
|
authToken: token ?? void 0,
|
|
6539
6636
|
apiBaseUrl
|
|
6540
6637
|
});
|
|
6541
|
-
const { credentialId } = await createPasskeyViaPopup(popupOptions);
|
|
6542
|
-
dispatch({ type: "PASSKEY_ACTIVATED", credentialId });
|
|
6638
|
+
const { credentialId, publicKey } = await createPasskeyViaPopup(popupOptions);
|
|
6639
|
+
dispatch({ type: "PASSKEY_ACTIVATED", credentialId, publicKey });
|
|
6543
6640
|
localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
|
|
6641
|
+
if (token) {
|
|
6642
|
+
reportPasskeyActivity(apiBaseUrl, token, credentialId).catch(() => {
|
|
6643
|
+
});
|
|
6644
|
+
}
|
|
6544
6645
|
} catch (err) {
|
|
6545
6646
|
captureException(err);
|
|
6546
6647
|
dispatch({
|
|
@@ -6556,19 +6657,25 @@ function usePasskeyHandlers(dispatch, apiBaseUrl, accounts, knownCredentialIds,
|
|
|
6556
6657
|
dispatch({ type: "SET_ERROR", error: null });
|
|
6557
6658
|
try {
|
|
6558
6659
|
const token = await getAccessToken();
|
|
6660
|
+
if (!token) throw new Error("Not authenticated");
|
|
6559
6661
|
const matched = await findDevicePasskeyViaPopup({
|
|
6560
6662
|
credentialIds: knownCredentialIds,
|
|
6561
6663
|
rpId: resolvePasskeyRpId(),
|
|
6562
|
-
authToken: token
|
|
6664
|
+
authToken: token,
|
|
6563
6665
|
apiBaseUrl
|
|
6564
6666
|
});
|
|
6565
6667
|
if (matched) {
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6668
|
+
let publicKey;
|
|
6669
|
+
try {
|
|
6670
|
+
const { config } = await fetchUserConfig(apiBaseUrl, token);
|
|
6671
|
+
const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
|
|
6672
|
+
publicKey = allPasskeys.find((p) => p.credentialId === matched)?.publicKey;
|
|
6673
|
+
} catch {
|
|
6571
6674
|
}
|
|
6675
|
+
dispatch({ type: "PASSKEY_ACTIVATED", credentialId: matched, publicKey });
|
|
6676
|
+
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
|
|
6677
|
+
reportPasskeyActivity(apiBaseUrl, token, matched).catch(() => {
|
|
6678
|
+
});
|
|
6572
6679
|
} else {
|
|
6573
6680
|
dispatch({
|
|
6574
6681
|
type: "SET_ERROR",
|
|
@@ -6936,7 +7043,9 @@ function useProviderHandlers(deps) {
|
|
|
6936
7043
|
reauthTokenRef,
|
|
6937
7044
|
authenticated,
|
|
6938
7045
|
merchantAuthorization,
|
|
6939
|
-
destination
|
|
7046
|
+
destination,
|
|
7047
|
+
guestSessionToken,
|
|
7048
|
+
selectedProviderId
|
|
6940
7049
|
} = deps;
|
|
6941
7050
|
const wagmiConfig2 = useConfig();
|
|
6942
7051
|
const { connectAsync, connectors } = useConnect();
|
|
@@ -7249,7 +7358,7 @@ function useProviderHandlers(deps) {
|
|
|
7249
7358
|
reauthTokenRef
|
|
7250
7359
|
]);
|
|
7251
7360
|
const handleNavigateToTokenPicker = useCallback(() => {
|
|
7252
|
-
dispatch({ type: "SET_USER_INTENT", intent: "
|
|
7361
|
+
dispatch({ type: "SET_USER_INTENT", intent: { step: "token-picker" } });
|
|
7253
7362
|
}, [dispatch]);
|
|
7254
7363
|
const handleSelectAuthorizedToken = useCallback((walletId, tokenSymbol) => {
|
|
7255
7364
|
dispatch({ type: "SELECT_TOKEN", walletId, tokenSymbol });
|
|
@@ -7332,6 +7441,76 @@ function useProviderHandlers(deps) {
|
|
|
7332
7441
|
reauthSessionIdRef,
|
|
7333
7442
|
reauthTokenRef
|
|
7334
7443
|
]);
|
|
7444
|
+
const handlePreauthorize = useCallback(async () => {
|
|
7445
|
+
if (!guestSessionToken || !selectedProviderId) {
|
|
7446
|
+
dispatch({
|
|
7447
|
+
type: "SET_ERROR",
|
|
7448
|
+
error: "Missing guest session or wallet provider. Try again from the payment screen."
|
|
7449
|
+
});
|
|
7450
|
+
return;
|
|
7451
|
+
}
|
|
7452
|
+
const isMobile = !shouldUseWalletConnector({
|
|
7453
|
+
useWalletConnector: useWalletConnectorProp,
|
|
7454
|
+
userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
|
|
7455
|
+
});
|
|
7456
|
+
const providerName = providers.find((p) => p.id === selectedProviderId)?.name ?? "Wallet";
|
|
7457
|
+
if (!isMobile) {
|
|
7458
|
+
dispatch({ type: "GUEST_PREAUTH_BEGIN" });
|
|
7459
|
+
}
|
|
7460
|
+
try {
|
|
7461
|
+
const created = await createGuestAccount(
|
|
7462
|
+
apiBaseUrl,
|
|
7463
|
+
guestSessionToken,
|
|
7464
|
+
selectedProviderId,
|
|
7465
|
+
providerName
|
|
7466
|
+
);
|
|
7467
|
+
const session = await fetchAuthorizationSessionByToken(
|
|
7468
|
+
apiBaseUrl,
|
|
7469
|
+
created.sessionToken
|
|
7470
|
+
);
|
|
7471
|
+
if (isMobile) {
|
|
7472
|
+
handlingMobileReturnRef.current = false;
|
|
7473
|
+
mobileSetupFlowRef.current = true;
|
|
7474
|
+
setupAccountIdRef.current = created.accountId;
|
|
7475
|
+
persistMobileFlowState({
|
|
7476
|
+
accountId: created.accountId,
|
|
7477
|
+
sessionId: session.id,
|
|
7478
|
+
deeplinkUri: created.sessionUri,
|
|
7479
|
+
providerId: selectedProviderId,
|
|
7480
|
+
isSetup: true,
|
|
7481
|
+
guestSessionToken
|
|
7482
|
+
});
|
|
7483
|
+
triggerDeeplink(created.sessionUri);
|
|
7484
|
+
dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: created.sessionUri });
|
|
7485
|
+
}
|
|
7486
|
+
dispatch({
|
|
7487
|
+
type: "GUEST_PREAUTH_DETECTED",
|
|
7488
|
+
accountId: created.accountId,
|
|
7489
|
+
sessionId: session.id
|
|
7490
|
+
});
|
|
7491
|
+
} catch (err) {
|
|
7492
|
+
captureException(err);
|
|
7493
|
+
if (!isMobile) {
|
|
7494
|
+
dispatch({ type: "GUEST_PREAUTH_END" });
|
|
7495
|
+
}
|
|
7496
|
+
dispatch({
|
|
7497
|
+
type: "SET_ERROR",
|
|
7498
|
+
error: err instanceof Error ? err.message : "Failed to start preauthorization"
|
|
7499
|
+
});
|
|
7500
|
+
onError?.(err instanceof Error ? err.message : "Failed to start preauthorization");
|
|
7501
|
+
}
|
|
7502
|
+
}, [
|
|
7503
|
+
guestSessionToken,
|
|
7504
|
+
selectedProviderId,
|
|
7505
|
+
providers,
|
|
7506
|
+
apiBaseUrl,
|
|
7507
|
+
dispatch,
|
|
7508
|
+
onError,
|
|
7509
|
+
useWalletConnectorProp,
|
|
7510
|
+
mobileSetupFlowRef,
|
|
7511
|
+
handlingMobileReturnRef,
|
|
7512
|
+
setupAccountIdRef
|
|
7513
|
+
]);
|
|
7335
7514
|
return {
|
|
7336
7515
|
handlePrepareProvider,
|
|
7337
7516
|
handleSelectProvider,
|
|
@@ -7340,7 +7519,8 @@ function useProviderHandlers(deps) {
|
|
|
7340
7519
|
handleIncreaseLimit,
|
|
7341
7520
|
handleNavigateToTokenPicker,
|
|
7342
7521
|
handleSelectAuthorizedToken,
|
|
7343
|
-
handleAuthorizeToken
|
|
7522
|
+
handleAuthorizeToken,
|
|
7523
|
+
handlePreauthorize
|
|
7344
7524
|
};
|
|
7345
7525
|
}
|
|
7346
7526
|
|
|
@@ -7544,11 +7724,15 @@ function useGuestTransferHandlers(deps) {
|
|
|
7544
7724
|
};
|
|
7545
7725
|
execute();
|
|
7546
7726
|
}, [isGuestFlow, guestTransferId, guestSessionToken, settingSender, apiBaseUrl, wagmiConfig2, switchChainAsync, dispatch, onComplete, onError]);
|
|
7727
|
+
const handleGuestBackFromTokenPicker = useCallback(() => {
|
|
7728
|
+
dispatch({ type: "GUEST_BACK_FROM_TOKEN_PICKER" });
|
|
7729
|
+
}, [dispatch]);
|
|
7547
7730
|
return {
|
|
7548
7731
|
guestTokenEntries,
|
|
7549
7732
|
loadingBalances,
|
|
7550
7733
|
settingSender,
|
|
7551
|
-
handleSelectGuestToken
|
|
7734
|
+
handleSelectGuestToken,
|
|
7735
|
+
handleGuestBackFromTokenPicker
|
|
7552
7736
|
};
|
|
7553
7737
|
}
|
|
7554
7738
|
function useOneTapSetupHandlers(deps) {
|
|
@@ -7558,16 +7742,24 @@ function useOneTapSetupHandlers(deps) {
|
|
|
7558
7742
|
apiBaseUrl,
|
|
7559
7743
|
authExecutor,
|
|
7560
7744
|
selectSourceChainName,
|
|
7561
|
-
selectSourceTokenSymbol
|
|
7745
|
+
selectSourceTokenSymbol,
|
|
7746
|
+
authorizationSessionIdForGuest
|
|
7562
7747
|
} = deps;
|
|
7563
7748
|
const [savingOneTapLimit, setSavingOneTapLimit] = useState(false);
|
|
7564
|
-
const oneTapLimitSavedDuringSetupRef = useRef(false);
|
|
7565
7749
|
const handleSetupOneTap = useCallback(async (limit) => {
|
|
7566
7750
|
setSavingOneTapLimit(true);
|
|
7567
7751
|
try {
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7752
|
+
if (authorizationSessionIdForGuest) {
|
|
7753
|
+
await updateUserConfigBySession(
|
|
7754
|
+
apiBaseUrl,
|
|
7755
|
+
authorizationSessionIdForGuest,
|
|
7756
|
+
{ defaultAllowance: limit }
|
|
7757
|
+
);
|
|
7758
|
+
} else {
|
|
7759
|
+
const token = await getAccessToken();
|
|
7760
|
+
if (!token) throw new Error("Not authenticated");
|
|
7761
|
+
await updateUserConfig(apiBaseUrl, token, { defaultAllowance: limit });
|
|
7762
|
+
}
|
|
7571
7763
|
if (authExecutor.pendingSelectSource) {
|
|
7572
7764
|
const action = authExecutor.pendingSelectSource;
|
|
7573
7765
|
const recommended = action.metadata?.recommended;
|
|
@@ -7582,12 +7774,12 @@ function useOneTapSetupHandlers(deps) {
|
|
|
7582
7774
|
chainName = recommended?.chainName ?? choices[0]?.chainName ?? "Base";
|
|
7583
7775
|
tokenSymbol = recommended?.tokenSymbol ?? choices[0]?.tokens[0]?.tokenSymbol ?? "USDC";
|
|
7584
7776
|
}
|
|
7585
|
-
|
|
7777
|
+
dispatch({ type: "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP", saved: true });
|
|
7586
7778
|
authExecutor.resolveSelectSource({ chainName, tokenSymbol });
|
|
7587
7779
|
} else if (authExecutor.pendingOneTapSetup) {
|
|
7588
7780
|
authExecutor.resolveOneTapSetup();
|
|
7589
7781
|
}
|
|
7590
|
-
dispatch({ type: "SET_USER_INTENT", intent:
|
|
7782
|
+
dispatch({ type: "SET_USER_INTENT", intent: { step: "deposit" } });
|
|
7591
7783
|
} catch (err) {
|
|
7592
7784
|
captureException(err);
|
|
7593
7785
|
dispatch({
|
|
@@ -7597,127 +7789,84 @@ function useOneTapSetupHandlers(deps) {
|
|
|
7597
7789
|
} finally {
|
|
7598
7790
|
setSavingOneTapLimit(false);
|
|
7599
7791
|
}
|
|
7600
|
-
}, [
|
|
7792
|
+
}, [
|
|
7793
|
+
getAccessToken,
|
|
7794
|
+
apiBaseUrl,
|
|
7795
|
+
authExecutor,
|
|
7796
|
+
dispatch,
|
|
7797
|
+
selectSourceChainName,
|
|
7798
|
+
selectSourceTokenSymbol,
|
|
7799
|
+
authorizationSessionIdForGuest
|
|
7800
|
+
]);
|
|
7601
7801
|
return {
|
|
7602
7802
|
handleSetupOneTap,
|
|
7603
|
-
savingOneTapLimit
|
|
7604
|
-
oneTapLimitSavedDuringSetupRef
|
|
7803
|
+
savingOneTapLimit
|
|
7605
7804
|
};
|
|
7606
7805
|
}
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
})
|
|
7615
|
-
|
|
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.`;
|
|
7806
|
+
function usePrivySessionSyncEffect(deps) {
|
|
7807
|
+
const { dispatch, ready, authenticated } = deps;
|
|
7808
|
+
useEffect(() => {
|
|
7809
|
+
dispatch({
|
|
7810
|
+
type: "SYNC_PRIVY_SESSION",
|
|
7811
|
+
ready,
|
|
7812
|
+
authenticated
|
|
7813
|
+
});
|
|
7814
|
+
}, [dispatch, ready, authenticated]);
|
|
7651
7815
|
}
|
|
7652
|
-
|
|
7653
|
-
// src/hooks/usePaymentEffects.ts
|
|
7654
|
-
function usePaymentEffects(deps) {
|
|
7816
|
+
function useOtpEffects(deps) {
|
|
7655
7817
|
const {
|
|
7656
7818
|
state,
|
|
7657
7819
|
dispatch,
|
|
7658
|
-
ready,
|
|
7659
7820
|
authenticated,
|
|
7660
|
-
apiBaseUrl,
|
|
7661
|
-
depositAmount,
|
|
7662
|
-
onComplete,
|
|
7663
|
-
onError,
|
|
7664
|
-
polling,
|
|
7665
|
-
authExecutor,
|
|
7666
|
-
reloadAccounts,
|
|
7667
7821
|
activeOtpStatus,
|
|
7668
7822
|
activeOtpErrorMessage,
|
|
7669
7823
|
otpCode,
|
|
7670
|
-
handleVerifyLoginCode
|
|
7824
|
+
handleVerifyLoginCode
|
|
7825
|
+
} = deps;
|
|
7826
|
+
useEffect(() => {
|
|
7827
|
+
if (authenticated || !state.verificationTarget) return;
|
|
7828
|
+
if (activeOtpErrorMessage) dispatch({ type: "SET_ERROR", error: activeOtpErrorMessage });
|
|
7829
|
+
}, [activeOtpErrorMessage, authenticated, state.verificationTarget, dispatch]);
|
|
7830
|
+
useEffect(() => {
|
|
7831
|
+
if (state.verificationTarget && !authenticated && /^\d{6}$/.test(otpCode.trim()) && activeOtpStatus === "awaiting-code-input") {
|
|
7832
|
+
handleVerifyLoginCode();
|
|
7833
|
+
}
|
|
7834
|
+
}, [otpCode, state.verificationTarget, authenticated, activeOtpStatus, handleVerifyLoginCode]);
|
|
7835
|
+
}
|
|
7836
|
+
function usePasskeyCheckEffect(deps) {
|
|
7837
|
+
const {
|
|
7838
|
+
dispatch,
|
|
7839
|
+
ready,
|
|
7840
|
+
authenticated,
|
|
7841
|
+
apiBaseUrl,
|
|
7842
|
+
activeCredentialId,
|
|
7843
|
+
passkeyConfigLoaded,
|
|
7844
|
+
checkingPasskeyRef,
|
|
7671
7845
|
setAuthInput,
|
|
7672
7846
|
setOtpCode,
|
|
7847
|
+
polling,
|
|
7673
7848
|
mobileSetupFlowRef,
|
|
7674
7849
|
handlingMobileReturnRef,
|
|
7675
7850
|
setupAccountIdRef,
|
|
7676
7851
|
reauthSessionIdRef,
|
|
7677
7852
|
reauthTokenRef,
|
|
7678
|
-
|
|
7679
|
-
pollingTransferIdRef,
|
|
7680
|
-
processingStartedAtRef,
|
|
7681
|
-
checkingPasskeyRef,
|
|
7682
|
-
pendingSelectSourceAction,
|
|
7683
|
-
selectSourceChoices,
|
|
7684
|
-
selectSourceRecommended,
|
|
7685
|
-
setSelectSourceChainName,
|
|
7686
|
-
setSelectSourceTokenSymbol,
|
|
7687
|
-
initializedSelectSourceActionRef,
|
|
7688
|
-
oneTapLimitSavedDuringSetupRef,
|
|
7689
|
-
handleAuthorizedMobileReturn
|
|
7853
|
+
pollingTransferIdRef
|
|
7690
7854
|
} = deps;
|
|
7691
7855
|
const { getAccessToken } = usePrivy();
|
|
7692
|
-
const onCompleteRef = useRef(onComplete);
|
|
7693
|
-
onCompleteRef.current = onComplete;
|
|
7856
|
+
const onCompleteRef = useRef(deps.onComplete);
|
|
7857
|
+
onCompleteRef.current = deps.onComplete;
|
|
7694
7858
|
const getAccessTokenRef = useRef(getAccessToken);
|
|
7695
7859
|
getAccessTokenRef.current = getAccessToken;
|
|
7696
7860
|
const pollingRef = useRef(polling);
|
|
7697
7861
|
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]);
|
|
7862
|
+
const handleAuthorizedMobileReturnRef = useRef(deps.handleAuthorizedMobileReturn);
|
|
7863
|
+
handleAuthorizedMobileReturnRef.current = deps.handleAuthorizedMobileReturn;
|
|
7715
7864
|
useEffect(() => {
|
|
7716
7865
|
if (!ready || !authenticated) {
|
|
7717
7866
|
checkingPasskeyRef.current = false;
|
|
7718
7867
|
return;
|
|
7719
7868
|
}
|
|
7720
|
-
if (
|
|
7869
|
+
if (passkeyConfigLoaded || activeCredentialId) return;
|
|
7721
7870
|
if (checkingPasskeyRef.current) return;
|
|
7722
7871
|
checkingPasskeyRef.current = true;
|
|
7723
7872
|
let cancelled = false;
|
|
@@ -7815,11 +7964,7 @@ function usePaymentEffects(deps) {
|
|
|
7815
7964
|
return;
|
|
7816
7965
|
}
|
|
7817
7966
|
if (existingTransfer.status === "AUTHORIZED") {
|
|
7818
|
-
|
|
7819
|
-
await handleAuthorizedMobileReturnRef.current(existingTransfer, true);
|
|
7820
|
-
} else {
|
|
7821
|
-
await handleAuthorizedMobileReturnRef.current(existingTransfer, false);
|
|
7822
|
-
}
|
|
7967
|
+
await handleAuthorizedMobileReturnRef.current(existingTransfer, !!persisted.isSetup);
|
|
7823
7968
|
return;
|
|
7824
7969
|
}
|
|
7825
7970
|
if (persisted.isSetup) {
|
|
@@ -7861,7 +8006,18 @@ function usePaymentEffects(deps) {
|
|
|
7861
8006
|
if (token || cancelled) break;
|
|
7862
8007
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
7863
8008
|
}
|
|
7864
|
-
if (
|
|
8009
|
+
if (cancelled) return;
|
|
8010
|
+
if (!token) {
|
|
8011
|
+
dispatch({
|
|
8012
|
+
type: "PASSKEY_CONFIG_LOADED",
|
|
8013
|
+
knownIds: []
|
|
8014
|
+
});
|
|
8015
|
+
dispatch({
|
|
8016
|
+
type: "SET_ERROR",
|
|
8017
|
+
error: "Could not refresh your session. Try signing in again."
|
|
8018
|
+
});
|
|
8019
|
+
return;
|
|
8020
|
+
}
|
|
7865
8021
|
const { config } = await fetchUserConfig(apiBaseUrl, token);
|
|
7866
8022
|
if (cancelled) return;
|
|
7867
8023
|
const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
|
|
@@ -7870,11 +8026,13 @@ function usePaymentEffects(deps) {
|
|
|
7870
8026
|
knownIds: allPasskeys.map((p) => p.credentialId),
|
|
7871
8027
|
oneTapLimit: config.defaultAllowance ?? void 0
|
|
7872
8028
|
});
|
|
7873
|
-
if (allPasskeys.length === 0)
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
8029
|
+
if (allPasskeys.length === 0) return;
|
|
8030
|
+
if (activeCredentialId && allPasskeys.some((p) => p.credentialId === activeCredentialId)) {
|
|
8031
|
+
const pk = allPasskeys.find((p) => p.credentialId === activeCredentialId)?.publicKey;
|
|
8032
|
+
if (pk) {
|
|
8033
|
+
dispatch({ type: "PASSKEY_ACTIVATED", credentialId: activeCredentialId, publicKey: pk });
|
|
8034
|
+
}
|
|
8035
|
+
await restoreState(activeCredentialId, token);
|
|
7878
8036
|
return;
|
|
7879
8037
|
}
|
|
7880
8038
|
if (cancelled) return;
|
|
@@ -7892,13 +8050,22 @@ function usePaymentEffects(deps) {
|
|
|
7892
8050
|
}
|
|
7893
8051
|
if (cancelled) return;
|
|
7894
8052
|
if (matched) {
|
|
7895
|
-
|
|
8053
|
+
const publicKey = allPasskeys.find((p) => p.credentialId === matched)?.publicKey;
|
|
8054
|
+
dispatch({ type: "PASSKEY_ACTIVATED", credentialId: matched, publicKey });
|
|
7896
8055
|
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
|
|
7897
8056
|
reportPasskeyActivity(apiBaseUrl, token, matched).catch(() => {
|
|
7898
8057
|
});
|
|
7899
8058
|
await restoreState(matched, token);
|
|
7900
8059
|
}
|
|
7901
|
-
} catch {
|
|
8060
|
+
} catch (err) {
|
|
8061
|
+
dispatch({
|
|
8062
|
+
type: "PASSKEY_CONFIG_LOADED",
|
|
8063
|
+
knownIds: []
|
|
8064
|
+
});
|
|
8065
|
+
dispatch({
|
|
8066
|
+
type: "SET_ERROR",
|
|
8067
|
+
error: err instanceof Error ? err.message : "Unable to load passkey settings."
|
|
8068
|
+
});
|
|
7902
8069
|
}
|
|
7903
8070
|
};
|
|
7904
8071
|
checkPasskey();
|
|
@@ -7906,7 +8073,39 @@ function usePaymentEffects(deps) {
|
|
|
7906
8073
|
cancelled = true;
|
|
7907
8074
|
checkingPasskeyRef.current = false;
|
|
7908
8075
|
};
|
|
7909
|
-
}, [ready, authenticated, apiBaseUrl,
|
|
8076
|
+
}, [ready, authenticated, apiBaseUrl, activeCredentialId, passkeyConfigLoaded]);
|
|
8077
|
+
}
|
|
8078
|
+
|
|
8079
|
+
// src/dataLoading.ts
|
|
8080
|
+
function resolveDataLoadAction({
|
|
8081
|
+
authenticated,
|
|
8082
|
+
accountsCount,
|
|
8083
|
+
hasActiveCredential,
|
|
8084
|
+
loading
|
|
8085
|
+
}) {
|
|
8086
|
+
if (!authenticated || accountsCount > 0 || !hasActiveCredential) {
|
|
8087
|
+
return "reset";
|
|
8088
|
+
}
|
|
8089
|
+
if (loading) {
|
|
8090
|
+
return "wait";
|
|
8091
|
+
}
|
|
8092
|
+
return "load";
|
|
8093
|
+
}
|
|
8094
|
+
|
|
8095
|
+
// src/hooks/useDataLoadEffect.ts
|
|
8096
|
+
function useDataLoadEffect(deps) {
|
|
8097
|
+
const {
|
|
8098
|
+
state,
|
|
8099
|
+
dispatch,
|
|
8100
|
+
authenticated,
|
|
8101
|
+
apiBaseUrl,
|
|
8102
|
+
depositAmount,
|
|
8103
|
+
loadingDataRef
|
|
8104
|
+
} = deps;
|
|
8105
|
+
const { getAccessToken } = usePrivy();
|
|
8106
|
+
const getAccessTokenRef = useRef(getAccessToken);
|
|
8107
|
+
getAccessTokenRef.current = getAccessToken;
|
|
8108
|
+
const lastAccountFetchRef = useRef(0);
|
|
7910
8109
|
useEffect(() => {
|
|
7911
8110
|
const loadAction = resolveDataLoadAction({
|
|
7912
8111
|
authenticated,
|
|
@@ -8006,6 +8205,61 @@ function usePaymentEffects(deps) {
|
|
|
8006
8205
|
cancelled = true;
|
|
8007
8206
|
};
|
|
8008
8207
|
}, [authenticated, state.providers.length, state.activeCredentialId, apiBaseUrl]);
|
|
8208
|
+
useEffect(() => {
|
|
8209
|
+
if (state.accounts.length > 0 && state.activeCredentialId && !state.loadingData && !state.transfer && authenticated && Date.now() - lastAccountFetchRef.current > 15e3) {
|
|
8210
|
+
lastAccountFetchRef.current = Date.now();
|
|
8211
|
+
deps.reloadAccounts();
|
|
8212
|
+
}
|
|
8213
|
+
}, [
|
|
8214
|
+
state.accounts.length,
|
|
8215
|
+
state.activeCredentialId,
|
|
8216
|
+
state.loadingData,
|
|
8217
|
+
state.transfer,
|
|
8218
|
+
authenticated,
|
|
8219
|
+
deps
|
|
8220
|
+
]);
|
|
8221
|
+
}
|
|
8222
|
+
|
|
8223
|
+
// src/processingStatus.ts
|
|
8224
|
+
var PROCESSING_TIMEOUT_MS = 18e4;
|
|
8225
|
+
function resolvePreferredTransfer(polledTransfer, localTransfer) {
|
|
8226
|
+
return polledTransfer ?? localTransfer;
|
|
8227
|
+
}
|
|
8228
|
+
function getTransferStatus(polledTransfer, localTransfer) {
|
|
8229
|
+
const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
|
|
8230
|
+
return transfer?.status ?? "UNKNOWN";
|
|
8231
|
+
}
|
|
8232
|
+
function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
|
|
8233
|
+
if (!processingStartedAtMs) return false;
|
|
8234
|
+
return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
|
|
8235
|
+
}
|
|
8236
|
+
var STATUS_DISPLAY_LABELS = {
|
|
8237
|
+
CREATED: "created",
|
|
8238
|
+
AUTHORIZED: "authorized",
|
|
8239
|
+
SENDING: "sending",
|
|
8240
|
+
SENT: "confirming delivery",
|
|
8241
|
+
COMPLETED: "completed",
|
|
8242
|
+
FAILED: "failed"
|
|
8243
|
+
};
|
|
8244
|
+
function getStatusDisplayLabel(status) {
|
|
8245
|
+
return STATUS_DISPLAY_LABELS[status] ?? status;
|
|
8246
|
+
}
|
|
8247
|
+
function buildProcessingTimeoutMessage(status) {
|
|
8248
|
+
const label = getStatusDisplayLabel(status);
|
|
8249
|
+
return `Payment is taking longer than expected (status: ${label}). Please try again.`;
|
|
8250
|
+
}
|
|
8251
|
+
|
|
8252
|
+
// src/hooks/useProcessingEffect.ts
|
|
8253
|
+
function useProcessingEffect(deps) {
|
|
8254
|
+
const {
|
|
8255
|
+
state,
|
|
8256
|
+
dispatch,
|
|
8257
|
+
polling,
|
|
8258
|
+
processingStartedAtRef,
|
|
8259
|
+
onComplete,
|
|
8260
|
+
onError,
|
|
8261
|
+
reloadAccounts
|
|
8262
|
+
} = deps;
|
|
8009
8263
|
useEffect(() => {
|
|
8010
8264
|
if (!polling.transfer) return;
|
|
8011
8265
|
if (polling.transfer.status === "COMPLETED") {
|
|
@@ -8018,19 +8272,6 @@ function usePaymentEffects(deps) {
|
|
|
8018
8272
|
dispatch({ type: "TRANSFER_FAILED", transfer: polling.transfer, error: "Transfer failed." });
|
|
8019
8273
|
}
|
|
8020
8274
|
}, [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
8275
|
useEffect(() => {
|
|
8035
8276
|
const isProcessing = state.creatingTransfer || state.transfer != null && ["CREATED", "SENDING", "SENT"].includes(state.transfer.status);
|
|
8036
8277
|
if (!isProcessing) {
|
|
@@ -8066,6 +8307,26 @@ function usePaymentEffects(deps) {
|
|
|
8066
8307
|
dispatch,
|
|
8067
8308
|
processingStartedAtRef
|
|
8068
8309
|
]);
|
|
8310
|
+
}
|
|
8311
|
+
function useMobilePollingEffect(deps) {
|
|
8312
|
+
const {
|
|
8313
|
+
state,
|
|
8314
|
+
dispatch,
|
|
8315
|
+
polling,
|
|
8316
|
+
mobileSetupFlowRef,
|
|
8317
|
+
handlingMobileReturnRef,
|
|
8318
|
+
setupAccountIdRef,
|
|
8319
|
+
reauthSessionIdRef,
|
|
8320
|
+
reauthTokenRef,
|
|
8321
|
+
pollingTransferIdRef,
|
|
8322
|
+
reloadAccounts,
|
|
8323
|
+
apiBaseUrl
|
|
8324
|
+
} = deps;
|
|
8325
|
+
const { getAccessToken } = usePrivy();
|
|
8326
|
+
const getAccessTokenRef = useRef(getAccessToken);
|
|
8327
|
+
getAccessTokenRef.current = getAccessToken;
|
|
8328
|
+
const handleAuthorizedMobileReturnRef = useRef(deps.handleAuthorizedMobileReturn);
|
|
8329
|
+
handleAuthorizedMobileReturnRef.current = deps.handleAuthorizedMobileReturn;
|
|
8069
8330
|
useEffect(() => {
|
|
8070
8331
|
if (!state.mobileFlow) {
|
|
8071
8332
|
handlingMobileReturnRef.current = false;
|
|
@@ -8074,8 +8335,8 @@ function usePaymentEffects(deps) {
|
|
|
8074
8335
|
if (handlingMobileReturnRef.current) return;
|
|
8075
8336
|
const polledTransfer = polling.transfer;
|
|
8076
8337
|
if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
|
|
8077
|
-
void
|
|
8078
|
-
}, [state.mobileFlow, polling.transfer,
|
|
8338
|
+
void handleAuthorizedMobileReturnRef.current(polledTransfer, mobileSetupFlowRef.current);
|
|
8339
|
+
}, [state.mobileFlow, polling.transfer, handlingMobileReturnRef, mobileSetupFlowRef]);
|
|
8079
8340
|
useEffect(() => {
|
|
8080
8341
|
if (!state.mobileFlow || !mobileSetupFlowRef.current) return;
|
|
8081
8342
|
if (!state.activeCredentialId || !setupAccountIdRef.current) return;
|
|
@@ -8151,14 +8412,10 @@ function usePaymentEffects(deps) {
|
|
|
8151
8412
|
poll();
|
|
8152
8413
|
const intervalId = window.setInterval(poll, POLL_INTERVAL_MS);
|
|
8153
8414
|
const handleVisibility = () => {
|
|
8154
|
-
if (document.visibilityState === "visible" && !cancelled)
|
|
8155
|
-
poll();
|
|
8156
|
-
}
|
|
8415
|
+
if (document.visibilityState === "visible" && !cancelled) poll();
|
|
8157
8416
|
};
|
|
8158
8417
|
const handlePageShow = (e) => {
|
|
8159
|
-
if (e.persisted && !cancelled)
|
|
8160
|
-
poll();
|
|
8161
|
-
}
|
|
8418
|
+
if (e.persisted && !cancelled) poll();
|
|
8162
8419
|
};
|
|
8163
8420
|
document.addEventListener("visibilitychange", handleVisibility);
|
|
8164
8421
|
window.addEventListener("pageshow", handlePageShow);
|
|
@@ -8209,6 +8466,16 @@ function usePaymentEffects(deps) {
|
|
|
8209
8466
|
handlingMobileReturnRef,
|
|
8210
8467
|
pollingTransferIdRef
|
|
8211
8468
|
]);
|
|
8469
|
+
}
|
|
8470
|
+
function useSelectSourceEffect(deps) {
|
|
8471
|
+
const {
|
|
8472
|
+
pendingSelectSourceAction,
|
|
8473
|
+
selectSourceChoices,
|
|
8474
|
+
selectSourceRecommended,
|
|
8475
|
+
setSelectSourceChainName,
|
|
8476
|
+
setSelectSourceTokenSymbol,
|
|
8477
|
+
initializedSelectSourceActionRef
|
|
8478
|
+
} = deps;
|
|
8212
8479
|
useEffect(() => {
|
|
8213
8480
|
if (!pendingSelectSourceAction) {
|
|
8214
8481
|
initializedSelectSourceActionRef.current = null;
|
|
@@ -8239,38 +8506,27 @@ function usePaymentEffects(deps) {
|
|
|
8239
8506
|
setSelectSourceTokenSymbol,
|
|
8240
8507
|
initializedSelectSourceActionRef
|
|
8241
8508
|
]);
|
|
8509
|
+
}
|
|
8510
|
+
function useOneTapAutoResolveEffect(deps) {
|
|
8511
|
+
const { authExecutor, dispatch, oneTapLimitSavedDuringSetup, reloadAccounts } = deps;
|
|
8242
8512
|
const pendingOneTapSetupAction = authExecutor.pendingOneTapSetup;
|
|
8243
8513
|
useEffect(() => {
|
|
8244
|
-
if (pendingOneTapSetupAction &&
|
|
8245
|
-
|
|
8514
|
+
if (pendingOneTapSetupAction && oneTapLimitSavedDuringSetup) {
|
|
8515
|
+
dispatch({ type: "SET_ONE_TAP_LIMIT_SAVED_DURING_SETUP", saved: false });
|
|
8246
8516
|
authExecutor.resolveOneTapSetup();
|
|
8247
8517
|
}
|
|
8248
|
-
}, [pendingOneTapSetupAction, authExecutor,
|
|
8518
|
+
}, [pendingOneTapSetupAction, authExecutor, dispatch, oneTapLimitSavedDuringSetup]);
|
|
8249
8519
|
useEffect(() => {
|
|
8250
|
-
if (pendingOneTapSetupAction && !
|
|
8520
|
+
if (pendingOneTapSetupAction && !oneTapLimitSavedDuringSetup) {
|
|
8251
8521
|
reloadAccounts();
|
|
8252
8522
|
}
|
|
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]);
|
|
8523
|
+
}, [pendingOneTapSetupAction, reloadAccounts, oneTapLimitSavedDuringSetup]);
|
|
8524
|
+
}
|
|
8525
|
+
function useGuestPreauthEffect(deps) {
|
|
8526
|
+
const { state, dispatch, authenticated, apiBaseUrl, reloadAccounts } = deps;
|
|
8527
|
+
const { getAccessToken } = usePrivy();
|
|
8528
|
+
const getAccessTokenRef = useRef(getAccessToken);
|
|
8529
|
+
getAccessTokenRef.current = getAccessToken;
|
|
8274
8530
|
const settingOwnerRef = useRef(false);
|
|
8275
8531
|
useEffect(() => {
|
|
8276
8532
|
if (!state.guestPreauthAccountId) return;
|
|
@@ -8279,6 +8535,8 @@ function usePaymentEffects(deps) {
|
|
|
8279
8535
|
if (!authenticated) return;
|
|
8280
8536
|
if (!state.guestSessionToken) return;
|
|
8281
8537
|
if (settingOwnerRef.current) return;
|
|
8538
|
+
const hasActive = state.accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
|
|
8539
|
+
if (!hasActive) return;
|
|
8282
8540
|
settingOwnerRef.current = true;
|
|
8283
8541
|
let cancelled = false;
|
|
8284
8542
|
const setOwner = async () => {
|
|
@@ -8316,12 +8574,69 @@ function usePaymentEffects(deps) {
|
|
|
8316
8574
|
state.activeCredentialId,
|
|
8317
8575
|
state.activePublicKey,
|
|
8318
8576
|
state.guestSessionToken,
|
|
8577
|
+
state.accounts,
|
|
8319
8578
|
authenticated,
|
|
8320
8579
|
apiBaseUrl,
|
|
8321
8580
|
dispatch,
|
|
8322
8581
|
reloadAccounts
|
|
8323
8582
|
]);
|
|
8324
8583
|
}
|
|
8584
|
+
function useGuestDesktopPreauthSessionEffect(deps) {
|
|
8585
|
+
const { state, authExecutor, reloadAccounts, dispatch, desktopGuestPreauth } = deps;
|
|
8586
|
+
const preauthExecutingRef = useRef(false);
|
|
8587
|
+
useEffect(() => {
|
|
8588
|
+
if (!desktopGuestPreauth) return;
|
|
8589
|
+
if (!state.guestPreauthorizing) return;
|
|
8590
|
+
if (!state.guestPreauthSessionId || preauthExecutingRef.current) return;
|
|
8591
|
+
preauthExecutingRef.current = true;
|
|
8592
|
+
const runPreauthSession = async () => {
|
|
8593
|
+
try {
|
|
8594
|
+
await authExecutor.executeSessionById(state.guestPreauthSessionId);
|
|
8595
|
+
await reloadAccounts();
|
|
8596
|
+
} catch {
|
|
8597
|
+
} finally {
|
|
8598
|
+
preauthExecutingRef.current = false;
|
|
8599
|
+
dispatch({ type: "GUEST_PREAUTH_END" });
|
|
8600
|
+
}
|
|
8601
|
+
};
|
|
8602
|
+
void runPreauthSession();
|
|
8603
|
+
}, [
|
|
8604
|
+
desktopGuestPreauth,
|
|
8605
|
+
state.guestPreauthorizing,
|
|
8606
|
+
state.guestPreauthSessionId,
|
|
8607
|
+
authExecutor,
|
|
8608
|
+
reloadAccounts,
|
|
8609
|
+
dispatch
|
|
8610
|
+
]);
|
|
8611
|
+
}
|
|
8612
|
+
function useGuestPreauthPhaseSyncEffect(deps) {
|
|
8613
|
+
const { state, dispatch, authExecutor, isDesktop } = deps;
|
|
8614
|
+
useEffect(() => {
|
|
8615
|
+
if (!state.guestPreauthorizing || !isDesktop) return;
|
|
8616
|
+
const pending = authExecutor.pendingSelectSource;
|
|
8617
|
+
if (pending) {
|
|
8618
|
+
const intent = {
|
|
8619
|
+
step: "select-source",
|
|
8620
|
+
action: pending,
|
|
8621
|
+
isDesktop
|
|
8622
|
+
};
|
|
8623
|
+
if (state.phase.step === "select-source" && state.phase.action.id === pending.id) {
|
|
8624
|
+
return;
|
|
8625
|
+
}
|
|
8626
|
+
dispatch({ type: "SET_USER_INTENT", intent });
|
|
8627
|
+
return;
|
|
8628
|
+
}
|
|
8629
|
+
if (state.phase.step === "select-source") {
|
|
8630
|
+
dispatch({ type: "SET_USER_INTENT", intent: { step: "one-tap-setup", action: null } });
|
|
8631
|
+
}
|
|
8632
|
+
}, [
|
|
8633
|
+
state.guestPreauthorizing,
|
|
8634
|
+
state.phase,
|
|
8635
|
+
isDesktop,
|
|
8636
|
+
authExecutor.pendingSelectSource,
|
|
8637
|
+
dispatch
|
|
8638
|
+
]);
|
|
8639
|
+
}
|
|
8325
8640
|
function BlinkPayment(props) {
|
|
8326
8641
|
const resetKey = useRef(0);
|
|
8327
8642
|
const handleBoundaryReset = useCallback(() => {
|
|
@@ -8343,6 +8658,10 @@ function BlinkPaymentInner({
|
|
|
8343
8658
|
const { apiBaseUrl, depositAmount } = useBlinkConfig();
|
|
8344
8659
|
const { ready, authenticated, logout, getAccessToken } = usePrivy();
|
|
8345
8660
|
useLoginWithOAuth();
|
|
8661
|
+
const isDesktop = shouldUseWalletConnector({
|
|
8662
|
+
useWalletConnector: useWalletConnectorProp,
|
|
8663
|
+
userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
|
|
8664
|
+
});
|
|
8346
8665
|
const [state, dispatch] = useReducer(
|
|
8347
8666
|
paymentReducer,
|
|
8348
8667
|
{
|
|
@@ -8426,7 +8745,9 @@ function BlinkPaymentInner({
|
|
|
8426
8745
|
reauthTokenRef: mobileFlowRefs.reauthTokenRef,
|
|
8427
8746
|
authenticated,
|
|
8428
8747
|
merchantAuthorization,
|
|
8429
|
-
destination
|
|
8748
|
+
destination,
|
|
8749
|
+
guestSessionToken: state.guestSessionToken,
|
|
8750
|
+
selectedProviderId: state.selectedProviderId
|
|
8430
8751
|
});
|
|
8431
8752
|
const oneTapSetup = useOneTapSetupHandlers({
|
|
8432
8753
|
dispatch,
|
|
@@ -8434,7 +8755,8 @@ function BlinkPaymentInner({
|
|
|
8434
8755
|
apiBaseUrl,
|
|
8435
8756
|
authExecutor,
|
|
8436
8757
|
selectSourceChainName: sourceSelection.selectSourceChainName,
|
|
8437
|
-
selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol
|
|
8758
|
+
selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
|
|
8759
|
+
authorizationSessionIdForGuest: state.guestPreauthSessionId
|
|
8438
8760
|
});
|
|
8439
8761
|
const guestTransfer = useGuestTransferHandlers({
|
|
8440
8762
|
dispatch,
|
|
@@ -8448,13 +8770,12 @@ function BlinkPaymentInner({
|
|
|
8448
8770
|
clearMobileFlowState();
|
|
8449
8771
|
transfer.processingStartedAtRef.current = null;
|
|
8450
8772
|
transfer.pollingTransferIdRef.current = null;
|
|
8451
|
-
oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
|
|
8452
8773
|
dispatch({
|
|
8453
8774
|
type: "NEW_PAYMENT",
|
|
8454
8775
|
depositAmount,
|
|
8455
8776
|
firstAccountId: state.accounts.length > 0 ? state.accounts[0].id : null
|
|
8456
8777
|
});
|
|
8457
|
-
}, [depositAmount, state.accounts, transfer
|
|
8778
|
+
}, [depositAmount, state.accounts, transfer]);
|
|
8458
8779
|
const handleLogout = useCallback(async () => {
|
|
8459
8780
|
try {
|
|
8460
8781
|
await logout();
|
|
@@ -8466,65 +8787,113 @@ function BlinkPaymentInner({
|
|
|
8466
8787
|
}
|
|
8467
8788
|
polling.stopPolling();
|
|
8468
8789
|
passkey.checkingPasskeyRef.current = false;
|
|
8469
|
-
oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
|
|
8470
8790
|
auth.setAuthInput("");
|
|
8471
8791
|
auth.setOtpCode("");
|
|
8472
|
-
dispatch({ type: "LOGOUT", depositAmount });
|
|
8473
|
-
}, [logout, polling, depositAmount, auth, passkey,
|
|
8474
|
-
|
|
8792
|
+
dispatch({ type: "LOGOUT", depositAmount, privyReady: ready });
|
|
8793
|
+
}, [logout, polling, depositAmount, auth, passkey, ready]);
|
|
8794
|
+
useEffect(() => {
|
|
8795
|
+
if (depositAmount != null) {
|
|
8796
|
+
dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
|
|
8797
|
+
}
|
|
8798
|
+
}, [depositAmount, dispatch]);
|
|
8799
|
+
usePrivySessionSyncEffect({ dispatch, ready, authenticated });
|
|
8800
|
+
useOtpEffects({
|
|
8475
8801
|
state,
|
|
8476
8802
|
dispatch,
|
|
8477
|
-
ready,
|
|
8478
8803
|
authenticated,
|
|
8479
|
-
apiBaseUrl,
|
|
8480
|
-
depositAmount,
|
|
8481
|
-
onComplete,
|
|
8482
|
-
onError,
|
|
8483
|
-
polling,
|
|
8484
|
-
authExecutor,
|
|
8485
|
-
reloadAccounts: transfer.reloadAccounts,
|
|
8486
8804
|
activeOtpStatus: auth.activeOtpStatus,
|
|
8487
8805
|
activeOtpErrorMessage: auth.activeOtpErrorMessage,
|
|
8488
8806
|
otpCode: auth.otpCode,
|
|
8489
8807
|
handleVerifyLoginCode: auth.handleVerifyLoginCode,
|
|
8490
8808
|
setAuthInput: auth.setAuthInput,
|
|
8809
|
+
setOtpCode: auth.setOtpCode
|
|
8810
|
+
});
|
|
8811
|
+
usePasskeyCheckEffect({
|
|
8812
|
+
dispatch,
|
|
8813
|
+
ready,
|
|
8814
|
+
authenticated,
|
|
8815
|
+
apiBaseUrl,
|
|
8816
|
+
activeCredentialId: state.activeCredentialId,
|
|
8817
|
+
passkeyConfigLoaded: state.passkeyConfigLoaded,
|
|
8818
|
+
checkingPasskeyRef: passkey.checkingPasskeyRef,
|
|
8819
|
+
setAuthInput: auth.setAuthInput,
|
|
8491
8820
|
setOtpCode: auth.setOtpCode,
|
|
8821
|
+
polling,
|
|
8492
8822
|
mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
|
|
8493
8823
|
handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
|
|
8494
8824
|
setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
|
|
8495
8825
|
reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
|
|
8496
8826
|
reauthTokenRef: mobileFlowRefs.reauthTokenRef,
|
|
8497
|
-
loadingDataRef: mobileFlowRefs.loadingDataRef,
|
|
8498
8827
|
pollingTransferIdRef: transfer.pollingTransferIdRef,
|
|
8828
|
+
handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn,
|
|
8829
|
+
onComplete
|
|
8830
|
+
});
|
|
8831
|
+
useDataLoadEffect({
|
|
8832
|
+
state,
|
|
8833
|
+
dispatch,
|
|
8834
|
+
authenticated,
|
|
8835
|
+
apiBaseUrl,
|
|
8836
|
+
depositAmount,
|
|
8837
|
+
loadingDataRef: mobileFlowRefs.loadingDataRef,
|
|
8838
|
+
reloadAccounts: transfer.reloadAccounts
|
|
8839
|
+
});
|
|
8840
|
+
useProcessingEffect({
|
|
8841
|
+
state,
|
|
8842
|
+
dispatch,
|
|
8843
|
+
polling,
|
|
8499
8844
|
processingStartedAtRef: transfer.processingStartedAtRef,
|
|
8500
|
-
|
|
8845
|
+
onComplete,
|
|
8846
|
+
onError,
|
|
8847
|
+
reloadAccounts: transfer.reloadAccounts
|
|
8848
|
+
});
|
|
8849
|
+
useMobilePollingEffect({
|
|
8850
|
+
state,
|
|
8851
|
+
dispatch,
|
|
8852
|
+
polling,
|
|
8853
|
+
mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
|
|
8854
|
+
handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
|
|
8855
|
+
setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
|
|
8856
|
+
reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
|
|
8857
|
+
reauthTokenRef: mobileFlowRefs.reauthTokenRef,
|
|
8858
|
+
pollingTransferIdRef: transfer.pollingTransferIdRef,
|
|
8859
|
+
reloadAccounts: transfer.reloadAccounts,
|
|
8860
|
+
handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn,
|
|
8861
|
+
apiBaseUrl
|
|
8862
|
+
});
|
|
8863
|
+
useSelectSourceEffect({
|
|
8501
8864
|
pendingSelectSourceAction: sourceSelection.pendingSelectSourceAction,
|
|
8502
8865
|
selectSourceChoices: sourceSelection.selectSourceChoices,
|
|
8503
8866
|
selectSourceRecommended: sourceSelection.selectSourceRecommended,
|
|
8504
8867
|
setSelectSourceChainName: sourceSelection.setSelectSourceChainName,
|
|
8505
8868
|
setSelectSourceTokenSymbol: sourceSelection.setSelectSourceTokenSymbol,
|
|
8506
|
-
initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef
|
|
8507
|
-
oneTapLimitSavedDuringSetupRef: oneTapSetup.oneTapLimitSavedDuringSetupRef,
|
|
8508
|
-
handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn
|
|
8869
|
+
initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef
|
|
8509
8870
|
});
|
|
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,
|
|
8871
|
+
useOneTapAutoResolveEffect({
|
|
8872
|
+
authExecutor,
|
|
8873
|
+
dispatch,
|
|
8874
|
+
oneTapLimitSavedDuringSetup: state.oneTapLimitSavedDuringSetup,
|
|
8875
|
+
reloadAccounts: transfer.reloadAccounts
|
|
8876
|
+
});
|
|
8877
|
+
useGuestPreauthEffect({
|
|
8878
|
+
state,
|
|
8879
|
+
dispatch,
|
|
8525
8880
|
authenticated,
|
|
8526
|
-
|
|
8527
|
-
|
|
8881
|
+
apiBaseUrl,
|
|
8882
|
+
reloadAccounts: transfer.reloadAccounts
|
|
8883
|
+
});
|
|
8884
|
+
useGuestPreauthPhaseSyncEffect({
|
|
8885
|
+
state,
|
|
8886
|
+
dispatch,
|
|
8887
|
+
authExecutor,
|
|
8888
|
+
isDesktop
|
|
8889
|
+
});
|
|
8890
|
+
useGuestDesktopPreauthSessionEffect({
|
|
8891
|
+
state,
|
|
8892
|
+
authExecutor,
|
|
8893
|
+
reloadAccounts: transfer.reloadAccounts,
|
|
8894
|
+
dispatch,
|
|
8895
|
+
desktopGuestPreauth: isDesktop
|
|
8896
|
+
});
|
|
8528
8897
|
const handlers = useMemo(() => ({
|
|
8529
8898
|
onSendLoginCode: auth.handleSendLoginCode,
|
|
8530
8899
|
onVerifyLoginCode: auth.handleVerifyLoginCode,
|
|
@@ -8547,7 +8916,7 @@ function BlinkPaymentInner({
|
|
|
8547
8916
|
onBackFromOpenWallet: () => dispatch({ type: "CLEAR_MOBILE_STATE" }),
|
|
8548
8917
|
onLogout: handleLogout,
|
|
8549
8918
|
onNewPayment: handleNewPayment,
|
|
8550
|
-
|
|
8919
|
+
onSetPhase: (phase) => dispatch({ type: "SET_USER_INTENT", intent: phase }),
|
|
8551
8920
|
onSetAuthInput: auth.setAuthInput,
|
|
8552
8921
|
onSetOtpCode: (code) => {
|
|
8553
8922
|
auth.setOtpCode(code);
|
|
@@ -8561,30 +8930,9 @@ function BlinkPaymentInner({
|
|
|
8561
8930
|
onSelectAuthorizedToken: provider.handleSelectAuthorizedToken,
|
|
8562
8931
|
onAuthorizeToken: provider.handleAuthorizeToken,
|
|
8563
8932
|
onSelectGuestToken: guestTransfer.handleSelectGuestToken,
|
|
8933
|
+
onGuestBackFromTokenPicker: guestTransfer.handleGuestBackFromTokenPicker,
|
|
8564
8934
|
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
|
-
}
|
|
8935
|
+
onPreauthorize: provider.handlePreauthorize
|
|
8588
8936
|
}), [
|
|
8589
8937
|
auth,
|
|
8590
8938
|
passkey,
|
|
@@ -8595,56 +8943,57 @@ function BlinkPaymentInner({
|
|
|
8595
8943
|
oneTapSetup,
|
|
8596
8944
|
guestTransfer,
|
|
8597
8945
|
handleLogout,
|
|
8598
|
-
handleNewPayment
|
|
8599
|
-
state.guestPreauthAccountId,
|
|
8600
|
-
state.guestSessionToken,
|
|
8601
|
-
state.selectedProviderId,
|
|
8602
|
-
state.providers,
|
|
8603
|
-
apiBaseUrl
|
|
8946
|
+
handleNewPayment
|
|
8604
8947
|
]);
|
|
8605
8948
|
return /* @__PURE__ */ jsx(
|
|
8606
8949
|
StepRenderer,
|
|
8607
8950
|
{
|
|
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
|
-
|
|
8951
|
+
flow: {
|
|
8952
|
+
state,
|
|
8953
|
+
authenticated,
|
|
8954
|
+
activeOtpStatus: auth.activeOtpStatus,
|
|
8955
|
+
isDesktop,
|
|
8956
|
+
merchantName,
|
|
8957
|
+
onBack,
|
|
8958
|
+
onDismiss,
|
|
8959
|
+
depositAmount
|
|
8960
|
+
},
|
|
8961
|
+
remote: {
|
|
8962
|
+
pollingTransfer: polling.transfer,
|
|
8963
|
+
pollingError: polling.error,
|
|
8964
|
+
authExecutorError: authExecutor.error,
|
|
8965
|
+
transferSigningSigning: transferSigning.signing,
|
|
8966
|
+
transferSigningError: transferSigning.error,
|
|
8967
|
+
pendingSelectSource: authExecutor.pendingSelectSource,
|
|
8968
|
+
pendingOneTapSetup: authExecutor.pendingOneTapSetup
|
|
8969
|
+
},
|
|
8970
|
+
derived: {
|
|
8971
|
+
pendingConnections: derived.pendingConnections,
|
|
8972
|
+
depositEligibleAccounts: derived.depositEligibleAccounts,
|
|
8973
|
+
sourceName: derived.sourceName,
|
|
8974
|
+
maxSourceBalance: derived.maxSourceBalance,
|
|
8975
|
+
tokenCount: derived.tokenCount,
|
|
8976
|
+
selectedAccount: derived.selectedAccount,
|
|
8977
|
+
selectedSource: derived.selectedSource,
|
|
8978
|
+
selectSourceChoices: sourceSelection.selectSourceChoices,
|
|
8979
|
+
selectSourceRecommended: sourceSelection.selectSourceRecommended,
|
|
8980
|
+
selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance
|
|
8981
|
+
},
|
|
8982
|
+
forms: {
|
|
8983
|
+
authInput: auth.authInput,
|
|
8984
|
+
otpCode: auth.otpCode,
|
|
8985
|
+
selectSourceChainName: sourceSelection.selectSourceChainName,
|
|
8986
|
+
selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
|
|
8987
|
+
savingOneTapLimit: oneTapSetup.savingOneTapLimit,
|
|
8988
|
+
guestTokenEntries: guestTransfer.guestTokenEntries,
|
|
8989
|
+
guestLoadingBalances: guestTransfer.loadingBalances,
|
|
8990
|
+
guestSettingSender: guestTransfer.settingSender
|
|
8991
|
+
},
|
|
8643
8992
|
handlers
|
|
8644
8993
|
}
|
|
8645
8994
|
);
|
|
8646
8995
|
}
|
|
8647
8996
|
|
|
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,
|
|
8997
|
+
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
8998
|
//# sourceMappingURL=index.js.map
|
|
8650
8999
|
//# sourceMappingURL=index.js.map
|