@naculus/connect-appkit-core 0.7.0 → 0.8.0
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/README.md +13 -0
- package/dist/index.cjs +141 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -6
- package/dist/index.d.ts +79 -6
- package/dist/index.js +131 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# `@naculus/connect-appkit-core`
|
|
2
|
+
|
|
3
|
+
## Authorization and settlement views
|
|
4
|
+
|
|
5
|
+
`describeAuthorization` builds network-free consent text from a connect-core
|
|
6
|
+
`Authorization` or `ListedAuthorization`. Pass asset metadata explicitly to
|
|
7
|
+
show symbols and decimal amounts; without it, the view deliberately shows the
|
|
8
|
+
asset address and base units. `explainSpend` turns every evaluator refusal into
|
|
9
|
+
UI text without changing the authorization decision.
|
|
10
|
+
|
|
11
|
+
`describePayment` marks every server-reported settlement as `unverified`.
|
|
12
|
+
`applySettlementVerification` is the only helper that changes that state, and
|
|
13
|
+
the settlement label becomes verified only for a `verified` result.
|
package/dist/index.cjs
CHANGED
|
@@ -21,10 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
DEFAULT_NOTIFICATION_SETTINGS: () => DEFAULT_NOTIFICATION_SETTINGS,
|
|
24
|
+
DEFAULT_VERIFY_RETRY: () => DEFAULT_VERIFY_RETRY,
|
|
24
25
|
InvalidUserOperationReceiptError: () => InvalidUserOperationReceiptError,
|
|
25
26
|
NOTIFICATION_SETTINGS_KEY: () => NOTIFICATION_SETTINGS_KEY,
|
|
26
27
|
__resetResolverForTests: () => __resetResolverForTests,
|
|
27
28
|
addInAppNotification: () => addInAppNotification,
|
|
29
|
+
applySettlementVerification: () => applySettlementVerification,
|
|
28
30
|
assertHexSignature: () => assertHexSignature,
|
|
29
31
|
atomicSupportFor: () => atomicSupportFor,
|
|
30
32
|
bareEvmAddress: () => bareEvmAddress,
|
|
@@ -45,13 +47,16 @@ __export(src_exports, {
|
|
|
45
47
|
decodeRevertReason: () => decodeRevertReason,
|
|
46
48
|
defaultSiwxDomain: () => defaultSiwxDomain,
|
|
47
49
|
defaultSiwxUri: () => defaultSiwxUri,
|
|
50
|
+
describeAuthorization: () => describeAuthorization,
|
|
48
51
|
describeChain: () => describeChain,
|
|
49
52
|
describePayment: () => describePayment,
|
|
50
53
|
eip155ChainIdToNumber: () => eip155ChainIdToNumber,
|
|
51
54
|
encodeErc20Approve: () => encodeErc20Approve,
|
|
52
55
|
encodeErc20Transfer: () => encodeErc20Transfer,
|
|
53
56
|
encodeErc20TransferFrom: () => encodeErc20TransferFrom,
|
|
57
|
+
explainSpend: () => explainSpend,
|
|
54
58
|
fetchUserOperationReceipt: () => fetchUserOperationReceipt,
|
|
59
|
+
formatAuthorizationAmount: () => formatAuthorizationAmount,
|
|
55
60
|
getResolver: () => getResolver,
|
|
56
61
|
getSharedSessionKeyManager: () => getSharedSessionKeyManager,
|
|
57
62
|
initialWeb3State: () => initialWeb3State,
|
|
@@ -1374,7 +1379,125 @@ function createDelegationPolicyFlow(deps) {
|
|
|
1374
1379
|
};
|
|
1375
1380
|
}
|
|
1376
1381
|
|
|
1382
|
+
// src/authorization.ts
|
|
1383
|
+
var import_connect_core17 = require("@naculus/connect-core");
|
|
1384
|
+
function assetAddress(asset) {
|
|
1385
|
+
return asset.split(/[:/]/).at(-1);
|
|
1386
|
+
}
|
|
1387
|
+
function groupDigits(value) {
|
|
1388
|
+
return value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
1389
|
+
}
|
|
1390
|
+
function formatAuthorizationAmount(amount, metadata) {
|
|
1391
|
+
const baseUnits = amount.toString();
|
|
1392
|
+
if (!metadata || !metadata.symbol || !Number.isSafeInteger(metadata.decimals) || metadata.decimals < 0) {
|
|
1393
|
+
return {
|
|
1394
|
+
formatted: `${groupDigits(baseUnits)} base units`
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
const decimals = metadata.decimals;
|
|
1398
|
+
const padded = baseUnits.padStart(decimals + 1, "0");
|
|
1399
|
+
const integer = decimals === 0 ? padded : padded.slice(0, -decimals);
|
|
1400
|
+
const fraction = decimals === 0 ? "" : padded.slice(-decimals).replace(/0+$/, "");
|
|
1401
|
+
const value = `${groupDigits(integer)}${fraction ? `.${fraction}` : ""}`;
|
|
1402
|
+
return { formatted: `${value} ${metadata.symbol}` };
|
|
1403
|
+
}
|
|
1404
|
+
function relativeExpiry(expiresAt, now, locale) {
|
|
1405
|
+
const seconds = expiresAt - now;
|
|
1406
|
+
const absolute = Math.abs(seconds);
|
|
1407
|
+
const divisor = absolute >= 31536e3 ? 31536e3 : absolute >= 2592e3 ? 2592e3 : absolute >= 86400 ? 86400 : absolute >= 3600 ? 3600 : absolute >= 60 ? 60 : 1;
|
|
1408
|
+
const unit = divisor === 31536e3 ? "year" : divisor === 2592e3 ? "month" : divisor === 86400 ? "day" : divisor === 3600 ? "hour" : divisor === 60 ? "minute" : "second";
|
|
1409
|
+
return new Intl.RelativeTimeFormat(locale).format(
|
|
1410
|
+
Math.round(seconds / divisor),
|
|
1411
|
+
unit
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
function describeAuthorization(input, options = {}) {
|
|
1415
|
+
const now = options.now ?? Math.floor(Date.now() / 1e3);
|
|
1416
|
+
const locale = options.locale ?? "en";
|
|
1417
|
+
const flags = "flags" in input ? input.flags : [];
|
|
1418
|
+
const label = "label" in input ? input.label ?? null : null;
|
|
1419
|
+
return {
|
|
1420
|
+
label,
|
|
1421
|
+
principal: input.principal ?? null,
|
|
1422
|
+
grants: input.grants.map((grant) => {
|
|
1423
|
+
const metadata = options.assets?.[grant.asset];
|
|
1424
|
+
return {
|
|
1425
|
+
asset: grant.asset,
|
|
1426
|
+
assetLabel: metadata?.symbol || assetAddress(grant.asset),
|
|
1427
|
+
recipients: grant.recipients.map((full) => ({
|
|
1428
|
+
shortened: full.length <= 14 ? full : `${full.slice(0, 6)}\u2026${full.slice(-6)}`,
|
|
1429
|
+
full
|
|
1430
|
+
})),
|
|
1431
|
+
perPayment: formatAuthorizationAmount(grant.maxPerPayment, metadata),
|
|
1432
|
+
total: formatAuthorizationAmount(grant.maxTotal, metadata),
|
|
1433
|
+
count: grant.maxCount ?? null,
|
|
1434
|
+
rails: grant.rails.map((r) => r.replace("-", " "))
|
|
1435
|
+
};
|
|
1436
|
+
}),
|
|
1437
|
+
expiry: {
|
|
1438
|
+
timestamp: input.expiresAt,
|
|
1439
|
+
absolute: new Intl.DateTimeFormat(locale, {
|
|
1440
|
+
year: "numeric",
|
|
1441
|
+
month: "short",
|
|
1442
|
+
day: "numeric",
|
|
1443
|
+
hour: "2-digit",
|
|
1444
|
+
minute: "2-digit",
|
|
1445
|
+
hourCycle: "h23",
|
|
1446
|
+
timeZone: "UTC",
|
|
1447
|
+
timeZoneName: "short"
|
|
1448
|
+
}).format(input.expiresAt * 1e3),
|
|
1449
|
+
relative: relativeExpiry(input.expiresAt, now, locale)
|
|
1450
|
+
},
|
|
1451
|
+
warnings: flags.map(
|
|
1452
|
+
(flag) => flag === "unrestricted-recipient-legacy" ? "This key can pay any recipient." : "This key can do more than shown here."
|
|
1453
|
+
)
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
var REFUSAL_SENTENCES = {
|
|
1457
|
+
expired: "This authorization has expired.",
|
|
1458
|
+
"not-yet-valid": "This authorization is not valid yet.",
|
|
1459
|
+
"no-matching-grant": "No grant covers this asset.",
|
|
1460
|
+
"recipient-not-allowed": "This recipient is not allowed.",
|
|
1461
|
+
"over-per-payment": "This amount exceeds the per-payment limit.",
|
|
1462
|
+
"over-total": "This amount exceeds the remaining total limit.",
|
|
1463
|
+
"over-count": "This authorization has reached its payment count limit.",
|
|
1464
|
+
"rail-not-allowed": "This payment rail is not allowed.",
|
|
1465
|
+
"invalid-authorization": "The authorization or spend request is invalid."
|
|
1466
|
+
};
|
|
1467
|
+
function explainSpend(authorization, request, options = {}) {
|
|
1468
|
+
const verdict = (0, import_connect_core17.evaluateSpend)(authorization, request);
|
|
1469
|
+
if (!verdict.allow) {
|
|
1470
|
+
return {
|
|
1471
|
+
allowed: false,
|
|
1472
|
+
reason: verdict.reason,
|
|
1473
|
+
sentence: REFUSAL_SENTENCES[verdict.reason]
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
const remainingTotal = authorization.grants[verdict.grant].maxTotal - request.spentSoFar - request.amount;
|
|
1477
|
+
const remainingTotalFormatted = formatAuthorizationAmount(
|
|
1478
|
+
remainingTotal,
|
|
1479
|
+
options.assets?.[authorization.grants[verdict.grant].asset]
|
|
1480
|
+
);
|
|
1481
|
+
return {
|
|
1482
|
+
allowed: true,
|
|
1483
|
+
grant: verdict.grant,
|
|
1484
|
+
remainingTotal,
|
|
1485
|
+
remainingTotalFormatted,
|
|
1486
|
+
sentence: `Allowed by grant ${verdict.grant + 1}; ${remainingTotalFormatted.formatted} remain.`
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1377
1490
|
// src/payment-fetch.ts
|
|
1491
|
+
var DEFAULT_VERIFY_RETRY = { attempts: 5, delayMs: 4e3 };
|
|
1492
|
+
function applySettlementVerification(payment, result) {
|
|
1493
|
+
return {
|
|
1494
|
+
...payment,
|
|
1495
|
+
verification: {
|
|
1496
|
+
status: result.status,
|
|
1497
|
+
..."reason" in result && result.reason !== void 0 ? { reason: result.reason } : {}
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1378
1501
|
function record(value) {
|
|
1379
1502
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1380
1503
|
}
|
|
@@ -1404,7 +1527,8 @@ function describePayment(input, result) {
|
|
|
1404
1527
|
payTo: text(paid.payTo),
|
|
1405
1528
|
amount: text(paid.amount),
|
|
1406
1529
|
// A hash reported for another chain is not this payment's.
|
|
1407
|
-
reference: settlement?.success === true && settlement.network === paid.network ? text(settlement.transaction) : null
|
|
1530
|
+
reference: settlement?.success === true && settlement.network === paid.network ? text(settlement.transaction) : null,
|
|
1531
|
+
verification: { status: "unverified" }
|
|
1408
1532
|
};
|
|
1409
1533
|
}
|
|
1410
1534
|
const challenge = record(record(paid.challenge)?.params);
|
|
@@ -1418,7 +1542,8 @@ function describePayment(input, result) {
|
|
|
1418
1542
|
asset: text(request.currency),
|
|
1419
1543
|
payTo: text(request.recipient),
|
|
1420
1544
|
amount: text(request.amount),
|
|
1421
|
-
reference: text(record(result.receipt)?.reference)
|
|
1545
|
+
reference: text(record(result.receipt)?.reference),
|
|
1546
|
+
verification: { status: "unverified" }
|
|
1422
1547
|
};
|
|
1423
1548
|
}
|
|
1424
1549
|
return {
|
|
@@ -1428,7 +1553,8 @@ function describePayment(input, result) {
|
|
|
1428
1553
|
asset: null,
|
|
1429
1554
|
payTo: null,
|
|
1430
1555
|
amount: null,
|
|
1431
|
-
reference: null
|
|
1556
|
+
reference: null,
|
|
1557
|
+
verification: { status: "unverified" }
|
|
1432
1558
|
};
|
|
1433
1559
|
}
|
|
1434
1560
|
|
|
@@ -1463,11 +1589,11 @@ function readMppRecoveryChannelId(error) {
|
|
|
1463
1589
|
}
|
|
1464
1590
|
|
|
1465
1591
|
// src/solana-session-key.ts
|
|
1466
|
-
var
|
|
1592
|
+
var import_connect_core18 = require("@naculus/connect-core");
|
|
1467
1593
|
function send(rpc) {
|
|
1468
1594
|
const sendTransaction = rpc.sendTransaction;
|
|
1469
1595
|
if (!sendTransaction) {
|
|
1470
|
-
throw new
|
|
1596
|
+
throw new import_connect_core18.WalletError(
|
|
1471
1597
|
"invalid_input",
|
|
1472
1598
|
"The Solana RPC must implement sendTransaction to approve or revoke."
|
|
1473
1599
|
);
|
|
@@ -1505,7 +1631,7 @@ function createSolanaSessionKeyFlow(deps) {
|
|
|
1505
1631
|
} catch (cause) {
|
|
1506
1632
|
await manager.revoke(draft.id).catch(() => {
|
|
1507
1633
|
});
|
|
1508
|
-
throw new
|
|
1634
|
+
throw new import_connect_core18.WalletError(
|
|
1509
1635
|
"rpc_error",
|
|
1510
1636
|
`The approval could not be confirmed as sent (${cause instanceof Error ? cause.message : String(cause)}); the key is revoked here. Call revoke("${draft.id}") to send the on-chain Revoke in case it landed.`
|
|
1511
1637
|
);
|
|
@@ -1516,7 +1642,7 @@ function createSolanaSessionKeyFlow(deps) {
|
|
|
1516
1642
|
async revoke(id) {
|
|
1517
1643
|
const key = (await list()).find((k) => k.id === id);
|
|
1518
1644
|
if (!key) {
|
|
1519
|
-
throw new
|
|
1645
|
+
throw new import_connect_core18.WalletError(
|
|
1520
1646
|
"invalid_input",
|
|
1521
1647
|
`No Solana session key ${id} for this owner.`
|
|
1522
1648
|
);
|
|
@@ -1528,7 +1654,7 @@ function createSolanaSessionKeyFlow(deps) {
|
|
|
1528
1654
|
rpc
|
|
1529
1655
|
);
|
|
1530
1656
|
const signed = await owner.signTransaction(transaction);
|
|
1531
|
-
const revocation = (0,
|
|
1657
|
+
const revocation = (0, import_connect_core18.verifySignedOwnerTransaction)(signed, {
|
|
1532
1658
|
kind: "revoke",
|
|
1533
1659
|
revocation: {
|
|
1534
1660
|
owner: key.owner,
|
|
@@ -1543,7 +1669,7 @@ function createSolanaSessionKeyFlow(deps) {
|
|
|
1543
1669
|
}
|
|
1544
1670
|
|
|
1545
1671
|
// src/web3-store.ts
|
|
1546
|
-
var
|
|
1672
|
+
var import_connect_core19 = require("@naculus/connect-core");
|
|
1547
1673
|
function withTimeout(promise, ms, label = "Operation") {
|
|
1548
1674
|
let timeoutId;
|
|
1549
1675
|
const timeout = new Promise((_, reject) => {
|
|
@@ -1565,7 +1691,7 @@ async function withRetry(fn, options) {
|
|
|
1565
1691
|
lastError = error;
|
|
1566
1692
|
if (attempt < options.maxRetries) {
|
|
1567
1693
|
const delay = options.baseDelay * 2 ** attempt;
|
|
1568
|
-
|
|
1694
|
+
import_connect_core19.logger.warn(
|
|
1569
1695
|
"appkit/core",
|
|
1570
1696
|
`Retry attempt ${attempt + 1}/${options.maxRetries} after ${delay}ms`
|
|
1571
1697
|
);
|
|
@@ -1680,10 +1806,12 @@ function siwxResultToSession(result, now = /* @__PURE__ */ new Date()) {
|
|
|
1680
1806
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1681
1807
|
0 && (module.exports = {
|
|
1682
1808
|
DEFAULT_NOTIFICATION_SETTINGS,
|
|
1809
|
+
DEFAULT_VERIFY_RETRY,
|
|
1683
1810
|
InvalidUserOperationReceiptError,
|
|
1684
1811
|
NOTIFICATION_SETTINGS_KEY,
|
|
1685
1812
|
__resetResolverForTests,
|
|
1686
1813
|
addInAppNotification,
|
|
1814
|
+
applySettlementVerification,
|
|
1687
1815
|
assertHexSignature,
|
|
1688
1816
|
atomicSupportFor,
|
|
1689
1817
|
bareEvmAddress,
|
|
@@ -1704,13 +1832,16 @@ function siwxResultToSession(result, now = /* @__PURE__ */ new Date()) {
|
|
|
1704
1832
|
decodeRevertReason,
|
|
1705
1833
|
defaultSiwxDomain,
|
|
1706
1834
|
defaultSiwxUri,
|
|
1835
|
+
describeAuthorization,
|
|
1707
1836
|
describeChain,
|
|
1708
1837
|
describePayment,
|
|
1709
1838
|
eip155ChainIdToNumber,
|
|
1710
1839
|
encodeErc20Approve,
|
|
1711
1840
|
encodeErc20Transfer,
|
|
1712
1841
|
encodeErc20TransferFrom,
|
|
1842
|
+
explainSpend,
|
|
1713
1843
|
fetchUserOperationReceipt,
|
|
1844
|
+
formatAuthorizationAmount,
|
|
1714
1845
|
getResolver,
|
|
1715
1846
|
getSharedSessionKeyManager,
|
|
1716
1847
|
initialWeb3State,
|