From 636ef278f4fad34442a8a6a75a8ca26c83b60d11 Mon Sep 17 00:00:00 2001 From: CAst Date: Thu, 25 Apr 2024 19:27:08 +0500 Subject: [PATCH] Handle 429 error (#102) * [handle-429-error] improve createProvider method * [handle-429-error] improves --- src/index.ts | 2 +- src/utils/apiUrls.ts | 2 ++ src/utils/createProvider.ts | 56 +++++++++++++++++++++++++++++-------- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4781cdbb..d39e95fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,5 +3,5 @@ import './types/global' export * from './utils/enums' export { createContract } from './contracts' -export { BigDecimal, configs, getGas } from './utils' export { default as StakeWiseSDK } from './StakeWiseSDK' +export { BigDecimal, configs, getGas, createProvider } from './utils' diff --git a/src/utils/apiUrls.ts b/src/utils/apiUrls.ts index adcb5e6b..68ce9c99 100644 --- a/src/utils/apiUrls.ts +++ b/src/utils/apiUrls.ts @@ -1,11 +1,13 @@ import configs from './configs' +const getWeb3Url = (options: StakeWise.Options) => options.endpoints?.web3 || configs[options.network].network.url const getBackendUrl = (options: StakeWise.Options) => options.endpoints?.api || configs[options.network].api.backend const getSubgraphqlUrl = (options: StakeWise.Options) => options.endpoints?.subgraph || configs[options.network].api.subgraph export default { + getWeb3Url, getBackendUrl, getSubgraphqlUrl, } diff --git a/src/utils/createProvider.ts b/src/utils/createProvider.ts index a6bb4106..89868ce9 100644 --- a/src/utils/createProvider.ts +++ b/src/utils/createProvider.ts @@ -1,26 +1,60 @@ -import { JsonRpcProvider, FallbackProvider } from 'ethers' +import { JsonRpcProvider, FallbackProvider, FetchRequest, FetchResponse } from 'ethers' -import configs from './configs' +import apiUrls from './apiUrls' const createProvider = (options: StakeWise.Options) => { - const urls = options.endpoints?.web3 + const { network } = options - if (!urls) { - return new JsonRpcProvider(configs[options.network].network.url) - } + const urls = apiUrls.getWeb3Url(options) if (Array.isArray(urls)) { if (urls.length === 1) { return new JsonRpcProvider(urls[0]) } else { - const providers: ConstructorParameters[0] = urls.map((url, index) => ({ - provider: new JsonRpcProvider(url), - priority: index + 1, - })) + const providers: ConstructorParameters[0] = urls.map((url, index) => { + // Why do we use FetchRequest and change the behavior of the 429 error? + // Because inside ethers error 429 is handled only by resending the request + // and in this case no additional node is started. This is a problem for public nodes, + // so we think it is better to use an additional node in case of 429 error + const fetchRequest = new FetchRequest(url) + + fetchRequest.setThrottleParams({ + slotInterval: 2 * 1000, + maxAttempts: 2, + }) + + fetchRequest.processFunc = async (_, resp) => { + if (resp.statusCode === 429) { + // mutate the response to 500 to start an additional node + // https://github.com/ethers-io/ethers.js/blob/main/src.ts/utils/fetch.ts#L519 + const newResponse = new FetchResponse(500, '', resp.headers, null) + + return newResponse + } + + return resp + } + + fetchRequest.retryFunc = async (_, response) => { + if (response.statusCode === 429) { + // stop retry attempts + // https://github.com/ethers-io/ethers.js/blob/main/src.ts/utils/fetch.ts#L556 + return false + } + + return true + } + + return { + provider: new JsonRpcProvider(fetchRequest, network), + priority: index + 1, + weight: 1, + } + }) - const provider = new FallbackProvider(providers) as StakeWise.CustomFallbackProvider + const provider = new FallbackProvider(providers, network) as StakeWise.CustomFallbackProvider provider.getSigner = () => { throw new Error('Pass your provider to the SDK class instance to get a signer')