Skip to content

Commit 17d9841

Browse files
committed
feat: Implement waterfall style fallback provider to improve performance
- Replace ethers fallback provider with a simple waterfall-style provider that allows redundancy without costing any performance. - Fix issue with card deeplink race condition (view binding not set before invokeUrlAction is invoked).
1 parent c379c81 commit 17d9841

File tree

8 files changed

+72
-19
lines changed

8 files changed

+72
-19
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {AbstractProvider, getBigInt, Network, PerformActionRequest} from "ethers";
2+
3+
export class WaterfallFallbackProvider extends AbstractProvider {
4+
5+
constructor(private providers: Array<AbstractProvider>) {
6+
super();
7+
}
8+
9+
async _detectNetwork(): Promise<Network> {
10+
return Network.from(getBigInt(await this._perform({ method: "chainId" })));
11+
}
12+
13+
async _perform<T = any>(req: PerformActionRequest): Promise<T> {
14+
15+
for (const provider of this.providers){
16+
try {
17+
return provider._perform(req);
18+
} catch (e) {
19+
console.error("Provider error, falling back to next provider ", e);
20+
}
21+
}
22+
}
23+
24+
async destroy(): Promise<void> {
25+
for (const provider of this.providers) {
26+
provider.destroy();
27+
}
28+
super.destroy();
29+
}
30+
}

javascript/card-sdk/src/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import {IEngineAdapter, RequestFromView} from "./messaging/IEngineAdapter";
1414
import {PostMessageAdapter} from "./messaging/PostMessageAdapter";
1515
import {LocalStorageAdapter} from "./storage/localStorageAdapter";
16+
import {WaterfallFallbackProvider} from "./ethereum/WaterfallFallbackProvider";
1617

1718
export interface IChainConfig {
1819
rpc: string|string[],
@@ -146,9 +147,7 @@ class TokenScriptSDK implements ITokenScriptSDK {
146147
for (const url of rpcUrls){
147148
providers.push(new ethers.JsonRpcProvider(url, chain, { staticNetwork: new Network(chain.toString(), chain) }));
148149
}
149-
return new ethers.FallbackProvider(providers, chain, {
150-
quorum: 2
151-
});
150+
return new WaterfallFallbackProvider(providers);
152151
} else {
153152
return new ethers.JsonRpcProvider(rpcUrls[0], chain, { staticNetwork: new Network(chain.toString(), chain) });
154153
}

javascript/card-sdk/src/messaging/PostMessageAdapter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export class PostMessageAdapter implements IEngineAdapter {
4040
switch (event.data?.method) {
4141
case ViewEvent.TOKENS_UPDATED:
4242
this.sdk.tokens.dataChanged(params.oldTokens, params.updatedTokens, params.cardId);
43+
//this.sdk.emitEvent("DATA_CHANGED", params);
4344
break;
4445

4546
case ViewEvent.ON_CONFIRM:

javascript/card-sdk/src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export interface ITokenScriptSDK {
4545
showLoader: () => void,
4646
hideLoader: () => void,
4747
setActionButton: (options: { show?: boolean, disable?: boolean, text?: string }) => void,
48-
executeTransaction: (txName?: string|TXOptions, listener?: ITransactionListener) => Promise<any|false>,
48+
executeTransaction: (optionsOrTxName?: string|TXOptions, listener?: ITransactionListener) => Promise<ITransactionStatus|false>,
4949
showTransactionToast: (status: "submitted"|"confirmed", chain: number, txHash: string) => void,
5050
showMessageToast: (type: 'success'|'info'|'warning'|'error', title: string, description: string) => void,
5151
closeCard: () => void,

javascript/engine-js/src/view/sdk/v1.txt

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

javascript/engine-js/src/wallet/EthersAdapter.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from "ethers";
1111
import {ITransactionListener} from "../TokenScript";
1212
import {ErrorDecoder, ErrorType} from "ethers-decode-error";
13+
import {WaterfallFallbackProvider} from "./WaterfallFallbackProvider";
1314

1415
export interface IChainConfig {
1516
rpc: string|string[],
@@ -23,7 +24,7 @@ export class EthersAdapter implements IWalletAdapter {
2324

2425
private ethersProvider: ethers.BrowserProvider;
2526

26-
private rpcProviders: {[chainId: number]: ethers.JsonRpcProvider|ethers.FallbackProvider} = {};
27+
private rpcProviders: {[chainId: number]: ethers.JsonRpcProvider|WaterfallFallbackProvider} = {};
2728

2829
constructor(
2930
public getWalletEthersProvider: () => Promise<ethers.BrowserProvider>,
@@ -330,18 +331,10 @@ export class EthersAdapter implements IWalletAdapter {
330331
const rpcUrls = this.getRpcUrls(chain);
331332

332333
if (rpcUrls.length > 1){
333-
this.rpcProviders[chain] = new ethers.FallbackProvider(
334-
rpcUrls.map((url, index) => {
335-
return {
336-
provider: new ethers.JsonRpcProvider(url, chain, { staticNetwork: new Network(chain.toString(), chain) }),
337-
stallTimeout: 3000,
338-
priority: index + 1,
339-
}
340-
}),
341-
chain,
342-
{
343-
quorum: 1
344-
}
334+
this.rpcProviders[chain] = new WaterfallFallbackProvider(
335+
rpcUrls.map((url) => {
336+
return new ethers.JsonRpcProvider(url, chain, { staticNetwork: new Network(chain.toString(), chain) })
337+
})
345338
);
346339
} else {
347340
this.rpcProviders[chain] = new ethers.JsonRpcProvider(rpcUrls[0], chain, { staticNetwork: new Network(chain.toString(), chain) });
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {AbstractProvider, getBigInt, Network, PerformActionRequest} from "ethers";
2+
3+
export class WaterfallFallbackProvider extends AbstractProvider {
4+
5+
constructor(private providers: Array<AbstractProvider>) {
6+
super();
7+
}
8+
9+
async _detectNetwork(): Promise<Network> {
10+
return Network.from(getBigInt(await this._perform({ method: "chainId" })));
11+
}
12+
13+
async _perform<T = any>(req: PerformActionRequest): Promise<T> {
14+
15+
for (const provider of this.providers){
16+
try {
17+
return provider._perform(req);
18+
} catch (e) {
19+
console.error("Provider error, falling back to next provider ", e);
20+
}
21+
}
22+
}
23+
24+
async destroy(): Promise<void> {
25+
for (const provider of this.providers) {
26+
provider.destroy();
27+
}
28+
super.destroy();
29+
}
30+
}

javascript/tokenscript-viewer/src/components/common/tokens-grid/tokens-grid.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export class TokensGrid {
155155
await cardRes.card.isEnabledOrReason(context) === true
156156
) {
157157
this.urlActionInvoked = true;
158-
this.showCard(cardRes.card, token, cardRes.index);
158+
setTimeout(() => this.showCard(cardRes.card, token, cardRes.index), 100);
159159
return;
160160
}
161161
}

0 commit comments

Comments
 (0)