@lydianpay/lydianconnect 1.3.0 → 1.5.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.d.cts CHANGED
@@ -46,6 +46,13 @@ interface Connection {
46
46
  readonly account: string;
47
47
  /** CAIP-2 chain id, e.g. "eip155:1". */
48
48
  readonly chainId: string;
49
+ /**
50
+ * The CAIP-2 chains the wallet's session granted, across all namespaces.
51
+ * WalletConnect only — SDK and injected connections leave it undefined, since
52
+ * switching (`wallet_switchEthereumChain`) has no approval concept and any
53
+ * chain in the provider's family is reachable anyway.
54
+ */
55
+ readonly approvedChains?: readonly string[];
49
56
  request<T = unknown>(args: {
50
57
  method: string;
51
58
  params?: unknown;
@@ -67,13 +74,28 @@ interface LydianConnectConfig extends AppConfig {
67
74
  /** WalletConnect Cloud project id — powers the fallback. */
68
75
  walletConnectProjectId: string;
69
76
  }
70
- interface ConnectInput {
77
+ /**
78
+ * Input to {@link LydianConnect.connect} — one of two operations:
79
+ *
80
+ * - `chainId` **pins**: requests target that chain and EVM wallets switch onto it.
81
+ * - `chains` **discovers**: propose several chains, the wallet approves a subset
82
+ * (see {@link Connection.approvedChains}), nothing is pinned. Connect again
83
+ * with `chainId` once the user picks one — a WalletConnect session that
84
+ * approved it is reused without a second prompt.
85
+ */
86
+ type ConnectInput = {
71
87
  walletId: string;
72
88
  /** Chain to connect on — a number (EVM, e.g. 137) or a CAIP-2 id ('eip155:137'). */
73
89
  chainId: number | string;
74
90
  /** Cancel a pending connection. */
75
91
  signal?: AbortSignal;
76
- }
92
+ } | {
93
+ walletId: string;
94
+ /** Chains to discover across — same formats as `chainId`; must be non-empty. */
95
+ chains: readonly (number | string)[];
96
+ /** Cancel a pending connection. */
97
+ signal?: AbortSignal;
98
+ };
77
99
  type LydianConnectEvent =
78
100
  /**
79
101
  * WalletConnect only — the wc: URI.
package/dist/index.d.ts CHANGED
@@ -46,6 +46,13 @@ interface Connection {
46
46
  readonly account: string;
47
47
  /** CAIP-2 chain id, e.g. "eip155:1". */
48
48
  readonly chainId: string;
49
+ /**
50
+ * The CAIP-2 chains the wallet's session granted, across all namespaces.
51
+ * WalletConnect only — SDK and injected connections leave it undefined, since
52
+ * switching (`wallet_switchEthereumChain`) has no approval concept and any
53
+ * chain in the provider's family is reachable anyway.
54
+ */
55
+ readonly approvedChains?: readonly string[];
49
56
  request<T = unknown>(args: {
50
57
  method: string;
51
58
  params?: unknown;
@@ -67,13 +74,28 @@ interface LydianConnectConfig extends AppConfig {
67
74
  /** WalletConnect Cloud project id — powers the fallback. */
68
75
  walletConnectProjectId: string;
69
76
  }
70
- interface ConnectInput {
77
+ /**
78
+ * Input to {@link LydianConnect.connect} — one of two operations:
79
+ *
80
+ * - `chainId` **pins**: requests target that chain and EVM wallets switch onto it.
81
+ * - `chains` **discovers**: propose several chains, the wallet approves a subset
82
+ * (see {@link Connection.approvedChains}), nothing is pinned. Connect again
83
+ * with `chainId` once the user picks one — a WalletConnect session that
84
+ * approved it is reused without a second prompt.
85
+ */
86
+ type ConnectInput = {
71
87
  walletId: string;
72
88
  /** Chain to connect on — a number (EVM, e.g. 137) or a CAIP-2 id ('eip155:137'). */
73
89
  chainId: number | string;
74
90
  /** Cancel a pending connection. */
75
91
  signal?: AbortSignal;
76
- }
92
+ } | {
93
+ walletId: string;
94
+ /** Chains to discover across — same formats as `chainId`; must be non-empty. */
95
+ chains: readonly (number | string)[];
96
+ /** Cancel a pending connection. */
97
+ signal?: AbortSignal;
98
+ };
77
99
  type LydianConnectEvent =
78
100
  /**
79
101
  * WalletConnect only — the wc: URI.
package/dist/index.js CHANGED
@@ -150,6 +150,20 @@ function buildNamespace(chainId) {
150
150
  }
151
151
  };
152
152
  }
153
+ function buildNamespaces(chains) {
154
+ const byName = /* @__PURE__ */ new Map();
155
+ chains.forEach((chain) => {
156
+ const single = buildNamespace(chain);
157
+ const caip = single.value.chains?.[0];
158
+ const merged = byName.get(single.name);
159
+ if (!merged) {
160
+ byName.set(single.name, single);
161
+ } else if (caip && !merged.value.chains?.includes(caip)) {
162
+ merged.value.chains = [...merged.value.chains ?? [], caip];
163
+ }
164
+ });
165
+ return [...byName.values()];
166
+ }
153
167
 
154
168
  // node_modules/@lydianpay/lydianconnect-catalog/dist/index.js
155
169
  var ICON_BASE = "https://wallet-assets.lydian.com/wallets";
@@ -247,7 +261,7 @@ var WALLET_CATALOG = WALLETS.map((w) => ({
247
261
 
248
262
  // src/core/connector.ts
249
263
  function requestedChain(req) {
250
- return req.namespace.value.chains?.[0];
264
+ return req.pinned;
251
265
  }
252
266
  var BaseConnector = class {
253
267
  constructor() {
@@ -298,6 +312,10 @@ var BaseConnector = class {
298
312
  function deriveUrl() {
299
313
  return typeof window !== "undefined" ? window.location.origin : "";
300
314
  }
315
+ function deriveRedirectUrl() {
316
+ if (typeof window === "undefined") return "";
317
+ return window.location.origin + window.location.pathname;
318
+ }
301
319
 
302
320
  // src/connectors/eip155/chain.ts
303
321
  var CHAIN_SETTLE = { timeoutMs: 4e3, intervalMs: 100 };
@@ -603,9 +621,8 @@ function rpcFor2(provider) {
603
621
  return ({ method, params }) => rpc(provider, method, params);
604
622
  }
605
623
  function evmChainIds(req) {
606
- const wanted = req.namespace.value.chains?.[0];
607
- const n = wanted ? Number(wanted.split(":")[1]) : NaN;
608
- return Number.isFinite(n) && n > 0 ? [n] : void 0;
624
+ const ids = (req.namespace.value.chains ?? []).map((chain) => Number(chain.split(":")[1])).filter((n) => Number.isFinite(n) && n > 0);
625
+ return ids.length > 0 ? ids : void 0;
609
626
  }
610
627
 
611
628
  // src/connectors/injected/connector.ts
@@ -1258,13 +1275,16 @@ var WalletConnectConnector = class extends BaseConnector {
1258
1275
  }
1259
1276
  async getClient() {
1260
1277
  if (this.client) return this.client;
1278
+ const redirectUrl = deriveRedirectUrl();
1261
1279
  const client = await SignClient.init({
1262
1280
  projectId: this.options.projectId,
1263
1281
  metadata: {
1264
1282
  name: this.app.appName,
1265
1283
  description: this.app.description ?? this.app.appName,
1266
1284
  url: deriveUrl(),
1267
- icons: this.app.icon ? [this.app.icon] : []
1285
+ icons: this.app.icon ? [this.app.icon] : [],
1286
+ // Wallets may use this to return the user after deep-link approvals.
1287
+ ...redirectUrl ? { redirect: { universal: redirectUrl } } : {}
1268
1288
  },
1269
1289
  relayUrl: this.options.relayUrl
1270
1290
  });
@@ -1296,8 +1316,11 @@ var WalletConnectConnector = class extends BaseConnector {
1296
1316
  const client = await this.getClient();
1297
1317
  const reused = await this.tryReuse(client, req);
1298
1318
  if (reused) return reused;
1319
+ const proposed = [req.namespace, ...req.extraNamespaces ?? []];
1299
1320
  const { uri, approval } = await client.connect({
1300
- requiredNamespaces: { [req.namespace.name]: req.namespace.value }
1321
+ optionalNamespaces: Object.fromEntries(
1322
+ proposed.map((ns) => [ns.name, ns.value])
1323
+ )
1301
1324
  });
1302
1325
  if (!uri) throw new Error("WalletConnect did not return a pairing URI");
1303
1326
  const link = isMobile() ? this.links[req.walletId] : void 0;
@@ -1329,7 +1352,8 @@ var WalletConnectConnector = class extends BaseConnector {
1329
1352
  client,
1330
1353
  req.walletId,
1331
1354
  session,
1332
- req.namespace
1355
+ req.namespace,
1356
+ req.pinned
1333
1357
  );
1334
1358
  this.topics.set(req.walletId, session.topic);
1335
1359
  this.persist();
@@ -1396,21 +1420,32 @@ var WalletConnectConnector = class extends BaseConnector {
1396
1420
  return null;
1397
1421
  }
1398
1422
  const ns = session.namespaces[req.namespace.name];
1399
- const wanted = req.namespace.value.chains?.[0];
1400
- if (!ns || wanted && !ns.chains?.includes(wanted)) return null;
1401
- return this.toConnection(client, req.walletId, session, req.namespace);
1423
+ if (!ns) return null;
1424
+ if (req.pinned && !grantedChains(ns).includes(req.pinned)) return null;
1425
+ return this.toConnection(
1426
+ client,
1427
+ req.walletId,
1428
+ session,
1429
+ req.namespace,
1430
+ req.pinned
1431
+ );
1402
1432
  }
1403
- toConnection(client, walletId, session, namespace) {
1433
+ toConnection(client, walletId, session, namespace, wanted) {
1404
1434
  const ns = session.namespaces[namespace.name];
1405
1435
  const account = (ns?.accounts ?? []).map((a) => a.split(":")[2]).find((a) => !!a) ?? "";
1406
- const approved = ns?.chains?.length ? ns.chains : (ns?.accounts ?? []).map((a) => a.split(":").slice(0, 2).join(":"));
1407
- const wanted = namespace.value.chains?.[0];
1436
+ const approved = grantedChains(ns);
1408
1437
  const chainId = wanted && approved.includes(wanted) ? wanted : approved[0] ?? `${namespace.name}:1`;
1438
+ const approvedChains = [
1439
+ ...new Set(
1440
+ Object.values(session.namespaces).flatMap((n) => grantedChains(n))
1441
+ )
1442
+ ];
1409
1443
  const topic = session.topic;
1410
1444
  return {
1411
1445
  walletId,
1412
1446
  account,
1413
1447
  chainId,
1448
+ approvedChains,
1414
1449
  request: (args) => client.request({
1415
1450
  topic,
1416
1451
  chainId,
@@ -1471,6 +1506,11 @@ var WalletConnectConnector = class extends BaseConnector {
1471
1506
  }
1472
1507
  }
1473
1508
  };
1509
+ function grantedChains(ns) {
1510
+ if (!ns) return [];
1511
+ if (ns.chains?.length) return ns.chains;
1512
+ return (ns.accounts ?? []).map((a) => a.split(":").slice(0, 2).join(":"));
1513
+ }
1474
1514
  function firstNamespace(session) {
1475
1515
  const name = Object.keys(session.namespaces)[0];
1476
1516
  if (!name) return null;
@@ -1515,14 +1555,35 @@ var LydianConnect = class {
1515
1555
  return this.manager.init();
1516
1556
  }
1517
1557
  async connect(input) {
1518
- if (input.chainId === void 0 || input.chainId === null) {
1558
+ const chains = "chains" in input ? input.chains : void 0;
1559
+ const chainId = "chainId" in input ? input.chainId : void 0;
1560
+ if (chains !== void 0 && chainId !== void 0) {
1561
+ throw new Error(
1562
+ "connect() takes either chainId (pinned) or chains (discovery), not both"
1563
+ );
1564
+ }
1565
+ if (chains !== void 0) {
1566
+ const [primary, ...extra] = buildNamespaces(chains);
1567
+ if (!primary) {
1568
+ throw new Error("connect() discovery requires a non-empty chains list");
1569
+ }
1570
+ return this.manager.connect({
1571
+ walletId: input.walletId,
1572
+ namespace: primary,
1573
+ extraNamespaces: extra.length > 0 ? extra : void 0,
1574
+ signal: input.signal
1575
+ });
1576
+ }
1577
+ if (chainId === void 0 || chainId === null) {
1519
1578
  throw new Error(
1520
- 'connect() requires a chainId (a number like 137 or an "eip155:<id>" string)'
1579
+ 'connect() requires a chainId (a number like 137 or an "eip155:<id>" string) or a chains list'
1521
1580
  );
1522
1581
  }
1582
+ const namespace = buildNamespace(chainId);
1523
1583
  return this.manager.connect({
1524
1584
  walletId: input.walletId,
1525
- namespace: buildNamespace(input.chainId),
1585
+ namespace,
1586
+ pinned: namespace.value.chains?.[0],
1526
1587
  signal: input.signal
1527
1588
  });
1528
1589
  }