@talismn/chain-connectors 1.0.6 → 1.1.1
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 +10 -0
- package/dist/index.d.mts +48 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +150 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -7,3 +7,13 @@ RPC connections to the networks that the Talisman wallet supports, one connector
|
|
|
7
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
8
|
|
|
9
9
|
The `*Stub` variants connect to a single network from its RPC list, without chaindata.
|
|
10
|
+
|
|
11
|
+
`ChainConnectorDotPapi` runs over a dapp's polkadot-api clients instead of opening sockets of its own. A dapp that uses `@talismn/balances` next to polkadot-api then has one socket per chain:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
const chainConnector = new ChainConnectorDotPapi((networkId) => clients[networkId])
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
It supports the requests and the `state_subscribeStorage` subscription that balances uses. A storage subscription queries all its keys at each new best block and reports the values that changed, so each balances subscription costs one `state_queryStorageAt` per block.
|
|
18
|
+
|
|
19
|
+
Pass a client for every Polkadot SDK network that balances is enabled on, connected to full nodes over a websocket: balances uses the legacy JSON-RPC methods (`state_queryStorageAt`, `state_call`, `state_getKeysPaged`, …), which light clients do not serve. Keep each client alive as long as the connector, and change its RPCs with the provider's `switch()` rather than by recreating it: a subscription stays on the client it started with, and a destroyed client gives no signal, so its subscriptions stop updating.
|
package/dist/index.d.mts
CHANGED
|
@@ -18,11 +18,24 @@ export declare class ChainConnectionError extends Error {
|
|
|
18
18
|
chainId: string;
|
|
19
19
|
constructor(chainId: string, options?: ErrorOptions);
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Passed to subscription callbacks when the chain has been unreachable for a while. Not
|
|
23
|
+
* terminal: the subscription stays registered and is re-issued when a socket connects.
|
|
24
|
+
*/
|
|
21
25
|
export declare class StaleRpcError extends Error {
|
|
22
26
|
type: "STALE_RPC_ERROR";
|
|
23
27
|
chainId: string;
|
|
24
28
|
constructor(chainId: string, options?: ErrorOptions);
|
|
25
29
|
}
|
|
30
|
+
export type ChainConnectorDotOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* How long a new socket may take to open before the provider gives up on the endpoint
|
|
33
|
+
* and tries the next one. Defaults to polkadot-api's 5 seconds. A slow device or
|
|
34
|
+
* network can take longer than that to connect while it is busy, as a phone app is
|
|
35
|
+
* at startup, and every endpoint then times out in turn.
|
|
36
|
+
*/
|
|
37
|
+
connectionTimeout?: number;
|
|
38
|
+
};
|
|
26
39
|
/**
|
|
27
40
|
* ChainConnector provides an interface similar to a websocket JSON-RPC provider, but with three points of difference:
|
|
28
41
|
*
|
|
@@ -39,7 +52,7 @@ export declare class StaleRpcError extends Error {
|
|
|
39
52
|
*/
|
|
40
53
|
export declare class ChainConnectorDot implements IChainConnectorDot {
|
|
41
54
|
#private;
|
|
42
|
-
constructor(chaindataChainProvider: IChaindataNetworkProvider);
|
|
55
|
+
constructor(chaindataChainProvider: IChaindataNetworkProvider, options?: ChainConnectorDotOptions);
|
|
43
56
|
send<T = any>(chainId: DotNetworkId, method: string, params: unknown[], _isCacheable?: boolean | undefined, extraOptions?: {
|
|
44
57
|
/**
|
|
45
58
|
* 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:
|
|
@@ -81,6 +94,40 @@ export declare class ChainConnectorDot implements IChainConnectorDot {
|
|
|
81
94
|
private getTalismanSub;
|
|
82
95
|
}
|
|
83
96
|
//#endregion
|
|
97
|
+
//#region src/dot/ChainConnectorDotPapi.d.ts
|
|
98
|
+
/** The parts of a polkadot-api `PolkadotClient` that the connector uses */
|
|
99
|
+
export type PapiClient = {
|
|
100
|
+
/** `PolkadotClient` types it without `abortSignal`, but it is substrate-client's `request`, which honours it */
|
|
101
|
+
_request: <Reply>(method: string, params: unknown[], abortSignal?: AbortSignal) => Promise<Reply>;
|
|
102
|
+
bestBlocks$: {
|
|
103
|
+
subscribe: (observer: {
|
|
104
|
+
next: (blocks: {
|
|
105
|
+
hash: string;
|
|
106
|
+
}[]) => void;
|
|
107
|
+
error: (error: unknown) => void;
|
|
108
|
+
complete: () => void;
|
|
109
|
+
}) => {
|
|
110
|
+
unsubscribe: () => void;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Runs `@talismn/balances` over a dapp's polkadot-api clients, so the dapp and its balances share one socket per chain.
|
|
116
|
+
*
|
|
117
|
+
* `state_subscribeStorage` is the only subscription balances uses. It is served by querying the keys at each new
|
|
118
|
+
* best block and reporting the values that changed, like the node does. One query runs at a time, for the latest
|
|
119
|
+
* best block. A failed query is retried at the next block, because balances treats a subscription error as final
|
|
120
|
+
* and would stop updating that network. Only when the first three queries fail does the subscription report the error.
|
|
121
|
+
*/
|
|
122
|
+
export declare class ChainConnectorDotPapi implements IChainConnectorDot {
|
|
123
|
+
#private;
|
|
124
|
+
constructor(clientFor: (networkId: DotNetworkId) => PapiClient | undefined);
|
|
125
|
+
send<T = unknown>(networkId: DotNetworkId, method: string, params: unknown[]): Promise<T>;
|
|
126
|
+
subscribe(networkId: DotNetworkId, subscribeMethod: string, _responseMethod: string, params: unknown[], callback: SubscriptionCallback, timeout?: number | false): Promise<(unsubscribeMethod: string) => void>;
|
|
127
|
+
reset(): Promise<void>;
|
|
128
|
+
private getClient;
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
84
131
|
//#region src/dot/ChainConnectorDotStub.d.ts
|
|
85
132
|
export declare class ChainConnectorDotStub implements IChainConnectorDot {
|
|
86
133
|
#private;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -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;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/dot/IChainConnectorDot.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotPapi.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;;;;qBCOnB,6BAA6B;EACxC;EACA;EAEA,YAAY,iBAAiB,UAAU;;;;;;qBAY5B,sBAAsB;EACjC;EACA;EAEA,YAAY,iBAAiB,UAAU;;YAmC7B;;;;;;;EAOV;;;;;;;;;;;;;;;;qBAiBW,6BAA6B;;EAOxC,YACE,wBAAwB,2BACxB,UAAU;EAON,KAAK,SACT,SAAS,cACT,gBACA,mBACA,oCACA;;;;;;;;;;IAUE;MAED,QAAQ;EAsDL,UACJ,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;;;;;;EA2FN,MAAM,SAAS,eAAY;;UA2BzB;;UA0BA;;;;;;UAgCM;UA2BA;UAgCN;UAoDA;UAeA;UAwBM;;UAYN;;UAQA;UAIA;;;;;YC/hBE;;EAEV,WAAW,OAAO,gBAAgB,mBAAmB,cAAc,gBAAgB,QAAQ;EAC3F;IACE,YAAY;MACV,OAAO;QAAU;;MACjB,QAAQ;MACR;;MACM;;;;;;;;;;;;qBAYC,iCAAiC;;EAG5C,YAAY,YAAY,WAAW,iBAAiB;EAI9C,KAAK,aAAa,WAAW,cAAc,gBAAgB,oBAAoB,QAAQ;EAIvF,UACJ,WAAW,cACX,yBACA,yBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EA8FN,SAAK;UAEH;;;;qBC1IG,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
|
@@ -18,11 +18,24 @@ export declare class ChainConnectionError extends Error {
|
|
|
18
18
|
chainId: string;
|
|
19
19
|
constructor(chainId: string, options?: ErrorOptions);
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Passed to subscription callbacks when the chain has been unreachable for a while. Not
|
|
23
|
+
* terminal: the subscription stays registered and is re-issued when a socket connects.
|
|
24
|
+
*/
|
|
21
25
|
export declare class StaleRpcError extends Error {
|
|
22
26
|
type: "STALE_RPC_ERROR";
|
|
23
27
|
chainId: string;
|
|
24
28
|
constructor(chainId: string, options?: ErrorOptions);
|
|
25
29
|
}
|
|
30
|
+
export type ChainConnectorDotOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* How long a new socket may take to open before the provider gives up on the endpoint
|
|
33
|
+
* and tries the next one. Defaults to polkadot-api's 5 seconds. A slow device or
|
|
34
|
+
* network can take longer than that to connect while it is busy, as a phone app is
|
|
35
|
+
* at startup, and every endpoint then times out in turn.
|
|
36
|
+
*/
|
|
37
|
+
connectionTimeout?: number;
|
|
38
|
+
};
|
|
26
39
|
/**
|
|
27
40
|
* ChainConnector provides an interface similar to a websocket JSON-RPC provider, but with three points of difference:
|
|
28
41
|
*
|
|
@@ -39,7 +52,7 @@ export declare class StaleRpcError extends Error {
|
|
|
39
52
|
*/
|
|
40
53
|
export declare class ChainConnectorDot implements IChainConnectorDot {
|
|
41
54
|
#private;
|
|
42
|
-
constructor(chaindataChainProvider: IChaindataNetworkProvider);
|
|
55
|
+
constructor(chaindataChainProvider: IChaindataNetworkProvider, options?: ChainConnectorDotOptions);
|
|
43
56
|
send<T = any>(chainId: DotNetworkId, method: string, params: unknown[], _isCacheable?: boolean | undefined, extraOptions?: {
|
|
44
57
|
/**
|
|
45
58
|
* 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:
|
|
@@ -81,6 +94,40 @@ export declare class ChainConnectorDot implements IChainConnectorDot {
|
|
|
81
94
|
private getTalismanSub;
|
|
82
95
|
}
|
|
83
96
|
//#endregion
|
|
97
|
+
//#region src/dot/ChainConnectorDotPapi.d.ts
|
|
98
|
+
/** The parts of a polkadot-api `PolkadotClient` that the connector uses */
|
|
99
|
+
export type PapiClient = {
|
|
100
|
+
/** `PolkadotClient` types it without `abortSignal`, but it is substrate-client's `request`, which honours it */
|
|
101
|
+
_request: <Reply>(method: string, params: unknown[], abortSignal?: AbortSignal) => Promise<Reply>;
|
|
102
|
+
bestBlocks$: {
|
|
103
|
+
subscribe: (observer: {
|
|
104
|
+
next: (blocks: {
|
|
105
|
+
hash: string;
|
|
106
|
+
}[]) => void;
|
|
107
|
+
error: (error: unknown) => void;
|
|
108
|
+
complete: () => void;
|
|
109
|
+
}) => {
|
|
110
|
+
unsubscribe: () => void;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Runs `@talismn/balances` over a dapp's polkadot-api clients, so the dapp and its balances share one socket per chain.
|
|
116
|
+
*
|
|
117
|
+
* `state_subscribeStorage` is the only subscription balances uses. It is served by querying the keys at each new
|
|
118
|
+
* best block and reporting the values that changed, like the node does. One query runs at a time, for the latest
|
|
119
|
+
* best block. A failed query is retried at the next block, because balances treats a subscription error as final
|
|
120
|
+
* and would stop updating that network. Only when the first three queries fail does the subscription report the error.
|
|
121
|
+
*/
|
|
122
|
+
export declare class ChainConnectorDotPapi implements IChainConnectorDot {
|
|
123
|
+
#private;
|
|
124
|
+
constructor(clientFor: (networkId: DotNetworkId) => PapiClient | undefined);
|
|
125
|
+
send<T = unknown>(networkId: DotNetworkId, method: string, params: unknown[]): Promise<T>;
|
|
126
|
+
subscribe(networkId: DotNetworkId, subscribeMethod: string, _responseMethod: string, params: unknown[], callback: SubscriptionCallback, timeout?: number | false): Promise<(unsubscribeMethod: string) => void>;
|
|
127
|
+
reset(): Promise<void>;
|
|
128
|
+
private getClient;
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
84
131
|
//#region src/dot/ChainConnectorDotStub.d.ts
|
|
85
132
|
export declare class ChainConnectorDotStub implements IChainConnectorDot {
|
|
86
133
|
#private;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/dot/IChainConnectorDot.ts","../src/dot/ChainConnectorDot.ts","../src/dot/ChainConnectorDotPapi.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;;;;qBCOnB,6BAA6B;EACxC;EACA;EAEA,YAAY,iBAAiB,UAAU;;;;;;qBAY5B,sBAAsB;EACjC;EACA;EAEA,YAAY,iBAAiB,UAAU;;YAmC7B;;;;;;;EAOV;;;;;;;;;;;;;;;;qBAiBW,6BAA6B;;EAOxC,YACE,wBAAwB,2BACxB,UAAU;EAON,KAAK,SACT,SAAS,cACT,gBACA,mBACA,oCACA;;;;;;;;;;IAUE;MAED,QAAQ;EAsDL,UACJ,SAAS,cACT,yBACA,wBACA,mBACA,UAAU,sBACV,2BACC,SAAS;;;;;;EA2FN,MAAM,SAAS,eAAY;;UA2BzB;;UA0BA;;;;;;UAgCM;UA2BA;UAgCN;UAoDA;UAeA;UAwBM;;UAYN;;UAQA;UAIA;;;;;YC/hBE;;EAEV,WAAW,OAAO,gBAAgB,mBAAmB,cAAc,gBAAgB,QAAQ;EAC3F;IACE,YAAY;MACV,OAAO;QAAU;;MACjB,QAAQ;MACR;;MACM;;;;;;;;;;;;qBAYC,iCAAiC;;EAG5C,YAAY,YAAY,WAAW,iBAAiB;EAI9C,KAAK,aAAa,WAAW,cAAc,gBAAgB,oBAAoB,QAAQ;EAIvF,UACJ,WAAW,cACX,yBACA,yBACA,mBACA,UAAU,sBACV,2BACC,SAAS;EA8FN,SAAK;UAEH;;;;qBC1IG,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
|
@@ -40,8 +40,14 @@ const BAD_RPC_ERRORS = {
|
|
|
40
40
|
"-32097": "Rate limit exceeded",
|
|
41
41
|
"-32098": "Capacity exceeded"
|
|
42
42
|
};
|
|
43
|
-
const RESPONSE_TIMEOUT = 3e4;
|
|
43
|
+
const RESPONSE_TIMEOUT$1 = 3e4;
|
|
44
44
|
const KEEP_ALIVE_INTERVAL = 2e4;
|
|
45
|
+
/**
|
|
46
|
+
* How long a connection stays open once its last user releases it. A one-off request is
|
|
47
|
+
* often followed by more traffic to the same chain (balances read the runtime version,
|
|
48
|
+
* then subscribe to storage), which then reuses the socket instead of opening a new one.
|
|
49
|
+
*/
|
|
50
|
+
const IDLE_CLOSE_DELAY = 3e4;
|
|
45
51
|
/** in-flight requests reject with DestroyedError when a connection is torn down - expected, not worth logging */
|
|
46
52
|
const isDestroyedError = (error) => error instanceof Error && error.name === "DestroyedError";
|
|
47
53
|
const STALE_NOTIFY_TIMEOUT = 3e4;
|
|
@@ -54,6 +60,10 @@ var ChainConnectionError = class extends Error {
|
|
|
54
60
|
this.chainId = chainId;
|
|
55
61
|
}
|
|
56
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Passed to subscription callbacks when the chain has been unreachable for a while. Not
|
|
65
|
+
* terminal: the subscription stays registered and is re-issued when a socket connects.
|
|
66
|
+
*/
|
|
57
67
|
var StaleRpcError = class extends Error {
|
|
58
68
|
type;
|
|
59
69
|
chainId;
|
|
@@ -79,10 +89,12 @@ var StaleRpcError = class extends Error {
|
|
|
79
89
|
*/
|
|
80
90
|
var ChainConnectorDot = class {
|
|
81
91
|
#chaindataChainProvider;
|
|
92
|
+
#connectionTimeout;
|
|
82
93
|
#connections = {};
|
|
83
94
|
#pendingConnections = {};
|
|
84
|
-
constructor(chaindataChainProvider) {
|
|
95
|
+
constructor(chaindataChainProvider, options) {
|
|
85
96
|
this.#chaindataChainProvider = chaindataChainProvider;
|
|
97
|
+
this.#connectionTimeout = options?.connectionTimeout;
|
|
86
98
|
}
|
|
87
99
|
async send(chainId, method, params, _isCacheable, extraOptions) {
|
|
88
100
|
const talismanSub = this.getTalismanSub();
|
|
@@ -171,8 +183,7 @@ var ChainConnectorDot = class {
|
|
|
171
183
|
subscription.cancelRequest?.();
|
|
172
184
|
subscription.stopFollow?.();
|
|
173
185
|
const current = this.#connections[chainId];
|
|
174
|
-
|
|
175
|
-
if (subscription.serverSubId !== null && current && !isLastUser) try {
|
|
186
|
+
if (subscription.serverSubId !== null && current) try {
|
|
176
187
|
current.client.request(unsubscribeMethod, [subscription.serverSubId]).catch((error) => {
|
|
177
188
|
if (!isDestroyedError(error)) log_default.warn(`Failed to unsubscribe from ${chainId}`, error);
|
|
178
189
|
});
|
|
@@ -202,11 +213,11 @@ var ChainConnectorDot = class {
|
|
|
202
213
|
for (const subscription of fresh.subscriptions) this.startSubscription(fresh, subscription);
|
|
203
214
|
} catch (error) {
|
|
204
215
|
log_default.warn(`Failed to recreate connection for ${chainId} after reset`, error);
|
|
205
|
-
for (const subscription of connection.subscriptions) subscription.callback(new
|
|
216
|
+
for (const subscription of connection.subscriptions) subscription.callback(new ChainConnectionError(chainId, { cause: error }), null);
|
|
206
217
|
}
|
|
207
218
|
}
|
|
208
219
|
/** Sends a request over a connection, throwing `Error("TIMEOUT")` if no response arrives in time */
|
|
209
|
-
request(connection, method, params, timeoutMs = RESPONSE_TIMEOUT) {
|
|
220
|
+
request(connection, method, params, timeoutMs = RESPONSE_TIMEOUT$1) {
|
|
210
221
|
return new Promise((resolve, reject) => {
|
|
211
222
|
const timer = setTimeout(() => {
|
|
212
223
|
cancel();
|
|
@@ -260,6 +271,10 @@ var ChainConnectorDot = class {
|
|
|
260
271
|
});
|
|
261
272
|
connection = await this.#pendingConnections[chainId];
|
|
262
273
|
}
|
|
274
|
+
if (connection.idleTimeout) {
|
|
275
|
+
clearTimeout(connection.idleTimeout);
|
|
276
|
+
connection.idleTimeout = null;
|
|
277
|
+
}
|
|
263
278
|
const socketUserId = this.getExclusiveRandomId([...connection.users]);
|
|
264
279
|
connection.users.add(socketUserId);
|
|
265
280
|
return [socketUserId, connection];
|
|
@@ -270,7 +285,10 @@ var ChainConnectorDot = class {
|
|
|
270
285
|
const rpcs = chain.rpcs.concat();
|
|
271
286
|
if (!rpcs.length) throw new Error(`No healthy RPCs available for chain ${chainId}`);
|
|
272
287
|
let connection = null;
|
|
273
|
-
const provider = (0, _polkadot_api_ws_provider.getWsProvider)(rpcs, {
|
|
288
|
+
const provider = (0, _polkadot_api_ws_provider.getWsProvider)(rpcs, {
|
|
289
|
+
onStatusChanged: (status) => this.handleStatusChange(connection, status),
|
|
290
|
+
...this.#connectionTimeout !== void 0 && { timeout: this.#connectionTimeout }
|
|
291
|
+
});
|
|
274
292
|
connection = {
|
|
275
293
|
chainId,
|
|
276
294
|
provider,
|
|
@@ -279,7 +297,8 @@ var ChainConnectorDot = class {
|
|
|
279
297
|
subscriptions: /* @__PURE__ */ new Set(),
|
|
280
298
|
wasConnected: false,
|
|
281
299
|
keepAliveInterval: null,
|
|
282
|
-
staleTimeout: null
|
|
300
|
+
staleTimeout: null,
|
|
301
|
+
idleTimeout: null
|
|
283
302
|
};
|
|
284
303
|
return connection;
|
|
285
304
|
}
|
|
@@ -321,15 +340,21 @@ var ChainConnectorDot = class {
|
|
|
321
340
|
const connection = this.#connections[chainId];
|
|
322
341
|
if (!connection) return;
|
|
323
342
|
connection.users.delete(socketUserId);
|
|
324
|
-
if (connection.users.size > 0) return;
|
|
325
|
-
|
|
326
|
-
|
|
343
|
+
if (connection.users.size > 0 || connection.idleTimeout) return;
|
|
344
|
+
connection.idleTimeout = setTimeout(() => {
|
|
345
|
+
connection.idleTimeout = null;
|
|
346
|
+
if (connection.users.size > 0 || this.#connections[chainId] !== connection) return;
|
|
347
|
+
this.destroyConnection(connection);
|
|
348
|
+
delete this.#connections[chainId];
|
|
349
|
+
}, IDLE_CLOSE_DELAY);
|
|
327
350
|
}
|
|
328
351
|
destroyConnection(connection) {
|
|
329
352
|
if (connection.keepAliveInterval) clearInterval(connection.keepAliveInterval);
|
|
330
353
|
if (connection.staleTimeout) clearTimeout(connection.staleTimeout);
|
|
354
|
+
if (connection.idleTimeout) clearTimeout(connection.idleTimeout);
|
|
331
355
|
connection.keepAliveInterval = null;
|
|
332
356
|
connection.staleTimeout = null;
|
|
357
|
+
connection.idleTimeout = null;
|
|
333
358
|
for (const subscription of connection.subscriptions) {
|
|
334
359
|
subscription.stopFollow?.();
|
|
335
360
|
subscription.cancelRequest?.();
|
|
@@ -376,6 +401,119 @@ var ChainConnectorDot = class {
|
|
|
376
401
|
}
|
|
377
402
|
};
|
|
378
403
|
//#endregion
|
|
404
|
+
//#region src/dot/ChainConnectorDotPapi.ts
|
|
405
|
+
const RESPONSE_TIMEOUT = 3e4;
|
|
406
|
+
const FAILURES_BEFORE_FIRST_RESULT = 3;
|
|
407
|
+
/**
|
|
408
|
+
* Runs `@talismn/balances` over a dapp's polkadot-api clients, so the dapp and its balances share one socket per chain.
|
|
409
|
+
*
|
|
410
|
+
* `state_subscribeStorage` is the only subscription balances uses. It is served by querying the keys at each new
|
|
411
|
+
* best block and reporting the values that changed, like the node does. One query runs at a time, for the latest
|
|
412
|
+
* best block. A failed query is retried at the next block, because balances treats a subscription error as final
|
|
413
|
+
* and would stop updating that network. Only when the first three queries fail does the subscription report the error.
|
|
414
|
+
*/
|
|
415
|
+
var ChainConnectorDotPapi = class {
|
|
416
|
+
#clientFor;
|
|
417
|
+
constructor(clientFor) {
|
|
418
|
+
this.#clientFor = clientFor;
|
|
419
|
+
}
|
|
420
|
+
async send(networkId, method, params) {
|
|
421
|
+
return requestWithTimeout(this.getClient(networkId), method, params, new AbortController());
|
|
422
|
+
}
|
|
423
|
+
async subscribe(networkId, subscribeMethod, _responseMethod, params, callback, timeout = 3e4) {
|
|
424
|
+
const client = this.getClient(networkId);
|
|
425
|
+
if (subscribeMethod !== "state_subscribeStorage") throw new Error(`ChainConnectorDotPapi does not support ${subscribeMethod}`);
|
|
426
|
+
const [keys] = params;
|
|
427
|
+
const values = /* @__PURE__ */ new Map();
|
|
428
|
+
let bestBlockHash;
|
|
429
|
+
let queriedBlockHash;
|
|
430
|
+
let inFlight;
|
|
431
|
+
let delivered = false;
|
|
432
|
+
let failures = 0;
|
|
433
|
+
let active = true;
|
|
434
|
+
let subscription;
|
|
435
|
+
const staleTimer = timeout ? setTimeout(() => callback(new StaleRpcError(networkId), null), timeout) : void 0;
|
|
436
|
+
const stop = () => {
|
|
437
|
+
active = false;
|
|
438
|
+
clearTimeout(staleTimer);
|
|
439
|
+
inFlight?.abort();
|
|
440
|
+
subscription?.unsubscribe();
|
|
441
|
+
};
|
|
442
|
+
const finish = (error) => {
|
|
443
|
+
if (!active) return;
|
|
444
|
+
stop();
|
|
445
|
+
callback(error, null);
|
|
446
|
+
};
|
|
447
|
+
const reportChanges = (result) => {
|
|
448
|
+
const changes = result.changes.filter(([key, value]) => values.get(key) !== value);
|
|
449
|
+
for (const [key, value] of changes) values.set(key, value);
|
|
450
|
+
if (!changes.length) return;
|
|
451
|
+
delivered = true;
|
|
452
|
+
clearTimeout(staleTimer);
|
|
453
|
+
callback(null, {
|
|
454
|
+
block: result.block,
|
|
455
|
+
changes
|
|
456
|
+
});
|
|
457
|
+
};
|
|
458
|
+
const reportFailure = (error) => {
|
|
459
|
+
if (delivered || ++failures < FAILURES_BEFORE_FIRST_RESULT) return log_default.warn(`state_queryStorageAt failed on ${networkId}, retrying at the next block`, error);
|
|
460
|
+
finish(error);
|
|
461
|
+
};
|
|
462
|
+
const queryBestBlock = async () => {
|
|
463
|
+
if (!active || inFlight || !bestBlockHash || bestBlockHash === queriedBlockHash) return;
|
|
464
|
+
const blockHash = bestBlockHash;
|
|
465
|
+
queriedBlockHash = blockHash;
|
|
466
|
+
const controller = new AbortController();
|
|
467
|
+
inFlight = controller;
|
|
468
|
+
try {
|
|
469
|
+
const [result] = await requestWithTimeout(client, "state_queryStorageAt", [keys, blockHash], controller);
|
|
470
|
+
if (!active) return;
|
|
471
|
+
if (!result) throw new Error(`Empty state_queryStorageAt response on ${networkId}`);
|
|
472
|
+
reportChanges(result);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (active) reportFailure(error);
|
|
475
|
+
} finally {
|
|
476
|
+
if (inFlight === controller) inFlight = void 0;
|
|
477
|
+
queryBestBlock();
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
try {
|
|
481
|
+
subscription = client.bestBlocks$.subscribe({
|
|
482
|
+
next: ([best]) => {
|
|
483
|
+
bestBlockHash = best?.hash;
|
|
484
|
+
queryBestBlock();
|
|
485
|
+
},
|
|
486
|
+
error: (error) => finish(error),
|
|
487
|
+
complete: () => finish(/* @__PURE__ */ new Error(`polkadot-api client for ${networkId} stopped following blocks`))
|
|
488
|
+
});
|
|
489
|
+
} catch (error) {
|
|
490
|
+
stop();
|
|
491
|
+
throw error;
|
|
492
|
+
}
|
|
493
|
+
if (!active) subscription.unsubscribe();
|
|
494
|
+
return stop;
|
|
495
|
+
}
|
|
496
|
+
async reset() {}
|
|
497
|
+
getClient(networkId) {
|
|
498
|
+
const client = this.#clientFor(networkId);
|
|
499
|
+
if (!client) throw new Error(`No polkadot-api client for network ${networkId}`);
|
|
500
|
+
return client;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
/** rejects on its own timer too, so a client that ignores the abort signal cannot hang the caller */
|
|
504
|
+
const requestWithTimeout = (client, method, params, controller) => new Promise((resolve, reject) => {
|
|
505
|
+
const timer = setTimeout(() => {
|
|
506
|
+
controller.abort();
|
|
507
|
+
reject(/* @__PURE__ */ new Error("Timeout"));
|
|
508
|
+
}, RESPONSE_TIMEOUT);
|
|
509
|
+
try {
|
|
510
|
+
client._request(method, params, controller.signal).then(resolve, reject).finally(() => clearTimeout(timer));
|
|
511
|
+
} catch (error) {
|
|
512
|
+
clearTimeout(timer);
|
|
513
|
+
reject(error);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
//#endregion
|
|
379
517
|
//#region src/dot/ChainConnectorDotStub.ts
|
|
380
518
|
const TIMEOUT = 1e4;
|
|
381
519
|
var ChainConnectorDotStub = class {
|
|
@@ -733,6 +871,7 @@ var ChainConnectorSolStub = class {
|
|
|
733
871
|
//#endregion
|
|
734
872
|
exports.ChainConnectionError = ChainConnectionError;
|
|
735
873
|
exports.ChainConnectorDot = ChainConnectorDot;
|
|
874
|
+
exports.ChainConnectorDotPapi = ChainConnectorDotPapi;
|
|
736
875
|
exports.ChainConnectorDotStub = ChainConnectorDotStub;
|
|
737
876
|
exports.ChainConnectorEth = ChainConnectorEth;
|
|
738
877
|
exports.ChainConnectorEthStub = ChainConnectorEthStub;
|