@yeying-community/web3-bs 1.0.13 → 1.0.15

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.
@@ -79,6 +79,17 @@
79
79
  }
80
80
  return accounts[0] || null;
81
81
  }
82
+ function normalizeAccount(account) {
83
+ const normalized = (account || '').trim();
84
+ return normalized || null;
85
+ }
86
+ function isSameAccount(left, right) {
87
+ const normalizedLeft = normalizeAccount(left);
88
+ const normalizedRight = normalizeAccount(right);
89
+ return Boolean(normalizedLeft &&
90
+ normalizedRight &&
91
+ normalizedLeft.toLowerCase() === normalizedRight.toLowerCase());
92
+ }
82
93
  function isYeYingProvider(provider, info) {
83
94
  if (!provider)
84
95
  return false;
@@ -342,6 +353,49 @@
342
353
  writeStoredAccount(storageKey, account);
343
354
  return { account, accounts };
344
355
  }
356
+ async function resolveWalletAccount(options = {}) {
357
+ const provider = options.provider || (await requireProvider());
358
+ let accounts = await getAccounts(provider);
359
+ if (accounts.length === 0 && options.autoConnect) {
360
+ accounts = await requestAccounts({ provider });
361
+ }
362
+ const expectedAccount = normalizeAccount(options.expectedAccount);
363
+ const walletAccount = normalizeAccount(accounts[0]);
364
+ if (!walletAccount) {
365
+ return {
366
+ status: 'unavailable',
367
+ account: null,
368
+ walletAccount: null,
369
+ expectedAccount,
370
+ accounts,
371
+ };
372
+ }
373
+ if (!expectedAccount) {
374
+ return {
375
+ status: 'wallet',
376
+ account: walletAccount,
377
+ walletAccount,
378
+ expectedAccount: null,
379
+ accounts,
380
+ };
381
+ }
382
+ if (isSameAccount(expectedAccount, walletAccount)) {
383
+ return {
384
+ status: 'matched',
385
+ account: walletAccount,
386
+ walletAccount,
387
+ expectedAccount,
388
+ accounts,
389
+ };
390
+ }
391
+ return {
392
+ status: 'mismatch',
393
+ account: null,
394
+ walletAccount,
395
+ expectedAccount,
396
+ accounts,
397
+ };
398
+ }
345
399
  function watchAccounts(provider, handler, options = {}) {
346
400
  const storageKey = options.storageKey || DEFAULT_ACCOUNT_STORAGE_KEY;
347
401
  const preferStored = options.preferStored !== false;
@@ -816,6 +870,73 @@
816
870
  function normalizeUcanExpiry(exp, fallbackMs) {
817
871
  return normalizeExpiry(exp, fallbackMs);
818
872
  }
873
+ function normalizeEthAccount(account) {
874
+ return String(account || '').trim().toLowerCase();
875
+ }
876
+ function parseUcanAccountFromIssuer(issuer) {
877
+ const normalized = String(issuer || '').trim().toLowerCase();
878
+ const prefix = 'did:pkh:eth:';
879
+ if (!normalized.startsWith(prefix))
880
+ return null;
881
+ const account = normalized.slice(prefix.length);
882
+ return account || null;
883
+ }
884
+ function getUcanIssuerForAccount(account) {
885
+ return `did:pkh:eth:${normalizeEthAccount(account)}`;
886
+ }
887
+ function extractUcanStatementPayload(message) {
888
+ if (!message || typeof message !== 'string')
889
+ return null;
890
+ const marker = 'UCAN-AUTH';
891
+ const index = message.indexOf(marker);
892
+ if (index < 0)
893
+ return null;
894
+ const jsonStart = message.indexOf('{', index + marker.length);
895
+ const jsonEnd = message.lastIndexOf('}');
896
+ if (jsonStart < 0 || jsonEnd < jsonStart)
897
+ return null;
898
+ try {
899
+ const parsed = JSON.parse(message.slice(jsonStart, jsonEnd + 1));
900
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
901
+ return null;
902
+ }
903
+ return parsed;
904
+ }
905
+ catch {
906
+ return null;
907
+ }
908
+ }
909
+ function getUcanRootServiceHosts(root) {
910
+ const payload = extractUcanStatementPayload(root.siwe?.message);
911
+ const hosts = payload?.service_hosts;
912
+ if (!hosts || typeof hosts !== 'object' || Array.isArray(hosts)) {
913
+ return null;
914
+ }
915
+ return hosts;
916
+ }
917
+ function getServiceHostMismatch(root, expectedServiceHosts) {
918
+ if (!expectedServiceHosts)
919
+ return null;
920
+ const entries = Object.entries(expectedServiceHosts)
921
+ .map(([key, value]) => [key, String(value || '').trim()])
922
+ .filter(([, value]) => Boolean(value));
923
+ if (!entries.length)
924
+ return null;
925
+ const hosts = getUcanRootServiceHosts(root);
926
+ if (!hosts) {
927
+ return {
928
+ expected: entries.map(([key, value]) => `${key}:${value}`).join('|'),
929
+ actual: '',
930
+ };
931
+ }
932
+ for (const [key, expected] of entries) {
933
+ const actual = typeof hosts[key] === 'string' ? hosts[key].trim() : '';
934
+ if (actual !== expected) {
935
+ return { expected, actual };
936
+ }
937
+ }
938
+ return null;
939
+ }
819
940
  function decodeUcanPayload(token) {
820
941
  const parts = String(token || '').split('.');
821
942
  if (parts.length < 2)
@@ -1243,6 +1364,105 @@
1243
1364
  function isRootExpired(root, nowMs) {
1244
1365
  return Boolean(root.exp && nowMs > root.exp);
1245
1366
  }
1367
+ async function resolveUcanAuthorization(options = {}) {
1368
+ const root = options.root === undefined
1369
+ ? await getStoredUcanRoot(options.sessionId || DEFAULT_SESSION_ID)
1370
+ : options.root;
1371
+ const nowMs = options.nowMs ?? Date.now();
1372
+ if (!root) {
1373
+ return { status: 'unauthorized', reason: 'missing_root', root };
1374
+ }
1375
+ if (typeof root.exp !== 'number' || root.exp <= nowMs) {
1376
+ return {
1377
+ status: 'unauthorized',
1378
+ reason: 'expired',
1379
+ root,
1380
+ expiresAt: root.exp,
1381
+ };
1382
+ }
1383
+ if (typeof root.nbf === 'number' && root.nbf > nowMs) {
1384
+ return {
1385
+ status: 'unauthorized',
1386
+ reason: 'not_before',
1387
+ root,
1388
+ expiresAt: root.exp,
1389
+ };
1390
+ }
1391
+ const accountFromIssuer = parseUcanAccountFromIssuer(root.iss);
1392
+ if (!accountFromIssuer) {
1393
+ return {
1394
+ status: 'unauthorized',
1395
+ reason: 'invalid_issuer',
1396
+ root,
1397
+ expiresAt: root.exp,
1398
+ actual: root.iss,
1399
+ };
1400
+ }
1401
+ const currentAccount = normalizeEthAccount(options.currentAccount);
1402
+ if (!currentAccount && !options.recoverAccountFromRoot) {
1403
+ return {
1404
+ status: 'unauthorized',
1405
+ reason: 'missing_account',
1406
+ root,
1407
+ account: null,
1408
+ expiresAt: root.exp,
1409
+ };
1410
+ }
1411
+ const account = currentAccount || accountFromIssuer;
1412
+ const expectedIssuer = getUcanIssuerForAccount(account);
1413
+ if (root.iss !== expectedIssuer) {
1414
+ return {
1415
+ status: 'unauthorized',
1416
+ reason: 'issuer_mismatch',
1417
+ root,
1418
+ account,
1419
+ expiresAt: root.exp,
1420
+ expected: expectedIssuer,
1421
+ actual: root.iss,
1422
+ };
1423
+ }
1424
+ if (options.expectedCapabilities &&
1425
+ !capsEqual(root.cap, options.expectedCapabilities)) {
1426
+ return {
1427
+ status: 'unauthorized',
1428
+ reason: 'capability_mismatch',
1429
+ root,
1430
+ account,
1431
+ expiresAt: root.exp,
1432
+ };
1433
+ }
1434
+ const expectedAudience = String(options.expectedAudience || '').trim();
1435
+ if (expectedAudience && root.aud !== expectedAudience) {
1436
+ return {
1437
+ status: 'unauthorized',
1438
+ reason: 'audience_mismatch',
1439
+ root,
1440
+ account,
1441
+ expiresAt: root.exp,
1442
+ expected: expectedAudience,
1443
+ actual: root.aud,
1444
+ };
1445
+ }
1446
+ const serviceHostMismatch = getServiceHostMismatch(root, options.expectedServiceHosts);
1447
+ if (serviceHostMismatch) {
1448
+ return {
1449
+ status: 'unauthorized',
1450
+ reason: 'service_host_mismatch',
1451
+ root,
1452
+ account,
1453
+ expiresAt: root.exp,
1454
+ expected: serviceHostMismatch.expected,
1455
+ actual: serviceHostMismatch.actual,
1456
+ };
1457
+ }
1458
+ return {
1459
+ status: 'authorized',
1460
+ account,
1461
+ root,
1462
+ restoredAccount: !currentAccount && account === accountFromIssuer,
1463
+ expiresAt: root.exp,
1464
+ };
1465
+ }
1246
1466
  async function getOrCreateUcanRoot(options) {
1247
1467
  const provider = options.provider || (await requireProvider());
1248
1468
  const session = options.session || (await createUcanSession({ id: options.sessionId, provider }));
@@ -2538,9 +2758,12 @@
2538
2758
  exports.normalizeUcanExpiry = normalizeUcanExpiry;
2539
2759
  exports.onAccountsChanged = onAccountsChanged;
2540
2760
  exports.onChainChanged = onChainChanged;
2761
+ exports.parseUcanAccountFromIssuer = parseUcanAccountFromIssuer;
2541
2762
  exports.refreshAccessToken = refreshAccessToken;
2542
2763
  exports.requestAccounts = requestAccounts;
2543
2764
  exports.requireProvider = requireProvider;
2765
+ exports.resolveUcanAuthorization = resolveUcanAuthorization;
2766
+ exports.resolveWalletAccount = resolveWalletAccount;
2544
2767
  exports.setAccessToken = setAccessToken;
2545
2768
  exports.setCentralSessionToken = setCentralSessionToken;
2546
2769
  exports.signMessage = signMessage;