@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/README.md CHANGED
@@ -41,7 +41,11 @@ lc.on((event) => {
41
41
  | `icon` | no | Icon shown in the connect prompt. |
42
42
  | `description` | no | Defaults to `appName`. |
43
43
 
44
- `connect({ walletId, chainId })` — `chainId` is the chain the user is paying on (a number like `137`, or a CAIP-2 id like `'eip155:137'`).
44
+ `connect({ walletId, chainId })` — `chainId` is the chain the user is paying on (a number like `137`, or a CAIP-2 id like `'eip155:137'`). The connection is pinned to it: requests target it, and EVM wallets are switched onto it.
45
+
46
+ `connect({ walletId, chains })` — multi-network discovery: propose every chain the app can serve and let the wallet approve a subset, reported on `connection.approvedChains` (WalletConnect sessions only; SDK/injected connections leave it `undefined`). Nothing is pinned and no chain switch is forced. Connect again with `chainId` once the user picks a chain — a WalletConnect session that approved it is reused without a second prompt.
47
+
48
+ The WalletConnect client also advertises a return link derived from the page URL (origin + path, no query/hash); wallets may use it to bring the user back to the page after a deep-link approval.
45
49
 
46
50
  ## Supported wallets
47
51
 
package/dist/index.cjs CHANGED
@@ -156,6 +156,20 @@ function buildNamespace(chainId) {
156
156
  }
157
157
  };
158
158
  }
159
+ function buildNamespaces(chains) {
160
+ const byName = /* @__PURE__ */ new Map();
161
+ chains.forEach((chain) => {
162
+ const single = buildNamespace(chain);
163
+ const caip = single.value.chains?.[0];
164
+ const merged = byName.get(single.name);
165
+ if (!merged) {
166
+ byName.set(single.name, single);
167
+ } else if (caip && !merged.value.chains?.includes(caip)) {
168
+ merged.value.chains = [...merged.value.chains ?? [], caip];
169
+ }
170
+ });
171
+ return [...byName.values()];
172
+ }
159
173
 
160
174
  // node_modules/@lydianpay/lydianconnect-catalog/dist/index.js
161
175
  var ICON_BASE = "https://wallet-assets.lydian.com/wallets";
@@ -253,7 +267,7 @@ var WALLET_CATALOG = WALLETS.map((w) => ({
253
267
 
254
268
  // src/core/connector.ts
255
269
  function requestedChain(req) {
256
- return req.namespace.value.chains?.[0];
270
+ return req.pinned;
257
271
  }
258
272
  var BaseConnector = class {
259
273
  constructor() {
@@ -304,6 +318,10 @@ var BaseConnector = class {
304
318
  function deriveUrl() {
305
319
  return typeof window !== "undefined" ? window.location.origin : "";
306
320
  }
321
+ function deriveRedirectUrl() {
322
+ if (typeof window === "undefined") return "";
323
+ return window.location.origin + window.location.pathname;
324
+ }
307
325
 
308
326
  // src/connectors/eip155/chain.ts
309
327
  var CHAIN_SETTLE = { timeoutMs: 4e3, intervalMs: 100 };
@@ -609,9 +627,8 @@ function rpcFor2(provider) {
609
627
  return ({ method, params }) => rpc(provider, method, params);
610
628
  }
611
629
  function evmChainIds(req) {
612
- const wanted = req.namespace.value.chains?.[0];
613
- const n = wanted ? Number(wanted.split(":")[1]) : NaN;
614
- return Number.isFinite(n) && n > 0 ? [n] : void 0;
630
+ const ids = (req.namespace.value.chains ?? []).map((chain) => Number(chain.split(":")[1])).filter((n) => Number.isFinite(n) && n > 0);
631
+ return ids.length > 0 ? ids : void 0;
615
632
  }
616
633
 
617
634
  // src/connectors/injected/connector.ts
@@ -1264,13 +1281,16 @@ var WalletConnectConnector = class extends BaseConnector {
1264
1281
  }
1265
1282
  async getClient() {
1266
1283
  if (this.client) return this.client;
1284
+ const redirectUrl = deriveRedirectUrl();
1267
1285
  const client = await SignClient__default.default.init({
1268
1286
  projectId: this.options.projectId,
1269
1287
  metadata: {
1270
1288
  name: this.app.appName,
1271
1289
  description: this.app.description ?? this.app.appName,
1272
1290
  url: deriveUrl(),
1273
- icons: this.app.icon ? [this.app.icon] : []
1291
+ icons: this.app.icon ? [this.app.icon] : [],
1292
+ // Wallets may use this to return the user after deep-link approvals.
1293
+ ...redirectUrl ? { redirect: { universal: redirectUrl } } : {}
1274
1294
  },
1275
1295
  relayUrl: this.options.relayUrl
1276
1296
  });
@@ -1302,8 +1322,11 @@ var WalletConnectConnector = class extends BaseConnector {
1302
1322
  const client = await this.getClient();
1303
1323
  const reused = await this.tryReuse(client, req);
1304
1324
  if (reused) return reused;
1325
+ const proposed = [req.namespace, ...req.extraNamespaces ?? []];
1305
1326
  const { uri, approval } = await client.connect({
1306
- requiredNamespaces: { [req.namespace.name]: req.namespace.value }
1327
+ optionalNamespaces: Object.fromEntries(
1328
+ proposed.map((ns) => [ns.name, ns.value])
1329
+ )
1307
1330
  });
1308
1331
  if (!uri) throw new Error("WalletConnect did not return a pairing URI");
1309
1332
  const link = isMobile() ? this.links[req.walletId] : void 0;
@@ -1335,7 +1358,8 @@ var WalletConnectConnector = class extends BaseConnector {
1335
1358
  client,
1336
1359
  req.walletId,
1337
1360
  session,
1338
- req.namespace
1361
+ req.namespace,
1362
+ req.pinned
1339
1363
  );
1340
1364
  this.topics.set(req.walletId, session.topic);
1341
1365
  this.persist();
@@ -1402,21 +1426,32 @@ var WalletConnectConnector = class extends BaseConnector {
1402
1426
  return null;
1403
1427
  }
1404
1428
  const ns = session.namespaces[req.namespace.name];
1405
- const wanted = req.namespace.value.chains?.[0];
1406
- if (!ns || wanted && !ns.chains?.includes(wanted)) return null;
1407
- return this.toConnection(client, req.walletId, session, req.namespace);
1429
+ if (!ns) return null;
1430
+ if (req.pinned && !grantedChains(ns).includes(req.pinned)) return null;
1431
+ return this.toConnection(
1432
+ client,
1433
+ req.walletId,
1434
+ session,
1435
+ req.namespace,
1436
+ req.pinned
1437
+ );
1408
1438
  }
1409
- toConnection(client, walletId, session, namespace) {
1439
+ toConnection(client, walletId, session, namespace, wanted) {
1410
1440
  const ns = session.namespaces[namespace.name];
1411
1441
  const account = (ns?.accounts ?? []).map((a) => a.split(":")[2]).find((a) => !!a) ?? "";
1412
- const approved = ns?.chains?.length ? ns.chains : (ns?.accounts ?? []).map((a) => a.split(":").slice(0, 2).join(":"));
1413
- const wanted = namespace.value.chains?.[0];
1442
+ const approved = grantedChains(ns);
1414
1443
  const chainId = wanted && approved.includes(wanted) ? wanted : approved[0] ?? `${namespace.name}:1`;
1444
+ const approvedChains = [
1445
+ ...new Set(
1446
+ Object.values(session.namespaces).flatMap((n) => grantedChains(n))
1447
+ )
1448
+ ];
1415
1449
  const topic = session.topic;
1416
1450
  return {
1417
1451
  walletId,
1418
1452
  account,
1419
1453
  chainId,
1454
+ approvedChains,
1420
1455
  request: (args) => client.request({
1421
1456
  topic,
1422
1457
  chainId,
@@ -1477,6 +1512,11 @@ var WalletConnectConnector = class extends BaseConnector {
1477
1512
  }
1478
1513
  }
1479
1514
  };
1515
+ function grantedChains(ns) {
1516
+ if (!ns) return [];
1517
+ if (ns.chains?.length) return ns.chains;
1518
+ return (ns.accounts ?? []).map((a) => a.split(":").slice(0, 2).join(":"));
1519
+ }
1480
1520
  function firstNamespace(session) {
1481
1521
  const name = Object.keys(session.namespaces)[0];
1482
1522
  if (!name) return null;
@@ -1521,14 +1561,35 @@ var LydianConnect = class {
1521
1561
  return this.manager.init();
1522
1562
  }
1523
1563
  async connect(input) {
1524
- if (input.chainId === void 0 || input.chainId === null) {
1564
+ const chains = "chains" in input ? input.chains : void 0;
1565
+ const chainId = "chainId" in input ? input.chainId : void 0;
1566
+ if (chains !== void 0 && chainId !== void 0) {
1567
+ throw new Error(
1568
+ "connect() takes either chainId (pinned) or chains (discovery), not both"
1569
+ );
1570
+ }
1571
+ if (chains !== void 0) {
1572
+ const [primary, ...extra] = buildNamespaces(chains);
1573
+ if (!primary) {
1574
+ throw new Error("connect() discovery requires a non-empty chains list");
1575
+ }
1576
+ return this.manager.connect({
1577
+ walletId: input.walletId,
1578
+ namespace: primary,
1579
+ extraNamespaces: extra.length > 0 ? extra : void 0,
1580
+ signal: input.signal
1581
+ });
1582
+ }
1583
+ if (chainId === void 0 || chainId === null) {
1525
1584
  throw new Error(
1526
- 'connect() requires a chainId (a number like 137 or an "eip155:<id>" string)'
1585
+ 'connect() requires a chainId (a number like 137 or an "eip155:<id>" string) or a chains list'
1527
1586
  );
1528
1587
  }
1588
+ const namespace = buildNamespace(chainId);
1529
1589
  return this.manager.connect({
1530
1590
  walletId: input.walletId,
1531
- namespace: buildNamespace(input.chainId),
1591
+ namespace,
1592
+ pinned: namespace.value.chains?.[0],
1532
1593
  signal: input.signal
1533
1594
  });
1534
1595
  }