From 0c14a4c226d9cadc10dfc345a862c1a3941ad71b Mon Sep 17 00:00:00 2001 From: Sebastian Pape <0xneo11@gmail.com> Date: Sun, 30 Jun 2024 08:29:25 +0200 Subject: [PATCH] v10.18.7: fix solana rpc list and error rotation --- dev.html | 2 +- dist/esm/index.js | 65 +++++++++++++++++++++++----------- dist/esm/index.solana.js | 65 +++++++++++++++++++++++----------- dist/umd/index.js | 65 +++++++++++++++++++++++----------- dist/umd/index.solana.js | 65 +++++++++++++++++++++++----------- package.evm.json | 4 +-- package.json | 6 ++-- package.solana.json | 4 +-- src/clients/solana/provider.js | 65 +++++++++++++++++++++++----------- yarn.lock | 8 ++--- 10 files changed, 237 insertions(+), 112 deletions(-) diff --git a/dev.html b/dev.html index 68df63e..71c7fc8 100644 --- a/dev.html +++ b/dev.html @@ -8,7 +8,7 @@ - + diff --git a/dist/esm/index.js b/dist/esm/index.js index 6134aa3..f8a8a36 100644 --- a/dist/esm/index.js +++ b/dist/esm/index.js @@ -265,30 +265,55 @@ class StaticJsonRpcSequentialProvider extends Connection { this._rpcRequest = this._rpcRequestReplacement.bind(this); } + handleError(error, attempt, chunk) { + if(attempt < MAX_RETRY && error && [ + 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' + ].some((errorType)=>error.toString().match(errorType))) { + const index = this._endpoints.indexOf(this._endpoint)+1; + this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index]; + this._provider = new Connection(this._endpoint); + this.requestChunk(chunk, attempt+1); + } else { + chunk.forEach((inflightRequest) => { + inflightRequest.reject(error); + }); + } + } + + batchRequest(requests, attempt) { + return new Promise((resolve, reject) => { + if (requests.length === 0) resolve([]); // Do nothing if requests is empty + + const batch = requests.map(params => { + return this._rpcClient.request(params.methodName, params.args) + }); + + fetch( + this._endpoint, + { + method: 'POST', + body: JSON.stringify(batch), + headers: { 'Content-Type': 'application/json' }, + } + ).then((response)=>{ + if(response.ok) { + response.json().then((parsedJson)=>{ + resolve(parsedJson); + }).catch(reject); + } else { + reject(`${response.status} ${response.text}`); + } + }).catch(reject); + }) + } + requestChunk(chunk, attempt) { const batch = chunk.map((inflight) => inflight.request); - const handleError = (error)=>{ - if(attempt < MAX_RETRY && error && [ - 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' - ].some((errorType)=>error.toString().match(errorType))) { - const index = this._endpoints.indexOf(this._endpoint)+1; - this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index]; - this._provider = new Connection(this._endpoint); - this.requestChunk(chunk, attempt+1); - } else { - chunk.forEach((inflightRequest) => { - inflightRequest.reject(error); - }); - } - }; - try { - return this._provider._rpcBatchRequest(batch) + return this.batchRequest(batch, attempt) .then((result) => { - // For each result, feed it to the correct Promise, depending - // on whether it was a success or error chunk.forEach((inflightRequest, index) => { const payload = result[index]; if (_optionalChain$2([payload, 'optionalAccess', _ => _.error])) { @@ -302,8 +327,8 @@ class StaticJsonRpcSequentialProvider extends Connection { inflightRequest.reject(); } }); - }).catch(handleError) - } catch (error){ return handleError(error) } + }).catch((error)=>this.handleError(error, attempt, chunk)) + } catch (error){ return this.handleError(error, attempt, chunk) } } _rpcRequestReplacement(methodName, args) { diff --git a/dist/esm/index.solana.js b/dist/esm/index.solana.js index 6c11be5..536f270 100644 --- a/dist/esm/index.solana.js +++ b/dist/esm/index.solana.js @@ -44,30 +44,55 @@ class StaticJsonRpcSequentialProvider extends Connection { this._rpcRequest = this._rpcRequestReplacement.bind(this); } + handleError(error, attempt, chunk) { + if(attempt < MAX_RETRY && error && [ + 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' + ].some((errorType)=>error.toString().match(errorType))) { + const index = this._endpoints.indexOf(this._endpoint)+1; + this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index]; + this._provider = new Connection(this._endpoint); + this.requestChunk(chunk, attempt+1); + } else { + chunk.forEach((inflightRequest) => { + inflightRequest.reject(error); + }); + } + } + + batchRequest(requests, attempt) { + return new Promise((resolve, reject) => { + if (requests.length === 0) resolve([]); // Do nothing if requests is empty + + const batch = requests.map(params => { + return this._rpcClient.request(params.methodName, params.args) + }); + + fetch( + this._endpoint, + { + method: 'POST', + body: JSON.stringify(batch), + headers: { 'Content-Type': 'application/json' }, + } + ).then((response)=>{ + if(response.ok) { + response.json().then((parsedJson)=>{ + resolve(parsedJson); + }).catch(reject); + } else { + reject(`${response.status} ${response.text}`); + } + }).catch(reject); + }) + } + requestChunk(chunk, attempt) { const batch = chunk.map((inflight) => inflight.request); - const handleError = (error)=>{ - if(attempt < MAX_RETRY && error && [ - 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' - ].some((errorType)=>error.toString().match(errorType))) { - const index = this._endpoints.indexOf(this._endpoint)+1; - this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index]; - this._provider = new Connection(this._endpoint); - this.requestChunk(chunk, attempt+1); - } else { - chunk.forEach((inflightRequest) => { - inflightRequest.reject(error); - }); - } - }; - try { - return this._provider._rpcBatchRequest(batch) + return this.batchRequest(batch, attempt) .then((result) => { - // For each result, feed it to the correct Promise, depending - // on whether it was a success or error chunk.forEach((inflightRequest, index) => { const payload = result[index]; if (_optionalChain$2([payload, 'optionalAccess', _ => _.error])) { @@ -81,8 +106,8 @@ class StaticJsonRpcSequentialProvider extends Connection { inflightRequest.reject(); } }); - }).catch(handleError) - } catch (error){ return handleError(error) } + }).catch((error)=>this.handleError(error, attempt, chunk)) + } catch (error){ return this.handleError(error, attempt, chunk) } } _rpcRequestReplacement(methodName, args) { diff --git a/dist/umd/index.js b/dist/umd/index.js index 6501314..a19cfb9 100644 --- a/dist/umd/index.js +++ b/dist/umd/index.js @@ -271,30 +271,55 @@ this._rpcRequest = this._rpcRequestReplacement.bind(this); } + handleError(error, attempt, chunk) { + if(attempt < MAX_RETRY && error && [ + 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' + ].some((errorType)=>error.toString().match(errorType))) { + const index = this._endpoints.indexOf(this._endpoint)+1; + this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index]; + this._provider = new solanaWeb3_js.Connection(this._endpoint); + this.requestChunk(chunk, attempt+1); + } else { + chunk.forEach((inflightRequest) => { + inflightRequest.reject(error); + }); + } + } + + batchRequest(requests, attempt) { + return new Promise((resolve, reject) => { + if (requests.length === 0) resolve([]); // Do nothing if requests is empty + + const batch = requests.map(params => { + return this._rpcClient.request(params.methodName, params.args) + }); + + fetch( + this._endpoint, + { + method: 'POST', + body: JSON.stringify(batch), + headers: { 'Content-Type': 'application/json' }, + } + ).then((response)=>{ + if(response.ok) { + response.json().then((parsedJson)=>{ + resolve(parsedJson); + }).catch(reject); + } else { + reject(`${response.status} ${response.text}`); + } + }).catch(reject); + }) + } + requestChunk(chunk, attempt) { const batch = chunk.map((inflight) => inflight.request); - const handleError = (error)=>{ - if(attempt < MAX_RETRY && error && [ - 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' - ].some((errorType)=>error.toString().match(errorType))) { - const index = this._endpoints.indexOf(this._endpoint)+1; - this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index]; - this._provider = new solanaWeb3_js.Connection(this._endpoint); - this.requestChunk(chunk, attempt+1); - } else { - chunk.forEach((inflightRequest) => { - inflightRequest.reject(error); - }); - } - }; - try { - return this._provider._rpcBatchRequest(batch) + return this.batchRequest(batch, attempt) .then((result) => { - // For each result, feed it to the correct Promise, depending - // on whether it was a success or error chunk.forEach((inflightRequest, index) => { const payload = result[index]; if (_optionalChain$2([payload, 'optionalAccess', _ => _.error])) { @@ -308,8 +333,8 @@ inflightRequest.reject(); } }); - }).catch(handleError) - } catch (error){ return handleError(error) } + }).catch((error)=>this.handleError(error, attempt, chunk)) + } catch (error){ return this.handleError(error, attempt, chunk) } } _rpcRequestReplacement(methodName, args) { diff --git a/dist/umd/index.solana.js b/dist/umd/index.solana.js index 55f4181..aef8538 100644 --- a/dist/umd/index.solana.js +++ b/dist/umd/index.solana.js @@ -50,30 +50,55 @@ this._rpcRequest = this._rpcRequestReplacement.bind(this); } + handleError(error, attempt, chunk) { + if(attempt < MAX_RETRY && error && [ + 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' + ].some((errorType)=>error.toString().match(errorType))) { + const index = this._endpoints.indexOf(this._endpoint)+1; + this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index]; + this._provider = new solanaWeb3_js.Connection(this._endpoint); + this.requestChunk(chunk, attempt+1); + } else { + chunk.forEach((inflightRequest) => { + inflightRequest.reject(error); + }); + } + } + + batchRequest(requests, attempt) { + return new Promise((resolve, reject) => { + if (requests.length === 0) resolve([]); // Do nothing if requests is empty + + const batch = requests.map(params => { + return this._rpcClient.request(params.methodName, params.args) + }); + + fetch( + this._endpoint, + { + method: 'POST', + body: JSON.stringify(batch), + headers: { 'Content-Type': 'application/json' }, + } + ).then((response)=>{ + if(response.ok) { + response.json().then((parsedJson)=>{ + resolve(parsedJson); + }).catch(reject); + } else { + reject(`${response.status} ${response.text}`); + } + }).catch(reject); + }) + } + requestChunk(chunk, attempt) { const batch = chunk.map((inflight) => inflight.request); - const handleError = (error)=>{ - if(attempt < MAX_RETRY && error && [ - 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' - ].some((errorType)=>error.toString().match(errorType))) { - const index = this._endpoints.indexOf(this._endpoint)+1; - this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index]; - this._provider = new solanaWeb3_js.Connection(this._endpoint); - this.requestChunk(chunk, attempt+1); - } else { - chunk.forEach((inflightRequest) => { - inflightRequest.reject(error); - }); - } - }; - try { - return this._provider._rpcBatchRequest(batch) + return this.batchRequest(batch, attempt) .then((result) => { - // For each result, feed it to the correct Promise, depending - // on whether it was a success or error chunk.forEach((inflightRequest, index) => { const payload = result[index]; if (_optionalChain$2([payload, 'optionalAccess', _ => _.error])) { @@ -87,8 +112,8 @@ inflightRequest.reject(); } }); - }).catch(handleError) - } catch (error){ return handleError(error) } + }).catch((error)=>this.handleError(error, attempt, chunk)) + } catch (error){ return this.handleError(error, attempt, chunk) } } _rpcRequestReplacement(methodName, args) { diff --git a/package.evm.json b/package.evm.json index 7a1d8ea..f7b9bfc 100644 --- a/package.evm.json +++ b/package.evm.json @@ -1,7 +1,7 @@ { "name": "@depay/web3-client-evm", "moduleName": "Web3Client", - "version": "10.18.6", + "version": "10.18.7", "description": "A web3 client to fetch blockchain data just like you are used to with HTTP clients.", "main": "dist/umd/index.evm.js", "module": "dist/esm/index.evm.js", @@ -23,7 +23,7 @@ "homepage": "https://depay.com", "private": false, "peerDependencies": { - "@depay/web3-blockchains": "^9.3.6", + "@depay/web3-blockchains": "^9.4.2", "ethers": "^5.7.1" }, "engines": { diff --git a/package.json b/package.json index 2adedfe..c8924d2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@depay/web3-client", "moduleName": "Web3Client", - "version": "10.18.6", + "version": "10.18.7", "description": "A web3 client to fetch blockchain data just like you are used to with HTTP clients.", "main": "dist/umd/index.js", "module": "dist/esm/index.js", @@ -34,7 +34,7 @@ "private": false, "peerDependencies": { "@depay/solana-web3.js": "^1.25.1", - "@depay/web3-blockchains": "^9.3.6", + "@depay/web3-blockchains": "^9.4.2", "ethers": "^5.7.1" }, "engines": { @@ -44,7 +44,7 @@ "@babel/core": "^7.12.9", "@babel/preset-env": "^7.12.7", "@depay/solana-web3.js": "^1.25.1", - "@depay/web3-blockchains": "^9.3.6", + "@depay/web3-blockchains": "^9.4.2", "@depay/web3-mock": "^14.17.0", "@rollup/plugin-commonjs": "^22.0.1", "@rollup/plugin-json": "^4.1.0", diff --git a/package.solana.json b/package.solana.json index bee6e65..8e3e438 100644 --- a/package.solana.json +++ b/package.solana.json @@ -1,7 +1,7 @@ { "name": "@depay/web3-client-solana", "moduleName": "Web3Client", - "version": "10.18.6", + "version": "10.18.7", "description": "A web3 client to fetch blockchain data just like you are used to with HTTP clients.", "main": "dist/umd/index.solana.js", "module": "dist/esm/index.solana.js", @@ -23,7 +23,7 @@ "homepage": "https://depay.com", "private": false, "peerDependencies": { - "@depay/web3-blockchains": "^9.3.6", + "@depay/web3-blockchains": "^9.4.2", "@depay/solana-web3.js": "^1.25.1", "ethers": "^5.7.1" }, diff --git a/src/clients/solana/provider.js b/src/clients/solana/provider.js index 9f67609..fda814a 100644 --- a/src/clients/solana/provider.js +++ b/src/clients/solana/provider.js @@ -18,30 +18,55 @@ class StaticJsonRpcSequentialProvider extends Connection { this._rpcRequest = this._rpcRequestReplacement.bind(this) } + handleError(error, attempt, chunk) { + if(attempt < MAX_RETRY && error && [ + 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' + ].some((errorType)=>error.toString().match(errorType))) { + const index = this._endpoints.indexOf(this._endpoint)+1 + this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index] + this._provider = new Connection(this._endpoint) + this.requestChunk(chunk, attempt+1) + } else { + chunk.forEach((inflightRequest) => { + inflightRequest.reject(error) + }) + } + } + + batchRequest(requests, attempt) { + return new Promise((resolve, reject) => { + if (requests.length === 0) resolve([]) // Do nothing if requests is empty + + const batch = requests.map(params => { + return this._rpcClient.request(params.methodName, params.args) + }) + + fetch( + this._endpoint, + { + method: 'POST', + body: JSON.stringify(batch), + headers: { 'Content-Type': 'application/json' }, + } + ).then((response)=>{ + if(response.ok) { + response.json().then((parsedJson)=>{ + resolve(parsedJson) + }).catch(reject) + } else { + reject(`${response.status} ${response.text}`) + } + }).catch(reject) + }) + } + requestChunk(chunk, attempt) { const batch = chunk.map((inflight) => inflight.request) - const handleError = (error)=>{ - if(attempt < MAX_RETRY && error && [ - 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400' - ].some((errorType)=>error.toString().match(errorType))) { - const index = this._endpoints.indexOf(this._endpoint)+1 - this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index] - this._provider = new Connection(this._endpoint) - this.requestChunk(chunk, attempt+1) - } else { - chunk.forEach((inflightRequest) => { - inflightRequest.reject(error) - }) - } - } - try { - return this._provider._rpcBatchRequest(batch) + return this.batchRequest(batch, attempt) .then((result) => { - // For each result, feed it to the correct Promise, depending - // on whether it was a success or error chunk.forEach((inflightRequest, index) => { const payload = result[index] if (payload?.error) { @@ -55,8 +80,8 @@ class StaticJsonRpcSequentialProvider extends Connection { inflightRequest.reject() } }) - }).catch(handleError) - } catch (error){ return handleError(error) } + }).catch((error)=>this.handleError(error, attempt, chunk)) + } catch (error){ return this.handleError(error, attempt, chunk) } } _rpcRequestReplacement(methodName, args) { diff --git a/yarn.lock b/yarn.lock index 642220c..9744526 100644 --- a/yarn.lock +++ b/yarn.lock @@ -908,10 +908,10 @@ resolved "https://registry.yarnpkg.com/@depay/web3-blockchains/-/web3-blockchains-9.1.4.tgz#f006c29c887c433e1824e2bfabf8f39ad13da907" integrity sha512-CQnXCNAt3sA1MphZDMPbrhAPtemzeQ/NKeHcd2aBF61nTjJCRUmSh1Ox8Z6rlSjgDP66842iy6JAoRiFDtlmFw== -"@depay/web3-blockchains@^9.3.6": - version "9.3.6" - resolved "https://registry.yarnpkg.com/@depay/web3-blockchains/-/web3-blockchains-9.3.6.tgz#ca17eb47ebf5b7c614076a8434992df8691c6d87" - integrity sha512-PdNxxR9w8rY+vMjZyx/oBFiJpSBrCNKJT9beBB7LSvBdtNxrcFO6h0xBpDQc+jHTsjDHWxmn3k6RxuTYNp3N0Q== +"@depay/web3-blockchains@^9.4.2": + version "9.4.2" + resolved "https://registry.yarnpkg.com/@depay/web3-blockchains/-/web3-blockchains-9.4.2.tgz#61cbe215482b6c2e70255291d10c7c9b4d2bd90c" + integrity sha512-1aT9ZhMEXl87BOZsdFgVEl9aY1MgO0uB7/Qf4Wb4+L0omiydygIhITPh4JlCNz2yVdFtcU35N4/xS+4jli+nCg== "@depay/web3-mock@^14.17.0": version "14.17.0"