@naculus/connect-appkit-core 0.6.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/dist/index.js CHANGED
@@ -478,6 +478,13 @@ function saveNotificationSettings(storage, settings) {
478
478
  function updateNotificationSettings(current, update) {
479
479
  return { ...current, ...update };
480
480
  }
481
+ function replayNotificationSettings(persisted, updates) {
482
+ let settings = persisted ?? DEFAULT_NOTIFICATION_SETTINGS;
483
+ for (const update of updates) {
484
+ settings = updateNotificationSettings(settings, update(settings));
485
+ }
486
+ return settings;
487
+ }
481
488
  function channelsUpdate(channels) {
482
489
  const update = {};
483
490
  if (channels.includes("telegram")) update.telegram = true;
@@ -1289,7 +1296,127 @@ function createDelegationPolicyFlow(deps) {
1289
1296
  };
1290
1297
  }
1291
1298
 
1299
+ // src/authorization.ts
1300
+ import {
1301
+ evaluateSpend
1302
+ } from "@naculus/connect-core";
1303
+ function assetAddress(asset) {
1304
+ return asset.split(/[:/]/).at(-1);
1305
+ }
1306
+ function groupDigits(value) {
1307
+ return value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
1308
+ }
1309
+ function formatAuthorizationAmount(amount, metadata) {
1310
+ const baseUnits = amount.toString();
1311
+ if (!metadata || !metadata.symbol || !Number.isSafeInteger(metadata.decimals) || metadata.decimals < 0) {
1312
+ return {
1313
+ formatted: `${groupDigits(baseUnits)} base units`
1314
+ };
1315
+ }
1316
+ const decimals = metadata.decimals;
1317
+ const padded = baseUnits.padStart(decimals + 1, "0");
1318
+ const integer = decimals === 0 ? padded : padded.slice(0, -decimals);
1319
+ const fraction = decimals === 0 ? "" : padded.slice(-decimals).replace(/0+$/, "");
1320
+ const value = `${groupDigits(integer)}${fraction ? `.${fraction}` : ""}`;
1321
+ return { formatted: `${value} ${metadata.symbol}` };
1322
+ }
1323
+ function relativeExpiry(expiresAt, now, locale) {
1324
+ const seconds = expiresAt - now;
1325
+ const absolute = Math.abs(seconds);
1326
+ const divisor = absolute >= 31536e3 ? 31536e3 : absolute >= 2592e3 ? 2592e3 : absolute >= 86400 ? 86400 : absolute >= 3600 ? 3600 : absolute >= 60 ? 60 : 1;
1327
+ const unit = divisor === 31536e3 ? "year" : divisor === 2592e3 ? "month" : divisor === 86400 ? "day" : divisor === 3600 ? "hour" : divisor === 60 ? "minute" : "second";
1328
+ return new Intl.RelativeTimeFormat(locale).format(
1329
+ Math.round(seconds / divisor),
1330
+ unit
1331
+ );
1332
+ }
1333
+ function describeAuthorization(input, options = {}) {
1334
+ const now = options.now ?? Math.floor(Date.now() / 1e3);
1335
+ const locale = options.locale ?? "en";
1336
+ const flags = "flags" in input ? input.flags : [];
1337
+ const label = "label" in input ? input.label ?? null : null;
1338
+ return {
1339
+ label,
1340
+ principal: input.principal ?? null,
1341
+ grants: input.grants.map((grant) => {
1342
+ const metadata = options.assets?.[grant.asset];
1343
+ return {
1344
+ asset: grant.asset,
1345
+ assetLabel: metadata?.symbol || assetAddress(grant.asset),
1346
+ recipients: grant.recipients.map((full) => ({
1347
+ shortened: full.length <= 14 ? full : `${full.slice(0, 6)}\u2026${full.slice(-6)}`,
1348
+ full
1349
+ })),
1350
+ perPayment: formatAuthorizationAmount(grant.maxPerPayment, metadata),
1351
+ total: formatAuthorizationAmount(grant.maxTotal, metadata),
1352
+ count: grant.maxCount ?? null,
1353
+ rails: grant.rails.map((r) => r.replace("-", " "))
1354
+ };
1355
+ }),
1356
+ expiry: {
1357
+ timestamp: input.expiresAt,
1358
+ absolute: new Intl.DateTimeFormat(locale, {
1359
+ year: "numeric",
1360
+ month: "short",
1361
+ day: "numeric",
1362
+ hour: "2-digit",
1363
+ minute: "2-digit",
1364
+ hourCycle: "h23",
1365
+ timeZone: "UTC",
1366
+ timeZoneName: "short"
1367
+ }).format(input.expiresAt * 1e3),
1368
+ relative: relativeExpiry(input.expiresAt, now, locale)
1369
+ },
1370
+ warnings: flags.map(
1371
+ (flag) => flag === "unrestricted-recipient-legacy" ? "This key can pay any recipient." : "This key can do more than shown here."
1372
+ )
1373
+ };
1374
+ }
1375
+ var REFUSAL_SENTENCES = {
1376
+ expired: "This authorization has expired.",
1377
+ "not-yet-valid": "This authorization is not valid yet.",
1378
+ "no-matching-grant": "No grant covers this asset.",
1379
+ "recipient-not-allowed": "This recipient is not allowed.",
1380
+ "over-per-payment": "This amount exceeds the per-payment limit.",
1381
+ "over-total": "This amount exceeds the remaining total limit.",
1382
+ "over-count": "This authorization has reached its payment count limit.",
1383
+ "rail-not-allowed": "This payment rail is not allowed.",
1384
+ "invalid-authorization": "The authorization or spend request is invalid."
1385
+ };
1386
+ function explainSpend(authorization, request, options = {}) {
1387
+ const verdict = evaluateSpend(authorization, request);
1388
+ if (!verdict.allow) {
1389
+ return {
1390
+ allowed: false,
1391
+ reason: verdict.reason,
1392
+ sentence: REFUSAL_SENTENCES[verdict.reason]
1393
+ };
1394
+ }
1395
+ const remainingTotal = authorization.grants[verdict.grant].maxTotal - request.spentSoFar - request.amount;
1396
+ const remainingTotalFormatted = formatAuthorizationAmount(
1397
+ remainingTotal,
1398
+ options.assets?.[authorization.grants[verdict.grant].asset]
1399
+ );
1400
+ return {
1401
+ allowed: true,
1402
+ grant: verdict.grant,
1403
+ remainingTotal,
1404
+ remainingTotalFormatted,
1405
+ sentence: `Allowed by grant ${verdict.grant + 1}; ${remainingTotalFormatted.formatted} remain.`
1406
+ };
1407
+ }
1408
+
1292
1409
  // src/payment-fetch.ts
1410
+ var DEFAULT_VERIFY_RETRY = { attempts: 5, delayMs: 4e3 };
1411
+ function applySettlementVerification(payment, result) {
1412
+ return {
1413
+ ...payment,
1414
+ verification: {
1415
+ status: result.status,
1416
+ ..."reason" in result && result.reason !== void 0 ? { reason: result.reason } : {}
1417
+ }
1418
+ };
1419
+ }
1293
1420
  function record(value) {
1294
1421
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
1295
1422
  }
@@ -1319,7 +1446,8 @@ function describePayment(input, result) {
1319
1446
  payTo: text(paid.payTo),
1320
1447
  amount: text(paid.amount),
1321
1448
  // A hash reported for another chain is not this payment's.
1322
- reference: settlement?.success === true && settlement.network === paid.network ? text(settlement.transaction) : null
1449
+ reference: settlement?.success === true && settlement.network === paid.network ? text(settlement.transaction) : null,
1450
+ verification: { status: "unverified" }
1323
1451
  };
1324
1452
  }
1325
1453
  const challenge = record(record(paid.challenge)?.params);
@@ -1333,7 +1461,8 @@ function describePayment(input, result) {
1333
1461
  asset: text(request.currency),
1334
1462
  payTo: text(request.recipient),
1335
1463
  amount: text(request.amount),
1336
- reference: text(record(result.receipt)?.reference)
1464
+ reference: text(record(result.receipt)?.reference),
1465
+ verification: { status: "unverified" }
1337
1466
  };
1338
1467
  }
1339
1468
  return {
@@ -1343,10 +1472,41 @@ function describePayment(input, result) {
1343
1472
  asset: null,
1344
1473
  payTo: null,
1345
1474
  amount: null,
1346
- reference: null
1475
+ reference: null,
1476
+ verification: { status: "unverified" }
1347
1477
  };
1348
1478
  }
1349
1479
 
1480
+ // src/mpp-session.ts
1481
+ function record2(value) {
1482
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
1483
+ }
1484
+ function readMppSessionChannel(value) {
1485
+ const channel = record2(value);
1486
+ if (!channel || typeof channel.channelId !== "string" || typeof channel.payer !== "string" || typeof channel.payee !== "string" || typeof channel.mint !== "string" || typeof channel.channelProgram !== "string" || typeof channel.deposit !== "bigint" || typeof channel.gracePeriodSeconds !== "number" || typeof channel.openSlot !== "bigint" || typeof channel.salt !== "bigint") {
1487
+ return null;
1488
+ }
1489
+ return channel;
1490
+ }
1491
+ function readCurrentMppSessionChannel(session) {
1492
+ for (let index = session.channels.length - 1; index >= 0; index--) {
1493
+ const channel = readMppSessionChannel(session.channels[index]);
1494
+ if (channel) return channel;
1495
+ }
1496
+ return null;
1497
+ }
1498
+ function readMppSessionReceipt(value) {
1499
+ const receipt = record2(value);
1500
+ if (receipt?.status !== "success" || typeof receipt.method !== "string" || typeof receipt.timestamp !== "string" || typeof receipt.reference !== "string") {
1501
+ return null;
1502
+ }
1503
+ return receipt;
1504
+ }
1505
+ function readMppRecoveryChannelId(error) {
1506
+ const channelId = record2(error)?.channelId;
1507
+ return typeof channelId === "string" && channelId !== "" ? channelId : null;
1508
+ }
1509
+
1350
1510
  // src/solana-session-key.ts
1351
1511
  import {
1352
1512
  verifySignedOwnerTransaction,
@@ -1567,10 +1727,12 @@ function siwxResultToSession(result, now = /* @__PURE__ */ new Date()) {
1567
1727
  }
1568
1728
  export {
1569
1729
  DEFAULT_NOTIFICATION_SETTINGS,
1730
+ DEFAULT_VERIFY_RETRY,
1570
1731
  InvalidUserOperationReceiptError,
1571
1732
  NOTIFICATION_SETTINGS_KEY,
1572
1733
  __resetResolverForTests,
1573
1734
  addInAppNotification,
1735
+ applySettlementVerification,
1574
1736
  assertHexSignature,
1575
1737
  atomicSupportFor,
1576
1738
  bareEvmAddress,
@@ -1591,13 +1753,16 @@ export {
1591
1753
  decodeRevertReason,
1592
1754
  defaultSiwxDomain,
1593
1755
  defaultSiwxUri,
1756
+ describeAuthorization,
1594
1757
  describeChain,
1595
1758
  describePayment,
1596
1759
  eip155ChainIdToNumber,
1597
1760
  encodeErc20Approve,
1598
1761
  encodeErc20Transfer,
1599
1762
  encodeErc20TransferFrom,
1763
+ explainSpend,
1600
1764
  fetchUserOperationReceipt,
1765
+ formatAuthorizationAmount,
1601
1766
  getResolver,
1602
1767
  getSharedSessionKeyManager,
1603
1768
  initialWeb3State,
@@ -1613,8 +1778,13 @@ export {
1613
1778
  normalizeCapabilities,
1614
1779
  normalizeEip155ChainId,
1615
1780
  parseUserOperationReceipt,
1781
+ readCurrentMppSessionChannel,
1616
1782
  readDecimals,
1783
+ readMppRecoveryChannelId,
1784
+ readMppSessionChannel,
1785
+ readMppSessionReceipt,
1617
1786
  readSolanaRoles,
1787
+ replayNotificationSettings,
1618
1788
  resetSharedSessionKeyManager,
1619
1789
  resolveChain,
1620
1790
  resolveContextSimulationEndpoint,