@talismn/chain-connectors 1.0.3 → 1.0.5

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
@@ -1 +1,9 @@
1
- # @talismn/chain-connector
1
+ # @talismn/chain-connectors
2
+
3
+ RPC connections to the networks that the Talisman wallet supports, one connector per platform:
4
+
5
+ - `ChainConnectorDot`: Polkadot SDK chains, over `@polkadot-api/substrate-client` and `@polkadot-api/ws-provider`
6
+ - `ChainConnectorEth`: EVM networks, viem clients per network (`getPublicClientForEvmNetwork`, cached; `getWalletClientForEvmNetwork`)
7
+ - `ChainConnectorSol`: Solana networks, `@solana/kit` RPC and transport per network (`getRpc`, `getTransport`, cached). The transport fails over through the network's RPCs in order and sends the next request to the RPC that answered last.
8
+
9
+ The `*Stub` variants connect to a single network from its RPC list, without chaindata.
package/dist/index.d.mts CHANGED
@@ -118,8 +118,17 @@ export declare class ChainConnectorEthStub implements IChainConnectorEth {
118
118
  //#endregion
119
119
  //#region src/sol/getSolRpc.d.ts
120
120
  export type SolRpc = Rpc<SolanaRpcApi>;
121
- export declare const getSolTransport: (_networkId: SolNetworkId, rpcs: string[]) => RpcTransport;
122
- export declare const getSolRpc: (networkId: SolNetworkId, rpcs: string[]) => SolRpc;
121
+ export type SolTransportOptions = {
122
+ /** Called with the url of the RPC that answered, after every successful request */
123
+ onRpcSuccess?: (url: string) => void;
124
+ };
125
+ /**
126
+ * Returns a transport over all of the network's RPCs, in the given order. A request fails over to
127
+ * the next RPC on error, and the RPC that answered is tried first for the next request. When every
128
+ * RPC fails and one of them answered HTTP 429, the whole list is retried with backoff.
129
+ */
130
+ export declare const getSolTransport: (_networkId: SolNetworkId, rpcs: string[], options?: SolTransportOptions) => RpcTransport;
131
+ export declare const getSolRpc: (networkId: SolNetworkId, rpcs: string[], options?: SolTransportOptions) => SolRpc;
123
132
  //#endregion
124
133
  //#region src/sol/IChainConnectorSol.d.ts
125
134
  export interface IChainConnectorSol {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/dot/IChainConnectorDot.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotStub.ts","../src/eth/IChainConnectorEth.ts","../src/eth/ChainConnectorEth.ts","../src/eth/ChainConnectorEthStub.ts","../src/sol/getSolRpc.ts","../src/sol/IChainConnectorSol.ts","../src/sol/ChainConnectorSol.ts","../src/sol/ChainConnectorSolStub.ts"],"mappings":";;;;;YAIY,wBAAwB,OAAO,cAAc;iBAExC;EACf,KAAK,aACH,SAAS,cACT,gBACA,mBACA,uBACA;IACE;MAED,QAAQ;EAEX,UACE,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EAEZ,MAAM,SAAS,eAAe;;;;qBCCnB,6BAA6B;EACxC;EACA;EAEA,YAAY,iBAAiB,UAAU;;qBAQ5B,sBAAsB;EACjC;EACA;EAEA,YAAY,iBAAiB,UAAU;;;;;;;;;;;;;;;;qBA+C5B,6BAA6B;;EAMxC,YAAY,wBAAwB;EAK9B,KAAK,SACT,SAAS,cACT,gBACA,mBACA,oCACA;;;;;;;;;;IAUE;MAED,QAAQ;EAsDL,UACJ,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;;;;;;EA6FN,MAAM,SAAS,eAAY;;UAyBzB;;UA0BA;;;;;;UAgCM;UAuBA;UA8BN;UAoDA;UAWA;UAsBM;;UAYN;;UAQA;UAIA;;;;qBC5fG,iCAAiC;;EAG5C,YAAY,SAAS;EAIf,KAAK,aACT,UAAU,cACV,gBACA,mBACA,yBACC,QAAQ;EAOL,UACJ,UAAU,cACV,yBACA,yBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EA4BZ,SAAS;EAIT;;;;iBChEe;EACf,+BAA+B,cAAc,iBAAiB,QAAQ;EACtE,+BACE,cAAc,cACd,0BAA0B,YACvB,QAAQ;EACb,yBAAyB,eAAe;;;;qBCE7B,6BAA6B;;EAGxC,YAAY,mBAAmB,4BAA4B;EAIrD,6BAA6B,cAAc,eAAe,QAAQ;EAOlE,6BACJ,cAAc,cACd,0BAA0B,UACzB,QAAQ;EAOJ,uBAAuB,eAAe;;;;qBC5BlC,iCAAiC;;EAG5C,YAAY,SAAS;EAIf,gCAAgC,QAAQ;EAIxC,6BACJ,YAAY,cACZ,0BAA0B,UACzB,QAAQ;EAIX;;;;YCrBU,SAAS,IAAI;qBA2DZ,kBAAe,YAAgB,cAAY,mBAAmB;qBAG9D,YAAS,WAAe,cAAY,mBAAmB;;;iBC7DnD;EACf,SAAS,WAAW,iBAAiB,QAAQ;;EAE7C,eAAe,WAAW,iBAAiB,QAAQ;;;;qBCIxC,6BAA6B;;EAKxC,YAAY,mBAAmB,4BAA4B;EAWrD,aAAa,WAAW,eAAe,QAAQ;EAU/C,OAAO,WAAW,eAAe,QAAQ;;EAQ/C,uBAAuB,YAAY;;;;qBCtCxB,iCAAiC;;EAI5C,YAAY,SAAS,KAAK;EAKpB,UAAU,QAAQ;EAIlB,gBAAgB,QAAQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/dot/IChainConnectorDot.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotStub.ts","../src/eth/IChainConnectorEth.ts","../src/eth/ChainConnectorEth.ts","../src/eth/ChainConnectorEthStub.ts","../src/sol/getSolRpc.ts","../src/sol/IChainConnectorSol.ts","../src/sol/ChainConnectorSol.ts","../src/sol/ChainConnectorSolStub.ts"],"mappings":";;;;;YAIY,wBAAwB,OAAO,cAAc;iBAExC;EACf,KAAK,aACH,SAAS,cACT,gBACA,mBACA,uBACA;IACE;MAED,QAAQ;EAEX,UACE,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EAEZ,MAAM,SAAS,eAAe;;;;qBCCnB,6BAA6B;EACxC;EACA;EAEA,YAAY,iBAAiB,UAAU;;qBAQ5B,sBAAsB;EACjC;EACA;EAEA,YAAY,iBAAiB,UAAU;;;;;;;;;;;;;;;;qBA+C5B,6BAA6B;;EAMxC,YAAY,wBAAwB;EAK9B,KAAK,SACT,SAAS,cACT,gBACA,mBACA,oCACA;;;;;;;;;;IAUE;MAED,QAAQ;EAsDL,UACJ,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;;;;;;EA6FN,MAAM,SAAS,eAAY;;UAyBzB;;UA0BA;;;;;;UAgCM;UAuBA;UA8BN;UAoDA;UAWA;UAsBM;;UAYN;;UAQA;UAIA;;;;qBC5fG,iCAAiC;;EAG5C,YAAY,SAAS;EAIf,KAAK,aACT,UAAU,cACV,gBACA,mBACA,yBACC,QAAQ;EAOL,UACJ,UAAU,cACV,yBACA,yBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EA4BZ,SAAS;EAIT;;;;iBChEe;EACf,+BAA+B,cAAc,iBAAiB,QAAQ;EACtE,+BACE,cAAc,cACd,0BAA0B,YACvB,QAAQ;EACb,yBAAyB,eAAe;;;;qBCE7B,6BAA6B;;EAGxC,YAAY,mBAAmB,4BAA4B;EAIrD,6BAA6B,cAAc,eAAe,QAAQ;EAOlE,6BACJ,cAAc,cACd,0BAA0B,UACzB,QAAQ;EAOJ,uBAAuB,eAAe;;;;qBC5BlC,iCAAiC;;EAG5C,YAAY,SAAS;EAIf,gCAAgC,QAAQ;EAIxC,6BACJ,YAAY,cACZ,0BAA0B,UACzB,QAAQ;EAIX;;;;YCrBU,SAAS,IAAI;YAqHb;;EAEV,gBAAgB;;;;;;;qBAuDL,kBAAe,YACd,cAAY,gBACV,UACL,wBACR;qBAIU,YAAS,WACT,cAAY,gBACT,UACJ,wBACT;;;iBCzLc;EACf,SAAS,WAAW,iBAAiB,QAAQ;;EAE7C,eAAe,WAAW,iBAAiB,QAAQ;;;;qBCIxC,6BAA6B;;EAKxC,YAAY,mBAAmB,4BAA4B;EAWrD,aAAa,WAAW,eAAe,QAAQ;EAU/C,OAAO,WAAW,eAAe,QAAQ;;EAQ/C,uBAAuB,YAAY;;;;qBCtCxB,iCAAiC;;EAI5C,YAAY,SAAS,KAAK;EAKpB,UAAU,QAAQ;EAIlB,gBAAgB,QAAQ"}
package/dist/index.d.ts CHANGED
@@ -118,8 +118,17 @@ export declare class ChainConnectorEthStub implements IChainConnectorEth {
118
118
  //#endregion
119
119
  //#region src/sol/getSolRpc.d.ts
120
120
  export type SolRpc = Rpc<SolanaRpcApi>;
121
- export declare const getSolTransport: (_networkId: SolNetworkId, rpcs: string[]) => RpcTransport;
122
- export declare const getSolRpc: (networkId: SolNetworkId, rpcs: string[]) => SolRpc;
121
+ export type SolTransportOptions = {
122
+ /** Called with the url of the RPC that answered, after every successful request */
123
+ onRpcSuccess?: (url: string) => void;
124
+ };
125
+ /**
126
+ * Returns a transport over all of the network's RPCs, in the given order. A request fails over to
127
+ * the next RPC on error, and the RPC that answered is tried first for the next request. When every
128
+ * RPC fails and one of them answered HTTP 429, the whole list is retried with backoff.
129
+ */
130
+ export declare const getSolTransport: (_networkId: SolNetworkId, rpcs: string[], options?: SolTransportOptions) => RpcTransport;
131
+ export declare const getSolRpc: (networkId: SolNetworkId, rpcs: string[], options?: SolTransportOptions) => SolRpc;
123
132
  //#endregion
124
133
  //#region src/sol/IChainConnectorSol.d.ts
125
134
  export interface IChainConnectorSol {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/dot/IChainConnectorDot.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotStub.ts","../src/eth/IChainConnectorEth.ts","../src/eth/ChainConnectorEth.ts","../src/eth/ChainConnectorEthStub.ts","../src/sol/getSolRpc.ts","../src/sol/IChainConnectorSol.ts","../src/sol/ChainConnectorSol.ts","../src/sol/ChainConnectorSolStub.ts"],"mappings":";;;;;YAIY,wBAAwB,OAAO,cAAc;iBAExC;EACf,KAAK,aACH,SAAS,cACT,gBACA,mBACA,uBACA;IACE;MAED,QAAQ;EAEX,UACE,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EAEZ,MAAM,SAAS,eAAe;;;;qBCCnB,6BAA6B;EACxC;EACA;EAEA,YAAY,iBAAiB,UAAU;;qBAQ5B,sBAAsB;EACjC;EACA;EAEA,YAAY,iBAAiB,UAAU;;;;;;;;;;;;;;;;qBA+C5B,6BAA6B;;EAMxC,YAAY,wBAAwB;EAK9B,KAAK,SACT,SAAS,cACT,gBACA,mBACA,oCACA;;;;;;;;;;IAUE;MAED,QAAQ;EAsDL,UACJ,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;;;;;;EA6FN,MAAM,SAAS,eAAY;;UAyBzB;;UA0BA;;;;;;UAgCM;UAuBA;UA8BN;UAoDA;UAWA;UAsBM;;UAYN;;UAQA;UAIA;;;;qBC5fG,iCAAiC;;EAG5C,YAAY,SAAS;EAIf,KAAK,aACT,UAAU,cACV,gBACA,mBACA,yBACC,QAAQ;EAOL,UACJ,UAAU,cACV,yBACA,yBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EA4BZ,SAAS;EAIT;;;;iBChEe;EACf,+BAA+B,cAAc,iBAAiB,QAAQ;EACtE,+BACE,cAAc,cACd,0BAA0B,YACvB,QAAQ;EACb,yBAAyB,eAAe;;;;qBCE7B,6BAA6B;;EAGxC,YAAY,mBAAmB,4BAA4B;EAIrD,6BAA6B,cAAc,eAAe,QAAQ;EAOlE,6BACJ,cAAc,cACd,0BAA0B,UACzB,QAAQ;EAOJ,uBAAuB,eAAe;;;;qBC5BlC,iCAAiC;;EAG5C,YAAY,SAAS;EAIf,gCAAgC,QAAQ;EAIxC,6BACJ,YAAY,cACZ,0BAA0B,UACzB,QAAQ;EAIX;;;;YCrBU,SAAS,IAAI;qBA2DZ,kBAAe,YAAgB,cAAY,mBAAmB;qBAG9D,YAAS,WAAe,cAAY,mBAAmB;;;iBC7DnD;EACf,SAAS,WAAW,iBAAiB,QAAQ;;EAE7C,eAAe,WAAW,iBAAiB,QAAQ;;;;qBCIxC,6BAA6B;;EAKxC,YAAY,mBAAmB,4BAA4B;EAWrD,aAAa,WAAW,eAAe,QAAQ;EAU/C,OAAO,WAAW,eAAe,QAAQ;;EAQ/C,uBAAuB,YAAY;;;;qBCtCxB,iCAAiC;;EAI5C,YAAY,SAAS,KAAK;EAKpB,UAAU,QAAQ;EAIlB,gBAAgB,QAAQ"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/dot/IChainConnectorDot.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotStub.ts","../src/eth/IChainConnectorEth.ts","../src/eth/ChainConnectorEth.ts","../src/eth/ChainConnectorEthStub.ts","../src/sol/getSolRpc.ts","../src/sol/IChainConnectorSol.ts","../src/sol/ChainConnectorSol.ts","../src/sol/ChainConnectorSolStub.ts"],"mappings":";;;;;YAIY,wBAAwB,OAAO,cAAc;iBAExC;EACf,KAAK,aACH,SAAS,cACT,gBACA,mBACA,uBACA;IACE;MAED,QAAQ;EAEX,UACE,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EAEZ,MAAM,SAAS,eAAe;;;;qBCCnB,6BAA6B;EACxC;EACA;EAEA,YAAY,iBAAiB,UAAU;;qBAQ5B,sBAAsB;EACjC;EACA;EAEA,YAAY,iBAAiB,UAAU;;;;;;;;;;;;;;;;qBA+C5B,6BAA6B;;EAMxC,YAAY,wBAAwB;EAK9B,KAAK,SACT,SAAS,cACT,gBACA,mBACA,oCACA;;;;;;;;;;IAUE;MAED,QAAQ;EAsDL,UACJ,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;;;;;;EA6FN,MAAM,SAAS,eAAY;;UAyBzB;;UA0BA;;;;;;UAgCM;UAuBA;UA8BN;UAoDA;UAWA;UAsBM;;UAYN;;UAQA;UAIA;;;;qBC5fG,iCAAiC;;EAG5C,YAAY,SAAS;EAIf,KAAK,aACT,UAAU,cACV,gBACA,mBACA,yBACC,QAAQ;EAOL,UACJ,UAAU,cACV,yBACA,yBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EA4BZ,SAAS;EAIT;;;;iBChEe;EACf,+BAA+B,cAAc,iBAAiB,QAAQ;EACtE,+BACE,cAAc,cACd,0BAA0B,YACvB,QAAQ;EACb,yBAAyB,eAAe;;;;qBCE7B,6BAA6B;;EAGxC,YAAY,mBAAmB,4BAA4B;EAIrD,6BAA6B,cAAc,eAAe,QAAQ;EAOlE,6BACJ,cAAc,cACd,0BAA0B,UACzB,QAAQ;EAOJ,uBAAuB,eAAe;;;;qBC5BlC,iCAAiC;;EAG5C,YAAY,SAAS;EAIf,gCAAgC,QAAQ;EAIxC,6BACJ,YAAY,cACZ,0BAA0B,UACzB,QAAQ;EAIX;;;;YCrBU,SAAS,IAAI;YAqHb;;EAEV,gBAAgB;;;;;;;qBAuDL,kBAAe,YACd,cAAY,gBACV,UACL,wBACR;qBAIU,YAAS,WACT,cAAY,gBACT,UACJ,wBACT;;;iBCzLc;EACf,SAAS,WAAW,iBAAiB,QAAQ;;EAE7C,eAAe,WAAW,iBAAiB,QAAQ;;;;qBCIxC,6BAA6B;;EAKxC,YAAY,mBAAmB,4BAA4B;EAWrD,aAAa,WAAW,eAAe,QAAQ;EAU/C,OAAO,WAAW,eAAe,QAAQ;;EAQ/C,uBAAuB,YAAY;;;;qBCtCxB,iCAAiC;;EAI5C,YAAY,SAAS,KAAK;EAKpB,UAAU,QAAQ;EAIlB,gBAAgB,QAAQ"}
package/dist/index.js CHANGED
@@ -555,11 +555,15 @@ var ChainConnectorEthStub = class {
555
555
  //#region src/sol/getSolRpc.ts
556
556
  const MAX_429_RETRIES = 5;
557
557
  const BASE_BACKOFF_MS = 500;
558
- /** Returns the delay to wait before retrying, or `null` if the error is not a retryable 429. */
559
- const get429RetryDelay = (error, attempt) => {
558
+ const getRateLimitContext = (error) => {
560
559
  if (!(0, _solana_kit.isSolanaError)(error)) return null;
561
560
  const context = error.context;
562
- if (context.statusCode !== 429) return null;
561
+ return context.statusCode === 429 ? context : null;
562
+ };
563
+ /** Returns the delay to wait before retrying, or `null` if the error is not a retryable 429. */
564
+ const get429RetryDelay = (error, attempt) => {
565
+ const context = getRateLimitContext(error);
566
+ if (!context) return null;
563
567
  const retryAfter = Number(context.headers?.get("retry-after"));
564
568
  if (Number.isFinite(retryAfter) && retryAfter > 0) return retryAfter * 1e3;
565
569
  return BASE_BACKOFF_MS * 2 ** attempt;
@@ -592,8 +596,91 @@ const withRetryOn429 = (transport) => {
592
596
  };
593
597
  return wrapped;
594
598
  };
595
- const getSolTransport = (_networkId, rpcs) => withRetryOn429((0, _solana_kit.createDefaultRpcTransport)({ url: rpcs[0] }));
596
- const getSolRpc = (networkId, rpcs) => (0, _solana_kit.createSolanaRpcFromTransport)(getSolTransport(networkId, rpcs));
599
+ /**
600
+ * How long a request may wait on an RPC before moving on to the next one. The last RPC tried gets
601
+ * no timeout, so a slow but working node still answers when every other one has failed.
602
+ */
603
+ const FAILOVER_TIMEOUT_MS = 2e4;
604
+ const sendWithTimeout = async (transport, config, timeoutMs) => {
605
+ const controller = new AbortController();
606
+ const onAbort = () => controller.abort(config.signal?.reason);
607
+ config.signal?.addEventListener("abort", onAbort, { once: true });
608
+ const timeout = setTimeout(() => controller.abort(/* @__PURE__ */ new Error(`RPC request timed out after ${timeoutMs}ms`)), timeoutMs);
609
+ try {
610
+ return await transport({
611
+ ...config,
612
+ signal: controller.signal
613
+ });
614
+ } finally {
615
+ clearTimeout(timeout);
616
+ config.signal?.removeEventListener("abort", onAbort);
617
+ }
618
+ };
619
+ const isSendTransactionPayload = (payload) => typeof payload === "object" && payload !== null && "method" in payload && payload.method === "sendTransaction" && "params" in payload && Array.isArray(payload.params);
620
+ const withoutPreflight = (config) => {
621
+ if (!isSendTransactionPayload(config.payload)) return config;
622
+ const [transaction, sendConfig] = config.payload.params;
623
+ return {
624
+ ...config,
625
+ payload: {
626
+ ...config.payload,
627
+ params: [transaction, {
628
+ ...sendConfig,
629
+ skipPreflight: true
630
+ }]
631
+ }
632
+ };
633
+ };
634
+ /**
635
+ * Sends each request to one RPC at a time, starting from the one that answered last, and moves on to
636
+ * the next when the request fails. A transport only throws for connection, timeout and HTTP errors:
637
+ * JSON-RPC errors come back in the response body and are returned as is.
638
+ *
639
+ * A failed RPC other than a rate limited one may still have forwarded a transaction. Resending it is
640
+ * safe, as the network processes a signature once, but its preflight simulation on the next RPC
641
+ * could then fail although it landed. So after such a failure, every resend of that transaction,
642
+ * including the retries after a rate limit, skips preflight.
643
+ */
644
+ const withFailover = (urls, options) => {
645
+ const transports = urls.map((url) => (0, _solana_kit.createDefaultRpcTransport)({ url }));
646
+ let preferred = 0;
647
+ const mayHaveBeenProcessed = /* @__PURE__ */ new WeakSet();
648
+ const failover = async (config) => {
649
+ if (!transports.length) throw new Error("No RPCs found for Solana network");
650
+ const start = preferred;
651
+ let lastError;
652
+ let rateLimitError;
653
+ for (let attempt = 0; attempt < transports.length; attempt++) {
654
+ config.signal?.throwIfAborted();
655
+ const index = (start + attempt) % transports.length;
656
+ const transport = transports[index];
657
+ const isLastAttempt = attempt === transports.length - 1;
658
+ const request = mayHaveBeenProcessed.has(config) ? withoutPreflight(config) : config;
659
+ try {
660
+ const response = isLastAttempt ? await transport(request) : await sendWithTimeout(transport, request, FAILOVER_TIMEOUT_MS);
661
+ preferred = index;
662
+ options.onRpcSuccess?.(urls[index]);
663
+ return response;
664
+ } catch (error) {
665
+ if (config.signal?.aborted) throw error;
666
+ lastError = error;
667
+ if (!getRateLimitContext(error)) mayHaveBeenProcessed.add(config);
668
+ else rateLimitError ??= error;
669
+ }
670
+ }
671
+ throw rateLimitError ?? lastError;
672
+ };
673
+ return failover;
674
+ };
675
+ /**
676
+ * Returns a transport over all of the network's RPCs, in the given order. A request fails over to
677
+ * the next RPC on error, and the RPC that answered is tried first for the next request. When every
678
+ * RPC fails and one of them answered HTTP 429, the whole list is retried with backoff.
679
+ */
680
+ const getSolTransport = (_networkId, rpcs, options = {}) => {
681
+ return withRetryOn429(withFailover(rpcs, options));
682
+ };
683
+ const getSolRpc = (networkId, rpcs, options) => (0, _solana_kit.createSolanaRpcFromTransport)(getSolTransport(networkId, rpcs, options));
597
684
  //#endregion
598
685
  //#region src/sol/ChainConnectorSol.ts
599
686
  var ChainConnectorSol = class {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["packageJson.name","getWsProvider","createClient","WsEvent","createClient","getWsProvider","throwAfter","viemChains","fromPairs","toPairs","camelCase","shouldThrow","fallback","http","createPublicClient","createWalletClient","isSolanaError","createDefaultRpcTransport","createSolanaRpcFromTransport","createSolanaRpcFromTransport","createSolanaRpcFromTransport"],"sources":["../package.json","../src/log.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotStub.ts","../src/eth/getChainFromEvmNetwork.ts","../src/eth/getTransportForEvmNetwork.ts","../src/eth/getEvmNetworkPublicClient.ts","../src/eth/getEvmNetworkWalletClient.ts","../src/eth/ChainConnectorEth.ts","../src/eth/ChainConnectorEthStub.ts","../src/sol/getSolRpc.ts","../src/sol/ChainConnectorSol.ts","../src/sol/ChainConnectorSolStub.ts"],"sourcesContent":["","import anylogger from \"anylogger\"\n\nimport packageJson from \"../package.json\"\n\nexport default anylogger(packageJson.name)\n","import { createClient, type SubstrateClient } from \"@polkadot-api/substrate-client\"\nimport {\n getWsProvider,\n type StatusChange,\n WsEvent,\n type WsJsonRpcProvider,\n} from \"@polkadot-api/ws-provider\"\nimport type { DotNetworkId, IChaindataNetworkProvider } from \"@talismn/chaindata-provider\"\n\nimport log from \"../log\"\nimport type { IChainConnectorDot, SubscriptionCallback } from \"./IChainConnectorDot\"\n\n// errors that require an rpc fallback\n// https://docs.blastapi.io/blast-documentation/things-you-need-to-know/error-reference\nconst BAD_RPC_ERRORS: Record<string, string> = {\n \"-32097\": \"Rate limit exceeded\",\n \"-32098\": \"Capacity exceeded\",\n}\n\nconst RESPONSE_TIMEOUT = 30_000 // max wait for an rpc response (including connection time)\nconst KEEP_ALIVE_INTERVAL = 20_000 // periodic system_health to keep idle sockets alive (ws-provider kills quiet sockets after its 40s heartbeat)\n\n/** in-flight requests reject with DestroyedError when a connection is torn down - expected, not worth logging */\nconst isDestroyedError = (error: unknown): boolean =>\n error instanceof Error && error.name === \"DestroyedError\"\nconst STALE_NOTIFY_TIMEOUT = 30_000 // how long a chain can be disconnected before subscribers are notified\n\nexport class ChainConnectionError extends Error {\n type: \"CHAIN_CONNECTION_ERROR\"\n chainId: string\n\n constructor(chainId: string, options?: ErrorOptions) {\n super(`Unable to connect to chain ${chainId}`, options)\n\n this.type = \"CHAIN_CONNECTION_ERROR\"\n this.chainId = chainId\n }\n}\n\nexport class StaleRpcError extends Error {\n type: \"STALE_RPC_ERROR\"\n chainId: string\n\n constructor(chainId: string, options?: ErrorOptions) {\n super(`RPCs are stale/unavailable for chain ${chainId}`, options)\n\n this.type = \"STALE_RPC_ERROR\"\n this.chainId = chainId\n }\n}\n\ntype SocketUserId = number\n\ntype Subscription = {\n subscribeMethod: string\n params: unknown[]\n callback: SubscriptionCallback\n /** cancels the pending subscribe request, if any */\n cancelRequest: (() => void) | null\n /** stops routing notifications to the callback */\n stopFollow: (() => void) | null\n serverSubId: string | number | null\n unsubscribed: boolean\n}\n\ntype Connection = {\n chainId: DotNetworkId\n provider: WsJsonRpcProvider\n client: SubstrateClient\n users: Set<SocketUserId>\n subscriptions: Set<Subscription>\n wasConnected: boolean\n keepAliveInterval: ReturnType<typeof setInterval> | null\n staleTimeout: ReturnType<typeof setTimeout> | null\n}\n\n/**\n * ChainConnector provides an interface similar to a websocket JSON-RPC provider, but with three points of difference:\n *\n * 1. ChainConnector methods all accept a `chainId` instead of an array of RPCs. RPCs are then fetched internally from chaindata.\n * 2. ChainConnector creates only one socket connection per chain (via polkadot-api's ws-provider, which handles\n * endpoint rotation, reconnection and stale-socket detection) and ensures that all downstream requests to a chain\n * share that connection.\n * 3. Subscriptions return a callable `unsubscribe` method instead of an id, and are automatically re-established\n * when the provider reconnects (possibly to another endpoint).\n *\n * Additionally, when run on the clientside of a dapp where `window.talismanSub` is available, instead of spinning up new websocket\n * connections this class will forward all requests through to the wallet backend - where another instance of this class will\n * handle the websocket connections.\n */\nexport class ChainConnectorDot implements IChainConnectorDot {\n #chaindataChainProvider: IChaindataNetworkProvider\n\n #connections: Record<DotNetworkId, Connection> = {}\n #pendingConnections: Record<DotNetworkId, Promise<Connection>> = {}\n\n constructor(chaindataChainProvider: IChaindataNetworkProvider) {\n this.#chaindataChainProvider = chaindataChainProvider\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n async send<T = any>(\n chainId: DotNetworkId,\n method: string,\n params: unknown[],\n _isCacheable?: boolean | undefined,\n extraOptions?: {\n /**\n * Set to `true` if this query is speculative, i.e. if on some chains it's expected that it will raise a wasm unreachable error of the form:\n *\n * 4003: Client error: Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\n *\n * By setting expectErrors to true, this method won't pollute the logs with errors we intend to have happen.\n * An example use case of this is when you plan to catch the wasm unreachable error on chains that don't support the query, and then fall back\n * to another query or perhaps an empty result.\n */\n expectErrors?: boolean\n }\n ): Promise<T> {\n const talismanSub = this.getTalismanSub()\n if (talismanSub !== undefined) {\n try {\n const genesisHash = await this.getGenesisHash(chainId)\n return await talismanSub.send(genesisHash, method, params)\n } catch (error) {\n log.warn(\n `Failed to make wallet-proxied send request for chain ${chainId}. Falling back to plain websocket`,\n error\n )\n }\n }\n\n let socketUserId: SocketUserId\n let connection: Connection\n try {\n ;[socketUserId, connection] = await this.acquireConnection(chainId)\n } catch (error) {\n throw new StaleRpcError(chainId, { cause: error })\n }\n\n try {\n return await this.request<T>(connection, method, params)\n } catch (err) {\n const error = err as (Error & { code?: number; data?: unknown }) | null\n\n if (error?.message === \"TIMEOUT\") {\n log.error(`ChainConnector timeout`, { chainId, error })\n connection.provider.switch()\n throw new Error(\"Timeout\")\n }\n\n const badRpcError = BAD_RPC_ERRORS[error?.code?.toString() ?? \"\"]\n if (badRpcError) {\n log.error(`ChainConnector ${badRpcError}`, { error, chainId })\n connection.provider.switch()\n throw new Error(badRpcError)\n }\n\n if (!extraOptions?.expectErrors)\n log.error(\n `Failed to send ${method} on chain ${chainId}\\nparams: ${JSON.stringify(params)}`,\n {\n error,\n }\n )\n\n throw error\n } finally {\n this.releaseConnection(chainId, socketUserId)\n }\n }\n\n async subscribe(\n chainId: DotNetworkId,\n subscribeMethod: string,\n responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout: number | false = 30_000 // 30 seconds in milliseconds\n ): Promise<(unsubscribeMethod: string) => void> {\n const talismanSub = this.getTalismanSub()\n if (talismanSub !== undefined) {\n try {\n const genesisHash = await this.getGenesisHash(chainId)\n\n const subscriptionId = await talismanSub.subscribe(\n genesisHash,\n subscribeMethod,\n responseMethod,\n params,\n callback,\n timeout\n )\n\n return (unsubscribeMethod: string) =>\n talismanSub.unsubscribe(subscriptionId, unsubscribeMethod)\n } catch (error) {\n log.warn(\n `Failed to create wallet-proxied subscription for chain ${chainId}. Falling back to plain websocket`,\n error\n )\n }\n }\n\n let socketUserId: SocketUserId\n let connection: Connection\n try {\n ;[socketUserId, connection] = await this.acquireConnection(chainId)\n } catch (error) {\n throw new StaleRpcError(chainId, { cause: error })\n }\n\n const subscription: Subscription = {\n subscribeMethod,\n params,\n callback,\n cancelRequest: null,\n stopFollow: null,\n serverSubId: null,\n unsubscribed: false,\n }\n connection.subscriptions.add(subscription)\n\n // if the chain can't be reached at all, let the caller know after a while (the provider keeps retrying)\n if (timeout && !connection.wasConnected) {\n const staleWarning = setTimeout(() => {\n if (!subscription.unsubscribed && !connection.wasConnected)\n callback(new StaleRpcError(chainId), null)\n }, timeout)\n const clear = () => clearTimeout(staleWarning)\n // piggyback on the follow setup to clear the warning once anything happens\n const originalCallback = subscription.callback\n subscription.callback = (error, result) => {\n clear()\n subscription.callback = originalCallback\n originalCallback(error, result)\n }\n }\n\n this.startSubscription(connection, subscription)\n\n return (unsubscribeMethod: string) => {\n if (subscription.unsubscribed) return\n subscription.unsubscribed = true\n\n subscription.cancelRequest?.()\n subscription.stopFollow?.()\n\n // the connection may have been replaced by a reset() since we subscribed\n const current = this.#connections[chainId]\n // if we are the last user the connection is about to be destroyed: skip the\n // unsubscribe call, server-side subscriptions die with the socket anyway\n const isLastUser = !!current && current.users.size === 1 && current.users.has(socketUserId)\n if (subscription.serverSubId !== null && current && !isLastUser)\n try {\n current.client.request(unsubscribeMethod, [subscription.serverSubId]).catch((error) => {\n if (!isDestroyedError(error)) log.warn(`Failed to unsubscribe from ${chainId}`, error)\n })\n } catch (error) {\n if (!isDestroyedError(error)) log.warn(`Failed to unsubscribe from ${chainId}`, error)\n }\n\n current?.subscriptions.delete(subscription)\n this.releaseConnection(chainId, socketUserId)\n }\n }\n\n /**\n * Kills and recreates the connection for a chain, if any.\n * Useful after changing a network's rpcs to make sure the new list is applied for further requests.\n * Active subscriptions are automatically re-established on the new connection.\n */\n async reset(chainId: DotNetworkId) {\n log.info(\"ChainConnector reset\", chainId)\n const connection = this.#connections[chainId]\n if (!connection) return\n\n this.destroyConnection(connection)\n delete this.#connections[chainId]\n\n // recreate a connection for the active subscriptions, if any\n if (connection.subscriptions.size) {\n try {\n const fresh = await this.createConnection(chainId)\n fresh.users = connection.users\n fresh.subscriptions = connection.subscriptions\n this.#connections[chainId] = fresh\n for (const subscription of fresh.subscriptions) this.startSubscription(fresh, subscription)\n } catch (error) {\n log.warn(`Failed to recreate connection for ${chainId} after reset`, error)\n for (const subscription of connection.subscriptions)\n subscription.callback(new StaleRpcError(chainId, { cause: error }), null)\n }\n }\n }\n\n /** Sends a request over a connection, throwing `Error(\"TIMEOUT\")` if no response arrives in time */\n private request<T>(\n connection: Connection,\n method: string,\n params: unknown[],\n timeoutMs = RESPONSE_TIMEOUT\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n cancel()\n reject(new Error(\"TIMEOUT\"))\n }, timeoutMs)\n\n const cancel = connection.client._request<T, unknown>(method, params, {\n onSuccess: (result) => {\n clearTimeout(timer)\n resolve(result)\n },\n onError: (error) => {\n clearTimeout(timer)\n reject(error)\n },\n })\n })\n }\n\n /** Issues the subscribe request and routes notifications to the subscription callback */\n private startSubscription(connection: Connection, subscription: Subscription) {\n subscription.serverSubId = null\n subscription.stopFollow = null\n\n subscription.cancelRequest = connection.client._request<string | number, unknown>(\n subscription.subscribeMethod,\n subscription.params,\n {\n onSuccess: (serverSubId, follow) => {\n subscription.cancelRequest = null\n if (subscription.unsubscribed) return\n\n subscription.serverSubId = serverSubId\n // raw-client matches notifications by the raw (not stringified) subscription id, but types it as string\n subscription.stopFollow = follow(serverSubId as string, {\n next: (result) => subscription.callback(null, result),\n error: (error) => subscription.callback(error, null),\n })\n },\n onError: (error) => {\n subscription.cancelRequest = null\n if (!subscription.unsubscribed) subscription.callback(error, null)\n },\n }\n )\n }\n\n /**\n * Get (or create) the shared connection for a chain.\n *\n * The caller must call releaseConnection with the returned SocketUserId once they are finished with it.\n */\n private async acquireConnection(chainId: DotNetworkId): Promise<[SocketUserId, Connection]> {\n // a single creation promise per chain guarantees that concurrent callers share the same connection\n let connection = this.#connections[chainId]\n if (!connection) {\n if (!this.#pendingConnections[chainId]) {\n this.#pendingConnections[chainId] = this.createConnection(chainId)\n .then((created) => {\n this.#connections[chainId] = created\n return created\n })\n .finally(() => {\n delete this.#pendingConnections[chainId]\n })\n }\n connection = await this.#pendingConnections[chainId]\n }\n\n const socketUserId = this.getExclusiveRandomId([...connection.users])\n connection.users.add(socketUserId)\n\n return [socketUserId, connection]\n }\n\n private async createConnection(chainId: DotNetworkId): Promise<Connection> {\n const chain = await this.#chaindataChainProvider.getNetworkById(chainId, \"polkadot\")\n if (!chain) throw new Error(`Chain ${chainId} not found in store`)\n\n const rpcs = chain.rpcs.concat()\n if (!rpcs.length) throw new Error(`No healthy RPCs available for chain ${chainId}`)\n\n // will be assigned below - the provider config needs to reference the connection\n let connection: Connection = null as unknown as Connection\n\n const provider = getWsProvider(rpcs, {\n onStatusChanged: (status: StatusChange) => this.handleStatusChange(connection, status),\n })\n\n const client = createClient(provider)\n\n connection = {\n chainId,\n provider,\n client,\n users: new Set(),\n subscriptions: new Set(),\n wasConnected: false,\n keepAliveInterval: null,\n staleTimeout: null,\n }\n\n return connection\n }\n\n private handleStatusChange(connection: Connection, status: StatusChange) {\n // connection is still being constructed on the initial CONNECTING event\n if (!connection) return\n // ignore events from a connection that has been reset/destroyed\n if (this.#connections[connection.chainId] !== connection) return\n\n if (status.type === WsEvent.CONNECTED) {\n if (connection.staleTimeout) {\n clearTimeout(connection.staleTimeout)\n connection.staleTimeout = null\n }\n\n const isReconnect = connection.wasConnected\n connection.wasConnected = true\n\n // periodically send a request to keep the socket alive when there is no other traffic\n if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval)\n connection.keepAliveInterval = setInterval(() => {\n this.request(connection, \"system_health\", [], KEEP_ALIVE_INTERVAL).catch((error) => {\n if (!isDestroyedError(error))\n log.warn(`Failed keep-alive for socket ${connection.chainId}`, error)\n })\n }, KEEP_ALIVE_INTERVAL)\n\n // server-side subscriptions died with the previous socket : re-establish them\n // (skip author_* subscriptions : resubmitting an extrinsic is not safe)\n if (isReconnect)\n for (const subscription of connection.subscriptions) {\n if (subscription.unsubscribed) continue\n if (subscription.subscribeMethod.startsWith(\"author_\")) continue\n subscription.stopFollow?.()\n subscription.cancelRequest?.()\n this.startSubscription(connection, subscription)\n }\n } else {\n if (connection.keepAliveInterval) {\n clearInterval(connection.keepAliveInterval)\n connection.keepAliveInterval = null\n }\n\n // notify subscribers if the chain stays unreachable for too long\n if (!connection.staleTimeout && connection.subscriptions.size) {\n connection.staleTimeout = setTimeout(() => {\n connection.staleTimeout = null\n for (const subscription of connection.subscriptions)\n if (!subscription.unsubscribed)\n subscription.callback(new StaleRpcError(connection.chainId), null)\n }, STALE_NOTIFY_TIMEOUT)\n }\n }\n }\n\n private releaseConnection(chainId: DotNetworkId, socketUserId: SocketUserId): void {\n const connection = this.#connections[chainId]\n if (!connection) return\n\n connection.users.delete(socketUserId)\n if (connection.users.size > 0) return\n\n this.destroyConnection(connection)\n delete this.#connections[chainId]\n }\n\n private destroyConnection(connection: Connection): void {\n if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval)\n if (connection.staleTimeout) clearTimeout(connection.staleTimeout)\n connection.keepAliveInterval = null\n connection.staleTimeout = null\n\n // detach subscriptions before destroying the client so their callbacks don't receive a DestroyedError\n for (const subscription of connection.subscriptions) {\n subscription.stopFollow?.()\n subscription.cancelRequest?.()\n subscription.stopFollow = null\n subscription.cancelRequest = null\n subscription.serverSubId = null\n }\n\n try {\n connection.client.destroy()\n } catch (error) {\n log.warn(`Error occurred destroying connection ${connection.chainId}`, error)\n }\n }\n\n private async getGenesisHash(chainId: DotNetworkId): Promise<string> {\n const chain = await this.#chaindataChainProvider.getNetworkById(chainId, \"polkadot\")\n if (!chain) throw new Error(`Chain ${chainId} not found in store`)\n\n const { genesisHash } = chain\n if (typeof genesisHash !== \"string\")\n throw new Error(`Chain ${chainId} has no genesisHash in store`)\n\n return genesisHash\n }\n\n /** continues to generate a random number until it finds one which is not present in the exclude list */\n private getExclusiveRandomId(exclude: number[] = []): number {\n let id = this.getRandomId()\n while (exclude.includes(id)) {\n id = this.getRandomId()\n }\n return id\n }\n /** generates a random number */\n private getRandomId(): number {\n return Math.trunc(Math.random() * 10 ** 8)\n }\n\n private getTalismanSub() {\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n const talismanSub = typeof window !== \"undefined\" && (window as any).talismanSub\n\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashSend: Function | undefined = talismanSub?.rpcByGenesisHashSend\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashSubscribe: Function | undefined = talismanSub?.rpcByGenesisHashSubscribe\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashUnsubscribe: Function | undefined =\n talismanSub?.rpcByGenesisHashUnsubscribe\n\n if (typeof rpcByGenesisHashSend !== \"function\") return\n if (typeof rpcByGenesisHashSubscribe !== \"function\") return\n if (typeof rpcByGenesisHashUnsubscribe !== \"function\") return\n\n return {\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n send: <T = any>(genesisHash: string, method: string, params: unknown[]): Promise<T> =>\n rpcByGenesisHashSend(genesisHash, method, params),\n\n subscribe: (\n genesisHash: string,\n subscribeMethod: string,\n responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout: number | false\n ): Promise<string> =>\n rpcByGenesisHashSubscribe(\n genesisHash,\n subscribeMethod,\n responseMethod,\n params,\n callback,\n timeout\n ),\n\n unsubscribe: (subscriptionId: string, unsubscribeMethod: string): Promise<void> =>\n rpcByGenesisHashUnsubscribe(subscriptionId, unsubscribeMethod),\n }\n }\n}\n","import { createClient, type SubstrateClient } from \"@polkadot-api/substrate-client\"\nimport { getWsProvider } from \"@polkadot-api/ws-provider\"\nimport type { DotNetwork, DotNetworkId } from \"@talismn/chaindata-provider\"\nimport { throwAfter } from \"@talismn/util\"\n\nimport type { IChainConnectorDot, SubscriptionCallback } from \"./IChainConnectorDot\"\n\nconst TIMEOUT = 10_000\n\nexport class ChainConnectorDotStub implements IChainConnectorDot {\n #client: SubstrateClient\n\n constructor(network: DotNetwork) {\n this.#client = createClient(getWsProvider(network.rpcs.concat()))\n }\n\n async send<T = unknown>(\n _chainId: DotNetworkId,\n method: string,\n params: unknown[],\n _isCacheable?: boolean\n ): Promise<T> {\n return await Promise.race([\n this.#client.request<T>(method, params),\n throwAfter(TIMEOUT, `Request ${method} timed out after ${TIMEOUT}ms`),\n ])\n }\n\n async subscribe(\n _chainId: DotNetworkId,\n subscribeMethod: string,\n _responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout?: number | false\n ): Promise<(unsubscribeMethod: string) => void> {\n let stopFollow: (() => void) | null = null\n let unsubscribed = false\n\n const serverSubId = await Promise.race([\n new Promise<string | number>((resolve, reject) => {\n this.#client._request<string | number, unknown>(subscribeMethod, params, {\n onSuccess: (subId, follow) => {\n stopFollow = follow(subId as string, {\n next: (result) => callback(null, result),\n error: (error) => callback(error, null),\n })\n resolve(subId)\n },\n onError: reject,\n })\n }),\n throwAfter(timeout || TIMEOUT, `Subscription timed out after ${timeout || TIMEOUT}ms`),\n ])\n\n return (unsubscribeMethod: string) => {\n if (unsubscribed) return\n unsubscribed = true\n stopFollow?.()\n this.#client.request(unsubscribeMethod, [serverSubId]).catch(() => {}) // connection may already be gone\n }\n }\n\n reset(): Promise<void> {\n throw new Error(\"ChainConnectorDotStub does not implement reset\")\n }\n\n destroy() {\n this.#client.destroy()\n }\n}\n","import type { EthNetwork, EthNetworkId } from \"@talismn/chaindata-provider\"\nimport { camelCase, fromPairs, toPairs } from \"lodash-es\"\nimport type { Chain, ChainContract } from \"viem\"\nimport * as viemChains from \"viem/chains\"\n\n// exclude zoraTestnet which uses Hyperliquid's chain id\nconst { zoraTestnet, ...validViemChains } = viemChains\n\n// viem chains benefit from multicall config & other viem goodies\nconst VIEM_CHAINS = Object.keys(validViemChains).reduce(\n (acc, curr) => {\n const chain = validViemChains[curr as keyof typeof validViemChains]\n acc[chain.id] = chain\n return acc\n },\n {} as Record<number, Chain>\n)\n\nconst chainsCache = new Map<string, Chain>()\n\nexport const clearChainsCache = (networkId?: EthNetworkId) => {\n if (networkId) chainsCache.delete(networkId)\n else chainsCache.clear()\n}\n\nexport const getChainFromEvmNetwork = (network: EthNetwork): Chain => {\n const { symbol, decimals } = network.nativeCurrency\n\n if (!chainsCache.has(network.id)) {\n const chainRpcs = network.rpcs ?? []\n\n const viemChain = VIEM_CHAINS[Number(network.id)] ?? {}\n\n const chain: Chain = {\n ...viemChain,\n id: Number(network.id),\n name: network.name ?? `Ethereum Chain ${network.id}`,\n rpcUrls: {\n public: { http: chainRpcs },\n default: { http: chainRpcs },\n },\n nativeCurrency: {\n symbol,\n decimals,\n name: symbol,\n },\n contracts: {\n ...viemChain.contracts,\n ...(network.contracts\n ? fromPairs(\n toPairs(network.contracts).map(([name, address]): [string, ChainContract] => [\n camelCase(name),\n { address },\n ])\n )\n : {}),\n },\n }\n\n chainsCache.set(network.id, chain)\n }\n\n return chainsCache.get(network.id) as Chain\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { fallback, http, shouldThrow } from \"viem\"\n\nexport type TransportOptions = {\n batch?:\n | boolean\n | {\n batchSize?: number | undefined\n wait?: number | undefined\n }\n}\n\n// Frontier nodes report reverts with a message viem does not recognize, which makes fallback retry every rpc\nconst FRONTIER_EXECUTION_ERROR = /VM Exception while processing transaction/\n\nconst isFinalError = (error: Error) =>\n shouldThrow(error) || FRONTIER_EXECUTION_ERROR.test(error.message)\n\nexport const getTransportForEvmNetwork = (\n evmNetwork: EthNetwork,\n options: TransportOptions = {}\n) => {\n if (!evmNetwork.rpcs?.length) throw new Error(\"No RPCs found for EVM network\")\n\n const { batch } = options\n\n return fallback(\n evmNetwork.rpcs.map((url) => http(url, { batch, retryCount: 0 })),\n { retryCount: 0, shouldThrow: isFinalError }\n )\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { createPublicClient, type PublicClient } from \"viem\"\n\nimport { clearChainsCache, getChainFromEvmNetwork } from \"./getChainFromEvmNetwork\"\nimport { getTransportForEvmNetwork, type TransportOptions } from \"./getTransportForEvmNetwork\"\n\nconst MUTLICALL_BATCH_WAIT = 25\nconst MUTLICALL_BATCH_SIZE = 100\n\nconst HTTP_BATCH_WAIT = 25\nconst HTTP_BATCH_SIZE_WITH_MULTICALL = 10\nconst HTTP_BATCH_SIZE_WITHOUT_MULTICALL = 30\n\n// cache to reuse previously created public clients\nconst publicClientCache = new Map<string, PublicClient>()\n\nexport const clearPublicClientCache = (evmNetworkId?: string) => {\n clearChainsCache(evmNetworkId)\n\n if (evmNetworkId) publicClientCache.delete(evmNetworkId)\n else publicClientCache.clear()\n}\n\nexport const getEvmNetworkPublicClient = (network: EthNetwork): PublicClient => {\n const chain = getChainFromEvmNetwork(network)\n\n if (!publicClientCache.has(network.id)) {\n if (!network.rpcs.length) throw new Error(\"No RPCs found for Ethereum network\")\n\n const batch = chain.contracts?.multicall3\n ? { multicall: { wait: MUTLICALL_BATCH_WAIT, batchSize: MUTLICALL_BATCH_SIZE } }\n : undefined\n\n const transportOptions: TransportOptions = {\n batch: {\n batchSize: chain.contracts?.multicall3\n ? HTTP_BATCH_SIZE_WITH_MULTICALL\n : HTTP_BATCH_SIZE_WITHOUT_MULTICALL,\n wait: HTTP_BATCH_WAIT,\n },\n }\n\n const transport = getTransportForEvmNetwork(network, transportOptions)\n\n publicClientCache.set(\n network.id,\n createPublicClient({\n chain,\n transport,\n batch,\n })\n )\n }\n\n return publicClientCache.get(network.id) as PublicClient\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { type Account, createWalletClient, type WalletClient } from \"viem\"\n\nimport { getChainFromEvmNetwork } from \"./getChainFromEvmNetwork\"\nimport { getTransportForEvmNetwork } from \"./getTransportForEvmNetwork\"\n\ntype WalletClientOptions = {\n account?: `0x${string}` | Account\n}\n\nexport const getEvmNetworkWalletClient = (\n network: EthNetwork,\n options: WalletClientOptions = {}\n): WalletClient => {\n const chain = getChainFromEvmNetwork(network)\n\n const transport = getTransportForEvmNetwork(network)\n\n return createWalletClient({ chain, transport, account: options.account })\n}\n","import type {\n EthNetworkId,\n IChaindataNetworkProvider,\n IChaindataTokenProvider,\n} from \"@talismn/chaindata-provider\"\nimport type { Account, PublicClient, WalletClient } from \"viem\"\n\nimport { clearPublicClientCache, getEvmNetworkPublicClient } from \"./getEvmNetworkPublicClient\"\nimport { getEvmNetworkWalletClient } from \"./getEvmNetworkWalletClient\"\nimport type { IChainConnectorEth } from \"./IChainConnectorEth\"\n\nexport class ChainConnectorEth implements IChainConnectorEth {\n #chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider\n\n constructor(chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider) {\n this.#chaindataProvider = chaindataProvider\n }\n\n async getPublicClientForEvmNetwork(evmNetworkId: EthNetworkId): Promise<PublicClient | null> {\n const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, \"ethereum\")\n if (!network) return null\n\n return getEvmNetworkPublicClient(network)\n }\n\n async getWalletClientForEvmNetwork(\n evmNetworkId: EthNetworkId,\n account?: `0x${string}` | Account\n ): Promise<WalletClient | null> {\n const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, \"ethereum\")\n if (!network) return null\n\n return getEvmNetworkWalletClient(network, { account })\n }\n\n public clearRpcProvidersCache(evmNetworkId?: EthNetworkId) {\n clearPublicClientCache(evmNetworkId)\n }\n}\n","import type { EthNetwork, EthNetworkId } from \"@talismn/chaindata-provider\"\nimport type { Account, PublicClient, WalletClient } from \"viem\"\n\nimport { getEvmNetworkPublicClient } from \"./getEvmNetworkPublicClient\"\nimport { getEvmNetworkWalletClient } from \"./getEvmNetworkWalletClient\"\nimport type { IChainConnectorEth } from \"./IChainConnectorEth\"\n\nexport class ChainConnectorEthStub implements IChainConnectorEth {\n #network: EthNetwork\n\n constructor(network: EthNetwork) {\n this.#network = network\n }\n\n async getPublicClientForEvmNetwork(): Promise<PublicClient | null> {\n return getEvmNetworkPublicClient(this.#network)\n }\n\n async getWalletClientForEvmNetwork(\n _networkId: EthNetworkId,\n account?: `0x${string}` | Account\n ): Promise<WalletClient | null> {\n return getEvmNetworkWalletClient(this.#network, { account })\n }\n\n clearRpcProvidersCache(): void {\n // No-op for stub\n }\n}\n","import type { Rpc, RpcTransport, SolanaRpcApi } from \"@solana/kit\"\nimport { createDefaultRpcTransport, createSolanaRpcFromTransport, isSolanaError } from \"@solana/kit\"\nimport type { SolNetworkId } from \"@talismn/chaindata-provider\"\n\nexport type SolRpc = Rpc<SolanaRpcApi>\n\n// Public Solana RPC nodes rate-limit aggressively with HTTP 429. kit's default transport is\n// single-shot, unlike the old web3.js `Connection` which retried 429 up to 5x with backoff, so a\n// single 429 would fail an otherwise-valid request (e.g. a user's transaction submission, or token\n// discovery silently returning no tokens). This wrapper restores that behaviour.\nconst MAX_429_RETRIES = 5\nconst BASE_BACKOFF_MS = 500\n\n/** Returns the delay to wait before retrying, or `null` if the error is not a retryable 429. */\nconst get429RetryDelay = (error: unknown, attempt: number): number | null => {\n if (!isSolanaError(error)) return null\n\n const context = error.context as { statusCode?: number; headers?: Headers }\n if (context.statusCode !== 429) return null\n\n // honour the server's Retry-After header (delta-seconds) when present\n const retryAfter = Number(context.headers?.get(\"retry-after\"))\n if (Number.isFinite(retryAfter) && retryAfter > 0) return retryAfter * 1000\n\n // otherwise exponential backoff: 500ms, 1s, 2s, 4s, 8s\n return BASE_BACKOFF_MS * 2 ** attempt\n}\n\nconst sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason)\n\n const onAbort = () => {\n clearTimeout(timeout)\n reject(signal?.reason)\n }\n const timeout = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort)\n resolve()\n }, ms)\n signal?.addEventListener(\"abort\", onAbort, { once: true })\n })\n\n/**\n * Wraps a transport so HTTP 429 responses are retried with backoff. Solana RPC requests are\n * idempotent (`sendTransaction` is keyed by signature), so replaying a rate-limited request is safe.\n */\nconst withRetryOn429 = (transport: RpcTransport): RpcTransport => {\n const wrapped = async (config: Parameters<RpcTransport>[0]) => {\n for (let attempt = 0; ; attempt++) {\n try {\n return await transport(config)\n } catch (error) {\n const delay = attempt < MAX_429_RETRIES ? get429RetryDelay(error, attempt) : null\n if (delay === null) throw error\n await sleep(delay, config.signal)\n }\n }\n }\n return wrapped as RpcTransport\n}\n\n// TODO leverage multiple rpcs with fallback\nexport const getSolTransport = (_networkId: SolNetworkId, rpcs: string[]): RpcTransport =>\n withRetryOn429(createDefaultRpcTransport({ url: rpcs[0] }))\n\nexport const getSolRpc = (networkId: SolNetworkId, rpcs: string[]): SolRpc =>\n createSolanaRpcFromTransport(getSolTransport(networkId, rpcs))\n","import type { RpcTransport } from \"@solana/kit\"\nimport { createSolanaRpcFromTransport } from \"@solana/kit\"\nimport type {\n IChaindataNetworkProvider,\n IChaindataTokenProvider,\n SolNetworkId,\n} from \"@talismn/chaindata-provider\"\n\nimport type { SolRpc } from \"./getSolRpc\"\nimport { getSolTransport } from \"./getSolRpc\"\nimport type { IChainConnectorSol } from \"./IChainConnectorSol\"\n\nexport class ChainConnectorSol implements IChainConnectorSol {\n #chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider\n #transports = new Map<SolNetworkId, RpcTransport>()\n #rpcs = new Map<SolNetworkId, SolRpc>()\n\n constructor(chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider) {\n this.#chaindataProvider = chaindataProvider\n }\n\n async #getNetworkRpcs(networkId: SolNetworkId): Promise<string[]> {\n const network = await this.#chaindataProvider.getNetworkById(networkId, \"solana\")\n if (!network) throw new Error(`Network not found: ${networkId}`)\n\n return network.rpcs\n }\n\n async getTransport(networkId: SolNetworkId): Promise<RpcTransport> {\n if (!this.#transports.has(networkId))\n this.#transports.set(\n networkId,\n getSolTransport(networkId, await this.#getNetworkRpcs(networkId))\n )\n\n return this.#transports.get(networkId)!\n }\n\n async getRpc(networkId: SolNetworkId): Promise<SolRpc> {\n if (!this.#rpcs.has(networkId))\n this.#rpcs.set(networkId, createSolanaRpcFromTransport(await this.getTransport(networkId)))\n\n return this.#rpcs.get(networkId)!\n }\n\n /** Drops cached transports/rpcs so the next call re-reads the network's rpcs from chaindata */\n clearRpcProvidersCache(networkId?: SolNetworkId) {\n if (networkId) {\n this.#transports.delete(networkId)\n this.#rpcs.delete(networkId)\n } else {\n this.#transports.clear()\n this.#rpcs.clear()\n }\n }\n}\n","import type { RpcTransport } from \"@solana/kit\"\nimport { createSolanaRpcFromTransport } from \"@solana/kit\"\nimport type { SolNetwork } from \"@talismn/chaindata-provider\"\n\nimport type { SolRpc } from \"./getSolRpc\"\nimport { getSolTransport } from \"./getSolRpc\"\nimport type { IChainConnectorSol } from \"./IChainConnectorSol\"\n\nexport class ChainConnectorSolStub implements IChainConnectorSol {\n #transport: RpcTransport\n #rpc: SolRpc\n\n constructor(network: Pick<SolNetwork, \"id\" | \"rpcs\">) {\n this.#transport = getSolTransport(network.id, network.rpcs)\n this.#rpc = createSolanaRpcFromTransport(this.#transport)\n }\n\n async getRpc(): Promise<SolRpc> {\n return this.#rpc\n }\n\n async getTransport(): Promise<RpcTransport> {\n return this.#transport\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["packageJson.name","getWsProvider","createClient","WsEvent","createClient","getWsProvider","throwAfter","viemChains","fromPairs","toPairs","camelCase","shouldThrow","fallback","http","createPublicClient","createWalletClient","isSolanaError","createDefaultRpcTransport","createSolanaRpcFromTransport","createSolanaRpcFromTransport","createSolanaRpcFromTransport"],"sources":["../package.json","../src/log.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotStub.ts","../src/eth/getChainFromEvmNetwork.ts","../src/eth/getTransportForEvmNetwork.ts","../src/eth/getEvmNetworkPublicClient.ts","../src/eth/getEvmNetworkWalletClient.ts","../src/eth/ChainConnectorEth.ts","../src/eth/ChainConnectorEthStub.ts","../src/sol/getSolRpc.ts","../src/sol/ChainConnectorSol.ts","../src/sol/ChainConnectorSolStub.ts"],"sourcesContent":["","import anylogger from \"anylogger\"\n\nimport packageJson from \"../package.json\"\n\nexport default anylogger(packageJson.name)\n","import { createClient, type SubstrateClient } from \"@polkadot-api/substrate-client\"\nimport {\n getWsProvider,\n type StatusChange,\n WsEvent,\n type WsJsonRpcProvider,\n} from \"@polkadot-api/ws-provider\"\nimport type { DotNetworkId, IChaindataNetworkProvider } from \"@talismn/chaindata-provider\"\n\nimport log from \"../log\"\nimport type { IChainConnectorDot, SubscriptionCallback } from \"./IChainConnectorDot\"\n\n// errors that require an rpc fallback\n// https://docs.blastapi.io/blast-documentation/things-you-need-to-know/error-reference\nconst BAD_RPC_ERRORS: Record<string, string> = {\n \"-32097\": \"Rate limit exceeded\",\n \"-32098\": \"Capacity exceeded\",\n}\n\nconst RESPONSE_TIMEOUT = 30_000 // max wait for an rpc response (including connection time)\nconst KEEP_ALIVE_INTERVAL = 20_000 // periodic system_health to keep idle sockets alive (ws-provider kills quiet sockets after its 40s heartbeat)\n\n/** in-flight requests reject with DestroyedError when a connection is torn down - expected, not worth logging */\nconst isDestroyedError = (error: unknown): boolean =>\n error instanceof Error && error.name === \"DestroyedError\"\nconst STALE_NOTIFY_TIMEOUT = 30_000 // how long a chain can be disconnected before subscribers are notified\n\nexport class ChainConnectionError extends Error {\n type: \"CHAIN_CONNECTION_ERROR\"\n chainId: string\n\n constructor(chainId: string, options?: ErrorOptions) {\n super(`Unable to connect to chain ${chainId}`, options)\n\n this.type = \"CHAIN_CONNECTION_ERROR\"\n this.chainId = chainId\n }\n}\n\nexport class StaleRpcError extends Error {\n type: \"STALE_RPC_ERROR\"\n chainId: string\n\n constructor(chainId: string, options?: ErrorOptions) {\n super(`RPCs are stale/unavailable for chain ${chainId}`, options)\n\n this.type = \"STALE_RPC_ERROR\"\n this.chainId = chainId\n }\n}\n\ntype SocketUserId = number\n\ntype Subscription = {\n subscribeMethod: string\n params: unknown[]\n callback: SubscriptionCallback\n /** cancels the pending subscribe request, if any */\n cancelRequest: (() => void) | null\n /** stops routing notifications to the callback */\n stopFollow: (() => void) | null\n serverSubId: string | number | null\n unsubscribed: boolean\n}\n\ntype Connection = {\n chainId: DotNetworkId\n provider: WsJsonRpcProvider\n client: SubstrateClient\n users: Set<SocketUserId>\n subscriptions: Set<Subscription>\n wasConnected: boolean\n keepAliveInterval: ReturnType<typeof setInterval> | null\n staleTimeout: ReturnType<typeof setTimeout> | null\n}\n\n/**\n * ChainConnector provides an interface similar to a websocket JSON-RPC provider, but with three points of difference:\n *\n * 1. ChainConnector methods all accept a `chainId` instead of an array of RPCs. RPCs are then fetched internally from chaindata.\n * 2. ChainConnector creates only one socket connection per chain (via polkadot-api's ws-provider, which handles\n * endpoint rotation, reconnection and stale-socket detection) and ensures that all downstream requests to a chain\n * share that connection.\n * 3. Subscriptions return a callable `unsubscribe` method instead of an id, and are automatically re-established\n * when the provider reconnects (possibly to another endpoint).\n *\n * Additionally, when run on the clientside of a dapp where `window.talismanSub` is available, instead of spinning up new websocket\n * connections this class will forward all requests through to the wallet backend - where another instance of this class will\n * handle the websocket connections.\n */\nexport class ChainConnectorDot implements IChainConnectorDot {\n #chaindataChainProvider: IChaindataNetworkProvider\n\n #connections: Record<DotNetworkId, Connection> = {}\n #pendingConnections: Record<DotNetworkId, Promise<Connection>> = {}\n\n constructor(chaindataChainProvider: IChaindataNetworkProvider) {\n this.#chaindataChainProvider = chaindataChainProvider\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n async send<T = any>(\n chainId: DotNetworkId,\n method: string,\n params: unknown[],\n _isCacheable?: boolean | undefined,\n extraOptions?: {\n /**\n * Set to `true` if this query is speculative, i.e. if on some chains it's expected that it will raise a wasm unreachable error of the form:\n *\n * 4003: Client error: Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\n *\n * By setting expectErrors to true, this method won't pollute the logs with errors we intend to have happen.\n * An example use case of this is when you plan to catch the wasm unreachable error on chains that don't support the query, and then fall back\n * to another query or perhaps an empty result.\n */\n expectErrors?: boolean\n }\n ): Promise<T> {\n const talismanSub = this.getTalismanSub()\n if (talismanSub !== undefined) {\n try {\n const genesisHash = await this.getGenesisHash(chainId)\n return await talismanSub.send(genesisHash, method, params)\n } catch (error) {\n log.warn(\n `Failed to make wallet-proxied send request for chain ${chainId}. Falling back to plain websocket`,\n error\n )\n }\n }\n\n let socketUserId: SocketUserId\n let connection: Connection\n try {\n ;[socketUserId, connection] = await this.acquireConnection(chainId)\n } catch (error) {\n throw new StaleRpcError(chainId, { cause: error })\n }\n\n try {\n return await this.request<T>(connection, method, params)\n } catch (err) {\n const error = err as (Error & { code?: number; data?: unknown }) | null\n\n if (error?.message === \"TIMEOUT\") {\n log.error(`ChainConnector timeout`, { chainId, error })\n connection.provider.switch()\n throw new Error(\"Timeout\")\n }\n\n const badRpcError = BAD_RPC_ERRORS[error?.code?.toString() ?? \"\"]\n if (badRpcError) {\n log.error(`ChainConnector ${badRpcError}`, { error, chainId })\n connection.provider.switch()\n throw new Error(badRpcError)\n }\n\n if (!extraOptions?.expectErrors)\n log.error(\n `Failed to send ${method} on chain ${chainId}\\nparams: ${JSON.stringify(params)}`,\n {\n error,\n }\n )\n\n throw error\n } finally {\n this.releaseConnection(chainId, socketUserId)\n }\n }\n\n async subscribe(\n chainId: DotNetworkId,\n subscribeMethod: string,\n responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout: number | false = 30_000 // 30 seconds in milliseconds\n ): Promise<(unsubscribeMethod: string) => void> {\n const talismanSub = this.getTalismanSub()\n if (talismanSub !== undefined) {\n try {\n const genesisHash = await this.getGenesisHash(chainId)\n\n const subscriptionId = await talismanSub.subscribe(\n genesisHash,\n subscribeMethod,\n responseMethod,\n params,\n callback,\n timeout\n )\n\n return (unsubscribeMethod: string) =>\n talismanSub.unsubscribe(subscriptionId, unsubscribeMethod)\n } catch (error) {\n log.warn(\n `Failed to create wallet-proxied subscription for chain ${chainId}. Falling back to plain websocket`,\n error\n )\n }\n }\n\n let socketUserId: SocketUserId\n let connection: Connection\n try {\n ;[socketUserId, connection] = await this.acquireConnection(chainId)\n } catch (error) {\n throw new StaleRpcError(chainId, { cause: error })\n }\n\n const subscription: Subscription = {\n subscribeMethod,\n params,\n callback,\n cancelRequest: null,\n stopFollow: null,\n serverSubId: null,\n unsubscribed: false,\n }\n connection.subscriptions.add(subscription)\n\n // if the chain can't be reached at all, let the caller know after a while (the provider keeps retrying)\n if (timeout && !connection.wasConnected) {\n const staleWarning = setTimeout(() => {\n if (!subscription.unsubscribed && !connection.wasConnected)\n callback(new StaleRpcError(chainId), null)\n }, timeout)\n const clear = () => clearTimeout(staleWarning)\n // piggyback on the follow setup to clear the warning once anything happens\n const originalCallback = subscription.callback\n subscription.callback = (error, result) => {\n clear()\n subscription.callback = originalCallback\n originalCallback(error, result)\n }\n }\n\n this.startSubscription(connection, subscription)\n\n return (unsubscribeMethod: string) => {\n if (subscription.unsubscribed) return\n subscription.unsubscribed = true\n\n subscription.cancelRequest?.()\n subscription.stopFollow?.()\n\n // the connection may have been replaced by a reset() since we subscribed\n const current = this.#connections[chainId]\n // if we are the last user the connection is about to be destroyed: skip the\n // unsubscribe call, server-side subscriptions die with the socket anyway\n const isLastUser = !!current && current.users.size === 1 && current.users.has(socketUserId)\n if (subscription.serverSubId !== null && current && !isLastUser)\n try {\n current.client.request(unsubscribeMethod, [subscription.serverSubId]).catch((error) => {\n if (!isDestroyedError(error)) log.warn(`Failed to unsubscribe from ${chainId}`, error)\n })\n } catch (error) {\n if (!isDestroyedError(error)) log.warn(`Failed to unsubscribe from ${chainId}`, error)\n }\n\n current?.subscriptions.delete(subscription)\n this.releaseConnection(chainId, socketUserId)\n }\n }\n\n /**\n * Kills and recreates the connection for a chain, if any.\n * Useful after changing a network's rpcs to make sure the new list is applied for further requests.\n * Active subscriptions are automatically re-established on the new connection.\n */\n async reset(chainId: DotNetworkId) {\n log.info(\"ChainConnector reset\", chainId)\n const connection = this.#connections[chainId]\n if (!connection) return\n\n this.destroyConnection(connection)\n delete this.#connections[chainId]\n\n // recreate a connection for the active subscriptions, if any\n if (connection.subscriptions.size) {\n try {\n const fresh = await this.createConnection(chainId)\n fresh.users = connection.users\n fresh.subscriptions = connection.subscriptions\n this.#connections[chainId] = fresh\n for (const subscription of fresh.subscriptions) this.startSubscription(fresh, subscription)\n } catch (error) {\n log.warn(`Failed to recreate connection for ${chainId} after reset`, error)\n for (const subscription of connection.subscriptions)\n subscription.callback(new StaleRpcError(chainId, { cause: error }), null)\n }\n }\n }\n\n /** Sends a request over a connection, throwing `Error(\"TIMEOUT\")` if no response arrives in time */\n private request<T>(\n connection: Connection,\n method: string,\n params: unknown[],\n timeoutMs = RESPONSE_TIMEOUT\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n cancel()\n reject(new Error(\"TIMEOUT\"))\n }, timeoutMs)\n\n const cancel = connection.client._request<T, unknown>(method, params, {\n onSuccess: (result) => {\n clearTimeout(timer)\n resolve(result)\n },\n onError: (error) => {\n clearTimeout(timer)\n reject(error)\n },\n })\n })\n }\n\n /** Issues the subscribe request and routes notifications to the subscription callback */\n private startSubscription(connection: Connection, subscription: Subscription) {\n subscription.serverSubId = null\n subscription.stopFollow = null\n\n subscription.cancelRequest = connection.client._request<string | number, unknown>(\n subscription.subscribeMethod,\n subscription.params,\n {\n onSuccess: (serverSubId, follow) => {\n subscription.cancelRequest = null\n if (subscription.unsubscribed) return\n\n subscription.serverSubId = serverSubId\n // raw-client matches notifications by the raw (not stringified) subscription id, but types it as string\n subscription.stopFollow = follow(serverSubId as string, {\n next: (result) => subscription.callback(null, result),\n error: (error) => subscription.callback(error, null),\n })\n },\n onError: (error) => {\n subscription.cancelRequest = null\n if (!subscription.unsubscribed) subscription.callback(error, null)\n },\n }\n )\n }\n\n /**\n * Get (or create) the shared connection for a chain.\n *\n * The caller must call releaseConnection with the returned SocketUserId once they are finished with it.\n */\n private async acquireConnection(chainId: DotNetworkId): Promise<[SocketUserId, Connection]> {\n // a single creation promise per chain guarantees that concurrent callers share the same connection\n let connection = this.#connections[chainId]\n if (!connection) {\n if (!this.#pendingConnections[chainId]) {\n this.#pendingConnections[chainId] = this.createConnection(chainId)\n .then((created) => {\n this.#connections[chainId] = created\n return created\n })\n .finally(() => {\n delete this.#pendingConnections[chainId]\n })\n }\n connection = await this.#pendingConnections[chainId]\n }\n\n const socketUserId = this.getExclusiveRandomId([...connection.users])\n connection.users.add(socketUserId)\n\n return [socketUserId, connection]\n }\n\n private async createConnection(chainId: DotNetworkId): Promise<Connection> {\n const chain = await this.#chaindataChainProvider.getNetworkById(chainId, \"polkadot\")\n if (!chain) throw new Error(`Chain ${chainId} not found in store`)\n\n const rpcs = chain.rpcs.concat()\n if (!rpcs.length) throw new Error(`No healthy RPCs available for chain ${chainId}`)\n\n // will be assigned below - the provider config needs to reference the connection\n let connection: Connection = null as unknown as Connection\n\n const provider = getWsProvider(rpcs, {\n onStatusChanged: (status: StatusChange) => this.handleStatusChange(connection, status),\n })\n\n const client = createClient(provider)\n\n connection = {\n chainId,\n provider,\n client,\n users: new Set(),\n subscriptions: new Set(),\n wasConnected: false,\n keepAliveInterval: null,\n staleTimeout: null,\n }\n\n return connection\n }\n\n private handleStatusChange(connection: Connection, status: StatusChange) {\n // connection is still being constructed on the initial CONNECTING event\n if (!connection) return\n // ignore events from a connection that has been reset/destroyed\n if (this.#connections[connection.chainId] !== connection) return\n\n if (status.type === WsEvent.CONNECTED) {\n if (connection.staleTimeout) {\n clearTimeout(connection.staleTimeout)\n connection.staleTimeout = null\n }\n\n const isReconnect = connection.wasConnected\n connection.wasConnected = true\n\n // periodically send a request to keep the socket alive when there is no other traffic\n if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval)\n connection.keepAliveInterval = setInterval(() => {\n this.request(connection, \"system_health\", [], KEEP_ALIVE_INTERVAL).catch((error) => {\n if (!isDestroyedError(error))\n log.warn(`Failed keep-alive for socket ${connection.chainId}`, error)\n })\n }, KEEP_ALIVE_INTERVAL)\n\n // server-side subscriptions died with the previous socket : re-establish them\n // (skip author_* subscriptions : resubmitting an extrinsic is not safe)\n if (isReconnect)\n for (const subscription of connection.subscriptions) {\n if (subscription.unsubscribed) continue\n if (subscription.subscribeMethod.startsWith(\"author_\")) continue\n subscription.stopFollow?.()\n subscription.cancelRequest?.()\n this.startSubscription(connection, subscription)\n }\n } else {\n if (connection.keepAliveInterval) {\n clearInterval(connection.keepAliveInterval)\n connection.keepAliveInterval = null\n }\n\n // notify subscribers if the chain stays unreachable for too long\n if (!connection.staleTimeout && connection.subscriptions.size) {\n connection.staleTimeout = setTimeout(() => {\n connection.staleTimeout = null\n for (const subscription of connection.subscriptions)\n if (!subscription.unsubscribed)\n subscription.callback(new StaleRpcError(connection.chainId), null)\n }, STALE_NOTIFY_TIMEOUT)\n }\n }\n }\n\n private releaseConnection(chainId: DotNetworkId, socketUserId: SocketUserId): void {\n const connection = this.#connections[chainId]\n if (!connection) return\n\n connection.users.delete(socketUserId)\n if (connection.users.size > 0) return\n\n this.destroyConnection(connection)\n delete this.#connections[chainId]\n }\n\n private destroyConnection(connection: Connection): void {\n if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval)\n if (connection.staleTimeout) clearTimeout(connection.staleTimeout)\n connection.keepAliveInterval = null\n connection.staleTimeout = null\n\n // detach subscriptions before destroying the client so their callbacks don't receive a DestroyedError\n for (const subscription of connection.subscriptions) {\n subscription.stopFollow?.()\n subscription.cancelRequest?.()\n subscription.stopFollow = null\n subscription.cancelRequest = null\n subscription.serverSubId = null\n }\n\n try {\n connection.client.destroy()\n } catch (error) {\n log.warn(`Error occurred destroying connection ${connection.chainId}`, error)\n }\n }\n\n private async getGenesisHash(chainId: DotNetworkId): Promise<string> {\n const chain = await this.#chaindataChainProvider.getNetworkById(chainId, \"polkadot\")\n if (!chain) throw new Error(`Chain ${chainId} not found in store`)\n\n const { genesisHash } = chain\n if (typeof genesisHash !== \"string\")\n throw new Error(`Chain ${chainId} has no genesisHash in store`)\n\n return genesisHash\n }\n\n /** continues to generate a random number until it finds one which is not present in the exclude list */\n private getExclusiveRandomId(exclude: number[] = []): number {\n let id = this.getRandomId()\n while (exclude.includes(id)) {\n id = this.getRandomId()\n }\n return id\n }\n /** generates a random number */\n private getRandomId(): number {\n return Math.trunc(Math.random() * 10 ** 8)\n }\n\n private getTalismanSub() {\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n const talismanSub = typeof window !== \"undefined\" && (window as any).talismanSub\n\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashSend: Function | undefined = talismanSub?.rpcByGenesisHashSend\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashSubscribe: Function | undefined = talismanSub?.rpcByGenesisHashSubscribe\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashUnsubscribe: Function | undefined =\n talismanSub?.rpcByGenesisHashUnsubscribe\n\n if (typeof rpcByGenesisHashSend !== \"function\") return\n if (typeof rpcByGenesisHashSubscribe !== \"function\") return\n if (typeof rpcByGenesisHashUnsubscribe !== \"function\") return\n\n return {\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n send: <T = any>(genesisHash: string, method: string, params: unknown[]): Promise<T> =>\n rpcByGenesisHashSend(genesisHash, method, params),\n\n subscribe: (\n genesisHash: string,\n subscribeMethod: string,\n responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout: number | false\n ): Promise<string> =>\n rpcByGenesisHashSubscribe(\n genesisHash,\n subscribeMethod,\n responseMethod,\n params,\n callback,\n timeout\n ),\n\n unsubscribe: (subscriptionId: string, unsubscribeMethod: string): Promise<void> =>\n rpcByGenesisHashUnsubscribe(subscriptionId, unsubscribeMethod),\n }\n }\n}\n","import { createClient, type SubstrateClient } from \"@polkadot-api/substrate-client\"\nimport { getWsProvider } from \"@polkadot-api/ws-provider\"\nimport type { DotNetwork, DotNetworkId } from \"@talismn/chaindata-provider\"\nimport { throwAfter } from \"@talismn/util\"\n\nimport type { IChainConnectorDot, SubscriptionCallback } from \"./IChainConnectorDot\"\n\nconst TIMEOUT = 10_000\n\nexport class ChainConnectorDotStub implements IChainConnectorDot {\n #client: SubstrateClient\n\n constructor(network: DotNetwork) {\n this.#client = createClient(getWsProvider(network.rpcs.concat()))\n }\n\n async send<T = unknown>(\n _chainId: DotNetworkId,\n method: string,\n params: unknown[],\n _isCacheable?: boolean\n ): Promise<T> {\n return await Promise.race([\n this.#client.request<T>(method, params),\n throwAfter(TIMEOUT, `Request ${method} timed out after ${TIMEOUT}ms`),\n ])\n }\n\n async subscribe(\n _chainId: DotNetworkId,\n subscribeMethod: string,\n _responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout?: number | false\n ): Promise<(unsubscribeMethod: string) => void> {\n let stopFollow: (() => void) | null = null\n let unsubscribed = false\n\n const serverSubId = await Promise.race([\n new Promise<string | number>((resolve, reject) => {\n this.#client._request<string | number, unknown>(subscribeMethod, params, {\n onSuccess: (subId, follow) => {\n stopFollow = follow(subId as string, {\n next: (result) => callback(null, result),\n error: (error) => callback(error, null),\n })\n resolve(subId)\n },\n onError: reject,\n })\n }),\n throwAfter(timeout || TIMEOUT, `Subscription timed out after ${timeout || TIMEOUT}ms`),\n ])\n\n return (unsubscribeMethod: string) => {\n if (unsubscribed) return\n unsubscribed = true\n stopFollow?.()\n this.#client.request(unsubscribeMethod, [serverSubId]).catch(() => {}) // connection may already be gone\n }\n }\n\n reset(): Promise<void> {\n throw new Error(\"ChainConnectorDotStub does not implement reset\")\n }\n\n destroy() {\n this.#client.destroy()\n }\n}\n","import type { EthNetwork, EthNetworkId } from \"@talismn/chaindata-provider\"\nimport { camelCase, fromPairs, toPairs } from \"lodash-es\"\nimport type { Chain, ChainContract } from \"viem\"\nimport * as viemChains from \"viem/chains\"\n\n// exclude zoraTestnet which uses Hyperliquid's chain id\nconst { zoraTestnet, ...validViemChains } = viemChains\n\n// viem chains benefit from multicall config & other viem goodies\nconst VIEM_CHAINS = Object.keys(validViemChains).reduce(\n (acc, curr) => {\n const chain = validViemChains[curr as keyof typeof validViemChains]\n acc[chain.id] = chain\n return acc\n },\n {} as Record<number, Chain>\n)\n\nconst chainsCache = new Map<string, Chain>()\n\nexport const clearChainsCache = (networkId?: EthNetworkId) => {\n if (networkId) chainsCache.delete(networkId)\n else chainsCache.clear()\n}\n\nexport const getChainFromEvmNetwork = (network: EthNetwork): Chain => {\n const { symbol, decimals } = network.nativeCurrency\n\n if (!chainsCache.has(network.id)) {\n const chainRpcs = network.rpcs ?? []\n\n const viemChain = VIEM_CHAINS[Number(network.id)] ?? {}\n\n const chain: Chain = {\n ...viemChain,\n id: Number(network.id),\n name: network.name ?? `Ethereum Chain ${network.id}`,\n rpcUrls: {\n public: { http: chainRpcs },\n default: { http: chainRpcs },\n },\n nativeCurrency: {\n symbol,\n decimals,\n name: symbol,\n },\n contracts: {\n ...viemChain.contracts,\n ...(network.contracts\n ? fromPairs(\n toPairs(network.contracts).map(([name, address]): [string, ChainContract] => [\n camelCase(name),\n { address },\n ])\n )\n : {}),\n },\n }\n\n chainsCache.set(network.id, chain)\n }\n\n return chainsCache.get(network.id) as Chain\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { fallback, http, shouldThrow } from \"viem\"\n\nexport type TransportOptions = {\n batch?:\n | boolean\n | {\n batchSize?: number | undefined\n wait?: number | undefined\n }\n}\n\n// Frontier nodes report reverts with a message viem does not recognize, which makes fallback retry every rpc\nconst FRONTIER_EXECUTION_ERROR = /VM Exception while processing transaction/\n\nconst isFinalError = (error: Error) =>\n shouldThrow(error) || FRONTIER_EXECUTION_ERROR.test(error.message)\n\nexport const getTransportForEvmNetwork = (\n evmNetwork: EthNetwork,\n options: TransportOptions = {}\n) => {\n if (!evmNetwork.rpcs?.length) throw new Error(\"No RPCs found for EVM network\")\n\n const { batch } = options\n\n return fallback(\n evmNetwork.rpcs.map((url) => http(url, { batch, retryCount: 0 })),\n { retryCount: 0, shouldThrow: isFinalError }\n )\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { createPublicClient, type PublicClient } from \"viem\"\n\nimport { clearChainsCache, getChainFromEvmNetwork } from \"./getChainFromEvmNetwork\"\nimport { getTransportForEvmNetwork, type TransportOptions } from \"./getTransportForEvmNetwork\"\n\nconst MUTLICALL_BATCH_WAIT = 25\nconst MUTLICALL_BATCH_SIZE = 100\n\nconst HTTP_BATCH_WAIT = 25\nconst HTTP_BATCH_SIZE_WITH_MULTICALL = 10\nconst HTTP_BATCH_SIZE_WITHOUT_MULTICALL = 30\n\n// cache to reuse previously created public clients\nconst publicClientCache = new Map<string, PublicClient>()\n\nexport const clearPublicClientCache = (evmNetworkId?: string) => {\n clearChainsCache(evmNetworkId)\n\n if (evmNetworkId) publicClientCache.delete(evmNetworkId)\n else publicClientCache.clear()\n}\n\nexport const getEvmNetworkPublicClient = (network: EthNetwork): PublicClient => {\n const chain = getChainFromEvmNetwork(network)\n\n if (!publicClientCache.has(network.id)) {\n if (!network.rpcs.length) throw new Error(\"No RPCs found for Ethereum network\")\n\n const batch = chain.contracts?.multicall3\n ? { multicall: { wait: MUTLICALL_BATCH_WAIT, batchSize: MUTLICALL_BATCH_SIZE } }\n : undefined\n\n const transportOptions: TransportOptions = {\n batch: {\n batchSize: chain.contracts?.multicall3\n ? HTTP_BATCH_SIZE_WITH_MULTICALL\n : HTTP_BATCH_SIZE_WITHOUT_MULTICALL,\n wait: HTTP_BATCH_WAIT,\n },\n }\n\n const transport = getTransportForEvmNetwork(network, transportOptions)\n\n publicClientCache.set(\n network.id,\n createPublicClient({\n chain,\n transport,\n batch,\n })\n )\n }\n\n return publicClientCache.get(network.id) as PublicClient\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { type Account, createWalletClient, type WalletClient } from \"viem\"\n\nimport { getChainFromEvmNetwork } from \"./getChainFromEvmNetwork\"\nimport { getTransportForEvmNetwork } from \"./getTransportForEvmNetwork\"\n\ntype WalletClientOptions = {\n account?: `0x${string}` | Account\n}\n\nexport const getEvmNetworkWalletClient = (\n network: EthNetwork,\n options: WalletClientOptions = {}\n): WalletClient => {\n const chain = getChainFromEvmNetwork(network)\n\n const transport = getTransportForEvmNetwork(network)\n\n return createWalletClient({ chain, transport, account: options.account })\n}\n","import type {\n EthNetworkId,\n IChaindataNetworkProvider,\n IChaindataTokenProvider,\n} from \"@talismn/chaindata-provider\"\nimport type { Account, PublicClient, WalletClient } from \"viem\"\n\nimport { clearPublicClientCache, getEvmNetworkPublicClient } from \"./getEvmNetworkPublicClient\"\nimport { getEvmNetworkWalletClient } from \"./getEvmNetworkWalletClient\"\nimport type { IChainConnectorEth } from \"./IChainConnectorEth\"\n\nexport class ChainConnectorEth implements IChainConnectorEth {\n #chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider\n\n constructor(chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider) {\n this.#chaindataProvider = chaindataProvider\n }\n\n async getPublicClientForEvmNetwork(evmNetworkId: EthNetworkId): Promise<PublicClient | null> {\n const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, \"ethereum\")\n if (!network) return null\n\n return getEvmNetworkPublicClient(network)\n }\n\n async getWalletClientForEvmNetwork(\n evmNetworkId: EthNetworkId,\n account?: `0x${string}` | Account\n ): Promise<WalletClient | null> {\n const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, \"ethereum\")\n if (!network) return null\n\n return getEvmNetworkWalletClient(network, { account })\n }\n\n public clearRpcProvidersCache(evmNetworkId?: EthNetworkId) {\n clearPublicClientCache(evmNetworkId)\n }\n}\n","import type { EthNetwork, EthNetworkId } from \"@talismn/chaindata-provider\"\nimport type { Account, PublicClient, WalletClient } from \"viem\"\n\nimport { getEvmNetworkPublicClient } from \"./getEvmNetworkPublicClient\"\nimport { getEvmNetworkWalletClient } from \"./getEvmNetworkWalletClient\"\nimport type { IChainConnectorEth } from \"./IChainConnectorEth\"\n\nexport class ChainConnectorEthStub implements IChainConnectorEth {\n #network: EthNetwork\n\n constructor(network: EthNetwork) {\n this.#network = network\n }\n\n async getPublicClientForEvmNetwork(): Promise<PublicClient | null> {\n return getEvmNetworkPublicClient(this.#network)\n }\n\n async getWalletClientForEvmNetwork(\n _networkId: EthNetworkId,\n account?: `0x${string}` | Account\n ): Promise<WalletClient | null> {\n return getEvmNetworkWalletClient(this.#network, { account })\n }\n\n clearRpcProvidersCache(): void {\n // No-op for stub\n }\n}\n","import type { Rpc, RpcTransport, SolanaRpcApi } from \"@solana/kit\"\nimport { createDefaultRpcTransport, createSolanaRpcFromTransport, isSolanaError } from \"@solana/kit\"\nimport type { SolNetworkId } from \"@talismn/chaindata-provider\"\n\nexport type SolRpc = Rpc<SolanaRpcApi>\n\n// Public Solana RPC nodes rate-limit aggressively with HTTP 429. kit's default transport is\n// single-shot, unlike the old web3.js `Connection` which retried 429 up to 5x with backoff, so a\n// single 429 would fail an otherwise-valid request (e.g. a user's transaction submission, or token\n// discovery silently returning no tokens). This wrapper restores that behaviour.\nconst MAX_429_RETRIES = 5\nconst BASE_BACKOFF_MS = 500\n\ntype HttpErrorContext = { statusCode?: number; headers?: Headers }\n\nconst getRateLimitContext = (error: unknown): HttpErrorContext | null => {\n if (!isSolanaError(error)) return null\n\n const context = error.context as HttpErrorContext\n return context.statusCode === 429 ? context : null\n}\n\n/** Returns the delay to wait before retrying, or `null` if the error is not a retryable 429. */\nconst get429RetryDelay = (error: unknown, attempt: number): number | null => {\n const context = getRateLimitContext(error)\n if (!context) return null\n\n // honour the server's Retry-After header (delta-seconds) when present\n const retryAfter = Number(context.headers?.get(\"retry-after\"))\n if (Number.isFinite(retryAfter) && retryAfter > 0) return retryAfter * 1000\n\n // otherwise exponential backoff: 500ms, 1s, 2s, 4s, 8s\n return BASE_BACKOFF_MS * 2 ** attempt\n}\n\nconst sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason)\n\n const onAbort = () => {\n clearTimeout(timeout)\n reject(signal?.reason)\n }\n const timeout = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort)\n resolve()\n }, ms)\n signal?.addEventListener(\"abort\", onAbort, { once: true })\n })\n\n/**\n * Wraps a transport so HTTP 429 responses are retried with backoff. Solana RPC requests are\n * idempotent (`sendTransaction` is keyed by signature), so replaying a rate-limited request is safe.\n */\nconst withRetryOn429 = (transport: RpcTransport): RpcTransport => {\n const wrapped = async (config: Parameters<RpcTransport>[0]) => {\n for (let attempt = 0; ; attempt++) {\n try {\n return await transport(config)\n } catch (error) {\n const delay = attempt < MAX_429_RETRIES ? get429RetryDelay(error, attempt) : null\n if (delay === null) throw error\n await sleep(delay, config.signal)\n }\n }\n }\n return wrapped as RpcTransport\n}\n\n/**\n * How long a request may wait on an RPC before moving on to the next one. The last RPC tried gets\n * no timeout, so a slow but working node still answers when every other one has failed.\n */\nconst FAILOVER_TIMEOUT_MS = 20_000\n\nconst sendWithTimeout = async <TResponse>(\n transport: RpcTransport,\n config: RpcTransportConfig,\n timeoutMs: number\n): Promise<TResponse> => {\n const controller = new AbortController()\n const onAbort = () => controller.abort(config.signal?.reason)\n config.signal?.addEventListener(\"abort\", onAbort, { once: true })\n const timeout = setTimeout(\n () => controller.abort(new Error(`RPC request timed out after ${timeoutMs}ms`)),\n timeoutMs\n )\n\n try {\n return await transport<TResponse>({ ...config, signal: controller.signal })\n } finally {\n clearTimeout(timeout)\n config.signal?.removeEventListener(\"abort\", onAbort)\n }\n}\n\ntype RpcTransportConfig = Parameters<RpcTransport>[0]\n\nconst isSendTransactionPayload = (\n payload: unknown\n): payload is { method: \"sendTransaction\"; params: unknown[] } =>\n typeof payload === \"object\" &&\n payload !== null &&\n \"method\" in payload &&\n payload.method === \"sendTransaction\" &&\n \"params\" in payload &&\n Array.isArray(payload.params)\n\nconst withoutPreflight = (config: RpcTransportConfig): RpcTransportConfig => {\n if (!isSendTransactionPayload(config.payload)) return config\n\n const [transaction, sendConfig] = config.payload.params\n return {\n ...config,\n payload: {\n ...config.payload,\n params: [transaction, { ...(sendConfig as object | undefined), skipPreflight: true }],\n },\n }\n}\n\nexport type SolTransportOptions = {\n /** Called with the url of the RPC that answered, after every successful request */\n onRpcSuccess?: (url: string) => void\n}\n\n/**\n * Sends each request to one RPC at a time, starting from the one that answered last, and moves on to\n * the next when the request fails. A transport only throws for connection, timeout and HTTP errors:\n * JSON-RPC errors come back in the response body and are returned as is.\n *\n * A failed RPC other than a rate limited one may still have forwarded a transaction. Resending it is\n * safe, as the network processes a signature once, but its preflight simulation on the next RPC\n * could then fail although it landed. So after such a failure, every resend of that transaction,\n * including the retries after a rate limit, skips preflight.\n */\nconst withFailover = (urls: string[], options: SolTransportOptions): RpcTransport => {\n const transports = urls.map((url) => createDefaultRpcTransport({ url }))\n let preferred = 0\n const mayHaveBeenProcessed = new WeakSet<RpcTransportConfig>()\n\n const failover = async <TResponse>(config: RpcTransportConfig): Promise<TResponse> => {\n if (!transports.length) throw new Error(\"No RPCs found for Solana network\")\n\n const start = preferred\n let lastError: unknown\n let rateLimitError: unknown\n for (let attempt = 0; attempt < transports.length; attempt++) {\n config.signal?.throwIfAborted()\n const index = (start + attempt) % transports.length\n const transport = transports[index] as RpcTransport\n const isLastAttempt = attempt === transports.length - 1\n const request = mayHaveBeenProcessed.has(config) ? withoutPreflight(config) : config\n\n try {\n const response = isLastAttempt\n ? await transport<TResponse>(request)\n : await sendWithTimeout<TResponse>(transport, request, FAILOVER_TIMEOUT_MS)\n preferred = index\n options.onRpcSuccess?.(urls[index] as string)\n return response\n } catch (error) {\n if (config.signal?.aborted) throw error\n lastError = error\n if (!getRateLimitContext(error)) mayHaveBeenProcessed.add(config)\n else rateLimitError ??= error\n }\n }\n throw rateLimitError ?? lastError\n }\n return failover as RpcTransport\n}\n\n/**\n * Returns a transport over all of the network's RPCs, in the given order. A request fails over to\n * the next RPC on error, and the RPC that answered is tried first for the next request. When every\n * RPC fails and one of them answered HTTP 429, the whole list is retried with backoff.\n */\nexport const getSolTransport = (\n _networkId: SolNetworkId,\n rpcs: string[],\n options: SolTransportOptions = {}\n): RpcTransport => {\n return withRetryOn429(withFailover(rpcs, options))\n}\n\nexport const getSolRpc = (\n networkId: SolNetworkId,\n rpcs: string[],\n options?: SolTransportOptions\n): SolRpc => createSolanaRpcFromTransport(getSolTransport(networkId, rpcs, options))\n","import type { RpcTransport } from \"@solana/kit\"\nimport { createSolanaRpcFromTransport } from \"@solana/kit\"\nimport type {\n IChaindataNetworkProvider,\n IChaindataTokenProvider,\n SolNetworkId,\n} from \"@talismn/chaindata-provider\"\n\nimport type { SolRpc } from \"./getSolRpc\"\nimport { getSolTransport } from \"./getSolRpc\"\nimport type { IChainConnectorSol } from \"./IChainConnectorSol\"\n\nexport class ChainConnectorSol implements IChainConnectorSol {\n #chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider\n #transports = new Map<SolNetworkId, RpcTransport>()\n #rpcs = new Map<SolNetworkId, SolRpc>()\n\n constructor(chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider) {\n this.#chaindataProvider = chaindataProvider\n }\n\n async #getNetworkRpcs(networkId: SolNetworkId): Promise<string[]> {\n const network = await this.#chaindataProvider.getNetworkById(networkId, \"solana\")\n if (!network) throw new Error(`Network not found: ${networkId}`)\n\n return network.rpcs\n }\n\n async getTransport(networkId: SolNetworkId): Promise<RpcTransport> {\n if (!this.#transports.has(networkId))\n this.#transports.set(\n networkId,\n getSolTransport(networkId, await this.#getNetworkRpcs(networkId))\n )\n\n return this.#transports.get(networkId)!\n }\n\n async getRpc(networkId: SolNetworkId): Promise<SolRpc> {\n if (!this.#rpcs.has(networkId))\n this.#rpcs.set(networkId, createSolanaRpcFromTransport(await this.getTransport(networkId)))\n\n return this.#rpcs.get(networkId)!\n }\n\n /** Drops cached transports/rpcs so the next call re-reads the network's rpcs from chaindata */\n clearRpcProvidersCache(networkId?: SolNetworkId) {\n if (networkId) {\n this.#transports.delete(networkId)\n this.#rpcs.delete(networkId)\n } else {\n this.#transports.clear()\n this.#rpcs.clear()\n }\n }\n}\n","import type { RpcTransport } from \"@solana/kit\"\nimport { createSolanaRpcFromTransport } from \"@solana/kit\"\nimport type { SolNetwork } from \"@talismn/chaindata-provider\"\n\nimport type { SolRpc } from \"./getSolRpc\"\nimport { getSolTransport } from \"./getSolRpc\"\nimport type { IChainConnectorSol } from \"./IChainConnectorSol\"\n\nexport class ChainConnectorSolStub implements IChainConnectorSol {\n #transport: RpcTransport\n #rpc: SolRpc\n\n constructor(network: Pick<SolNetwork, \"id\" | \"rpcs\">) {\n this.#transport = getSolTransport(network.id, network.rpcs)\n this.#rpc = createSolanaRpcFromTransport(this.#transport)\n }\n\n async getRpc(): Promise<SolRpc> {\n return this.#rpc\n }\n\n async getTransport(): Promise<RpcTransport> {\n return this.#transport\n }\n}\n"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -530,11 +530,15 @@ var ChainConnectorEthStub = class {
530
530
  //#region src/sol/getSolRpc.ts
531
531
  const MAX_429_RETRIES = 5;
532
532
  const BASE_BACKOFF_MS = 500;
533
- /** Returns the delay to wait before retrying, or `null` if the error is not a retryable 429. */
534
- const get429RetryDelay = (error, attempt) => {
533
+ const getRateLimitContext = (error) => {
535
534
  if (!isSolanaError(error)) return null;
536
535
  const context = error.context;
537
- if (context.statusCode !== 429) return null;
536
+ return context.statusCode === 429 ? context : null;
537
+ };
538
+ /** Returns the delay to wait before retrying, or `null` if the error is not a retryable 429. */
539
+ const get429RetryDelay = (error, attempt) => {
540
+ const context = getRateLimitContext(error);
541
+ if (!context) return null;
538
542
  const retryAfter = Number(context.headers?.get("retry-after"));
539
543
  if (Number.isFinite(retryAfter) && retryAfter > 0) return retryAfter * 1e3;
540
544
  return BASE_BACKOFF_MS * 2 ** attempt;
@@ -567,8 +571,91 @@ const withRetryOn429 = (transport) => {
567
571
  };
568
572
  return wrapped;
569
573
  };
570
- const getSolTransport = (_networkId, rpcs) => withRetryOn429(createDefaultRpcTransport({ url: rpcs[0] }));
571
- const getSolRpc = (networkId, rpcs) => createSolanaRpcFromTransport(getSolTransport(networkId, rpcs));
574
+ /**
575
+ * How long a request may wait on an RPC before moving on to the next one. The last RPC tried gets
576
+ * no timeout, so a slow but working node still answers when every other one has failed.
577
+ */
578
+ const FAILOVER_TIMEOUT_MS = 2e4;
579
+ const sendWithTimeout = async (transport, config, timeoutMs) => {
580
+ const controller = new AbortController();
581
+ const onAbort = () => controller.abort(config.signal?.reason);
582
+ config.signal?.addEventListener("abort", onAbort, { once: true });
583
+ const timeout = setTimeout(() => controller.abort(/* @__PURE__ */ new Error(`RPC request timed out after ${timeoutMs}ms`)), timeoutMs);
584
+ try {
585
+ return await transport({
586
+ ...config,
587
+ signal: controller.signal
588
+ });
589
+ } finally {
590
+ clearTimeout(timeout);
591
+ config.signal?.removeEventListener("abort", onAbort);
592
+ }
593
+ };
594
+ const isSendTransactionPayload = (payload) => typeof payload === "object" && payload !== null && "method" in payload && payload.method === "sendTransaction" && "params" in payload && Array.isArray(payload.params);
595
+ const withoutPreflight = (config) => {
596
+ if (!isSendTransactionPayload(config.payload)) return config;
597
+ const [transaction, sendConfig] = config.payload.params;
598
+ return {
599
+ ...config,
600
+ payload: {
601
+ ...config.payload,
602
+ params: [transaction, {
603
+ ...sendConfig,
604
+ skipPreflight: true
605
+ }]
606
+ }
607
+ };
608
+ };
609
+ /**
610
+ * Sends each request to one RPC at a time, starting from the one that answered last, and moves on to
611
+ * the next when the request fails. A transport only throws for connection, timeout and HTTP errors:
612
+ * JSON-RPC errors come back in the response body and are returned as is.
613
+ *
614
+ * A failed RPC other than a rate limited one may still have forwarded a transaction. Resending it is
615
+ * safe, as the network processes a signature once, but its preflight simulation on the next RPC
616
+ * could then fail although it landed. So after such a failure, every resend of that transaction,
617
+ * including the retries after a rate limit, skips preflight.
618
+ */
619
+ const withFailover = (urls, options) => {
620
+ const transports = urls.map((url) => createDefaultRpcTransport({ url }));
621
+ let preferred = 0;
622
+ const mayHaveBeenProcessed = /* @__PURE__ */ new WeakSet();
623
+ const failover = async (config) => {
624
+ if (!transports.length) throw new Error("No RPCs found for Solana network");
625
+ const start = preferred;
626
+ let lastError;
627
+ let rateLimitError;
628
+ for (let attempt = 0; attempt < transports.length; attempt++) {
629
+ config.signal?.throwIfAborted();
630
+ const index = (start + attempt) % transports.length;
631
+ const transport = transports[index];
632
+ const isLastAttempt = attempt === transports.length - 1;
633
+ const request = mayHaveBeenProcessed.has(config) ? withoutPreflight(config) : config;
634
+ try {
635
+ const response = isLastAttempt ? await transport(request) : await sendWithTimeout(transport, request, FAILOVER_TIMEOUT_MS);
636
+ preferred = index;
637
+ options.onRpcSuccess?.(urls[index]);
638
+ return response;
639
+ } catch (error) {
640
+ if (config.signal?.aborted) throw error;
641
+ lastError = error;
642
+ if (!getRateLimitContext(error)) mayHaveBeenProcessed.add(config);
643
+ else rateLimitError ??= error;
644
+ }
645
+ }
646
+ throw rateLimitError ?? lastError;
647
+ };
648
+ return failover;
649
+ };
650
+ /**
651
+ * Returns a transport over all of the network's RPCs, in the given order. A request fails over to
652
+ * the next RPC on error, and the RPC that answered is tried first for the next request. When every
653
+ * RPC fails and one of them answered HTTP 429, the whole list is retried with backoff.
654
+ */
655
+ const getSolTransport = (_networkId, rpcs, options = {}) => {
656
+ return withRetryOn429(withFailover(rpcs, options));
657
+ };
658
+ const getSolRpc = (networkId, rpcs, options) => createSolanaRpcFromTransport(getSolTransport(networkId, rpcs, options));
572
659
  //#endregion
573
660
  //#region src/sol/ChainConnectorSol.ts
574
661
  var ChainConnectorSol = class {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["packageJson.name"],"sources":["../package.json","../src/log.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotStub.ts","../src/eth/getChainFromEvmNetwork.ts","../src/eth/getTransportForEvmNetwork.ts","../src/eth/getEvmNetworkPublicClient.ts","../src/eth/getEvmNetworkWalletClient.ts","../src/eth/ChainConnectorEth.ts","../src/eth/ChainConnectorEthStub.ts","../src/sol/getSolRpc.ts","../src/sol/ChainConnectorSol.ts","../src/sol/ChainConnectorSolStub.ts"],"sourcesContent":["","import anylogger from \"anylogger\"\n\nimport packageJson from \"../package.json\"\n\nexport default anylogger(packageJson.name)\n","import { createClient, type SubstrateClient } from \"@polkadot-api/substrate-client\"\nimport {\n getWsProvider,\n type StatusChange,\n WsEvent,\n type WsJsonRpcProvider,\n} from \"@polkadot-api/ws-provider\"\nimport type { DotNetworkId, IChaindataNetworkProvider } from \"@talismn/chaindata-provider\"\n\nimport log from \"../log\"\nimport type { IChainConnectorDot, SubscriptionCallback } from \"./IChainConnectorDot\"\n\n// errors that require an rpc fallback\n// https://docs.blastapi.io/blast-documentation/things-you-need-to-know/error-reference\nconst BAD_RPC_ERRORS: Record<string, string> = {\n \"-32097\": \"Rate limit exceeded\",\n \"-32098\": \"Capacity exceeded\",\n}\n\nconst RESPONSE_TIMEOUT = 30_000 // max wait for an rpc response (including connection time)\nconst KEEP_ALIVE_INTERVAL = 20_000 // periodic system_health to keep idle sockets alive (ws-provider kills quiet sockets after its 40s heartbeat)\n\n/** in-flight requests reject with DestroyedError when a connection is torn down - expected, not worth logging */\nconst isDestroyedError = (error: unknown): boolean =>\n error instanceof Error && error.name === \"DestroyedError\"\nconst STALE_NOTIFY_TIMEOUT = 30_000 // how long a chain can be disconnected before subscribers are notified\n\nexport class ChainConnectionError extends Error {\n type: \"CHAIN_CONNECTION_ERROR\"\n chainId: string\n\n constructor(chainId: string, options?: ErrorOptions) {\n super(`Unable to connect to chain ${chainId}`, options)\n\n this.type = \"CHAIN_CONNECTION_ERROR\"\n this.chainId = chainId\n }\n}\n\nexport class StaleRpcError extends Error {\n type: \"STALE_RPC_ERROR\"\n chainId: string\n\n constructor(chainId: string, options?: ErrorOptions) {\n super(`RPCs are stale/unavailable for chain ${chainId}`, options)\n\n this.type = \"STALE_RPC_ERROR\"\n this.chainId = chainId\n }\n}\n\ntype SocketUserId = number\n\ntype Subscription = {\n subscribeMethod: string\n params: unknown[]\n callback: SubscriptionCallback\n /** cancels the pending subscribe request, if any */\n cancelRequest: (() => void) | null\n /** stops routing notifications to the callback */\n stopFollow: (() => void) | null\n serverSubId: string | number | null\n unsubscribed: boolean\n}\n\ntype Connection = {\n chainId: DotNetworkId\n provider: WsJsonRpcProvider\n client: SubstrateClient\n users: Set<SocketUserId>\n subscriptions: Set<Subscription>\n wasConnected: boolean\n keepAliveInterval: ReturnType<typeof setInterval> | null\n staleTimeout: ReturnType<typeof setTimeout> | null\n}\n\n/**\n * ChainConnector provides an interface similar to a websocket JSON-RPC provider, but with three points of difference:\n *\n * 1. ChainConnector methods all accept a `chainId` instead of an array of RPCs. RPCs are then fetched internally from chaindata.\n * 2. ChainConnector creates only one socket connection per chain (via polkadot-api's ws-provider, which handles\n * endpoint rotation, reconnection and stale-socket detection) and ensures that all downstream requests to a chain\n * share that connection.\n * 3. Subscriptions return a callable `unsubscribe` method instead of an id, and are automatically re-established\n * when the provider reconnects (possibly to another endpoint).\n *\n * Additionally, when run on the clientside of a dapp where `window.talismanSub` is available, instead of spinning up new websocket\n * connections this class will forward all requests through to the wallet backend - where another instance of this class will\n * handle the websocket connections.\n */\nexport class ChainConnectorDot implements IChainConnectorDot {\n #chaindataChainProvider: IChaindataNetworkProvider\n\n #connections: Record<DotNetworkId, Connection> = {}\n #pendingConnections: Record<DotNetworkId, Promise<Connection>> = {}\n\n constructor(chaindataChainProvider: IChaindataNetworkProvider) {\n this.#chaindataChainProvider = chaindataChainProvider\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n async send<T = any>(\n chainId: DotNetworkId,\n method: string,\n params: unknown[],\n _isCacheable?: boolean | undefined,\n extraOptions?: {\n /**\n * Set to `true` if this query is speculative, i.e. if on some chains it's expected that it will raise a wasm unreachable error of the form:\n *\n * 4003: Client error: Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\n *\n * By setting expectErrors to true, this method won't pollute the logs with errors we intend to have happen.\n * An example use case of this is when you plan to catch the wasm unreachable error on chains that don't support the query, and then fall back\n * to another query or perhaps an empty result.\n */\n expectErrors?: boolean\n }\n ): Promise<T> {\n const talismanSub = this.getTalismanSub()\n if (talismanSub !== undefined) {\n try {\n const genesisHash = await this.getGenesisHash(chainId)\n return await talismanSub.send(genesisHash, method, params)\n } catch (error) {\n log.warn(\n `Failed to make wallet-proxied send request for chain ${chainId}. Falling back to plain websocket`,\n error\n )\n }\n }\n\n let socketUserId: SocketUserId\n let connection: Connection\n try {\n ;[socketUserId, connection] = await this.acquireConnection(chainId)\n } catch (error) {\n throw new StaleRpcError(chainId, { cause: error })\n }\n\n try {\n return await this.request<T>(connection, method, params)\n } catch (err) {\n const error = err as (Error & { code?: number; data?: unknown }) | null\n\n if (error?.message === \"TIMEOUT\") {\n log.error(`ChainConnector timeout`, { chainId, error })\n connection.provider.switch()\n throw new Error(\"Timeout\")\n }\n\n const badRpcError = BAD_RPC_ERRORS[error?.code?.toString() ?? \"\"]\n if (badRpcError) {\n log.error(`ChainConnector ${badRpcError}`, { error, chainId })\n connection.provider.switch()\n throw new Error(badRpcError)\n }\n\n if (!extraOptions?.expectErrors)\n log.error(\n `Failed to send ${method} on chain ${chainId}\\nparams: ${JSON.stringify(params)}`,\n {\n error,\n }\n )\n\n throw error\n } finally {\n this.releaseConnection(chainId, socketUserId)\n }\n }\n\n async subscribe(\n chainId: DotNetworkId,\n subscribeMethod: string,\n responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout: number | false = 30_000 // 30 seconds in milliseconds\n ): Promise<(unsubscribeMethod: string) => void> {\n const talismanSub = this.getTalismanSub()\n if (talismanSub !== undefined) {\n try {\n const genesisHash = await this.getGenesisHash(chainId)\n\n const subscriptionId = await talismanSub.subscribe(\n genesisHash,\n subscribeMethod,\n responseMethod,\n params,\n callback,\n timeout\n )\n\n return (unsubscribeMethod: string) =>\n talismanSub.unsubscribe(subscriptionId, unsubscribeMethod)\n } catch (error) {\n log.warn(\n `Failed to create wallet-proxied subscription for chain ${chainId}. Falling back to plain websocket`,\n error\n )\n }\n }\n\n let socketUserId: SocketUserId\n let connection: Connection\n try {\n ;[socketUserId, connection] = await this.acquireConnection(chainId)\n } catch (error) {\n throw new StaleRpcError(chainId, { cause: error })\n }\n\n const subscription: Subscription = {\n subscribeMethod,\n params,\n callback,\n cancelRequest: null,\n stopFollow: null,\n serverSubId: null,\n unsubscribed: false,\n }\n connection.subscriptions.add(subscription)\n\n // if the chain can't be reached at all, let the caller know after a while (the provider keeps retrying)\n if (timeout && !connection.wasConnected) {\n const staleWarning = setTimeout(() => {\n if (!subscription.unsubscribed && !connection.wasConnected)\n callback(new StaleRpcError(chainId), null)\n }, timeout)\n const clear = () => clearTimeout(staleWarning)\n // piggyback on the follow setup to clear the warning once anything happens\n const originalCallback = subscription.callback\n subscription.callback = (error, result) => {\n clear()\n subscription.callback = originalCallback\n originalCallback(error, result)\n }\n }\n\n this.startSubscription(connection, subscription)\n\n return (unsubscribeMethod: string) => {\n if (subscription.unsubscribed) return\n subscription.unsubscribed = true\n\n subscription.cancelRequest?.()\n subscription.stopFollow?.()\n\n // the connection may have been replaced by a reset() since we subscribed\n const current = this.#connections[chainId]\n // if we are the last user the connection is about to be destroyed: skip the\n // unsubscribe call, server-side subscriptions die with the socket anyway\n const isLastUser = !!current && current.users.size === 1 && current.users.has(socketUserId)\n if (subscription.serverSubId !== null && current && !isLastUser)\n try {\n current.client.request(unsubscribeMethod, [subscription.serverSubId]).catch((error) => {\n if (!isDestroyedError(error)) log.warn(`Failed to unsubscribe from ${chainId}`, error)\n })\n } catch (error) {\n if (!isDestroyedError(error)) log.warn(`Failed to unsubscribe from ${chainId}`, error)\n }\n\n current?.subscriptions.delete(subscription)\n this.releaseConnection(chainId, socketUserId)\n }\n }\n\n /**\n * Kills and recreates the connection for a chain, if any.\n * Useful after changing a network's rpcs to make sure the new list is applied for further requests.\n * Active subscriptions are automatically re-established on the new connection.\n */\n async reset(chainId: DotNetworkId) {\n log.info(\"ChainConnector reset\", chainId)\n const connection = this.#connections[chainId]\n if (!connection) return\n\n this.destroyConnection(connection)\n delete this.#connections[chainId]\n\n // recreate a connection for the active subscriptions, if any\n if (connection.subscriptions.size) {\n try {\n const fresh = await this.createConnection(chainId)\n fresh.users = connection.users\n fresh.subscriptions = connection.subscriptions\n this.#connections[chainId] = fresh\n for (const subscription of fresh.subscriptions) this.startSubscription(fresh, subscription)\n } catch (error) {\n log.warn(`Failed to recreate connection for ${chainId} after reset`, error)\n for (const subscription of connection.subscriptions)\n subscription.callback(new StaleRpcError(chainId, { cause: error }), null)\n }\n }\n }\n\n /** Sends a request over a connection, throwing `Error(\"TIMEOUT\")` if no response arrives in time */\n private request<T>(\n connection: Connection,\n method: string,\n params: unknown[],\n timeoutMs = RESPONSE_TIMEOUT\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n cancel()\n reject(new Error(\"TIMEOUT\"))\n }, timeoutMs)\n\n const cancel = connection.client._request<T, unknown>(method, params, {\n onSuccess: (result) => {\n clearTimeout(timer)\n resolve(result)\n },\n onError: (error) => {\n clearTimeout(timer)\n reject(error)\n },\n })\n })\n }\n\n /** Issues the subscribe request and routes notifications to the subscription callback */\n private startSubscription(connection: Connection, subscription: Subscription) {\n subscription.serverSubId = null\n subscription.stopFollow = null\n\n subscription.cancelRequest = connection.client._request<string | number, unknown>(\n subscription.subscribeMethod,\n subscription.params,\n {\n onSuccess: (serverSubId, follow) => {\n subscription.cancelRequest = null\n if (subscription.unsubscribed) return\n\n subscription.serverSubId = serverSubId\n // raw-client matches notifications by the raw (not stringified) subscription id, but types it as string\n subscription.stopFollow = follow(serverSubId as string, {\n next: (result) => subscription.callback(null, result),\n error: (error) => subscription.callback(error, null),\n })\n },\n onError: (error) => {\n subscription.cancelRequest = null\n if (!subscription.unsubscribed) subscription.callback(error, null)\n },\n }\n )\n }\n\n /**\n * Get (or create) the shared connection for a chain.\n *\n * The caller must call releaseConnection with the returned SocketUserId once they are finished with it.\n */\n private async acquireConnection(chainId: DotNetworkId): Promise<[SocketUserId, Connection]> {\n // a single creation promise per chain guarantees that concurrent callers share the same connection\n let connection = this.#connections[chainId]\n if (!connection) {\n if (!this.#pendingConnections[chainId]) {\n this.#pendingConnections[chainId] = this.createConnection(chainId)\n .then((created) => {\n this.#connections[chainId] = created\n return created\n })\n .finally(() => {\n delete this.#pendingConnections[chainId]\n })\n }\n connection = await this.#pendingConnections[chainId]\n }\n\n const socketUserId = this.getExclusiveRandomId([...connection.users])\n connection.users.add(socketUserId)\n\n return [socketUserId, connection]\n }\n\n private async createConnection(chainId: DotNetworkId): Promise<Connection> {\n const chain = await this.#chaindataChainProvider.getNetworkById(chainId, \"polkadot\")\n if (!chain) throw new Error(`Chain ${chainId} not found in store`)\n\n const rpcs = chain.rpcs.concat()\n if (!rpcs.length) throw new Error(`No healthy RPCs available for chain ${chainId}`)\n\n // will be assigned below - the provider config needs to reference the connection\n let connection: Connection = null as unknown as Connection\n\n const provider = getWsProvider(rpcs, {\n onStatusChanged: (status: StatusChange) => this.handleStatusChange(connection, status),\n })\n\n const client = createClient(provider)\n\n connection = {\n chainId,\n provider,\n client,\n users: new Set(),\n subscriptions: new Set(),\n wasConnected: false,\n keepAliveInterval: null,\n staleTimeout: null,\n }\n\n return connection\n }\n\n private handleStatusChange(connection: Connection, status: StatusChange) {\n // connection is still being constructed on the initial CONNECTING event\n if (!connection) return\n // ignore events from a connection that has been reset/destroyed\n if (this.#connections[connection.chainId] !== connection) return\n\n if (status.type === WsEvent.CONNECTED) {\n if (connection.staleTimeout) {\n clearTimeout(connection.staleTimeout)\n connection.staleTimeout = null\n }\n\n const isReconnect = connection.wasConnected\n connection.wasConnected = true\n\n // periodically send a request to keep the socket alive when there is no other traffic\n if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval)\n connection.keepAliveInterval = setInterval(() => {\n this.request(connection, \"system_health\", [], KEEP_ALIVE_INTERVAL).catch((error) => {\n if (!isDestroyedError(error))\n log.warn(`Failed keep-alive for socket ${connection.chainId}`, error)\n })\n }, KEEP_ALIVE_INTERVAL)\n\n // server-side subscriptions died with the previous socket : re-establish them\n // (skip author_* subscriptions : resubmitting an extrinsic is not safe)\n if (isReconnect)\n for (const subscription of connection.subscriptions) {\n if (subscription.unsubscribed) continue\n if (subscription.subscribeMethod.startsWith(\"author_\")) continue\n subscription.stopFollow?.()\n subscription.cancelRequest?.()\n this.startSubscription(connection, subscription)\n }\n } else {\n if (connection.keepAliveInterval) {\n clearInterval(connection.keepAliveInterval)\n connection.keepAliveInterval = null\n }\n\n // notify subscribers if the chain stays unreachable for too long\n if (!connection.staleTimeout && connection.subscriptions.size) {\n connection.staleTimeout = setTimeout(() => {\n connection.staleTimeout = null\n for (const subscription of connection.subscriptions)\n if (!subscription.unsubscribed)\n subscription.callback(new StaleRpcError(connection.chainId), null)\n }, STALE_NOTIFY_TIMEOUT)\n }\n }\n }\n\n private releaseConnection(chainId: DotNetworkId, socketUserId: SocketUserId): void {\n const connection = this.#connections[chainId]\n if (!connection) return\n\n connection.users.delete(socketUserId)\n if (connection.users.size > 0) return\n\n this.destroyConnection(connection)\n delete this.#connections[chainId]\n }\n\n private destroyConnection(connection: Connection): void {\n if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval)\n if (connection.staleTimeout) clearTimeout(connection.staleTimeout)\n connection.keepAliveInterval = null\n connection.staleTimeout = null\n\n // detach subscriptions before destroying the client so their callbacks don't receive a DestroyedError\n for (const subscription of connection.subscriptions) {\n subscription.stopFollow?.()\n subscription.cancelRequest?.()\n subscription.stopFollow = null\n subscription.cancelRequest = null\n subscription.serverSubId = null\n }\n\n try {\n connection.client.destroy()\n } catch (error) {\n log.warn(`Error occurred destroying connection ${connection.chainId}`, error)\n }\n }\n\n private async getGenesisHash(chainId: DotNetworkId): Promise<string> {\n const chain = await this.#chaindataChainProvider.getNetworkById(chainId, \"polkadot\")\n if (!chain) throw new Error(`Chain ${chainId} not found in store`)\n\n const { genesisHash } = chain\n if (typeof genesisHash !== \"string\")\n throw new Error(`Chain ${chainId} has no genesisHash in store`)\n\n return genesisHash\n }\n\n /** continues to generate a random number until it finds one which is not present in the exclude list */\n private getExclusiveRandomId(exclude: number[] = []): number {\n let id = this.getRandomId()\n while (exclude.includes(id)) {\n id = this.getRandomId()\n }\n return id\n }\n /** generates a random number */\n private getRandomId(): number {\n return Math.trunc(Math.random() * 10 ** 8)\n }\n\n private getTalismanSub() {\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n const talismanSub = typeof window !== \"undefined\" && (window as any).talismanSub\n\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashSend: Function | undefined = talismanSub?.rpcByGenesisHashSend\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashSubscribe: Function | undefined = talismanSub?.rpcByGenesisHashSubscribe\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashUnsubscribe: Function | undefined =\n talismanSub?.rpcByGenesisHashUnsubscribe\n\n if (typeof rpcByGenesisHashSend !== \"function\") return\n if (typeof rpcByGenesisHashSubscribe !== \"function\") return\n if (typeof rpcByGenesisHashUnsubscribe !== \"function\") return\n\n return {\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n send: <T = any>(genesisHash: string, method: string, params: unknown[]): Promise<T> =>\n rpcByGenesisHashSend(genesisHash, method, params),\n\n subscribe: (\n genesisHash: string,\n subscribeMethod: string,\n responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout: number | false\n ): Promise<string> =>\n rpcByGenesisHashSubscribe(\n genesisHash,\n subscribeMethod,\n responseMethod,\n params,\n callback,\n timeout\n ),\n\n unsubscribe: (subscriptionId: string, unsubscribeMethod: string): Promise<void> =>\n rpcByGenesisHashUnsubscribe(subscriptionId, unsubscribeMethod),\n }\n }\n}\n","import { createClient, type SubstrateClient } from \"@polkadot-api/substrate-client\"\nimport { getWsProvider } from \"@polkadot-api/ws-provider\"\nimport type { DotNetwork, DotNetworkId } from \"@talismn/chaindata-provider\"\nimport { throwAfter } from \"@talismn/util\"\n\nimport type { IChainConnectorDot, SubscriptionCallback } from \"./IChainConnectorDot\"\n\nconst TIMEOUT = 10_000\n\nexport class ChainConnectorDotStub implements IChainConnectorDot {\n #client: SubstrateClient\n\n constructor(network: DotNetwork) {\n this.#client = createClient(getWsProvider(network.rpcs.concat()))\n }\n\n async send<T = unknown>(\n _chainId: DotNetworkId,\n method: string,\n params: unknown[],\n _isCacheable?: boolean\n ): Promise<T> {\n return await Promise.race([\n this.#client.request<T>(method, params),\n throwAfter(TIMEOUT, `Request ${method} timed out after ${TIMEOUT}ms`),\n ])\n }\n\n async subscribe(\n _chainId: DotNetworkId,\n subscribeMethod: string,\n _responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout?: number | false\n ): Promise<(unsubscribeMethod: string) => void> {\n let stopFollow: (() => void) | null = null\n let unsubscribed = false\n\n const serverSubId = await Promise.race([\n new Promise<string | number>((resolve, reject) => {\n this.#client._request<string | number, unknown>(subscribeMethod, params, {\n onSuccess: (subId, follow) => {\n stopFollow = follow(subId as string, {\n next: (result) => callback(null, result),\n error: (error) => callback(error, null),\n })\n resolve(subId)\n },\n onError: reject,\n })\n }),\n throwAfter(timeout || TIMEOUT, `Subscription timed out after ${timeout || TIMEOUT}ms`),\n ])\n\n return (unsubscribeMethod: string) => {\n if (unsubscribed) return\n unsubscribed = true\n stopFollow?.()\n this.#client.request(unsubscribeMethod, [serverSubId]).catch(() => {}) // connection may already be gone\n }\n }\n\n reset(): Promise<void> {\n throw new Error(\"ChainConnectorDotStub does not implement reset\")\n }\n\n destroy() {\n this.#client.destroy()\n }\n}\n","import type { EthNetwork, EthNetworkId } from \"@talismn/chaindata-provider\"\nimport { camelCase, fromPairs, toPairs } from \"lodash-es\"\nimport type { Chain, ChainContract } from \"viem\"\nimport * as viemChains from \"viem/chains\"\n\n// exclude zoraTestnet which uses Hyperliquid's chain id\nconst { zoraTestnet, ...validViemChains } = viemChains\n\n// viem chains benefit from multicall config & other viem goodies\nconst VIEM_CHAINS = Object.keys(validViemChains).reduce(\n (acc, curr) => {\n const chain = validViemChains[curr as keyof typeof validViemChains]\n acc[chain.id] = chain\n return acc\n },\n {} as Record<number, Chain>\n)\n\nconst chainsCache = new Map<string, Chain>()\n\nexport const clearChainsCache = (networkId?: EthNetworkId) => {\n if (networkId) chainsCache.delete(networkId)\n else chainsCache.clear()\n}\n\nexport const getChainFromEvmNetwork = (network: EthNetwork): Chain => {\n const { symbol, decimals } = network.nativeCurrency\n\n if (!chainsCache.has(network.id)) {\n const chainRpcs = network.rpcs ?? []\n\n const viemChain = VIEM_CHAINS[Number(network.id)] ?? {}\n\n const chain: Chain = {\n ...viemChain,\n id: Number(network.id),\n name: network.name ?? `Ethereum Chain ${network.id}`,\n rpcUrls: {\n public: { http: chainRpcs },\n default: { http: chainRpcs },\n },\n nativeCurrency: {\n symbol,\n decimals,\n name: symbol,\n },\n contracts: {\n ...viemChain.contracts,\n ...(network.contracts\n ? fromPairs(\n toPairs(network.contracts).map(([name, address]): [string, ChainContract] => [\n camelCase(name),\n { address },\n ])\n )\n : {}),\n },\n }\n\n chainsCache.set(network.id, chain)\n }\n\n return chainsCache.get(network.id) as Chain\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { fallback, http, shouldThrow } from \"viem\"\n\nexport type TransportOptions = {\n batch?:\n | boolean\n | {\n batchSize?: number | undefined\n wait?: number | undefined\n }\n}\n\n// Frontier nodes report reverts with a message viem does not recognize, which makes fallback retry every rpc\nconst FRONTIER_EXECUTION_ERROR = /VM Exception while processing transaction/\n\nconst isFinalError = (error: Error) =>\n shouldThrow(error) || FRONTIER_EXECUTION_ERROR.test(error.message)\n\nexport const getTransportForEvmNetwork = (\n evmNetwork: EthNetwork,\n options: TransportOptions = {}\n) => {\n if (!evmNetwork.rpcs?.length) throw new Error(\"No RPCs found for EVM network\")\n\n const { batch } = options\n\n return fallback(\n evmNetwork.rpcs.map((url) => http(url, { batch, retryCount: 0 })),\n { retryCount: 0, shouldThrow: isFinalError }\n )\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { createPublicClient, type PublicClient } from \"viem\"\n\nimport { clearChainsCache, getChainFromEvmNetwork } from \"./getChainFromEvmNetwork\"\nimport { getTransportForEvmNetwork, type TransportOptions } from \"./getTransportForEvmNetwork\"\n\nconst MUTLICALL_BATCH_WAIT = 25\nconst MUTLICALL_BATCH_SIZE = 100\n\nconst HTTP_BATCH_WAIT = 25\nconst HTTP_BATCH_SIZE_WITH_MULTICALL = 10\nconst HTTP_BATCH_SIZE_WITHOUT_MULTICALL = 30\n\n// cache to reuse previously created public clients\nconst publicClientCache = new Map<string, PublicClient>()\n\nexport const clearPublicClientCache = (evmNetworkId?: string) => {\n clearChainsCache(evmNetworkId)\n\n if (evmNetworkId) publicClientCache.delete(evmNetworkId)\n else publicClientCache.clear()\n}\n\nexport const getEvmNetworkPublicClient = (network: EthNetwork): PublicClient => {\n const chain = getChainFromEvmNetwork(network)\n\n if (!publicClientCache.has(network.id)) {\n if (!network.rpcs.length) throw new Error(\"No RPCs found for Ethereum network\")\n\n const batch = chain.contracts?.multicall3\n ? { multicall: { wait: MUTLICALL_BATCH_WAIT, batchSize: MUTLICALL_BATCH_SIZE } }\n : undefined\n\n const transportOptions: TransportOptions = {\n batch: {\n batchSize: chain.contracts?.multicall3\n ? HTTP_BATCH_SIZE_WITH_MULTICALL\n : HTTP_BATCH_SIZE_WITHOUT_MULTICALL,\n wait: HTTP_BATCH_WAIT,\n },\n }\n\n const transport = getTransportForEvmNetwork(network, transportOptions)\n\n publicClientCache.set(\n network.id,\n createPublicClient({\n chain,\n transport,\n batch,\n })\n )\n }\n\n return publicClientCache.get(network.id) as PublicClient\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { type Account, createWalletClient, type WalletClient } from \"viem\"\n\nimport { getChainFromEvmNetwork } from \"./getChainFromEvmNetwork\"\nimport { getTransportForEvmNetwork } from \"./getTransportForEvmNetwork\"\n\ntype WalletClientOptions = {\n account?: `0x${string}` | Account\n}\n\nexport const getEvmNetworkWalletClient = (\n network: EthNetwork,\n options: WalletClientOptions = {}\n): WalletClient => {\n const chain = getChainFromEvmNetwork(network)\n\n const transport = getTransportForEvmNetwork(network)\n\n return createWalletClient({ chain, transport, account: options.account })\n}\n","import type {\n EthNetworkId,\n IChaindataNetworkProvider,\n IChaindataTokenProvider,\n} from \"@talismn/chaindata-provider\"\nimport type { Account, PublicClient, WalletClient } from \"viem\"\n\nimport { clearPublicClientCache, getEvmNetworkPublicClient } from \"./getEvmNetworkPublicClient\"\nimport { getEvmNetworkWalletClient } from \"./getEvmNetworkWalletClient\"\nimport type { IChainConnectorEth } from \"./IChainConnectorEth\"\n\nexport class ChainConnectorEth implements IChainConnectorEth {\n #chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider\n\n constructor(chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider) {\n this.#chaindataProvider = chaindataProvider\n }\n\n async getPublicClientForEvmNetwork(evmNetworkId: EthNetworkId): Promise<PublicClient | null> {\n const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, \"ethereum\")\n if (!network) return null\n\n return getEvmNetworkPublicClient(network)\n }\n\n async getWalletClientForEvmNetwork(\n evmNetworkId: EthNetworkId,\n account?: `0x${string}` | Account\n ): Promise<WalletClient | null> {\n const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, \"ethereum\")\n if (!network) return null\n\n return getEvmNetworkWalletClient(network, { account })\n }\n\n public clearRpcProvidersCache(evmNetworkId?: EthNetworkId) {\n clearPublicClientCache(evmNetworkId)\n }\n}\n","import type { EthNetwork, EthNetworkId } from \"@talismn/chaindata-provider\"\nimport type { Account, PublicClient, WalletClient } from \"viem\"\n\nimport { getEvmNetworkPublicClient } from \"./getEvmNetworkPublicClient\"\nimport { getEvmNetworkWalletClient } from \"./getEvmNetworkWalletClient\"\nimport type { IChainConnectorEth } from \"./IChainConnectorEth\"\n\nexport class ChainConnectorEthStub implements IChainConnectorEth {\n #network: EthNetwork\n\n constructor(network: EthNetwork) {\n this.#network = network\n }\n\n async getPublicClientForEvmNetwork(): Promise<PublicClient | null> {\n return getEvmNetworkPublicClient(this.#network)\n }\n\n async getWalletClientForEvmNetwork(\n _networkId: EthNetworkId,\n account?: `0x${string}` | Account\n ): Promise<WalletClient | null> {\n return getEvmNetworkWalletClient(this.#network, { account })\n }\n\n clearRpcProvidersCache(): void {\n // No-op for stub\n }\n}\n","import type { Rpc, RpcTransport, SolanaRpcApi } from \"@solana/kit\"\nimport { createDefaultRpcTransport, createSolanaRpcFromTransport, isSolanaError } from \"@solana/kit\"\nimport type { SolNetworkId } from \"@talismn/chaindata-provider\"\n\nexport type SolRpc = Rpc<SolanaRpcApi>\n\n// Public Solana RPC nodes rate-limit aggressively with HTTP 429. kit's default transport is\n// single-shot, unlike the old web3.js `Connection` which retried 429 up to 5x with backoff, so a\n// single 429 would fail an otherwise-valid request (e.g. a user's transaction submission, or token\n// discovery silently returning no tokens). This wrapper restores that behaviour.\nconst MAX_429_RETRIES = 5\nconst BASE_BACKOFF_MS = 500\n\n/** Returns the delay to wait before retrying, or `null` if the error is not a retryable 429. */\nconst get429RetryDelay = (error: unknown, attempt: number): number | null => {\n if (!isSolanaError(error)) return null\n\n const context = error.context as { statusCode?: number; headers?: Headers }\n if (context.statusCode !== 429) return null\n\n // honour the server's Retry-After header (delta-seconds) when present\n const retryAfter = Number(context.headers?.get(\"retry-after\"))\n if (Number.isFinite(retryAfter) && retryAfter > 0) return retryAfter * 1000\n\n // otherwise exponential backoff: 500ms, 1s, 2s, 4s, 8s\n return BASE_BACKOFF_MS * 2 ** attempt\n}\n\nconst sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason)\n\n const onAbort = () => {\n clearTimeout(timeout)\n reject(signal?.reason)\n }\n const timeout = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort)\n resolve()\n }, ms)\n signal?.addEventListener(\"abort\", onAbort, { once: true })\n })\n\n/**\n * Wraps a transport so HTTP 429 responses are retried with backoff. Solana RPC requests are\n * idempotent (`sendTransaction` is keyed by signature), so replaying a rate-limited request is safe.\n */\nconst withRetryOn429 = (transport: RpcTransport): RpcTransport => {\n const wrapped = async (config: Parameters<RpcTransport>[0]) => {\n for (let attempt = 0; ; attempt++) {\n try {\n return await transport(config)\n } catch (error) {\n const delay = attempt < MAX_429_RETRIES ? get429RetryDelay(error, attempt) : null\n if (delay === null) throw error\n await sleep(delay, config.signal)\n }\n }\n }\n return wrapped as RpcTransport\n}\n\n// TODO leverage multiple rpcs with fallback\nexport const getSolTransport = (_networkId: SolNetworkId, rpcs: string[]): RpcTransport =>\n withRetryOn429(createDefaultRpcTransport({ url: rpcs[0] }))\n\nexport const getSolRpc = (networkId: SolNetworkId, rpcs: string[]): SolRpc =>\n createSolanaRpcFromTransport(getSolTransport(networkId, rpcs))\n","import type { RpcTransport } from \"@solana/kit\"\nimport { createSolanaRpcFromTransport } from \"@solana/kit\"\nimport type {\n IChaindataNetworkProvider,\n IChaindataTokenProvider,\n SolNetworkId,\n} from \"@talismn/chaindata-provider\"\n\nimport type { SolRpc } from \"./getSolRpc\"\nimport { getSolTransport } from \"./getSolRpc\"\nimport type { IChainConnectorSol } from \"./IChainConnectorSol\"\n\nexport class ChainConnectorSol implements IChainConnectorSol {\n #chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider\n #transports = new Map<SolNetworkId, RpcTransport>()\n #rpcs = new Map<SolNetworkId, SolRpc>()\n\n constructor(chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider) {\n this.#chaindataProvider = chaindataProvider\n }\n\n async #getNetworkRpcs(networkId: SolNetworkId): Promise<string[]> {\n const network = await this.#chaindataProvider.getNetworkById(networkId, \"solana\")\n if (!network) throw new Error(`Network not found: ${networkId}`)\n\n return network.rpcs\n }\n\n async getTransport(networkId: SolNetworkId): Promise<RpcTransport> {\n if (!this.#transports.has(networkId))\n this.#transports.set(\n networkId,\n getSolTransport(networkId, await this.#getNetworkRpcs(networkId))\n )\n\n return this.#transports.get(networkId)!\n }\n\n async getRpc(networkId: SolNetworkId): Promise<SolRpc> {\n if (!this.#rpcs.has(networkId))\n this.#rpcs.set(networkId, createSolanaRpcFromTransport(await this.getTransport(networkId)))\n\n return this.#rpcs.get(networkId)!\n }\n\n /** Drops cached transports/rpcs so the next call re-reads the network's rpcs from chaindata */\n clearRpcProvidersCache(networkId?: SolNetworkId) {\n if (networkId) {\n this.#transports.delete(networkId)\n this.#rpcs.delete(networkId)\n } else {\n this.#transports.clear()\n this.#rpcs.clear()\n }\n }\n}\n","import type { RpcTransport } from \"@solana/kit\"\nimport { createSolanaRpcFromTransport } from \"@solana/kit\"\nimport type { SolNetwork } from \"@talismn/chaindata-provider\"\n\nimport type { SolRpc } from \"./getSolRpc\"\nimport { getSolTransport } from \"./getSolRpc\"\nimport type { IChainConnectorSol } from \"./IChainConnectorSol\"\n\nexport class ChainConnectorSolStub implements IChainConnectorSol {\n #transport: RpcTransport\n #rpc: SolRpc\n\n constructor(network: Pick<SolNetwork, \"id\" | \"rpcs\">) {\n this.#transport = getSolTransport(network.id, network.rpcs)\n this.#rpc = createSolanaRpcFromTransport(this.#transport)\n }\n\n async getRpc(): Promise<SolRpc> {\n return this.#rpc\n }\n\n async getTransport(): Promise<RpcTransport> {\n return this.#transport\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["packageJson.name"],"sources":["../package.json","../src/log.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotStub.ts","../src/eth/getChainFromEvmNetwork.ts","../src/eth/getTransportForEvmNetwork.ts","../src/eth/getEvmNetworkPublicClient.ts","../src/eth/getEvmNetworkWalletClient.ts","../src/eth/ChainConnectorEth.ts","../src/eth/ChainConnectorEthStub.ts","../src/sol/getSolRpc.ts","../src/sol/ChainConnectorSol.ts","../src/sol/ChainConnectorSolStub.ts"],"sourcesContent":["","import anylogger from \"anylogger\"\n\nimport packageJson from \"../package.json\"\n\nexport default anylogger(packageJson.name)\n","import { createClient, type SubstrateClient } from \"@polkadot-api/substrate-client\"\nimport {\n getWsProvider,\n type StatusChange,\n WsEvent,\n type WsJsonRpcProvider,\n} from \"@polkadot-api/ws-provider\"\nimport type { DotNetworkId, IChaindataNetworkProvider } from \"@talismn/chaindata-provider\"\n\nimport log from \"../log\"\nimport type { IChainConnectorDot, SubscriptionCallback } from \"./IChainConnectorDot\"\n\n// errors that require an rpc fallback\n// https://docs.blastapi.io/blast-documentation/things-you-need-to-know/error-reference\nconst BAD_RPC_ERRORS: Record<string, string> = {\n \"-32097\": \"Rate limit exceeded\",\n \"-32098\": \"Capacity exceeded\",\n}\n\nconst RESPONSE_TIMEOUT = 30_000 // max wait for an rpc response (including connection time)\nconst KEEP_ALIVE_INTERVAL = 20_000 // periodic system_health to keep idle sockets alive (ws-provider kills quiet sockets after its 40s heartbeat)\n\n/** in-flight requests reject with DestroyedError when a connection is torn down - expected, not worth logging */\nconst isDestroyedError = (error: unknown): boolean =>\n error instanceof Error && error.name === \"DestroyedError\"\nconst STALE_NOTIFY_TIMEOUT = 30_000 // how long a chain can be disconnected before subscribers are notified\n\nexport class ChainConnectionError extends Error {\n type: \"CHAIN_CONNECTION_ERROR\"\n chainId: string\n\n constructor(chainId: string, options?: ErrorOptions) {\n super(`Unable to connect to chain ${chainId}`, options)\n\n this.type = \"CHAIN_CONNECTION_ERROR\"\n this.chainId = chainId\n }\n}\n\nexport class StaleRpcError extends Error {\n type: \"STALE_RPC_ERROR\"\n chainId: string\n\n constructor(chainId: string, options?: ErrorOptions) {\n super(`RPCs are stale/unavailable for chain ${chainId}`, options)\n\n this.type = \"STALE_RPC_ERROR\"\n this.chainId = chainId\n }\n}\n\ntype SocketUserId = number\n\ntype Subscription = {\n subscribeMethod: string\n params: unknown[]\n callback: SubscriptionCallback\n /** cancels the pending subscribe request, if any */\n cancelRequest: (() => void) | null\n /** stops routing notifications to the callback */\n stopFollow: (() => void) | null\n serverSubId: string | number | null\n unsubscribed: boolean\n}\n\ntype Connection = {\n chainId: DotNetworkId\n provider: WsJsonRpcProvider\n client: SubstrateClient\n users: Set<SocketUserId>\n subscriptions: Set<Subscription>\n wasConnected: boolean\n keepAliveInterval: ReturnType<typeof setInterval> | null\n staleTimeout: ReturnType<typeof setTimeout> | null\n}\n\n/**\n * ChainConnector provides an interface similar to a websocket JSON-RPC provider, but with three points of difference:\n *\n * 1. ChainConnector methods all accept a `chainId` instead of an array of RPCs. RPCs are then fetched internally from chaindata.\n * 2. ChainConnector creates only one socket connection per chain (via polkadot-api's ws-provider, which handles\n * endpoint rotation, reconnection and stale-socket detection) and ensures that all downstream requests to a chain\n * share that connection.\n * 3. Subscriptions return a callable `unsubscribe` method instead of an id, and are automatically re-established\n * when the provider reconnects (possibly to another endpoint).\n *\n * Additionally, when run on the clientside of a dapp where `window.talismanSub` is available, instead of spinning up new websocket\n * connections this class will forward all requests through to the wallet backend - where another instance of this class will\n * handle the websocket connections.\n */\nexport class ChainConnectorDot implements IChainConnectorDot {\n #chaindataChainProvider: IChaindataNetworkProvider\n\n #connections: Record<DotNetworkId, Connection> = {}\n #pendingConnections: Record<DotNetworkId, Promise<Connection>> = {}\n\n constructor(chaindataChainProvider: IChaindataNetworkProvider) {\n this.#chaindataChainProvider = chaindataChainProvider\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n async send<T = any>(\n chainId: DotNetworkId,\n method: string,\n params: unknown[],\n _isCacheable?: boolean | undefined,\n extraOptions?: {\n /**\n * Set to `true` if this query is speculative, i.e. if on some chains it's expected that it will raise a wasm unreachable error of the form:\n *\n * 4003: Client error: Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\n *\n * By setting expectErrors to true, this method won't pollute the logs with errors we intend to have happen.\n * An example use case of this is when you plan to catch the wasm unreachable error on chains that don't support the query, and then fall back\n * to another query or perhaps an empty result.\n */\n expectErrors?: boolean\n }\n ): Promise<T> {\n const talismanSub = this.getTalismanSub()\n if (talismanSub !== undefined) {\n try {\n const genesisHash = await this.getGenesisHash(chainId)\n return await talismanSub.send(genesisHash, method, params)\n } catch (error) {\n log.warn(\n `Failed to make wallet-proxied send request for chain ${chainId}. Falling back to plain websocket`,\n error\n )\n }\n }\n\n let socketUserId: SocketUserId\n let connection: Connection\n try {\n ;[socketUserId, connection] = await this.acquireConnection(chainId)\n } catch (error) {\n throw new StaleRpcError(chainId, { cause: error })\n }\n\n try {\n return await this.request<T>(connection, method, params)\n } catch (err) {\n const error = err as (Error & { code?: number; data?: unknown }) | null\n\n if (error?.message === \"TIMEOUT\") {\n log.error(`ChainConnector timeout`, { chainId, error })\n connection.provider.switch()\n throw new Error(\"Timeout\")\n }\n\n const badRpcError = BAD_RPC_ERRORS[error?.code?.toString() ?? \"\"]\n if (badRpcError) {\n log.error(`ChainConnector ${badRpcError}`, { error, chainId })\n connection.provider.switch()\n throw new Error(badRpcError)\n }\n\n if (!extraOptions?.expectErrors)\n log.error(\n `Failed to send ${method} on chain ${chainId}\\nparams: ${JSON.stringify(params)}`,\n {\n error,\n }\n )\n\n throw error\n } finally {\n this.releaseConnection(chainId, socketUserId)\n }\n }\n\n async subscribe(\n chainId: DotNetworkId,\n subscribeMethod: string,\n responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout: number | false = 30_000 // 30 seconds in milliseconds\n ): Promise<(unsubscribeMethod: string) => void> {\n const talismanSub = this.getTalismanSub()\n if (talismanSub !== undefined) {\n try {\n const genesisHash = await this.getGenesisHash(chainId)\n\n const subscriptionId = await talismanSub.subscribe(\n genesisHash,\n subscribeMethod,\n responseMethod,\n params,\n callback,\n timeout\n )\n\n return (unsubscribeMethod: string) =>\n talismanSub.unsubscribe(subscriptionId, unsubscribeMethod)\n } catch (error) {\n log.warn(\n `Failed to create wallet-proxied subscription for chain ${chainId}. Falling back to plain websocket`,\n error\n )\n }\n }\n\n let socketUserId: SocketUserId\n let connection: Connection\n try {\n ;[socketUserId, connection] = await this.acquireConnection(chainId)\n } catch (error) {\n throw new StaleRpcError(chainId, { cause: error })\n }\n\n const subscription: Subscription = {\n subscribeMethod,\n params,\n callback,\n cancelRequest: null,\n stopFollow: null,\n serverSubId: null,\n unsubscribed: false,\n }\n connection.subscriptions.add(subscription)\n\n // if the chain can't be reached at all, let the caller know after a while (the provider keeps retrying)\n if (timeout && !connection.wasConnected) {\n const staleWarning = setTimeout(() => {\n if (!subscription.unsubscribed && !connection.wasConnected)\n callback(new StaleRpcError(chainId), null)\n }, timeout)\n const clear = () => clearTimeout(staleWarning)\n // piggyback on the follow setup to clear the warning once anything happens\n const originalCallback = subscription.callback\n subscription.callback = (error, result) => {\n clear()\n subscription.callback = originalCallback\n originalCallback(error, result)\n }\n }\n\n this.startSubscription(connection, subscription)\n\n return (unsubscribeMethod: string) => {\n if (subscription.unsubscribed) return\n subscription.unsubscribed = true\n\n subscription.cancelRequest?.()\n subscription.stopFollow?.()\n\n // the connection may have been replaced by a reset() since we subscribed\n const current = this.#connections[chainId]\n // if we are the last user the connection is about to be destroyed: skip the\n // unsubscribe call, server-side subscriptions die with the socket anyway\n const isLastUser = !!current && current.users.size === 1 && current.users.has(socketUserId)\n if (subscription.serverSubId !== null && current && !isLastUser)\n try {\n current.client.request(unsubscribeMethod, [subscription.serverSubId]).catch((error) => {\n if (!isDestroyedError(error)) log.warn(`Failed to unsubscribe from ${chainId}`, error)\n })\n } catch (error) {\n if (!isDestroyedError(error)) log.warn(`Failed to unsubscribe from ${chainId}`, error)\n }\n\n current?.subscriptions.delete(subscription)\n this.releaseConnection(chainId, socketUserId)\n }\n }\n\n /**\n * Kills and recreates the connection for a chain, if any.\n * Useful after changing a network's rpcs to make sure the new list is applied for further requests.\n * Active subscriptions are automatically re-established on the new connection.\n */\n async reset(chainId: DotNetworkId) {\n log.info(\"ChainConnector reset\", chainId)\n const connection = this.#connections[chainId]\n if (!connection) return\n\n this.destroyConnection(connection)\n delete this.#connections[chainId]\n\n // recreate a connection for the active subscriptions, if any\n if (connection.subscriptions.size) {\n try {\n const fresh = await this.createConnection(chainId)\n fresh.users = connection.users\n fresh.subscriptions = connection.subscriptions\n this.#connections[chainId] = fresh\n for (const subscription of fresh.subscriptions) this.startSubscription(fresh, subscription)\n } catch (error) {\n log.warn(`Failed to recreate connection for ${chainId} after reset`, error)\n for (const subscription of connection.subscriptions)\n subscription.callback(new StaleRpcError(chainId, { cause: error }), null)\n }\n }\n }\n\n /** Sends a request over a connection, throwing `Error(\"TIMEOUT\")` if no response arrives in time */\n private request<T>(\n connection: Connection,\n method: string,\n params: unknown[],\n timeoutMs = RESPONSE_TIMEOUT\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n cancel()\n reject(new Error(\"TIMEOUT\"))\n }, timeoutMs)\n\n const cancel = connection.client._request<T, unknown>(method, params, {\n onSuccess: (result) => {\n clearTimeout(timer)\n resolve(result)\n },\n onError: (error) => {\n clearTimeout(timer)\n reject(error)\n },\n })\n })\n }\n\n /** Issues the subscribe request and routes notifications to the subscription callback */\n private startSubscription(connection: Connection, subscription: Subscription) {\n subscription.serverSubId = null\n subscription.stopFollow = null\n\n subscription.cancelRequest = connection.client._request<string | number, unknown>(\n subscription.subscribeMethod,\n subscription.params,\n {\n onSuccess: (serverSubId, follow) => {\n subscription.cancelRequest = null\n if (subscription.unsubscribed) return\n\n subscription.serverSubId = serverSubId\n // raw-client matches notifications by the raw (not stringified) subscription id, but types it as string\n subscription.stopFollow = follow(serverSubId as string, {\n next: (result) => subscription.callback(null, result),\n error: (error) => subscription.callback(error, null),\n })\n },\n onError: (error) => {\n subscription.cancelRequest = null\n if (!subscription.unsubscribed) subscription.callback(error, null)\n },\n }\n )\n }\n\n /**\n * Get (or create) the shared connection for a chain.\n *\n * The caller must call releaseConnection with the returned SocketUserId once they are finished with it.\n */\n private async acquireConnection(chainId: DotNetworkId): Promise<[SocketUserId, Connection]> {\n // a single creation promise per chain guarantees that concurrent callers share the same connection\n let connection = this.#connections[chainId]\n if (!connection) {\n if (!this.#pendingConnections[chainId]) {\n this.#pendingConnections[chainId] = this.createConnection(chainId)\n .then((created) => {\n this.#connections[chainId] = created\n return created\n })\n .finally(() => {\n delete this.#pendingConnections[chainId]\n })\n }\n connection = await this.#pendingConnections[chainId]\n }\n\n const socketUserId = this.getExclusiveRandomId([...connection.users])\n connection.users.add(socketUserId)\n\n return [socketUserId, connection]\n }\n\n private async createConnection(chainId: DotNetworkId): Promise<Connection> {\n const chain = await this.#chaindataChainProvider.getNetworkById(chainId, \"polkadot\")\n if (!chain) throw new Error(`Chain ${chainId} not found in store`)\n\n const rpcs = chain.rpcs.concat()\n if (!rpcs.length) throw new Error(`No healthy RPCs available for chain ${chainId}`)\n\n // will be assigned below - the provider config needs to reference the connection\n let connection: Connection = null as unknown as Connection\n\n const provider = getWsProvider(rpcs, {\n onStatusChanged: (status: StatusChange) => this.handleStatusChange(connection, status),\n })\n\n const client = createClient(provider)\n\n connection = {\n chainId,\n provider,\n client,\n users: new Set(),\n subscriptions: new Set(),\n wasConnected: false,\n keepAliveInterval: null,\n staleTimeout: null,\n }\n\n return connection\n }\n\n private handleStatusChange(connection: Connection, status: StatusChange) {\n // connection is still being constructed on the initial CONNECTING event\n if (!connection) return\n // ignore events from a connection that has been reset/destroyed\n if (this.#connections[connection.chainId] !== connection) return\n\n if (status.type === WsEvent.CONNECTED) {\n if (connection.staleTimeout) {\n clearTimeout(connection.staleTimeout)\n connection.staleTimeout = null\n }\n\n const isReconnect = connection.wasConnected\n connection.wasConnected = true\n\n // periodically send a request to keep the socket alive when there is no other traffic\n if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval)\n connection.keepAliveInterval = setInterval(() => {\n this.request(connection, \"system_health\", [], KEEP_ALIVE_INTERVAL).catch((error) => {\n if (!isDestroyedError(error))\n log.warn(`Failed keep-alive for socket ${connection.chainId}`, error)\n })\n }, KEEP_ALIVE_INTERVAL)\n\n // server-side subscriptions died with the previous socket : re-establish them\n // (skip author_* subscriptions : resubmitting an extrinsic is not safe)\n if (isReconnect)\n for (const subscription of connection.subscriptions) {\n if (subscription.unsubscribed) continue\n if (subscription.subscribeMethod.startsWith(\"author_\")) continue\n subscription.stopFollow?.()\n subscription.cancelRequest?.()\n this.startSubscription(connection, subscription)\n }\n } else {\n if (connection.keepAliveInterval) {\n clearInterval(connection.keepAliveInterval)\n connection.keepAliveInterval = null\n }\n\n // notify subscribers if the chain stays unreachable for too long\n if (!connection.staleTimeout && connection.subscriptions.size) {\n connection.staleTimeout = setTimeout(() => {\n connection.staleTimeout = null\n for (const subscription of connection.subscriptions)\n if (!subscription.unsubscribed)\n subscription.callback(new StaleRpcError(connection.chainId), null)\n }, STALE_NOTIFY_TIMEOUT)\n }\n }\n }\n\n private releaseConnection(chainId: DotNetworkId, socketUserId: SocketUserId): void {\n const connection = this.#connections[chainId]\n if (!connection) return\n\n connection.users.delete(socketUserId)\n if (connection.users.size > 0) return\n\n this.destroyConnection(connection)\n delete this.#connections[chainId]\n }\n\n private destroyConnection(connection: Connection): void {\n if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval)\n if (connection.staleTimeout) clearTimeout(connection.staleTimeout)\n connection.keepAliveInterval = null\n connection.staleTimeout = null\n\n // detach subscriptions before destroying the client so their callbacks don't receive a DestroyedError\n for (const subscription of connection.subscriptions) {\n subscription.stopFollow?.()\n subscription.cancelRequest?.()\n subscription.stopFollow = null\n subscription.cancelRequest = null\n subscription.serverSubId = null\n }\n\n try {\n connection.client.destroy()\n } catch (error) {\n log.warn(`Error occurred destroying connection ${connection.chainId}`, error)\n }\n }\n\n private async getGenesisHash(chainId: DotNetworkId): Promise<string> {\n const chain = await this.#chaindataChainProvider.getNetworkById(chainId, \"polkadot\")\n if (!chain) throw new Error(`Chain ${chainId} not found in store`)\n\n const { genesisHash } = chain\n if (typeof genesisHash !== \"string\")\n throw new Error(`Chain ${chainId} has no genesisHash in store`)\n\n return genesisHash\n }\n\n /** continues to generate a random number until it finds one which is not present in the exclude list */\n private getExclusiveRandomId(exclude: number[] = []): number {\n let id = this.getRandomId()\n while (exclude.includes(id)) {\n id = this.getRandomId()\n }\n return id\n }\n /** generates a random number */\n private getRandomId(): number {\n return Math.trunc(Math.random() * 10 ** 8)\n }\n\n private getTalismanSub() {\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n const talismanSub = typeof window !== \"undefined\" && (window as any).talismanSub\n\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashSend: Function | undefined = talismanSub?.rpcByGenesisHashSend\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashSubscribe: Function | undefined = talismanSub?.rpcByGenesisHashSubscribe\n // biome-ignore lint/complexity/noBannedTypes: legacy\n const rpcByGenesisHashUnsubscribe: Function | undefined =\n talismanSub?.rpcByGenesisHashUnsubscribe\n\n if (typeof rpcByGenesisHashSend !== \"function\") return\n if (typeof rpcByGenesisHashSubscribe !== \"function\") return\n if (typeof rpcByGenesisHashUnsubscribe !== \"function\") return\n\n return {\n // biome-ignore lint/suspicious/noExplicitAny: legacy\n send: <T = any>(genesisHash: string, method: string, params: unknown[]): Promise<T> =>\n rpcByGenesisHashSend(genesisHash, method, params),\n\n subscribe: (\n genesisHash: string,\n subscribeMethod: string,\n responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout: number | false\n ): Promise<string> =>\n rpcByGenesisHashSubscribe(\n genesisHash,\n subscribeMethod,\n responseMethod,\n params,\n callback,\n timeout\n ),\n\n unsubscribe: (subscriptionId: string, unsubscribeMethod: string): Promise<void> =>\n rpcByGenesisHashUnsubscribe(subscriptionId, unsubscribeMethod),\n }\n }\n}\n","import { createClient, type SubstrateClient } from \"@polkadot-api/substrate-client\"\nimport { getWsProvider } from \"@polkadot-api/ws-provider\"\nimport type { DotNetwork, DotNetworkId } from \"@talismn/chaindata-provider\"\nimport { throwAfter } from \"@talismn/util\"\n\nimport type { IChainConnectorDot, SubscriptionCallback } from \"./IChainConnectorDot\"\n\nconst TIMEOUT = 10_000\n\nexport class ChainConnectorDotStub implements IChainConnectorDot {\n #client: SubstrateClient\n\n constructor(network: DotNetwork) {\n this.#client = createClient(getWsProvider(network.rpcs.concat()))\n }\n\n async send<T = unknown>(\n _chainId: DotNetworkId,\n method: string,\n params: unknown[],\n _isCacheable?: boolean\n ): Promise<T> {\n return await Promise.race([\n this.#client.request<T>(method, params),\n throwAfter(TIMEOUT, `Request ${method} timed out after ${TIMEOUT}ms`),\n ])\n }\n\n async subscribe(\n _chainId: DotNetworkId,\n subscribeMethod: string,\n _responseMethod: string,\n params: unknown[],\n callback: SubscriptionCallback,\n timeout?: number | false\n ): Promise<(unsubscribeMethod: string) => void> {\n let stopFollow: (() => void) | null = null\n let unsubscribed = false\n\n const serverSubId = await Promise.race([\n new Promise<string | number>((resolve, reject) => {\n this.#client._request<string | number, unknown>(subscribeMethod, params, {\n onSuccess: (subId, follow) => {\n stopFollow = follow(subId as string, {\n next: (result) => callback(null, result),\n error: (error) => callback(error, null),\n })\n resolve(subId)\n },\n onError: reject,\n })\n }),\n throwAfter(timeout || TIMEOUT, `Subscription timed out after ${timeout || TIMEOUT}ms`),\n ])\n\n return (unsubscribeMethod: string) => {\n if (unsubscribed) return\n unsubscribed = true\n stopFollow?.()\n this.#client.request(unsubscribeMethod, [serverSubId]).catch(() => {}) // connection may already be gone\n }\n }\n\n reset(): Promise<void> {\n throw new Error(\"ChainConnectorDotStub does not implement reset\")\n }\n\n destroy() {\n this.#client.destroy()\n }\n}\n","import type { EthNetwork, EthNetworkId } from \"@talismn/chaindata-provider\"\nimport { camelCase, fromPairs, toPairs } from \"lodash-es\"\nimport type { Chain, ChainContract } from \"viem\"\nimport * as viemChains from \"viem/chains\"\n\n// exclude zoraTestnet which uses Hyperliquid's chain id\nconst { zoraTestnet, ...validViemChains } = viemChains\n\n// viem chains benefit from multicall config & other viem goodies\nconst VIEM_CHAINS = Object.keys(validViemChains).reduce(\n (acc, curr) => {\n const chain = validViemChains[curr as keyof typeof validViemChains]\n acc[chain.id] = chain\n return acc\n },\n {} as Record<number, Chain>\n)\n\nconst chainsCache = new Map<string, Chain>()\n\nexport const clearChainsCache = (networkId?: EthNetworkId) => {\n if (networkId) chainsCache.delete(networkId)\n else chainsCache.clear()\n}\n\nexport const getChainFromEvmNetwork = (network: EthNetwork): Chain => {\n const { symbol, decimals } = network.nativeCurrency\n\n if (!chainsCache.has(network.id)) {\n const chainRpcs = network.rpcs ?? []\n\n const viemChain = VIEM_CHAINS[Number(network.id)] ?? {}\n\n const chain: Chain = {\n ...viemChain,\n id: Number(network.id),\n name: network.name ?? `Ethereum Chain ${network.id}`,\n rpcUrls: {\n public: { http: chainRpcs },\n default: { http: chainRpcs },\n },\n nativeCurrency: {\n symbol,\n decimals,\n name: symbol,\n },\n contracts: {\n ...viemChain.contracts,\n ...(network.contracts\n ? fromPairs(\n toPairs(network.contracts).map(([name, address]): [string, ChainContract] => [\n camelCase(name),\n { address },\n ])\n )\n : {}),\n },\n }\n\n chainsCache.set(network.id, chain)\n }\n\n return chainsCache.get(network.id) as Chain\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { fallback, http, shouldThrow } from \"viem\"\n\nexport type TransportOptions = {\n batch?:\n | boolean\n | {\n batchSize?: number | undefined\n wait?: number | undefined\n }\n}\n\n// Frontier nodes report reverts with a message viem does not recognize, which makes fallback retry every rpc\nconst FRONTIER_EXECUTION_ERROR = /VM Exception while processing transaction/\n\nconst isFinalError = (error: Error) =>\n shouldThrow(error) || FRONTIER_EXECUTION_ERROR.test(error.message)\n\nexport const getTransportForEvmNetwork = (\n evmNetwork: EthNetwork,\n options: TransportOptions = {}\n) => {\n if (!evmNetwork.rpcs?.length) throw new Error(\"No RPCs found for EVM network\")\n\n const { batch } = options\n\n return fallback(\n evmNetwork.rpcs.map((url) => http(url, { batch, retryCount: 0 })),\n { retryCount: 0, shouldThrow: isFinalError }\n )\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { createPublicClient, type PublicClient } from \"viem\"\n\nimport { clearChainsCache, getChainFromEvmNetwork } from \"./getChainFromEvmNetwork\"\nimport { getTransportForEvmNetwork, type TransportOptions } from \"./getTransportForEvmNetwork\"\n\nconst MUTLICALL_BATCH_WAIT = 25\nconst MUTLICALL_BATCH_SIZE = 100\n\nconst HTTP_BATCH_WAIT = 25\nconst HTTP_BATCH_SIZE_WITH_MULTICALL = 10\nconst HTTP_BATCH_SIZE_WITHOUT_MULTICALL = 30\n\n// cache to reuse previously created public clients\nconst publicClientCache = new Map<string, PublicClient>()\n\nexport const clearPublicClientCache = (evmNetworkId?: string) => {\n clearChainsCache(evmNetworkId)\n\n if (evmNetworkId) publicClientCache.delete(evmNetworkId)\n else publicClientCache.clear()\n}\n\nexport const getEvmNetworkPublicClient = (network: EthNetwork): PublicClient => {\n const chain = getChainFromEvmNetwork(network)\n\n if (!publicClientCache.has(network.id)) {\n if (!network.rpcs.length) throw new Error(\"No RPCs found for Ethereum network\")\n\n const batch = chain.contracts?.multicall3\n ? { multicall: { wait: MUTLICALL_BATCH_WAIT, batchSize: MUTLICALL_BATCH_SIZE } }\n : undefined\n\n const transportOptions: TransportOptions = {\n batch: {\n batchSize: chain.contracts?.multicall3\n ? HTTP_BATCH_SIZE_WITH_MULTICALL\n : HTTP_BATCH_SIZE_WITHOUT_MULTICALL,\n wait: HTTP_BATCH_WAIT,\n },\n }\n\n const transport = getTransportForEvmNetwork(network, transportOptions)\n\n publicClientCache.set(\n network.id,\n createPublicClient({\n chain,\n transport,\n batch,\n })\n )\n }\n\n return publicClientCache.get(network.id) as PublicClient\n}\n","import type { EthNetwork } from \"@talismn/chaindata-provider\"\nimport { type Account, createWalletClient, type WalletClient } from \"viem\"\n\nimport { getChainFromEvmNetwork } from \"./getChainFromEvmNetwork\"\nimport { getTransportForEvmNetwork } from \"./getTransportForEvmNetwork\"\n\ntype WalletClientOptions = {\n account?: `0x${string}` | Account\n}\n\nexport const getEvmNetworkWalletClient = (\n network: EthNetwork,\n options: WalletClientOptions = {}\n): WalletClient => {\n const chain = getChainFromEvmNetwork(network)\n\n const transport = getTransportForEvmNetwork(network)\n\n return createWalletClient({ chain, transport, account: options.account })\n}\n","import type {\n EthNetworkId,\n IChaindataNetworkProvider,\n IChaindataTokenProvider,\n} from \"@talismn/chaindata-provider\"\nimport type { Account, PublicClient, WalletClient } from \"viem\"\n\nimport { clearPublicClientCache, getEvmNetworkPublicClient } from \"./getEvmNetworkPublicClient\"\nimport { getEvmNetworkWalletClient } from \"./getEvmNetworkWalletClient\"\nimport type { IChainConnectorEth } from \"./IChainConnectorEth\"\n\nexport class ChainConnectorEth implements IChainConnectorEth {\n #chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider\n\n constructor(chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider) {\n this.#chaindataProvider = chaindataProvider\n }\n\n async getPublicClientForEvmNetwork(evmNetworkId: EthNetworkId): Promise<PublicClient | null> {\n const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, \"ethereum\")\n if (!network) return null\n\n return getEvmNetworkPublicClient(network)\n }\n\n async getWalletClientForEvmNetwork(\n evmNetworkId: EthNetworkId,\n account?: `0x${string}` | Account\n ): Promise<WalletClient | null> {\n const network = await this.#chaindataProvider.getNetworkById(evmNetworkId, \"ethereum\")\n if (!network) return null\n\n return getEvmNetworkWalletClient(network, { account })\n }\n\n public clearRpcProvidersCache(evmNetworkId?: EthNetworkId) {\n clearPublicClientCache(evmNetworkId)\n }\n}\n","import type { EthNetwork, EthNetworkId } from \"@talismn/chaindata-provider\"\nimport type { Account, PublicClient, WalletClient } from \"viem\"\n\nimport { getEvmNetworkPublicClient } from \"./getEvmNetworkPublicClient\"\nimport { getEvmNetworkWalletClient } from \"./getEvmNetworkWalletClient\"\nimport type { IChainConnectorEth } from \"./IChainConnectorEth\"\n\nexport class ChainConnectorEthStub implements IChainConnectorEth {\n #network: EthNetwork\n\n constructor(network: EthNetwork) {\n this.#network = network\n }\n\n async getPublicClientForEvmNetwork(): Promise<PublicClient | null> {\n return getEvmNetworkPublicClient(this.#network)\n }\n\n async getWalletClientForEvmNetwork(\n _networkId: EthNetworkId,\n account?: `0x${string}` | Account\n ): Promise<WalletClient | null> {\n return getEvmNetworkWalletClient(this.#network, { account })\n }\n\n clearRpcProvidersCache(): void {\n // No-op for stub\n }\n}\n","import type { Rpc, RpcTransport, SolanaRpcApi } from \"@solana/kit\"\nimport { createDefaultRpcTransport, createSolanaRpcFromTransport, isSolanaError } from \"@solana/kit\"\nimport type { SolNetworkId } from \"@talismn/chaindata-provider\"\n\nexport type SolRpc = Rpc<SolanaRpcApi>\n\n// Public Solana RPC nodes rate-limit aggressively with HTTP 429. kit's default transport is\n// single-shot, unlike the old web3.js `Connection` which retried 429 up to 5x with backoff, so a\n// single 429 would fail an otherwise-valid request (e.g. a user's transaction submission, or token\n// discovery silently returning no tokens). This wrapper restores that behaviour.\nconst MAX_429_RETRIES = 5\nconst BASE_BACKOFF_MS = 500\n\ntype HttpErrorContext = { statusCode?: number; headers?: Headers }\n\nconst getRateLimitContext = (error: unknown): HttpErrorContext | null => {\n if (!isSolanaError(error)) return null\n\n const context = error.context as HttpErrorContext\n return context.statusCode === 429 ? context : null\n}\n\n/** Returns the delay to wait before retrying, or `null` if the error is not a retryable 429. */\nconst get429RetryDelay = (error: unknown, attempt: number): number | null => {\n const context = getRateLimitContext(error)\n if (!context) return null\n\n // honour the server's Retry-After header (delta-seconds) when present\n const retryAfter = Number(context.headers?.get(\"retry-after\"))\n if (Number.isFinite(retryAfter) && retryAfter > 0) return retryAfter * 1000\n\n // otherwise exponential backoff: 500ms, 1s, 2s, 4s, 8s\n return BASE_BACKOFF_MS * 2 ** attempt\n}\n\nconst sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason)\n\n const onAbort = () => {\n clearTimeout(timeout)\n reject(signal?.reason)\n }\n const timeout = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort)\n resolve()\n }, ms)\n signal?.addEventListener(\"abort\", onAbort, { once: true })\n })\n\n/**\n * Wraps a transport so HTTP 429 responses are retried with backoff. Solana RPC requests are\n * idempotent (`sendTransaction` is keyed by signature), so replaying a rate-limited request is safe.\n */\nconst withRetryOn429 = (transport: RpcTransport): RpcTransport => {\n const wrapped = async (config: Parameters<RpcTransport>[0]) => {\n for (let attempt = 0; ; attempt++) {\n try {\n return await transport(config)\n } catch (error) {\n const delay = attempt < MAX_429_RETRIES ? get429RetryDelay(error, attempt) : null\n if (delay === null) throw error\n await sleep(delay, config.signal)\n }\n }\n }\n return wrapped as RpcTransport\n}\n\n/**\n * How long a request may wait on an RPC before moving on to the next one. The last RPC tried gets\n * no timeout, so a slow but working node still answers when every other one has failed.\n */\nconst FAILOVER_TIMEOUT_MS = 20_000\n\nconst sendWithTimeout = async <TResponse>(\n transport: RpcTransport,\n config: RpcTransportConfig,\n timeoutMs: number\n): Promise<TResponse> => {\n const controller = new AbortController()\n const onAbort = () => controller.abort(config.signal?.reason)\n config.signal?.addEventListener(\"abort\", onAbort, { once: true })\n const timeout = setTimeout(\n () => controller.abort(new Error(`RPC request timed out after ${timeoutMs}ms`)),\n timeoutMs\n )\n\n try {\n return await transport<TResponse>({ ...config, signal: controller.signal })\n } finally {\n clearTimeout(timeout)\n config.signal?.removeEventListener(\"abort\", onAbort)\n }\n}\n\ntype RpcTransportConfig = Parameters<RpcTransport>[0]\n\nconst isSendTransactionPayload = (\n payload: unknown\n): payload is { method: \"sendTransaction\"; params: unknown[] } =>\n typeof payload === \"object\" &&\n payload !== null &&\n \"method\" in payload &&\n payload.method === \"sendTransaction\" &&\n \"params\" in payload &&\n Array.isArray(payload.params)\n\nconst withoutPreflight = (config: RpcTransportConfig): RpcTransportConfig => {\n if (!isSendTransactionPayload(config.payload)) return config\n\n const [transaction, sendConfig] = config.payload.params\n return {\n ...config,\n payload: {\n ...config.payload,\n params: [transaction, { ...(sendConfig as object | undefined), skipPreflight: true }],\n },\n }\n}\n\nexport type SolTransportOptions = {\n /** Called with the url of the RPC that answered, after every successful request */\n onRpcSuccess?: (url: string) => void\n}\n\n/**\n * Sends each request to one RPC at a time, starting from the one that answered last, and moves on to\n * the next when the request fails. A transport only throws for connection, timeout and HTTP errors:\n * JSON-RPC errors come back in the response body and are returned as is.\n *\n * A failed RPC other than a rate limited one may still have forwarded a transaction. Resending it is\n * safe, as the network processes a signature once, but its preflight simulation on the next RPC\n * could then fail although it landed. So after such a failure, every resend of that transaction,\n * including the retries after a rate limit, skips preflight.\n */\nconst withFailover = (urls: string[], options: SolTransportOptions): RpcTransport => {\n const transports = urls.map((url) => createDefaultRpcTransport({ url }))\n let preferred = 0\n const mayHaveBeenProcessed = new WeakSet<RpcTransportConfig>()\n\n const failover = async <TResponse>(config: RpcTransportConfig): Promise<TResponse> => {\n if (!transports.length) throw new Error(\"No RPCs found for Solana network\")\n\n const start = preferred\n let lastError: unknown\n let rateLimitError: unknown\n for (let attempt = 0; attempt < transports.length; attempt++) {\n config.signal?.throwIfAborted()\n const index = (start + attempt) % transports.length\n const transport = transports[index] as RpcTransport\n const isLastAttempt = attempt === transports.length - 1\n const request = mayHaveBeenProcessed.has(config) ? withoutPreflight(config) : config\n\n try {\n const response = isLastAttempt\n ? await transport<TResponse>(request)\n : await sendWithTimeout<TResponse>(transport, request, FAILOVER_TIMEOUT_MS)\n preferred = index\n options.onRpcSuccess?.(urls[index] as string)\n return response\n } catch (error) {\n if (config.signal?.aborted) throw error\n lastError = error\n if (!getRateLimitContext(error)) mayHaveBeenProcessed.add(config)\n else rateLimitError ??= error\n }\n }\n throw rateLimitError ?? lastError\n }\n return failover as RpcTransport\n}\n\n/**\n * Returns a transport over all of the network's RPCs, in the given order. A request fails over to\n * the next RPC on error, and the RPC that answered is tried first for the next request. When every\n * RPC fails and one of them answered HTTP 429, the whole list is retried with backoff.\n */\nexport const getSolTransport = (\n _networkId: SolNetworkId,\n rpcs: string[],\n options: SolTransportOptions = {}\n): RpcTransport => {\n return withRetryOn429(withFailover(rpcs, options))\n}\n\nexport const getSolRpc = (\n networkId: SolNetworkId,\n rpcs: string[],\n options?: SolTransportOptions\n): SolRpc => createSolanaRpcFromTransport(getSolTransport(networkId, rpcs, options))\n","import type { RpcTransport } from \"@solana/kit\"\nimport { createSolanaRpcFromTransport } from \"@solana/kit\"\nimport type {\n IChaindataNetworkProvider,\n IChaindataTokenProvider,\n SolNetworkId,\n} from \"@talismn/chaindata-provider\"\n\nimport type { SolRpc } from \"./getSolRpc\"\nimport { getSolTransport } from \"./getSolRpc\"\nimport type { IChainConnectorSol } from \"./IChainConnectorSol\"\n\nexport class ChainConnectorSol implements IChainConnectorSol {\n #chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider\n #transports = new Map<SolNetworkId, RpcTransport>()\n #rpcs = new Map<SolNetworkId, SolRpc>()\n\n constructor(chaindataProvider: IChaindataNetworkProvider & IChaindataTokenProvider) {\n this.#chaindataProvider = chaindataProvider\n }\n\n async #getNetworkRpcs(networkId: SolNetworkId): Promise<string[]> {\n const network = await this.#chaindataProvider.getNetworkById(networkId, \"solana\")\n if (!network) throw new Error(`Network not found: ${networkId}`)\n\n return network.rpcs\n }\n\n async getTransport(networkId: SolNetworkId): Promise<RpcTransport> {\n if (!this.#transports.has(networkId))\n this.#transports.set(\n networkId,\n getSolTransport(networkId, await this.#getNetworkRpcs(networkId))\n )\n\n return this.#transports.get(networkId)!\n }\n\n async getRpc(networkId: SolNetworkId): Promise<SolRpc> {\n if (!this.#rpcs.has(networkId))\n this.#rpcs.set(networkId, createSolanaRpcFromTransport(await this.getTransport(networkId)))\n\n return this.#rpcs.get(networkId)!\n }\n\n /** Drops cached transports/rpcs so the next call re-reads the network's rpcs from chaindata */\n clearRpcProvidersCache(networkId?: SolNetworkId) {\n if (networkId) {\n this.#transports.delete(networkId)\n this.#rpcs.delete(networkId)\n } else {\n this.#transports.clear()\n this.#rpcs.clear()\n }\n }\n}\n","import type { RpcTransport } from \"@solana/kit\"\nimport { createSolanaRpcFromTransport } from \"@solana/kit\"\nimport type { SolNetwork } from \"@talismn/chaindata-provider\"\n\nimport type { SolRpc } from \"./getSolRpc\"\nimport { getSolTransport } from \"./getSolRpc\"\nimport type { IChainConnectorSol } from \"./IChainConnectorSol\"\n\nexport class ChainConnectorSolStub implements IChainConnectorSol {\n #transport: RpcTransport\n #rpc: SolRpc\n\n constructor(network: Pick<SolNetwork, \"id\" | \"rpcs\">) {\n this.#transport = getSolTransport(network.id, network.rpcs)\n this.#rpc = createSolanaRpcFromTransport(this.#transport)\n }\n\n async getRpc(): Promise<SolRpc> {\n return this.#rpc\n }\n\n async getTransport(): Promise<RpcTransport> {\n return this.#transport\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@talismn/chain-connectors",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
+ "description": "RPC connectors for Polkadot SDK, EVM and Solana networks",
4
5
  "author": "Talisman",
5
6
  "homepage": "https://talisman.xyz",
6
7
  "license": "SEE LICENSE IN LICENSE",
@@ -23,12 +24,12 @@
23
24
  "dependencies": {
24
25
  "@polkadot-api/substrate-client": "0.7.0",
25
26
  "@polkadot-api/ws-provider": "0.9.1",
26
- "@solana/kit": "8.2.0",
27
- "@talismn/chaindata-provider": "2.0.3",
28
- "@talismn/util": "2.0.1",
27
+ "@solana/kit": "8.3.0",
28
+ "@talismn/chaindata-provider": "2.1.0",
29
+ "@talismn/util": "2.1.0",
29
30
  "anylogger": "^1.0.11",
30
31
  "lodash-es": "4.18.1",
31
- "viem": "2.56.3"
32
+ "viem": "2.56.8"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@talismn/tsconfig": "0.0.4",