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