Add RetryProvider (#520)

* Add RetryProvider

* Export

* Lint

* PR Review
pull/525/head v.0.2.0
Nam Chu Hoai 2 years ago committed by GitHub
parent d3e788ac3a
commit 969c51bdcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      typescript/infra/src/config/chain.ts
  2. 1
      typescript/sdk/src/ethers/index.ts
  3. 54
      typescript/sdk/src/ethers/retry-provider.ts
  4. 1
      typescript/sdk/src/index.ts
  5. 23
      typescript/utils/src/utils.ts

@ -3,7 +3,7 @@ import { NonceManager } from '@ethersproject/experimental';
import { StaticCeloJsonRpcProvider } from 'celo-ethers-provider';
import { ethers } from 'ethers';
import { ChainName } from '@abacus-network/sdk';
import { ChainName, RetryJsonRpcProvider } from '@abacus-network/sdk';
import { getSecretDeployerKey, getSecretRpcEndpoint } from '../agents';
@ -17,7 +17,10 @@ export async function fetchProvider(
const celoChainNames = new Set(['alfajores', 'baklava', 'celo']);
const provider = celoChainNames.has(chainName)
? new StaticCeloJsonRpcProvider(rpc)
: new ethers.providers.JsonRpcProvider(rpc);
: new RetryJsonRpcProvider(new ethers.providers.JsonRpcProvider(rpc), {
retryLimit: 2,
interval: 250,
});
return provider;
}

@ -0,0 +1 @@
export { RetryProvider, RetryJsonRpcProvider } from './retry-provider';

@ -0,0 +1,54 @@
// RetryProvider
//
// Mostly taken from the removed version that was in ethers.js
// See: https://github.com/ethers-io/ethers.js/discussions/3006
import { BaseProvider, JsonRpcProvider } from '@ethersproject/providers';
import { ethers } from 'ethers';
import { retryAsync } from '@abacus-network/utils/dist/src/utils';
export type RetryOptions = {
// The wait interval in between
interval: number;
// Maximum number of times to rety
retryLimit: number;
};
export class RetryProvider extends BaseProvider {
constructor(
readonly provider: BaseProvider,
readonly retryOptions: RetryOptions,
) {
super(provider.getNetwork());
ethers.utils.defineReadOnly(this, 'provider', provider);
ethers.utils.defineReadOnly(this, 'retryOptions', retryOptions);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
perform(method: string, params: any): Promise<any> {
return retryAsync(
() => this.provider.perform(method, params),
this.retryOptions.retryLimit,
this.retryOptions.interval,
);
}
}
// Need this separate class for JsonRpcProvider to still expose `getSigner`, so will retry at the request level
export class RetryJsonRpcProvider extends JsonRpcProvider {
constructor(
readonly provider: JsonRpcProvider,
readonly retryOptions: RetryOptions,
) {
super(provider.connection, provider.network);
}
async send(method: string, params: Array<any>): Promise<any> {
return retryAsync(
async () => this.provider.send(method, params),
this.retryOptions.retryLimit,
this.retryOptions.interval,
);
}
}

@ -57,3 +57,4 @@ export {
TestChainNames,
} from './types';
export { objMap, objMapEntries, promiseObjAll, utils } from './utils';
export { RetryProvider, RetryJsonRpcProvider } from './ethers';

@ -90,3 +90,26 @@ export function domainHash(domain: number): string {
[domain, 'ABACUS'],
);
}
export function sleep(ms: number): Promise<void> {
return new Promise<void>((resolve) => setTimeout(resolve, ms));
}
// Retries an async function when it raises an exeption
// if all the tries fail it raises the last thrown exeption
export async function retryAsync<T>(
runner: () => T,
attempts = 3,
delay = 500,
) {
let saveError;
for (let i = 0; i < attempts; i++) {
try {
return runner();
} catch (error) {
saveError = error;
await sleep(delay * (i + 1));
}
}
throw saveError;
}

Loading…
Cancel
Save