From 5ca4f31bc2fd65eb36a45e26c5d6444446c1d7b4 Mon Sep 17 00:00:00 2001 From: Georges KABBOUCHI Date: Tue, 5 Jul 2022 15:13:45 +0300 Subject: [PATCH] Add `JsonRpcRetryProvider` --- package.json | 7 +++-- src/abi/fetcher/AbiFetcher.ts | 4 +-- src/cast/CastDecoder.ts | 4 +-- src/index.ts | 1 + src/providers/index.ts | 1 + src/providers/retry-provider.ts | 51 +++++++++++++++++++++++++++++++++ yarn.lock | 12 +++++++- 7 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 src/providers/index.ts create mode 100644 src/providers/retry-provider.ts diff --git a/package.json b/package.json index f289b66..13648a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@instadapp/utils", - "version": "0.1.6", + "version": "0.1.7", "description": "", "repository": "instadapp/utils", "license": "MIT", @@ -31,11 +31,14 @@ "@ethersproject/contracts": "^5.6.2", "@ethersproject/providers": "^5.6.8", "async-retry": "^1.3.3", - "axios": "^0.27.2" + "axios": "^0.27.2", + "bluebird": "^3.7.2", + "waait": "^1.0.5" }, "devDependencies": { "@nuxtjs/eslint-config-typescript": "latest", "@types/async-retry": "^1.4.4", + "@types/bluebird": "^3.5.36", "@vitest/ui": "^0.16.0", "c8": "latest", "dotenv": "^16.0.1", diff --git a/src/abi/fetcher/AbiFetcher.ts b/src/abi/fetcher/AbiFetcher.ts index 0770df1..88088ae 100644 --- a/src/abi/fetcher/AbiFetcher.ts +++ b/src/abi/fetcher/AbiFetcher.ts @@ -1,11 +1,11 @@ import { getAddress } from '@ethersproject/address' -import { JsonRpcProvider } from '@ethersproject/providers' import { Contract } from '@ethersproject/contracts' import retry from 'async-retry' import axios from 'axios' import type { JsonFragment } from '@ethersproject/abi' import { IAbiFetcherOptions, ProxyFetchMode } from '../types' import { Network } from '../../types' +import { JsonRpcRetryProvider } from '../../providers' const DEFAULTS: IAbiFetcherOptions = { retries: 3, @@ -92,7 +92,7 @@ export class AbiFetcher { const originalAbi = await this._get(contractAddress, network) if (proxyFetchMode !== 'proxyOnly') { - const provider = new JsonRpcProvider(rpcProviderUrl[network]) + const provider = new JsonRpcRetryProvider(rpcProviderUrl[network]) let implementationAddress: string let implementationAbi: JsonFragment[] = [] diff --git a/src/cast/CastDecoder.ts b/src/cast/CastDecoder.ts index be82a4e..2692f30 100644 --- a/src/cast/CastDecoder.ts +++ b/src/cast/CastDecoder.ts @@ -1,8 +1,8 @@ import { Interface, defaultAbiCoder } from '@ethersproject/abi' import { isAddress } from '@ethersproject/address' import { Contract } from '@ethersproject/contracts' -import { JsonRpcProvider } from '@ethersproject/providers' import { AbiFetcher } from '../abi' +import { JsonRpcRetryProvider } from '../providers' import { Network } from '../types' import { ICastDecoderOptions } from './types' const connectorsV1AddressToName = { @@ -104,7 +104,7 @@ export class CastDecoder { stateMutability: 'view', type: 'function' } - ], new JsonRpcProvider(this.options.abiFetcher.options.rpcProviderUrl[network])) + ], new JsonRpcRetryProvider(this.options.abiFetcher.options.rpcProviderUrl[network])) const contractAddress = await contract.connectors(connectorName) diff --git a/src/index.ts b/src/index.ts index b86cb3e..2090190 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ export * from './abi' export * from './cast' +export * from './providers' export * from './types' diff --git a/src/providers/index.ts b/src/providers/index.ts new file mode 100644 index 0000000..383ad49 --- /dev/null +++ b/src/providers/index.ts @@ -0,0 +1 @@ +export * from './retry-provider' diff --git a/src/providers/retry-provider.ts b/src/providers/retry-provider.ts new file mode 100644 index 0000000..a2d6819 --- /dev/null +++ b/src/providers/retry-provider.ts @@ -0,0 +1,51 @@ +import { JsonRpcProvider } from '@ethersproject/providers' +import wait from 'waait' +import Bluebird from 'bluebird' + +export interface RetryOptions { + delay?: number; + timeouts: number[]; +} + +export function promiseTimeout (ms: number, promise: Promise): Promise { + return Bluebird.resolve(promise).timeout(ms) +} + +export function retryOperation ( + retriesLeft: number, + operation: () => Promise, + options: RetryOptions +) { + return new Promise((resolve, reject) => { + const { timeouts } = options + // Find the timeout for this specific iteration + const timeout = timeouts[timeouts.length - retriesLeft] + + // Wrap the original operation in a timeout + const execution = promiseTimeout(timeout, operation()) + + // If the promise is successful, resolve it and bubble the result up + return execution.then(resolve).catch((reason: any) => { + // If there are any retries left, we call the same retryOperation function again, + // but decrementing the number of retries left by 1 + if (retriesLeft - 1 > 0) { + // Delay the new attempt slightly + return wait(options.delay || 50) + .then(retryOperation.bind(null, retriesLeft - 1, operation, options)) + .then(resolve) + .catch(reject) + } + // Reject (and bubble the result up) if there are no more retries + return reject(reason) + }) + }) +} + +export class JsonRpcRetryProvider extends JsonRpcProvider { + public perform (method: string, params: any): Promise { + const timeouts = [5_000, 10_000] + const operation = () => super.perform(method, params) + + return retryOperation(2, operation, { timeouts, delay: 50 }) + } +} diff --git a/yarn.lock b/yarn.lock index e1b1759..e839ef8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -664,6 +664,11 @@ dependencies: "@types/retry" "*" +"@types/bluebird@^3.5.36": + version "3.5.36" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.36.tgz#00d9301d4dc35c2f6465a8aec634bb533674c652" + integrity sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q== + "@types/bn.js@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" @@ -1053,7 +1058,7 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== -bluebird@^3.5.0: +bluebird@^3.5.0, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -5693,6 +5698,11 @@ vue-eslint-parser@^8.0.1: lodash "^4.17.21" semver "^7.3.5" +waait@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/waait/-/waait-1.0.5.tgz#6a3c7aaa88bd0a1a545e9d47890b9595bebf9aa7" + integrity sha512-wp+unA4CpqxvBUKHHv8D86fK4jWByHAWyhEXXVHfVUZfK+16ylpj7hjQ58Z8j9ntu8XNukRQT8Fi5qbyJ8rkyw== + web3-bzz@1.7.4: version "1.7.4" resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.7.4.tgz#9419e606e38a9777443d4ce40506ebd796e06075"