@swype-org/react-sdk 0.1.2 → 0.1.3
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 +739 -462
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -19
- package/dist/index.d.ts +29 -19
- package/dist/index.js +741 -464
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createContext, useRef, useState, useCallback, useMemo, useContext, useEffect } from 'react';
|
|
2
2
|
import { PrivyProvider, usePrivy } from '@privy-io/react-auth';
|
|
3
|
-
import { createConfig, http, WagmiProvider,
|
|
3
|
+
import { createConfig, http, WagmiProvider, useConfig, useConnect, useSwitchChain } from 'wagmi';
|
|
4
4
|
import { mainnet, arbitrum, base } from 'wagmi/chains';
|
|
5
5
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { walletActions, createClient, custom } from 'viem';
|
|
8
|
+
import { parseAccount, getAddress } from 'viem/utils';
|
|
9
9
|
|
|
10
10
|
var __defProp = Object.defineProperty;
|
|
11
11
|
var __export = (target, all) => {
|
|
@@ -143,6 +143,7 @@ __export(api_exports, {
|
|
|
143
143
|
fetchProviders: () => fetchProviders,
|
|
144
144
|
fetchTransfer: () => fetchTransfer,
|
|
145
145
|
reportActionCompletion: () => reportActionCompletion,
|
|
146
|
+
signTransfer: () => signTransfer,
|
|
146
147
|
updateUserConfig: () => updateUserConfig,
|
|
147
148
|
updateUserConfigBySession: () => updateUserConfigBySession
|
|
148
149
|
});
|
|
@@ -211,6 +212,18 @@ async function fetchTransfer(apiBaseUrl, token, transferId) {
|
|
|
211
212
|
if (!res.ok) await throwApiError(res);
|
|
212
213
|
return await res.json();
|
|
213
214
|
}
|
|
215
|
+
async function signTransfer(apiBaseUrl, token, transferId, signedUserOp) {
|
|
216
|
+
const res = await fetch(`${apiBaseUrl}/v1/transfers/${transferId}`, {
|
|
217
|
+
method: "PATCH",
|
|
218
|
+
headers: {
|
|
219
|
+
"Content-Type": "application/json",
|
|
220
|
+
Authorization: `Bearer ${token}`
|
|
221
|
+
},
|
|
222
|
+
body: JSON.stringify({ signedUserOp })
|
|
223
|
+
});
|
|
224
|
+
if (!res.ok) await throwApiError(res);
|
|
225
|
+
return await res.json();
|
|
226
|
+
}
|
|
214
227
|
async function fetchAuthorizationSession(apiBaseUrl, sessionId) {
|
|
215
228
|
const res = await fetch(
|
|
216
229
|
`${apiBaseUrl}/v1/authorization-sessions/${sessionId}`
|
|
@@ -252,25 +265,442 @@ async function reportActionCompletion(apiBaseUrl, actionId, result) {
|
|
|
252
265
|
if (!res.ok) await throwApiError(res);
|
|
253
266
|
return await res.json();
|
|
254
267
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
268
|
+
|
|
269
|
+
// node_modules/@wagmi/core/dist/esm/version.js
|
|
270
|
+
var version = "2.22.1";
|
|
271
|
+
|
|
272
|
+
// node_modules/@wagmi/core/dist/esm/utils/getVersion.js
|
|
273
|
+
var getVersion = () => `@wagmi/core@${version}`;
|
|
274
|
+
|
|
275
|
+
// node_modules/@wagmi/core/dist/esm/errors/base.js
|
|
276
|
+
var __classPrivateFieldGet = function(receiver, state, kind, f) {
|
|
277
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
278
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
279
|
+
};
|
|
280
|
+
var _BaseError_instances;
|
|
281
|
+
var _BaseError_walk;
|
|
282
|
+
var BaseError = class _BaseError extends Error {
|
|
283
|
+
get docsBaseUrl() {
|
|
284
|
+
return "https://wagmi.sh/core";
|
|
285
|
+
}
|
|
286
|
+
get version() {
|
|
287
|
+
return getVersion();
|
|
288
|
+
}
|
|
289
|
+
constructor(shortMessage, options = {}) {
|
|
290
|
+
super();
|
|
291
|
+
_BaseError_instances.add(this);
|
|
292
|
+
Object.defineProperty(this, "details", {
|
|
293
|
+
enumerable: true,
|
|
294
|
+
configurable: true,
|
|
295
|
+
writable: true,
|
|
296
|
+
value: void 0
|
|
297
|
+
});
|
|
298
|
+
Object.defineProperty(this, "docsPath", {
|
|
299
|
+
enumerable: true,
|
|
300
|
+
configurable: true,
|
|
301
|
+
writable: true,
|
|
302
|
+
value: void 0
|
|
303
|
+
});
|
|
304
|
+
Object.defineProperty(this, "metaMessages", {
|
|
305
|
+
enumerable: true,
|
|
306
|
+
configurable: true,
|
|
307
|
+
writable: true,
|
|
308
|
+
value: void 0
|
|
309
|
+
});
|
|
310
|
+
Object.defineProperty(this, "shortMessage", {
|
|
311
|
+
enumerable: true,
|
|
312
|
+
configurable: true,
|
|
313
|
+
writable: true,
|
|
314
|
+
value: void 0
|
|
315
|
+
});
|
|
316
|
+
Object.defineProperty(this, "name", {
|
|
317
|
+
enumerable: true,
|
|
318
|
+
configurable: true,
|
|
319
|
+
writable: true,
|
|
320
|
+
value: "WagmiCoreError"
|
|
321
|
+
});
|
|
322
|
+
const details = options.cause instanceof _BaseError ? options.cause.details : options.cause?.message ? options.cause.message : options.details;
|
|
323
|
+
const docsPath = options.cause instanceof _BaseError ? options.cause.docsPath || options.docsPath : options.docsPath;
|
|
324
|
+
this.message = [
|
|
325
|
+
shortMessage || "An error occurred.",
|
|
326
|
+
"",
|
|
327
|
+
...options.metaMessages ? [...options.metaMessages, ""] : [],
|
|
328
|
+
...docsPath ? [
|
|
329
|
+
`Docs: ${this.docsBaseUrl}${docsPath}.html${options.docsSlug ? `#${options.docsSlug}` : ""}`
|
|
330
|
+
] : [],
|
|
331
|
+
...details ? [`Details: ${details}`] : [],
|
|
332
|
+
`Version: ${this.version}`
|
|
333
|
+
].join("\n");
|
|
334
|
+
if (options.cause)
|
|
335
|
+
this.cause = options.cause;
|
|
336
|
+
this.details = details;
|
|
337
|
+
this.docsPath = docsPath;
|
|
338
|
+
this.metaMessages = options.metaMessages;
|
|
339
|
+
this.shortMessage = shortMessage;
|
|
340
|
+
}
|
|
341
|
+
walk(fn) {
|
|
342
|
+
return __classPrivateFieldGet(this, _BaseError_instances, "m", _BaseError_walk).call(this, this, fn);
|
|
343
|
+
}
|
|
259
344
|
};
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
345
|
+
_BaseError_instances = /* @__PURE__ */ new WeakSet(), _BaseError_walk = function _BaseError_walk2(err, fn) {
|
|
346
|
+
if (fn?.(err))
|
|
347
|
+
return err;
|
|
348
|
+
if (err.cause)
|
|
349
|
+
return __classPrivateFieldGet(this, _BaseError_instances, "m", _BaseError_walk2).call(this, err.cause, fn);
|
|
350
|
+
return err;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// node_modules/@wagmi/core/dist/esm/errors/config.js
|
|
354
|
+
var ConnectorNotConnectedError = class extends BaseError {
|
|
355
|
+
constructor() {
|
|
356
|
+
super("Connector not connected.");
|
|
357
|
+
Object.defineProperty(this, "name", {
|
|
358
|
+
enumerable: true,
|
|
359
|
+
configurable: true,
|
|
360
|
+
writable: true,
|
|
361
|
+
value: "ConnectorNotConnectedError"
|
|
362
|
+
});
|
|
267
363
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
364
|
+
};
|
|
365
|
+
var ConnectorAccountNotFoundError = class extends BaseError {
|
|
366
|
+
constructor({ address, connector }) {
|
|
367
|
+
super(`Account "${address}" not found for connector "${connector.name}".`);
|
|
368
|
+
Object.defineProperty(this, "name", {
|
|
369
|
+
enumerable: true,
|
|
370
|
+
configurable: true,
|
|
371
|
+
writable: true,
|
|
372
|
+
value: "ConnectorAccountNotFoundError"
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
var ConnectorChainMismatchError = class extends BaseError {
|
|
377
|
+
constructor({ connectionChainId, connectorChainId }) {
|
|
378
|
+
super(`The current chain of the connector (id: ${connectorChainId}) does not match the connection's chain (id: ${connectionChainId}).`, {
|
|
379
|
+
metaMessages: [
|
|
380
|
+
`Current Chain ID: ${connectorChainId}`,
|
|
381
|
+
`Expected Chain ID: ${connectionChainId}`
|
|
382
|
+
]
|
|
383
|
+
});
|
|
384
|
+
Object.defineProperty(this, "name", {
|
|
385
|
+
enumerable: true,
|
|
386
|
+
configurable: true,
|
|
387
|
+
writable: true,
|
|
388
|
+
value: "ConnectorChainMismatchError"
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
var ConnectorUnavailableReconnectingError = class extends BaseError {
|
|
393
|
+
constructor({ connector }) {
|
|
394
|
+
super(`Connector "${connector.name}" unavailable while reconnecting.`, {
|
|
395
|
+
details: [
|
|
396
|
+
"During the reconnection step, the only connector methods guaranteed to be available are: `id`, `name`, `type`, `uid`.",
|
|
397
|
+
"All other methods are not guaranteed to be available until reconnection completes and connectors are fully restored.",
|
|
398
|
+
"This error commonly occurs for connectors that asynchronously inject after reconnection has already started."
|
|
399
|
+
].join(" ")
|
|
400
|
+
});
|
|
401
|
+
Object.defineProperty(this, "name", {
|
|
402
|
+
enumerable: true,
|
|
403
|
+
configurable: true,
|
|
404
|
+
writable: true,
|
|
405
|
+
value: "ConnectorUnavailableReconnectingError"
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
async function getConnectorClient(config, parameters = {}) {
|
|
410
|
+
const { assertChainId = true } = parameters;
|
|
411
|
+
let connection;
|
|
412
|
+
if (parameters.connector) {
|
|
413
|
+
const { connector: connector2 } = parameters;
|
|
414
|
+
if (config.state.status === "reconnecting" && !connector2.getAccounts && !connector2.getChainId)
|
|
415
|
+
throw new ConnectorUnavailableReconnectingError({ connector: connector2 });
|
|
416
|
+
const [accounts, chainId2] = await Promise.all([
|
|
417
|
+
connector2.getAccounts().catch((e) => {
|
|
418
|
+
if (parameters.account === null)
|
|
419
|
+
return [];
|
|
420
|
+
throw e;
|
|
421
|
+
}),
|
|
422
|
+
connector2.getChainId()
|
|
423
|
+
]);
|
|
424
|
+
connection = {
|
|
425
|
+
accounts,
|
|
426
|
+
chainId: chainId2,
|
|
427
|
+
connector: connector2
|
|
428
|
+
};
|
|
429
|
+
} else
|
|
430
|
+
connection = config.state.connections.get(config.state.current);
|
|
431
|
+
if (!connection)
|
|
432
|
+
throw new ConnectorNotConnectedError();
|
|
433
|
+
const chainId = parameters.chainId ?? connection.chainId;
|
|
434
|
+
const connectorChainId = await connection.connector.getChainId();
|
|
435
|
+
if (assertChainId && connectorChainId !== chainId)
|
|
436
|
+
throw new ConnectorChainMismatchError({
|
|
437
|
+
connectionChainId: chainId,
|
|
438
|
+
connectorChainId
|
|
439
|
+
});
|
|
440
|
+
const connector = connection.connector;
|
|
441
|
+
if (connector.getClient)
|
|
442
|
+
return connector.getClient({ chainId });
|
|
443
|
+
const account = parseAccount(parameters.account ?? connection.accounts[0]);
|
|
444
|
+
if (account)
|
|
445
|
+
account.address = getAddress(account.address);
|
|
446
|
+
if (parameters.account && !connection.accounts.some((x) => x.toLowerCase() === account.address.toLowerCase()))
|
|
447
|
+
throw new ConnectorAccountNotFoundError({
|
|
448
|
+
address: account.address,
|
|
449
|
+
connector
|
|
450
|
+
});
|
|
451
|
+
const chain = config.chains.find((chain2) => chain2.id === chainId);
|
|
452
|
+
const provider = await connection.connector.getProvider({ chainId });
|
|
453
|
+
return createClient({
|
|
454
|
+
account,
|
|
455
|
+
chain,
|
|
456
|
+
name: "Connector Client",
|
|
457
|
+
transport: (opts) => custom(provider)({ ...opts, retryCount: 0 })
|
|
271
458
|
});
|
|
272
|
-
|
|
273
|
-
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// node_modules/@wagmi/core/dist/esm/actions/getAccount.js
|
|
462
|
+
function getAccount(config) {
|
|
463
|
+
const uid = config.state.current;
|
|
464
|
+
const connection = config.state.connections.get(uid);
|
|
465
|
+
const addresses = connection?.accounts;
|
|
466
|
+
const address = addresses?.[0];
|
|
467
|
+
const chain = config.chains.find((chain2) => chain2.id === connection?.chainId);
|
|
468
|
+
const status = config.state.status;
|
|
469
|
+
switch (status) {
|
|
470
|
+
case "connected":
|
|
471
|
+
return {
|
|
472
|
+
address,
|
|
473
|
+
addresses,
|
|
474
|
+
chain,
|
|
475
|
+
chainId: connection?.chainId,
|
|
476
|
+
connector: connection?.connector,
|
|
477
|
+
isConnected: true,
|
|
478
|
+
isConnecting: false,
|
|
479
|
+
isDisconnected: false,
|
|
480
|
+
isReconnecting: false,
|
|
481
|
+
status
|
|
482
|
+
};
|
|
483
|
+
case "reconnecting":
|
|
484
|
+
return {
|
|
485
|
+
address,
|
|
486
|
+
addresses,
|
|
487
|
+
chain,
|
|
488
|
+
chainId: connection?.chainId,
|
|
489
|
+
connector: connection?.connector,
|
|
490
|
+
isConnected: !!address,
|
|
491
|
+
isConnecting: false,
|
|
492
|
+
isDisconnected: false,
|
|
493
|
+
isReconnecting: true,
|
|
494
|
+
status
|
|
495
|
+
};
|
|
496
|
+
case "connecting":
|
|
497
|
+
return {
|
|
498
|
+
address,
|
|
499
|
+
addresses,
|
|
500
|
+
chain,
|
|
501
|
+
chainId: connection?.chainId,
|
|
502
|
+
connector: connection?.connector,
|
|
503
|
+
isConnected: false,
|
|
504
|
+
isConnecting: true,
|
|
505
|
+
isDisconnected: false,
|
|
506
|
+
isReconnecting: false,
|
|
507
|
+
status
|
|
508
|
+
};
|
|
509
|
+
case "disconnected":
|
|
510
|
+
return {
|
|
511
|
+
address: void 0,
|
|
512
|
+
addresses: void 0,
|
|
513
|
+
chain: void 0,
|
|
514
|
+
chainId: void 0,
|
|
515
|
+
connector: void 0,
|
|
516
|
+
isConnected: false,
|
|
517
|
+
isConnecting: false,
|
|
518
|
+
isDisconnected: true,
|
|
519
|
+
isReconnecting: false,
|
|
520
|
+
status
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
async function getWalletClient(config, parameters = {}) {
|
|
525
|
+
const client = await getConnectorClient(config, parameters);
|
|
526
|
+
return client.extend(walletActions);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/authorization/upgrade.ts
|
|
530
|
+
function isNonEmptyString(value) {
|
|
531
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
532
|
+
}
|
|
533
|
+
function isValidChainId(value) {
|
|
534
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
535
|
+
}
|
|
536
|
+
function isRpcClient(value) {
|
|
537
|
+
return typeof value === "object" && value !== null && "request" in value && typeof value.request === "function";
|
|
538
|
+
}
|
|
539
|
+
function sleep(ms) {
|
|
540
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
541
|
+
}
|
|
542
|
+
async function pollCallsStatus(client, bundleId, label, timeoutMs = 12e4, intervalMs = 3e3) {
|
|
543
|
+
const deadline = Date.now() + timeoutMs;
|
|
544
|
+
while (Date.now() < deadline) {
|
|
545
|
+
const raw = await client.request({
|
|
546
|
+
method: "wallet_getCallsStatus",
|
|
547
|
+
params: [bundleId]
|
|
548
|
+
});
|
|
549
|
+
const result = raw;
|
|
550
|
+
const status = result.status;
|
|
551
|
+
console.info(`[swype-sdk][upgrade] ${label} status:`, status);
|
|
552
|
+
if (status === 200 || status === "CONFIRMED" || status === "confirmed") {
|
|
553
|
+
const txHash = result.receipts?.[0]?.transactionHash ?? "";
|
|
554
|
+
if (!txHash) {
|
|
555
|
+
console.warn(`[swype-sdk][upgrade] ${label}: confirmed but no receipt txHash.`);
|
|
556
|
+
}
|
|
557
|
+
return txHash;
|
|
558
|
+
}
|
|
559
|
+
if (typeof status === "number" && status >= 400) {
|
|
560
|
+
throw new Error(
|
|
561
|
+
`${label} failed with status ${status}. Receipts: ${JSON.stringify(result.receipts ?? [])}`
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
if (typeof status === "string" && (status.toLowerCase().includes("fail") || status.toLowerCase().includes("error"))) {
|
|
565
|
+
throw new Error(
|
|
566
|
+
`${label} failed: ${status}. Receipts: ${JSON.stringify(result.receipts ?? [])}`
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
await sleep(intervalMs);
|
|
570
|
+
}
|
|
571
|
+
throw new Error(`${label} timed out after ${timeoutMs / 1e3}s.`);
|
|
572
|
+
}
|
|
573
|
+
async function sendCalls(client, callsParams, label) {
|
|
574
|
+
try {
|
|
575
|
+
const result = await client.request({
|
|
576
|
+
method: "wallet_sendCalls",
|
|
577
|
+
params: [callsParams]
|
|
578
|
+
});
|
|
579
|
+
const bundleId = typeof result === "string" ? result : result?.id;
|
|
580
|
+
if (!bundleId) {
|
|
581
|
+
throw new Error(`${label}: wallet_sendCalls returned an unexpected result: ${JSON.stringify(result)}`);
|
|
582
|
+
}
|
|
583
|
+
return bundleId;
|
|
584
|
+
} catch (err) {
|
|
585
|
+
const msg = err.message ?? "";
|
|
586
|
+
if (msg.includes("Unauthorized") || msg.includes("-32006")) {
|
|
587
|
+
throw new Error(
|
|
588
|
+
"MetaMask smart accounts may not be enabled or may not be supported on this chain. Please ensure you are using a supported network and that smart accounts are enabled in MetaMask Settings \u2192 Experimental."
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
throw new Error(`${label} failed: ${msg}`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async function submitUpgradeTransaction(params) {
|
|
595
|
+
const { walletClient, sender, metadata } = params;
|
|
596
|
+
const addKeyCalldata = metadata?.addKeyCalldata;
|
|
597
|
+
if (!isNonEmptyString(addKeyCalldata)) {
|
|
598
|
+
throw new Error("Incomplete upgrade metadata: missing addKeyCalldata.");
|
|
599
|
+
}
|
|
600
|
+
const resolvedSmartAccountAddress = metadata?.smartAccountAddress ?? sender;
|
|
601
|
+
if (!isNonEmptyString(resolvedSmartAccountAddress)) {
|
|
602
|
+
throw new Error("No connected account address found for smart account upgrade.");
|
|
603
|
+
}
|
|
604
|
+
const chainId = metadata?.upgradePayload?.authorization?.chainId;
|
|
605
|
+
if (!isValidChainId(chainId)) {
|
|
606
|
+
throw new Error("Invalid or missing chainId in upgrade metadata.");
|
|
607
|
+
}
|
|
608
|
+
if (!isRpcClient(walletClient)) {
|
|
609
|
+
throw new Error(
|
|
610
|
+
"Connected wallet client does not support request(). EIP-7702 upgrade requires a wallet client with raw RPC access."
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
const hexChainId = `0x${chainId.toString(16)}`;
|
|
614
|
+
console.info(
|
|
615
|
+
"[swype-sdk][upgrade] Step 1/2: Triggering EIP-7702 upgrade via wallet_sendCalls.",
|
|
616
|
+
{ from: resolvedSmartAccountAddress, chainId: hexChainId }
|
|
617
|
+
);
|
|
618
|
+
const upgradeBundleId = await sendCalls(
|
|
619
|
+
walletClient,
|
|
620
|
+
{
|
|
621
|
+
version: "2.0.0",
|
|
622
|
+
from: resolvedSmartAccountAddress,
|
|
623
|
+
chainId: hexChainId,
|
|
624
|
+
atomicRequired: false,
|
|
625
|
+
calls: [{ to: resolvedSmartAccountAddress, value: "0x0" }]
|
|
626
|
+
},
|
|
627
|
+
"EIP-7702 upgrade"
|
|
628
|
+
);
|
|
629
|
+
console.info("[swype-sdk][upgrade] Upgrade bundle submitted. Polling for confirmation\u2026", {
|
|
630
|
+
bundleId: upgradeBundleId
|
|
631
|
+
});
|
|
632
|
+
const upgradeTxHash = await pollCallsStatus(
|
|
633
|
+
walletClient,
|
|
634
|
+
upgradeBundleId,
|
|
635
|
+
"EIP-7702 upgrade"
|
|
636
|
+
);
|
|
637
|
+
console.info("[swype-sdk][upgrade] Step 1/2 complete: account upgraded.", {
|
|
638
|
+
txHash: upgradeTxHash
|
|
639
|
+
});
|
|
640
|
+
console.info(
|
|
641
|
+
"[swype-sdk][upgrade] Step 2/2: Registering passkey via wallet_sendCalls (addKey)."
|
|
642
|
+
);
|
|
643
|
+
const addKeyBundleId = await sendCalls(
|
|
644
|
+
walletClient,
|
|
645
|
+
{
|
|
646
|
+
version: "2.0.0",
|
|
647
|
+
from: resolvedSmartAccountAddress,
|
|
648
|
+
chainId: hexChainId,
|
|
649
|
+
atomicRequired: false,
|
|
650
|
+
calls: [
|
|
651
|
+
{
|
|
652
|
+
to: resolvedSmartAccountAddress,
|
|
653
|
+
value: "0x0",
|
|
654
|
+
data: addKeyCalldata
|
|
655
|
+
}
|
|
656
|
+
]
|
|
657
|
+
},
|
|
658
|
+
"Passkey registration (addKey)"
|
|
659
|
+
);
|
|
660
|
+
console.info("[swype-sdk][upgrade] addKey bundle submitted. Polling for confirmation\u2026", {
|
|
661
|
+
bundleId: addKeyBundleId
|
|
662
|
+
});
|
|
663
|
+
const addKeyTxHash = await pollCallsStatus(
|
|
664
|
+
walletClient,
|
|
665
|
+
addKeyBundleId,
|
|
666
|
+
"Passkey registration"
|
|
667
|
+
);
|
|
668
|
+
console.info("[swype-sdk][upgrade] Step 2/2 complete: passkey registered.", {
|
|
669
|
+
txHash: addKeyTxHash
|
|
670
|
+
});
|
|
671
|
+
return {
|
|
672
|
+
txHash: addKeyTxHash || upgradeTxHash,
|
|
673
|
+
smartAccountAddress: resolvedSmartAccountAddress
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/hooks.ts
|
|
678
|
+
async function waitForWalletClient(wagmiConfig2, maxAttempts = 15, intervalMs = 200) {
|
|
679
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
680
|
+
try {
|
|
681
|
+
return await getWalletClient(wagmiConfig2);
|
|
682
|
+
} catch {
|
|
683
|
+
if (i === maxAttempts - 1) {
|
|
684
|
+
throw new Error("Wallet not ready. Please try again.");
|
|
685
|
+
}
|
|
686
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
throw new Error("Wallet not ready. Please try again.");
|
|
690
|
+
}
|
|
691
|
+
function extractErrorMessage(err) {
|
|
692
|
+
if (err instanceof Error) return err.message;
|
|
693
|
+
if (typeof err === "string") return err;
|
|
694
|
+
if (err && typeof err === "object") {
|
|
695
|
+
const maybeMessage = err.message;
|
|
696
|
+
if (typeof maybeMessage === "string") return maybeMessage;
|
|
697
|
+
}
|
|
698
|
+
return "Failed to upgrade account";
|
|
699
|
+
}
|
|
700
|
+
function isMetaMaskConnector(account) {
|
|
701
|
+
const connectorName = account.connector?.name?.toLowerCase() ?? "";
|
|
702
|
+
const connectorId = account.connector?.id?.toLowerCase() ?? "";
|
|
703
|
+
return connectorName.includes("metamask") || connectorId.includes("metamask");
|
|
274
704
|
}
|
|
275
705
|
function useTransferPolling(intervalMs = 3e3) {
|
|
276
706
|
const { apiBaseUrl } = useSwypeConfig();
|
|
@@ -322,14 +752,13 @@ function useTransferPolling(intervalMs = 3e3) {
|
|
|
322
752
|
}
|
|
323
753
|
function useAuthorizationExecutor() {
|
|
324
754
|
const { apiBaseUrl } = useSwypeConfig();
|
|
325
|
-
const
|
|
755
|
+
const wagmiConfig2 = useConfig();
|
|
326
756
|
const { connectAsync, connectors } = useConnect();
|
|
327
757
|
const { switchChainAsync } = useSwitchChain();
|
|
328
|
-
const { signTypedDataAsync } = useSignTypedData();
|
|
329
|
-
const { data: walletClient } = useWalletClient();
|
|
330
758
|
const [executing, setExecuting] = useState(false);
|
|
331
759
|
const [results, setResults] = useState([]);
|
|
332
760
|
const [error, setError] = useState(null);
|
|
761
|
+
const [currentAction, setCurrentAction] = useState(null);
|
|
333
762
|
const executingRef = useRef(false);
|
|
334
763
|
const [pendingSelectSource, setPendingSelectSource] = useState(null);
|
|
335
764
|
const selectSourceResolverRef = useRef(null);
|
|
@@ -340,31 +769,28 @@ function useAuthorizationExecutor() {
|
|
|
340
769
|
setPendingSelectSource(null);
|
|
341
770
|
}
|
|
342
771
|
}, []);
|
|
343
|
-
const [pendingAllowanceSelection, setPendingAllowanceSelection] = useState(null);
|
|
344
|
-
const allowanceSelectionResolverRef = useRef(null);
|
|
345
772
|
const sessionIdRef = useRef(null);
|
|
346
|
-
const resolveAllowanceSelection = useCallback((selection) => {
|
|
347
|
-
if (allowanceSelectionResolverRef.current) {
|
|
348
|
-
allowanceSelectionResolverRef.current(selection);
|
|
349
|
-
allowanceSelectionResolverRef.current = null;
|
|
350
|
-
setPendingAllowanceSelection(null);
|
|
351
|
-
}
|
|
352
|
-
}, []);
|
|
353
773
|
const executeOpenProvider = useCallback(
|
|
354
774
|
async (action) => {
|
|
355
775
|
try {
|
|
356
|
-
|
|
357
|
-
|
|
776
|
+
const account = getAccount(wagmiConfig2);
|
|
777
|
+
if (account.isConnected && account.address) {
|
|
778
|
+
const hexChainId2 = account.chainId ? `0x${account.chainId.toString(16)}` : void 0;
|
|
358
779
|
return {
|
|
359
780
|
actionId: action.id,
|
|
360
781
|
type: action.type,
|
|
361
782
|
status: "success",
|
|
362
|
-
message: `Connected. Account: ${address}, Chain: ${hexChainId2}`,
|
|
363
|
-
data: { accounts: [address], chainId: hexChainId2 }
|
|
783
|
+
message: `Connected. Account: ${account.address}, Chain: ${hexChainId2}`,
|
|
784
|
+
data: { accounts: [account.address], chainId: hexChainId2 }
|
|
364
785
|
};
|
|
365
786
|
}
|
|
366
787
|
const targetId = action.metadata?.wagmiConnectorId;
|
|
367
|
-
const
|
|
788
|
+
const metaMaskConnector = connectors.find((c) => {
|
|
789
|
+
const id = c.id.toLowerCase();
|
|
790
|
+
const name = c.name.toLowerCase();
|
|
791
|
+
return id.includes("metamask") || name.includes("metamask");
|
|
792
|
+
});
|
|
793
|
+
const connector = targetId ? connectors.find((c) => c.id === targetId) ?? metaMaskConnector ?? connectors[0] : metaMaskConnector ?? connectors[0];
|
|
368
794
|
if (!connector) {
|
|
369
795
|
return {
|
|
370
796
|
actionId: action.id,
|
|
@@ -391,7 +817,7 @@ function useAuthorizationExecutor() {
|
|
|
391
817
|
};
|
|
392
818
|
}
|
|
393
819
|
},
|
|
394
|
-
[
|
|
820
|
+
[wagmiConfig2, connectors, connectAsync]
|
|
395
821
|
);
|
|
396
822
|
const executeSelectSource = useCallback(
|
|
397
823
|
async (action) => {
|
|
@@ -439,6 +865,7 @@ function useAuthorizationExecutor() {
|
|
|
439
865
|
const executeSwitchChain = useCallback(
|
|
440
866
|
async (action) => {
|
|
441
867
|
try {
|
|
868
|
+
const account = getAccount(wagmiConfig2);
|
|
442
869
|
const targetChainIdHex = action.metadata?.targetChainId;
|
|
443
870
|
if (!targetChainIdHex) {
|
|
444
871
|
return {
|
|
@@ -450,7 +877,7 @@ function useAuthorizationExecutor() {
|
|
|
450
877
|
}
|
|
451
878
|
const targetChainIdNum = parseInt(targetChainIdHex, 16);
|
|
452
879
|
const hexChainId = `0x${targetChainIdNum.toString(16)}`;
|
|
453
|
-
if (
|
|
880
|
+
if (account.chainId === targetChainIdNum) {
|
|
454
881
|
return {
|
|
455
882
|
actionId: action.id,
|
|
456
883
|
type: action.type,
|
|
@@ -476,237 +903,145 @@ function useAuthorizationExecutor() {
|
|
|
476
903
|
};
|
|
477
904
|
}
|
|
478
905
|
},
|
|
479
|
-
[
|
|
906
|
+
[wagmiConfig2, switchChainAsync]
|
|
480
907
|
);
|
|
481
|
-
const
|
|
908
|
+
const executeRegisterPasskey = useCallback(
|
|
482
909
|
async (action) => {
|
|
483
910
|
try {
|
|
484
|
-
const
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
911
|
+
const account = getAccount(wagmiConfig2);
|
|
912
|
+
const challenge = new Uint8Array(32);
|
|
913
|
+
crypto.getRandomValues(challenge);
|
|
914
|
+
const credential = await navigator.credentials.create({
|
|
915
|
+
publicKey: {
|
|
916
|
+
challenge,
|
|
917
|
+
rp: {
|
|
918
|
+
name: "Swype",
|
|
919
|
+
id: window.location.hostname
|
|
920
|
+
},
|
|
921
|
+
user: {
|
|
922
|
+
id: new TextEncoder().encode(account.address ?? "user"),
|
|
923
|
+
name: account.address ?? "Swype User",
|
|
924
|
+
displayName: "Swype User"
|
|
925
|
+
},
|
|
926
|
+
pubKeyCredParams: [
|
|
927
|
+
{ alg: -7, type: "public-key" },
|
|
928
|
+
// ES256 (P-256)
|
|
929
|
+
{ alg: -257, type: "public-key" }
|
|
930
|
+
// RS256
|
|
931
|
+
],
|
|
932
|
+
authenticatorSelection: {
|
|
933
|
+
authenticatorAttachment: "platform",
|
|
934
|
+
residentKey: "preferred",
|
|
935
|
+
userVerification: "required"
|
|
936
|
+
},
|
|
937
|
+
timeout: 6e4
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
if (!credential) {
|
|
496
941
|
return {
|
|
497
942
|
actionId: action.id,
|
|
498
943
|
type: action.type,
|
|
499
944
|
status: "error",
|
|
500
|
-
message: "
|
|
945
|
+
message: "Passkey creation was cancelled."
|
|
501
946
|
};
|
|
502
947
|
}
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
{
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
stateMutability: "nonpayable",
|
|
518
|
-
inputs: [
|
|
519
|
-
{ name: "spender", type: "address" },
|
|
520
|
-
{ name: "amount", type: "uint256" }
|
|
521
|
-
],
|
|
522
|
-
outputs: [{ name: "", type: "bool" }]
|
|
523
|
-
}
|
|
524
|
-
];
|
|
525
|
-
const targetChainIdNum = metadataChainId ? parseInt(metadataChainId, 16) : currentChainId;
|
|
526
|
-
const chainClient = targetChainIdNum ? getPublicClientForChain(targetChainIdNum) : void 0;
|
|
527
|
-
if (chainClient) {
|
|
528
|
-
try {
|
|
529
|
-
const currentAllowance = await chainClient.readContract({
|
|
530
|
-
address: tokenAddress,
|
|
531
|
-
abi: ERC20_ABI,
|
|
532
|
-
functionName: "allowance",
|
|
533
|
-
args: [address, permit2Address]
|
|
534
|
-
});
|
|
535
|
-
if (currentAllowance > 0n) {
|
|
536
|
-
return {
|
|
537
|
-
actionId: action.id,
|
|
538
|
-
type: action.type,
|
|
539
|
-
status: "success",
|
|
540
|
-
message: `Permit2 already approved (allowance: ${currentAllowance.toString()}). Skipped.`,
|
|
541
|
-
data: { skipped: true, existingAllowance: currentAllowance.toString() }
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
} catch {
|
|
948
|
+
const response = credential.response;
|
|
949
|
+
const publicKeyBytes = response.getPublicKey?.();
|
|
950
|
+
const credentialId = btoa(
|
|
951
|
+
String.fromCharCode(...new Uint8Array(credential.rawId))
|
|
952
|
+
);
|
|
953
|
+
const publicKey = publicKeyBytes ? btoa(String.fromCharCode(...new Uint8Array(publicKeyBytes))) : "";
|
|
954
|
+
return {
|
|
955
|
+
actionId: action.id,
|
|
956
|
+
type: action.type,
|
|
957
|
+
status: "success",
|
|
958
|
+
message: "Passkey created successfully.",
|
|
959
|
+
data: {
|
|
960
|
+
credentialId,
|
|
961
|
+
publicKey
|
|
545
962
|
}
|
|
963
|
+
};
|
|
964
|
+
} catch (err) {
|
|
965
|
+
return {
|
|
966
|
+
actionId: action.id,
|
|
967
|
+
type: action.type,
|
|
968
|
+
status: "error",
|
|
969
|
+
message: err instanceof Error ? err.message : "Failed to create passkey"
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
[wagmiConfig2]
|
|
974
|
+
);
|
|
975
|
+
const executeUpgradeSmartAccount = useCallback(
|
|
976
|
+
async (action) => {
|
|
977
|
+
try {
|
|
978
|
+
const account = getAccount(wagmiConfig2);
|
|
979
|
+
const walletClient = await waitForWalletClient(wagmiConfig2);
|
|
980
|
+
const sender = account.address ?? walletClient.account?.address;
|
|
981
|
+
if (!sender) {
|
|
982
|
+
throw new Error("Wallet account not available. Please connect your wallet.");
|
|
546
983
|
}
|
|
547
|
-
if (!
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
status: "error",
|
|
552
|
-
message: "Wallet client not available."
|
|
553
|
-
};
|
|
984
|
+
if (!isMetaMaskConnector(account)) {
|
|
985
|
+
throw new Error(
|
|
986
|
+
"EIP-7702 smart account upgrade requires a MetaMask-compatible wallet. Please reconnect using MetaMask and try again."
|
|
987
|
+
);
|
|
554
988
|
}
|
|
555
|
-
const
|
|
556
|
-
const txHash = await
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
args: [permit2Address, MAX_UINT256]
|
|
989
|
+
const metadata = action.metadata;
|
|
990
|
+
const { txHash, smartAccountAddress } = await submitUpgradeTransaction({
|
|
991
|
+
walletClient,
|
|
992
|
+
sender,
|
|
993
|
+
metadata
|
|
561
994
|
});
|
|
562
|
-
if (chainClient) {
|
|
563
|
-
await chainClient.waitForTransactionReceipt({ hash: txHash });
|
|
564
|
-
}
|
|
565
995
|
return {
|
|
566
996
|
actionId: action.id,
|
|
567
997
|
type: action.type,
|
|
568
998
|
status: "success",
|
|
569
|
-
message:
|
|
570
|
-
data: {
|
|
999
|
+
message: "Account upgrade transaction submitted.",
|
|
1000
|
+
data: {
|
|
1001
|
+
txHash,
|
|
1002
|
+
smartAccountAddress
|
|
1003
|
+
}
|
|
571
1004
|
};
|
|
572
1005
|
} catch (err) {
|
|
1006
|
+
console.error("Failed to upgrade account", err);
|
|
1007
|
+
const message = extractErrorMessage(err);
|
|
1008
|
+
const isRejected = message.includes("rejected") || message.includes("denied") || message.includes("user rejected");
|
|
573
1009
|
return {
|
|
574
1010
|
actionId: action.id,
|
|
575
1011
|
type: action.type,
|
|
576
1012
|
status: "error",
|
|
577
|
-
message:
|
|
1013
|
+
message: isRejected ? "You rejected the upgrade transaction. Please approve the transaction prompt in your wallet to continue." : message
|
|
578
1014
|
};
|
|
579
1015
|
}
|
|
580
1016
|
},
|
|
581
|
-
[
|
|
1017
|
+
[wagmiConfig2]
|
|
582
1018
|
);
|
|
583
|
-
const
|
|
1019
|
+
const executeGrantPermissions = useCallback(
|
|
584
1020
|
async (action) => {
|
|
585
1021
|
try {
|
|
586
|
-
const
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
type: "function",
|
|
591
|
-
stateMutability: "view",
|
|
592
|
-
inputs: [
|
|
593
|
-
{ name: "owner", type: "address" },
|
|
594
|
-
{ name: "token", type: "address" },
|
|
595
|
-
{ name: "spender", type: "address" }
|
|
596
|
-
],
|
|
597
|
-
outputs: [
|
|
598
|
-
{ name: "amount", type: "uint160" },
|
|
599
|
-
{ name: "expiration", type: "uint48" },
|
|
600
|
-
{ name: "nonce", type: "uint48" }
|
|
601
|
-
]
|
|
602
|
-
}
|
|
603
|
-
];
|
|
604
|
-
const spenderAddress = action.metadata?.spenderAddress;
|
|
605
|
-
const tokenAddress = action.metadata?.tokenAddress;
|
|
606
|
-
const metadataChainId = action.metadata?.chainId;
|
|
607
|
-
const metadataAmount = action.metadata?.amount;
|
|
608
|
-
if (!spenderAddress || !tokenAddress) {
|
|
1022
|
+
const walletClient = await waitForWalletClient(wagmiConfig2);
|
|
1023
|
+
const signingPayload = action.metadata?.signingPayload;
|
|
1024
|
+
const tokens = action.metadata?.tokens;
|
|
1025
|
+
if (!signingPayload) {
|
|
609
1026
|
return {
|
|
610
1027
|
actionId: action.id,
|
|
611
1028
|
type: action.type,
|
|
612
1029
|
status: "error",
|
|
613
|
-
message: "
|
|
1030
|
+
message: "No signing payload in action metadata."
|
|
614
1031
|
};
|
|
615
1032
|
}
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
let onChainNonce;
|
|
619
|
-
if (chainClient && address) {
|
|
620
|
-
try {
|
|
621
|
-
const [existingAmount, existingExpiration, chainNonce] = await chainClient.readContract({
|
|
622
|
-
address: PERMIT2_ADDRESS,
|
|
623
|
-
abi: PERMIT2_ALLOWANCE_ABI,
|
|
624
|
-
functionName: "allowance",
|
|
625
|
-
args: [address, tokenAddress, spenderAddress]
|
|
626
|
-
});
|
|
627
|
-
onChainNonce = chainNonce;
|
|
628
|
-
if (metadataAmount) {
|
|
629
|
-
const requiredSmallestUnit = BigInt(metadataAmount);
|
|
630
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
631
|
-
if (existingAmount >= requiredSmallestUnit && existingExpiration > now) {
|
|
632
|
-
return {
|
|
633
|
-
actionId: action.id,
|
|
634
|
-
type: action.type,
|
|
635
|
-
status: "success",
|
|
636
|
-
message: `Permit2 allowance already sufficient (${existingAmount.toString()}). Skipped.`,
|
|
637
|
-
data: { skipped: true, existingAllowance: existingAmount.toString() }
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
} catch {
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
const allowanceSelection = await new Promise((resolve) => {
|
|
645
|
-
allowanceSelectionResolverRef.current = resolve;
|
|
646
|
-
setPendingAllowanceSelection(action);
|
|
647
|
-
});
|
|
648
|
-
if (allowanceSelection.topUpAmount > 0 && sessionIdRef.current) {
|
|
649
|
-
try {
|
|
650
|
-
await updateUserConfigBySession(
|
|
651
|
-
apiBaseUrl,
|
|
652
|
-
sessionIdRef.current,
|
|
653
|
-
{ defaultAllowance: allowanceSelection.topUpAmount }
|
|
654
|
-
);
|
|
655
|
-
} catch {
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
const tokenDecimals = Number(action.metadata?.tokenDecimals ?? 6);
|
|
659
|
-
const topUpSmallestUnit = BigInt(
|
|
660
|
-
Math.round(allowanceSelection.topUpAmount * 10 ** tokenDecimals)
|
|
661
|
-
);
|
|
662
|
-
const baseAmount = metadataAmount ? BigInt(metadataAmount) : BigInt(0);
|
|
663
|
-
const permitAmount = baseAmount + topUpSmallestUnit;
|
|
664
|
-
const nonce = onChainNonce ?? Number(action.metadata?.nonce ?? 0);
|
|
665
|
-
const sigDeadline = BigInt(Math.floor(Date.now() / 1e3) + 3600);
|
|
666
|
-
const expiration = Math.floor(Date.now() / 1e3) + 30 * 24 * 60 * 60;
|
|
667
|
-
const signature = await signTypedDataAsync({
|
|
668
|
-
domain: {
|
|
669
|
-
name: "Permit2",
|
|
670
|
-
chainId: permit2ChainId,
|
|
671
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
672
|
-
},
|
|
673
|
-
types: {
|
|
674
|
-
PermitSingle: [
|
|
675
|
-
{ name: "details", type: "PermitDetails" },
|
|
676
|
-
{ name: "spender", type: "address" },
|
|
677
|
-
{ name: "sigDeadline", type: "uint256" }
|
|
678
|
-
],
|
|
679
|
-
PermitDetails: [
|
|
680
|
-
{ name: "token", type: "address" },
|
|
681
|
-
{ name: "amount", type: "uint160" },
|
|
682
|
-
{ name: "expiration", type: "uint48" },
|
|
683
|
-
{ name: "nonce", type: "uint48" }
|
|
684
|
-
]
|
|
685
|
-
},
|
|
686
|
-
primaryType: "PermitSingle",
|
|
687
|
-
message: {
|
|
688
|
-
details: {
|
|
689
|
-
token: tokenAddress,
|
|
690
|
-
amount: permitAmount,
|
|
691
|
-
expiration,
|
|
692
|
-
nonce
|
|
693
|
-
},
|
|
694
|
-
spender: spenderAddress,
|
|
695
|
-
sigDeadline
|
|
696
|
-
}
|
|
1033
|
+
const signature = await walletClient.signMessage({
|
|
1034
|
+
message: JSON.stringify(signingPayload)
|
|
697
1035
|
});
|
|
1036
|
+
const tokenSummary = tokens?.map((t) => `${t.symbol} on ${t.chainName}`).join(", ") ?? "all tokens";
|
|
698
1037
|
return {
|
|
699
1038
|
actionId: action.id,
|
|
700
1039
|
type: action.type,
|
|
701
1040
|
status: "success",
|
|
702
|
-
message:
|
|
1041
|
+
message: `Permissions granted for ${tokenSummary}.`,
|
|
703
1042
|
data: {
|
|
704
|
-
signature,
|
|
705
|
-
|
|
706
|
-
nonce: nonce.toString(),
|
|
707
|
-
sigDeadline: sigDeadline.toString(),
|
|
708
|
-
expiration: expiration.toString(),
|
|
709
|
-
amount: permitAmount.toString()
|
|
1043
|
+
signedDelegation: signature,
|
|
1044
|
+
tokens
|
|
710
1045
|
}
|
|
711
1046
|
};
|
|
712
1047
|
} catch (err) {
|
|
@@ -714,14 +1049,15 @@ function useAuthorizationExecutor() {
|
|
|
714
1049
|
actionId: action.id,
|
|
715
1050
|
type: action.type,
|
|
716
1051
|
status: "error",
|
|
717
|
-
message: err instanceof Error ? err.message : "Failed to
|
|
1052
|
+
message: err instanceof Error ? err.message : "Failed to grant permissions"
|
|
718
1053
|
};
|
|
719
1054
|
}
|
|
720
1055
|
},
|
|
721
|
-
[
|
|
1056
|
+
[wagmiConfig2]
|
|
722
1057
|
);
|
|
723
1058
|
const executeAction = useCallback(
|
|
724
1059
|
async (action) => {
|
|
1060
|
+
setCurrentAction(action);
|
|
725
1061
|
switch (action.type) {
|
|
726
1062
|
case "OPEN_PROVIDER":
|
|
727
1063
|
return executeOpenProvider(action);
|
|
@@ -729,10 +1065,12 @@ function useAuthorizationExecutor() {
|
|
|
729
1065
|
return executeSelectSource(action);
|
|
730
1066
|
case "SWITCH_CHAIN":
|
|
731
1067
|
return executeSwitchChain(action);
|
|
732
|
-
case "
|
|
733
|
-
return
|
|
734
|
-
case "
|
|
735
|
-
return
|
|
1068
|
+
case "REGISTER_PASSKEY":
|
|
1069
|
+
return executeRegisterPasskey(action);
|
|
1070
|
+
case "UPGRADE_SMART_ACCOUNT":
|
|
1071
|
+
return executeUpgradeSmartAccount(action);
|
|
1072
|
+
case "GRANT_PERMISSIONS":
|
|
1073
|
+
return executeGrantPermissions(action);
|
|
736
1074
|
default:
|
|
737
1075
|
return {
|
|
738
1076
|
actionId: action.id,
|
|
@@ -742,16 +1080,15 @@ function useAuthorizationExecutor() {
|
|
|
742
1080
|
};
|
|
743
1081
|
}
|
|
744
1082
|
},
|
|
745
|
-
[executeOpenProvider, executeSelectSource, executeSwitchChain,
|
|
1083
|
+
[executeOpenProvider, executeSelectSource, executeSwitchChain, executeRegisterPasskey, executeUpgradeSmartAccount, executeGrantPermissions]
|
|
746
1084
|
);
|
|
747
1085
|
const executeSession = useCallback(
|
|
748
1086
|
async (transfer) => {
|
|
749
1087
|
if (executingRef.current) return;
|
|
750
1088
|
executingRef.current = true;
|
|
751
1089
|
if (!transfer.authorizationSessions || transfer.authorizationSessions.length === 0) {
|
|
752
|
-
setError("No authorization sessions available.");
|
|
753
1090
|
executingRef.current = false;
|
|
754
|
-
|
|
1091
|
+
throw new Error("No authorization sessions available.");
|
|
755
1092
|
}
|
|
756
1093
|
const sessionId = transfer.authorizationSessions[0].id;
|
|
757
1094
|
sessionIdRef.current = sessionId;
|
|
@@ -773,8 +1110,7 @@ function useAuthorizationExecutor() {
|
|
|
773
1110
|
actionPollRetries++;
|
|
774
1111
|
}
|
|
775
1112
|
if (pendingActions.length === 0 && currentSession.status !== "AUTHORIZED") {
|
|
776
|
-
|
|
777
|
-
return;
|
|
1113
|
+
throw new Error("Authorization actions were not created in time. Please try again.");
|
|
778
1114
|
}
|
|
779
1115
|
while (pendingActions.length > 0) {
|
|
780
1116
|
const action = pendingActions[0];
|
|
@@ -783,8 +1119,7 @@ function useAuthorizationExecutor() {
|
|
|
783
1119
|
if (result.status === "error") {
|
|
784
1120
|
allResults.push(result);
|
|
785
1121
|
setResults([...allResults]);
|
|
786
|
-
|
|
787
|
-
break;
|
|
1122
|
+
throw new Error(result.message);
|
|
788
1123
|
}
|
|
789
1124
|
completedActionIds.add(action.id);
|
|
790
1125
|
const updatedSession = await reportActionCompletion(
|
|
@@ -796,7 +1131,6 @@ function useAuthorizationExecutor() {
|
|
|
796
1131
|
pendingActions = currentSession.actions.filter((a) => a.status === "PENDING" && !completedActionIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
|
|
797
1132
|
if (action.type === "OPEN_PROVIDER" && pendingActions.length > 0) {
|
|
798
1133
|
const chainResults = [result];
|
|
799
|
-
let chainBroken = false;
|
|
800
1134
|
while (pendingActions.length > 0) {
|
|
801
1135
|
const nextAction = pendingActions[0];
|
|
802
1136
|
const nextResult = await executeAction(nextAction);
|
|
@@ -804,9 +1138,7 @@ function useAuthorizationExecutor() {
|
|
|
804
1138
|
chainResults.push(nextResult);
|
|
805
1139
|
allResults.push(...chainResults);
|
|
806
1140
|
setResults([...allResults]);
|
|
807
|
-
|
|
808
|
-
chainBroken = true;
|
|
809
|
-
break;
|
|
1141
|
+
throw new Error(nextResult.message);
|
|
810
1142
|
}
|
|
811
1143
|
completedActionIds.add(nextAction.id);
|
|
812
1144
|
const nextSession = await reportActionCompletion(
|
|
@@ -818,7 +1150,6 @@ function useAuthorizationExecutor() {
|
|
|
818
1150
|
chainResults.push(nextResult);
|
|
819
1151
|
pendingActions = currentSession.actions.filter((a) => a.status === "PENDING" && !completedActionIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
|
|
820
1152
|
}
|
|
821
|
-
if (chainBroken) break;
|
|
822
1153
|
allResults.push(...chainResults);
|
|
823
1154
|
setResults([...allResults]);
|
|
824
1155
|
continue;
|
|
@@ -827,8 +1158,11 @@ function useAuthorizationExecutor() {
|
|
|
827
1158
|
setResults([...allResults]);
|
|
828
1159
|
}
|
|
829
1160
|
} catch (err) {
|
|
830
|
-
|
|
1161
|
+
const msg = err instanceof Error ? err.message : "Authorization failed";
|
|
1162
|
+
setError(msg);
|
|
1163
|
+
throw err;
|
|
831
1164
|
} finally {
|
|
1165
|
+
setCurrentAction(null);
|
|
832
1166
|
setExecuting(false);
|
|
833
1167
|
executingRef.current = false;
|
|
834
1168
|
}
|
|
@@ -839,13 +1173,99 @@ function useAuthorizationExecutor() {
|
|
|
839
1173
|
executing,
|
|
840
1174
|
results,
|
|
841
1175
|
error,
|
|
1176
|
+
currentAction,
|
|
842
1177
|
pendingSelectSource,
|
|
843
1178
|
resolveSelectSource,
|
|
844
|
-
pendingAllowanceSelection,
|
|
845
|
-
resolveAllowanceSelection,
|
|
846
1179
|
executeSession
|
|
847
1180
|
};
|
|
848
1181
|
}
|
|
1182
|
+
function useTransferSigning(pollIntervalMs = 2e3) {
|
|
1183
|
+
const { apiBaseUrl } = useSwypeConfig();
|
|
1184
|
+
const { getAccessToken } = usePrivy();
|
|
1185
|
+
const [signing, setSigning] = useState(false);
|
|
1186
|
+
const [signPayload, setSignPayload] = useState(null);
|
|
1187
|
+
const [error, setError] = useState(null);
|
|
1188
|
+
const signTransfer2 = useCallback(
|
|
1189
|
+
async (transferId) => {
|
|
1190
|
+
setSigning(true);
|
|
1191
|
+
setError(null);
|
|
1192
|
+
setSignPayload(null);
|
|
1193
|
+
try {
|
|
1194
|
+
const token = await getAccessToken();
|
|
1195
|
+
if (!token) {
|
|
1196
|
+
throw new Error("Could not get access token");
|
|
1197
|
+
}
|
|
1198
|
+
const MAX_POLLS = 60;
|
|
1199
|
+
let payload = null;
|
|
1200
|
+
for (let i = 0; i < MAX_POLLS; i++) {
|
|
1201
|
+
const transfer = await fetchTransfer(apiBaseUrl, token, transferId);
|
|
1202
|
+
if (transfer.signPayload) {
|
|
1203
|
+
payload = transfer.signPayload;
|
|
1204
|
+
setSignPayload(payload);
|
|
1205
|
+
break;
|
|
1206
|
+
}
|
|
1207
|
+
if (transfer.status !== "AUTHORIZED" && transfer.status !== "CREATED") {
|
|
1208
|
+
throw new Error(`Unexpected transfer status: ${transfer.status}`);
|
|
1209
|
+
}
|
|
1210
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
1211
|
+
}
|
|
1212
|
+
if (!payload) {
|
|
1213
|
+
throw new Error("Timed out waiting for sign payload. Please try again.");
|
|
1214
|
+
}
|
|
1215
|
+
const userOpHashHex = payload.userOpHash;
|
|
1216
|
+
const hashBytes = new Uint8Array(
|
|
1217
|
+
(userOpHashHex.startsWith("0x") ? userOpHashHex.slice(2) : userOpHashHex).match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
|
1218
|
+
);
|
|
1219
|
+
const assertion = await navigator.credentials.get({
|
|
1220
|
+
publicKey: {
|
|
1221
|
+
challenge: hashBytes,
|
|
1222
|
+
rpId: window.location.hostname,
|
|
1223
|
+
userVerification: "required",
|
|
1224
|
+
timeout: 6e4
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
if (!assertion) {
|
|
1228
|
+
throw new Error("Passkey authentication was cancelled.");
|
|
1229
|
+
}
|
|
1230
|
+
const response = assertion.response;
|
|
1231
|
+
const signature = btoa(
|
|
1232
|
+
String.fromCharCode(...new Uint8Array(response.signature))
|
|
1233
|
+
);
|
|
1234
|
+
const authenticatorData = btoa(
|
|
1235
|
+
String.fromCharCode(
|
|
1236
|
+
...new Uint8Array(response.authenticatorData)
|
|
1237
|
+
)
|
|
1238
|
+
);
|
|
1239
|
+
const clientDataJSON = btoa(
|
|
1240
|
+
String.fromCharCode(
|
|
1241
|
+
...new Uint8Array(response.clientDataJSON)
|
|
1242
|
+
)
|
|
1243
|
+
);
|
|
1244
|
+
const signedUserOp = {
|
|
1245
|
+
...payload.userOp,
|
|
1246
|
+
signature,
|
|
1247
|
+
authenticatorData,
|
|
1248
|
+
clientDataJSON
|
|
1249
|
+
};
|
|
1250
|
+
const updatedTransfer = await signTransfer(
|
|
1251
|
+
apiBaseUrl,
|
|
1252
|
+
token,
|
|
1253
|
+
transferId,
|
|
1254
|
+
signedUserOp
|
|
1255
|
+
);
|
|
1256
|
+
return updatedTransfer;
|
|
1257
|
+
} catch (err) {
|
|
1258
|
+
const msg = err instanceof Error ? err.message : "Failed to sign transfer";
|
|
1259
|
+
setError(msg);
|
|
1260
|
+
throw err;
|
|
1261
|
+
} finally {
|
|
1262
|
+
setSigning(false);
|
|
1263
|
+
}
|
|
1264
|
+
},
|
|
1265
|
+
[apiBaseUrl, getAccessToken, pollIntervalMs]
|
|
1266
|
+
);
|
|
1267
|
+
return { signing, signPayload, error, signTransfer: signTransfer2 };
|
|
1268
|
+
}
|
|
849
1269
|
function Spinner({ size = 40, label }) {
|
|
850
1270
|
const { tokens } = useSwypeConfig();
|
|
851
1271
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1577,204 +1997,6 @@ function AdvancedSettings({
|
|
|
1577
1997
|
)
|
|
1578
1998
|
] });
|
|
1579
1999
|
}
|
|
1580
|
-
var TOP_UP_OPTIONS = [0, 25, 50, 100, 500];
|
|
1581
|
-
function AllowanceSelector({ action, onSelect }) {
|
|
1582
|
-
const { tokens } = useSwypeConfig();
|
|
1583
|
-
const metadataAmount = action.metadata?.amount;
|
|
1584
|
-
const tokenDecimals = Number(action.metadata?.tokenDecimals ?? 6);
|
|
1585
|
-
const currency = action.metadata?.currency ?? "USD";
|
|
1586
|
-
const transferAmountRaw = metadataAmount ? Number(BigInt(metadataAmount)) / 10 ** tokenDecimals : 0;
|
|
1587
|
-
const [selectedTopUp, setSelectedTopUp] = useState(0);
|
|
1588
|
-
const totalAllowance = transferAmountRaw + selectedTopUp;
|
|
1589
|
-
const formatAmount = (amount) => `$${amount.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
1590
|
-
return /* @__PURE__ */ jsxs(
|
|
1591
|
-
"div",
|
|
1592
|
-
{
|
|
1593
|
-
style: {
|
|
1594
|
-
padding: "4px 0",
|
|
1595
|
-
textAlign: "left"
|
|
1596
|
-
},
|
|
1597
|
-
children: [
|
|
1598
|
-
/* @__PURE__ */ jsx(
|
|
1599
|
-
"h3",
|
|
1600
|
-
{
|
|
1601
|
-
style: {
|
|
1602
|
-
fontSize: "1rem",
|
|
1603
|
-
fontWeight: 600,
|
|
1604
|
-
color: tokens.text,
|
|
1605
|
-
margin: "0 0 6px 0",
|
|
1606
|
-
textAlign: "center"
|
|
1607
|
-
},
|
|
1608
|
-
children: "Set spending limit"
|
|
1609
|
-
}
|
|
1610
|
-
),
|
|
1611
|
-
/* @__PURE__ */ jsx(
|
|
1612
|
-
"p",
|
|
1613
|
-
{
|
|
1614
|
-
style: {
|
|
1615
|
-
fontSize: "0.8rem",
|
|
1616
|
-
color: tokens.textMuted,
|
|
1617
|
-
margin: "0 0 16px 0",
|
|
1618
|
-
lineHeight: 1.5,
|
|
1619
|
-
textAlign: "center"
|
|
1620
|
-
},
|
|
1621
|
-
children: "Pre-authorize a higher amount so future payments go through instantly without wallet prompts."
|
|
1622
|
-
}
|
|
1623
|
-
),
|
|
1624
|
-
/* @__PURE__ */ jsxs(
|
|
1625
|
-
"div",
|
|
1626
|
-
{
|
|
1627
|
-
style: {
|
|
1628
|
-
display: "flex",
|
|
1629
|
-
justifyContent: "space-between",
|
|
1630
|
-
alignItems: "center",
|
|
1631
|
-
padding: "10px 14px",
|
|
1632
|
-
background: tokens.bgInput,
|
|
1633
|
-
borderRadius: tokens.radius,
|
|
1634
|
-
marginBottom: "12px",
|
|
1635
|
-
fontSize: "0.825rem"
|
|
1636
|
-
},
|
|
1637
|
-
children: [
|
|
1638
|
-
/* @__PURE__ */ jsx("span", { style: { color: tokens.textSecondary }, children: "This payment" }),
|
|
1639
|
-
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 600, color: tokens.text }, children: [
|
|
1640
|
-
formatAmount(transferAmountRaw),
|
|
1641
|
-
" ",
|
|
1642
|
-
currency
|
|
1643
|
-
] })
|
|
1644
|
-
]
|
|
1645
|
-
}
|
|
1646
|
-
),
|
|
1647
|
-
/* @__PURE__ */ jsx(
|
|
1648
|
-
"div",
|
|
1649
|
-
{
|
|
1650
|
-
style: {
|
|
1651
|
-
display: "flex",
|
|
1652
|
-
flexDirection: "column",
|
|
1653
|
-
gap: "6px",
|
|
1654
|
-
marginBottom: "16px"
|
|
1655
|
-
},
|
|
1656
|
-
children: TOP_UP_OPTIONS.map((topUp) => {
|
|
1657
|
-
const isSelected = selectedTopUp === topUp;
|
|
1658
|
-
const total = transferAmountRaw + topUp;
|
|
1659
|
-
const label = topUp === 0 ? "Just this payment" : `+${formatAmount(topUp)} for future payments`;
|
|
1660
|
-
return /* @__PURE__ */ jsxs(
|
|
1661
|
-
"button",
|
|
1662
|
-
{
|
|
1663
|
-
onClick: () => setSelectedTopUp(topUp),
|
|
1664
|
-
style: {
|
|
1665
|
-
display: "flex",
|
|
1666
|
-
alignItems: "center",
|
|
1667
|
-
justifyContent: "space-between",
|
|
1668
|
-
width: "100%",
|
|
1669
|
-
padding: "12px 14px",
|
|
1670
|
-
background: isSelected ? tokens.accent + "14" : "transparent",
|
|
1671
|
-
border: `1.5px solid ${isSelected ? tokens.accent : tokens.border}`,
|
|
1672
|
-
borderRadius: tokens.radius,
|
|
1673
|
-
cursor: "pointer",
|
|
1674
|
-
color: tokens.text,
|
|
1675
|
-
fontFamily: "inherit",
|
|
1676
|
-
fontSize: "0.825rem",
|
|
1677
|
-
textAlign: "left",
|
|
1678
|
-
outline: "none",
|
|
1679
|
-
transition: "all 0.12s ease"
|
|
1680
|
-
},
|
|
1681
|
-
children: [
|
|
1682
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
1683
|
-
/* @__PURE__ */ jsx(
|
|
1684
|
-
"div",
|
|
1685
|
-
{
|
|
1686
|
-
style: {
|
|
1687
|
-
width: 16,
|
|
1688
|
-
height: 16,
|
|
1689
|
-
borderRadius: "50%",
|
|
1690
|
-
border: `2px solid ${isSelected ? tokens.accent : tokens.border}`,
|
|
1691
|
-
display: "flex",
|
|
1692
|
-
alignItems: "center",
|
|
1693
|
-
justifyContent: "center",
|
|
1694
|
-
flexShrink: 0
|
|
1695
|
-
},
|
|
1696
|
-
children: isSelected && /* @__PURE__ */ jsx(
|
|
1697
|
-
"div",
|
|
1698
|
-
{
|
|
1699
|
-
style: {
|
|
1700
|
-
width: 8,
|
|
1701
|
-
height: 8,
|
|
1702
|
-
borderRadius: "50%",
|
|
1703
|
-
background: tokens.accent
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
)
|
|
1707
|
-
}
|
|
1708
|
-
),
|
|
1709
|
-
/* @__PURE__ */ jsx("span", { style: { fontWeight: isSelected ? 600 : 400 }, children: label })
|
|
1710
|
-
] }),
|
|
1711
|
-
/* @__PURE__ */ jsx(
|
|
1712
|
-
"span",
|
|
1713
|
-
{
|
|
1714
|
-
style: {
|
|
1715
|
-
fontWeight: 600,
|
|
1716
|
-
color: isSelected ? tokens.accent : tokens.textMuted,
|
|
1717
|
-
fontSize: "0.8rem",
|
|
1718
|
-
flexShrink: 0,
|
|
1719
|
-
marginLeft: "8px"
|
|
1720
|
-
},
|
|
1721
|
-
children: formatAmount(total)
|
|
1722
|
-
}
|
|
1723
|
-
)
|
|
1724
|
-
]
|
|
1725
|
-
},
|
|
1726
|
-
topUp
|
|
1727
|
-
);
|
|
1728
|
-
})
|
|
1729
|
-
}
|
|
1730
|
-
),
|
|
1731
|
-
/* @__PURE__ */ jsxs(
|
|
1732
|
-
"div",
|
|
1733
|
-
{
|
|
1734
|
-
style: {
|
|
1735
|
-
display: "flex",
|
|
1736
|
-
justifyContent: "space-between",
|
|
1737
|
-
alignItems: "center",
|
|
1738
|
-
padding: "10px 14px",
|
|
1739
|
-
background: tokens.bgInput,
|
|
1740
|
-
borderRadius: tokens.radius,
|
|
1741
|
-
marginBottom: "14px",
|
|
1742
|
-
fontSize: "0.825rem"
|
|
1743
|
-
},
|
|
1744
|
-
children: [
|
|
1745
|
-
/* @__PURE__ */ jsx("span", { style: { color: tokens.textSecondary }, children: "Total authorization" }),
|
|
1746
|
-
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 700, color: tokens.text, fontSize: "0.9rem" }, children: [
|
|
1747
|
-
formatAmount(totalAllowance),
|
|
1748
|
-
" ",
|
|
1749
|
-
currency
|
|
1750
|
-
] })
|
|
1751
|
-
]
|
|
1752
|
-
}
|
|
1753
|
-
),
|
|
1754
|
-
/* @__PURE__ */ jsx(
|
|
1755
|
-
"button",
|
|
1756
|
-
{
|
|
1757
|
-
onClick: () => onSelect({ topUpAmount: selectedTopUp }),
|
|
1758
|
-
style: {
|
|
1759
|
-
width: "100%",
|
|
1760
|
-
padding: "14px",
|
|
1761
|
-
background: tokens.accent,
|
|
1762
|
-
color: tokens.accentText,
|
|
1763
|
-
border: "none",
|
|
1764
|
-
borderRadius: tokens.radius,
|
|
1765
|
-
fontSize: "1rem",
|
|
1766
|
-
fontWeight: 600,
|
|
1767
|
-
cursor: "pointer",
|
|
1768
|
-
transition: "background 0.15s ease",
|
|
1769
|
-
fontFamily: "inherit"
|
|
1770
|
-
},
|
|
1771
|
-
children: "Authorize & Sign"
|
|
1772
|
-
}
|
|
1773
|
-
)
|
|
1774
|
-
]
|
|
1775
|
-
}
|
|
1776
|
-
);
|
|
1777
|
-
}
|
|
1778
2000
|
function isMobile() {
|
|
1779
2001
|
if (typeof navigator === "undefined") return false;
|
|
1780
2002
|
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
@@ -1785,11 +2007,11 @@ function computeSmartDefaults(accts, transferAmount) {
|
|
|
1785
2007
|
if (accts.length === 0) return null;
|
|
1786
2008
|
for (const acct of accts) {
|
|
1787
2009
|
for (const wallet of acct.wallets) {
|
|
1788
|
-
if (wallet.status === "ACTIVE"
|
|
1789
|
-
const
|
|
1790
|
-
(s) => s.
|
|
2010
|
+
if (wallet.status === "ACTIVE") {
|
|
2011
|
+
const bestSource = wallet.sources.find(
|
|
2012
|
+
(s) => s.balance.available.amount >= transferAmount
|
|
1791
2013
|
);
|
|
1792
|
-
if (
|
|
2014
|
+
if (bestSource) {
|
|
1793
2015
|
return { accountId: acct.id, walletId: wallet.id };
|
|
1794
2016
|
}
|
|
1795
2017
|
}
|
|
@@ -1846,6 +2068,7 @@ function SwypePayment({
|
|
|
1846
2068
|
const pollingTransferIdRef = useRef(null);
|
|
1847
2069
|
const authExecutor = useAuthorizationExecutor();
|
|
1848
2070
|
const polling = useTransferPolling();
|
|
2071
|
+
const transferSigning = useTransferSigning();
|
|
1849
2072
|
const sourceType = connectingNewAccount ? "providerId" : selectedWalletId ? "walletId" : selectedAccountId ? "accountId" : "providerId";
|
|
1850
2073
|
const sourceId = connectingNewAccount ? selectedProviderId ?? "" : selectedWalletId ? selectedWalletId : selectedAccountId ? selectedAccountId : selectedProviderId ?? "";
|
|
1851
2074
|
useEffect(() => {
|
|
@@ -2025,9 +2248,11 @@ function SwypePayment({
|
|
|
2025
2248
|
await authExecutor.executeSession(t);
|
|
2026
2249
|
}
|
|
2027
2250
|
}
|
|
2251
|
+
const signedTransfer = await transferSigning.signTransfer(t.id);
|
|
2252
|
+
setTransfer(signedTransfer);
|
|
2028
2253
|
polling.startPolling(t.id);
|
|
2029
2254
|
} catch (err) {
|
|
2030
|
-
const msg = err instanceof Error ? err.message : "Transfer
|
|
2255
|
+
const msg = err instanceof Error ? err.message : "Transfer failed";
|
|
2031
2256
|
setError(msg);
|
|
2032
2257
|
onError?.(msg);
|
|
2033
2258
|
setStep("ready");
|
|
@@ -2042,6 +2267,7 @@ function SwypePayment({
|
|
|
2042
2267
|
apiBaseUrl,
|
|
2043
2268
|
getAccessToken,
|
|
2044
2269
|
authExecutor,
|
|
2270
|
+
transferSigning,
|
|
2045
2271
|
polling,
|
|
2046
2272
|
onError
|
|
2047
2273
|
]);
|
|
@@ -2510,17 +2736,68 @@ function SwypePayment({
|
|
|
2510
2736
|
] });
|
|
2511
2737
|
}
|
|
2512
2738
|
if (step === "processing") {
|
|
2513
|
-
if (
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
{
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
}
|
|
2520
|
-
|
|
2739
|
+
if (transferSigning.signing && transferSigning.signPayload) {
|
|
2740
|
+
const payload = transferSigning.signPayload;
|
|
2741
|
+
return /* @__PURE__ */ jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "16px 0" }, children: [
|
|
2742
|
+
/* @__PURE__ */ jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", fill: "none", style: { margin: "0 auto 16px" }, children: [
|
|
2743
|
+
/* @__PURE__ */ jsx("rect", { width: "48", height: "48", rx: "12", fill: tokens.accent + "20" }),
|
|
2744
|
+
/* @__PURE__ */ jsx("path", { d: "M24 14v8M20 18h8M24 26v2M24 32v2", stroke: tokens.accent, strokeWidth: "2", strokeLinecap: "round" })
|
|
2745
|
+
] }),
|
|
2746
|
+
/* @__PURE__ */ jsx("h2", { style: { ...headingStyle, marginBottom: "8px" }, children: "Authorize Transfer" }),
|
|
2747
|
+
/* @__PURE__ */ jsx("p", { style: { fontSize: "0.85rem", color: tokens.textSecondary, margin: "0 0 16px 0", lineHeight: 1.5 }, children: "Use your passkey to confirm this payment." }),
|
|
2748
|
+
/* @__PURE__ */ jsxs("div", { style: { fontSize: "0.825rem", color: tokens.textSecondary, padding: "12px 14px", background: tokens.bgInput, borderRadius: tokens.radius, textAlign: "left", lineHeight: 1.7, marginBottom: "16px" }, children: [
|
|
2749
|
+
payload.amount && payload.tokenSymbol && /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
|
|
2750
|
+
/* @__PURE__ */ jsx("span", { children: "Amount" }),
|
|
2751
|
+
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 600, color: tokens.text }, children: [
|
|
2752
|
+
payload.amount,
|
|
2753
|
+
" ",
|
|
2754
|
+
payload.tokenSymbol
|
|
2755
|
+
] })
|
|
2756
|
+
] }),
|
|
2757
|
+
payload.bridgeRelayAddress && /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
|
|
2758
|
+
/* @__PURE__ */ jsx("span", { children: "Bridge relay" }),
|
|
2759
|
+
/* @__PURE__ */ jsxs("span", { style: { fontFamily: '"SF Mono", "Fira Code", monospace', fontSize: "0.75rem" }, children: [
|
|
2760
|
+
payload.bridgeRelayAddress.slice(0, 6),
|
|
2761
|
+
"...",
|
|
2762
|
+
payload.bridgeRelayAddress.slice(-4)
|
|
2763
|
+
] })
|
|
2764
|
+
] }),
|
|
2765
|
+
payload.estimatedFeeUsd && /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
|
|
2766
|
+
/* @__PURE__ */ jsx("span", { children: "Est. fee" }),
|
|
2767
|
+
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 600 }, children: [
|
|
2768
|
+
"$",
|
|
2769
|
+
payload.estimatedFeeUsd
|
|
2770
|
+
] })
|
|
2771
|
+
] })
|
|
2772
|
+
] }),
|
|
2773
|
+
/* @__PURE__ */ jsx(Spinner, { label: "Waiting for passkey..." })
|
|
2774
|
+
] }) });
|
|
2521
2775
|
}
|
|
2522
|
-
const
|
|
2523
|
-
const
|
|
2776
|
+
const currentActionType = authExecutor.currentAction?.type;
|
|
2777
|
+
const getRegistrationMessage = () => {
|
|
2778
|
+
switch (currentActionType) {
|
|
2779
|
+
case "REGISTER_PASSKEY":
|
|
2780
|
+
return {
|
|
2781
|
+
label: "Creating your passkey...",
|
|
2782
|
+
description: "Set up a passkey for secure, one-touch payments."
|
|
2783
|
+
};
|
|
2784
|
+
case "UPGRADE_SMART_ACCOUNT":
|
|
2785
|
+
return {
|
|
2786
|
+
label: "Upgrading your account...",
|
|
2787
|
+
description: "Approve the prompts in MetaMask to upgrade your account to a smart account and register your passkey. You may see two prompts."
|
|
2788
|
+
};
|
|
2789
|
+
case "GRANT_PERMISSIONS":
|
|
2790
|
+
return {
|
|
2791
|
+
label: "Setting up permissions...",
|
|
2792
|
+
description: "Signing delegation permissions. Transfer routing and gas sponsorship are handled by Swype backend."
|
|
2793
|
+
};
|
|
2794
|
+
default:
|
|
2795
|
+
return { label: "", description: "" };
|
|
2796
|
+
}
|
|
2797
|
+
};
|
|
2798
|
+
const regMsg = getRegistrationMessage();
|
|
2799
|
+
const statusLabel = creatingTransfer ? "Creating transfer..." : mobileFlow ? "Waiting for authorization..." : authExecutor.executing && regMsg.label ? regMsg.label : authExecutor.executing ? "Authorizing..." : transferSigning.signing ? "Preparing transfer..." : polling.isPolling ? "Processing payment..." : "Please wait...";
|
|
2800
|
+
const statusDescription = creatingTransfer ? "Setting up your transfer..." : mobileFlow ? "Complete the authorization in your wallet app, then return here." : authExecutor.executing && regMsg.description ? regMsg.description : authExecutor.executing ? "Complete the wallet prompts to authorize this payment." : transferSigning.signing ? "Waiting for backend to prepare your transfer payload..." : polling.isPolling ? "Your payment is being processed. This usually takes a few moments." : "Hang tight...";
|
|
2524
2801
|
return /* @__PURE__ */ jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "16px 0" }, children: [
|
|
2525
2802
|
/* @__PURE__ */ jsx(Spinner, { size: 48 }),
|
|
2526
2803
|
/* @__PURE__ */ jsx(
|
|
@@ -2564,11 +2841,11 @@ function SwypePayment({
|
|
|
2564
2841
|
},
|
|
2565
2842
|
r.actionId
|
|
2566
2843
|
)) }),
|
|
2567
|
-
(error || authExecutor.error || polling.error) && /* @__PURE__ */ jsx(
|
|
2844
|
+
(error || authExecutor.error || transferSigning.error || polling.error) && /* @__PURE__ */ jsx(
|
|
2568
2845
|
"div",
|
|
2569
2846
|
{
|
|
2570
2847
|
style: { ...errorStyle, marginTop: "16px", textAlign: "left" },
|
|
2571
|
-
children: error || authExecutor.error || polling.error
|
|
2848
|
+
children: error || authExecutor.error || transferSigning.error || polling.error
|
|
2572
2849
|
}
|
|
2573
2850
|
)
|
|
2574
2851
|
] }) });
|