@@ -64784,21 +64784,19 @@ const MAX_RETRY$1$1 = 5;
6478464784
6478564785class StaticJsonRpcBatchProvider$1 extends ethers.providers.JsonRpcProvider {
6478664786
64787- constructor(url, network, endpoints, failover ) {
64787+ constructor(url, network, endpoints) {
6478864788 super(url);
6478964789 this._network = network;
6479064790 this._endpoint = url;
6479164791 this._endpoints = endpoints;
64792- this._failover = failover;
6479364792 this._pendingBatch = [];
6479464793 }
6479564794
64796- handleError(error, attempt, chunk) {
64795+ handleError(error, endpoint, attempt, chunk) {
6479764796 if(attempt < MAX_RETRY$1$1 && error) {
64798- const index = this._endpoints.indexOf(this._endpoint)+1;
64799- this._failover();
64800- this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index];
64801- this.requestChunk(chunk, this._endpoint, attempt+1);
64797+ const index = this._endpoints.indexOf(endpoint) + 1;
64798+ const retryWithNextUrl = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index];
64799+ this.requestChunk(chunk, retryWithNextUrl, attempt+1);
6480264800 } else {
6480364801 chunk.forEach((inflightRequest) => {
6480464802 inflightRequest.reject(error);
@@ -64810,13 +64808,13 @@ class StaticJsonRpcBatchProvider$1 extends ethers.providers.JsonRpcProvider {
6481064808 return Promise.resolve(Blockchains.findByName(this._network).id)
6481164809 }
6481264810
64813- batchRequest(batch, attempt) {
64811+ batchRequest(batch, endpoint, attempt) {
6481464812 return new Promise((resolve, reject) => {
6481564813
6481664814 if (batch.length === 0) resolve([]); // Do nothing if requests is empty
6481764815
6481864816 fetch(
64819- this._endpoint ,
64817+ endpoint ,
6482064818 {
6482164819 method: 'POST',
6482264820 body: JSON.stringify(batch),
@@ -64826,6 +64824,9 @@ class StaticJsonRpcBatchProvider$1 extends ethers.providers.JsonRpcProvider {
6482664824 ).then((response)=>{
6482764825 if(response.ok) {
6482864826 response.json().then((parsedJson)=>{
64827+ if(!(parsedJson instanceof Array)) {
64828+ parsedJson = [parsedJson];
64829+ }
6482964830 if(parsedJson.find((entry)=>{
6483064831 return _optionalChain$6$2([entry, 'optionalAccess', _2 => _2.error]) && [-32062,-32016].includes(_optionalChain$6$2([entry, 'optionalAccess', _3 => _3.error, 'optionalAccess', _4 => _4.code]))
6483164832 })) {
@@ -64850,7 +64851,7 @@ class StaticJsonRpcBatchProvider$1 extends ethers.providers.JsonRpcProvider {
6485064851 const batch = chunk.map((inflight) => inflight.request);
6485164852
6485264853 try {
64853- return this.batchRequest(batch, attempt)
64854+ return this.batchRequest(batch, endpoint, attempt)
6485464855 .then((result) => {
6485564856 // For each result, feed it to the correct Promise, depending
6485664857 // on whether it was a success or error
@@ -64867,8 +64868,8 @@ class StaticJsonRpcBatchProvider$1 extends ethers.providers.JsonRpcProvider {
6486764868 inflightRequest.reject();
6486864869 }
6486964870 });
64870- }).catch((error) => this.handleError(error, attempt, chunk))
64871- } catch (error){ this.handleError(error, attempt, chunk); }
64871+ }).catch((error) => this.handleError(error, endpoint, attempt, chunk))
64872+ } catch (error){ this.handleError(error, endpoint, attempt, chunk); }
6487264873 }
6487364874
6487464875 send(method, params) {
@@ -64940,13 +64941,7 @@ const setProvider$2 = (blockchain, provider)=> {
6494064941const setProviderEndpoints$2 = async (blockchain, endpoints, detectFastest = true)=> {
6494164942
6494264943 getAllProviders$1()[blockchain] = endpoints.map((endpoint, index)=>
64943- new StaticJsonRpcBatchProvider$1(endpoint, blockchain, endpoints, ()=>{
64944- if(getAllProviders$1()[blockchain].length === 1) {
64945- setProviderEndpoints$2(blockchain, endpoints, detectFastest);
64946- } else {
64947- getAllProviders$1()[blockchain].splice(index, 1);
64948- }
64949- })
64944+ new StaticJsonRpcBatchProvider$1(endpoint, blockchain, endpoints)
6495064945 );
6495164946
6495264947 let provider;
@@ -65040,52 +65035,57 @@ const BATCH_INTERVAL = 10;
6504065035const CHUNK_SIZE = 25;
6504165036const MAX_RETRY = 10;
6504265037
65038+ function isHeliusEndpoint(url) {
65039+ return typeof url === 'string' && url.includes('helius-rpc.com')
65040+ }
65041+
6504365042class StaticJsonRpcSequentialProvider extends Connection {
6504465043
65045- constructor(url, network, endpoints, failover ) {
65044+ constructor(url, network, endpoints) {
6504665045 super(url);
65047- this._provider = new Connection(url);
6504865046 this._network = network;
6504965047 this._endpoint = url;
6505065048 this._endpoints = endpoints;
65051- this._failover = failover;
6505265049 this._pendingBatch = [];
65050+ this._nextId = 1;
65051+
65052+ // Solana-specific: replace Connection's internal RPC method so our code batches
6505365053 this._rpcRequest = this._rpcRequestReplacement.bind(this);
6505465054 }
6505565055
65056- handleError(error, attempt, chunk) {
65057- if(attempt < MAX_RETRY) {
65058- const index = this._endpoints.indexOf(this._endpoint)+1;
65059- this._endpoint = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index];
65060- this._provider = new Connection(this._endpoint);
65061- this.requestChunk(chunk, attempt+1);
65056+ handleError(error, endpoint, attempt, chunk) {
65057+ if(attempt < MAX_RETRY && error) {
65058+ const index = this._endpoints.indexOf(endpoint) + 1;
65059+ const retryWithNextUrl = index >= this._endpoints.length ? this._endpoints[0] : this._endpoints[index];
65060+ this.requestChunk(chunk, retryWithNextUrl, attempt+1);
6506265061 } else {
6506365062 chunk.forEach((inflightRequest) => {
6506465063 inflightRequest.reject(error);
6506565064 });
6506665065 }
6506765066 }
6506865067
65069- batchRequest(requests, attempt) {
65068+ // Same shape as ETH batchRequest, but we don’t special-case error codes here.
65069+ batchRequest(batch, endpoint, attempt) {
6507065070 return new Promise((resolve, reject) => {
65071- if (requests.length === 0) resolve([]); // Do nothing if requests is empty
65072-
65073- const batch = requests.map(params => {
65074- return this._rpcClient.request(params.methodName, params.args)
65075- });
65071+
65072+ if (batch.length === 0) resolve([]); // Do nothing if requests is empty
6507665073
6507765074 fetch(
65078- this._endpoint ,
65075+ endpoint ,
6507965076 {
6508065077 method: 'POST',
6508165078 body: JSON.stringify(batch),
6508265079 headers: { 'Content-Type': 'application/json' },
65083- signal: _optionalChain$4$2([AbortSignal, 'optionalAccess', _ => _.timeout]) ? AbortSignal.timeout(60000 ) : undefined // 60 -second timeout
65080+ signal: _optionalChain$4$2([AbortSignal, 'optionalAccess', _ => _.timeout]) ? AbortSignal.timeout(10000 ) : undefined // 10 -second timeout
6508465081 }
6508565082 ).then((response)=>{
6508665083 if(response.ok) {
6508765084 response.json().then((parsedJson)=>{
65088- if(parsedJson.find((entry)=>_optionalChain$4$2([entry, 'optionalAccess', _2 => _2.error]))) {
65085+ if(!(parsedJson instanceof Array)) {
65086+ parsedJson = [parsedJson];
65087+ }
65088+ if(parsedJson.find((entry)=> _optionalChain$4$2([entry, 'optionalAccess', _2 => _2.error]) )) {
6508965089 if(attempt < MAX_RETRY) {
6509065090 reject('Error in batch found!');
6509165091 } else {
@@ -65102,13 +65102,14 @@ class StaticJsonRpcSequentialProvider extends Connection {
6510265102 })
6510365103 }
6510465104
65105- requestChunk(chunk, attempt) {
65105+ requestChunk(chunk, endpoint, attempt) {
6510665106
6510765107 const batch = chunk.map((inflight) => inflight.request);
6510865108
6510965109 try {
65110- return this.batchRequest(batch, attempt)
65110+ return this.batchRequest(batch, endpoint, attempt)
6511165111 .then((result) => {
65112+ // For each result, feed it to the correct Promise
6511265113 chunk.forEach((inflightRequest, index) => {
6511365114 const payload = result[index];
6511465115 if (_optionalChain$4$2([payload, 'optionalAccess', _3 => _3.error])) {
@@ -65117,18 +65118,25 @@ class StaticJsonRpcSequentialProvider extends Connection {
6511765118 error.data = payload.error.data;
6511865119 inflightRequest.reject(error);
6511965120 } else if(payload) {
65121+ // Solana-specific: resolve with full JSON-RPC payload (not .result)
6512065122 inflightRequest.resolve(payload);
6512165123 } else {
6512265124 inflightRequest.reject();
6512365125 }
6512465126 });
65125- }).catch((error)=> this.handleError(error, attempt, chunk))
65126- } catch (error){ return this.handleError(error, attempt, chunk) }
65127+ }).catch((error) => this.handleError(error, endpoint , attempt, chunk))
65128+ } catch (error){ this.handleError(error, endpoint, attempt, chunk); }
6512765129 }
65128-
65129- _rpcRequestReplacement(methodName, args) {
6513065130
65131- const request = { methodName, args };
65131+ // Solana-specific replacement: just enqueue like ETH's send(), but using Connection’s hook.
65132+ _rpcRequestReplacement(method, params) {
65133+
65134+ const request = {
65135+ method: method,
65136+ params: Array.isArray(params) ? params : [],
65137+ id: (this._nextId++).toString(),
65138+ jsonrpc: "2.0"
65139+ };
6513265140
6513365141 if (this._pendingBatch == null) {
6513465142 this._pendingBatch = [];
@@ -65151,16 +65159,39 @@ class StaticJsonRpcSequentialProvider extends Connection {
6515165159 const batch = this._pendingBatch;
6515265160 this._pendingBatch = [];
6515365161 this._pendingBatchAggregator = null;
65154- // Prepare Chunks of CHUNK_SIZE
65155- const chunks = [];
65156- for (let i = 0; i < Math.ceil(batch.length / CHUNK_SIZE); i++) {
65157- chunks[i] = batch.slice(i*CHUNK_SIZE, (i+1)*CHUNK_SIZE);
65162+
65163+ const endpoint = this._endpoint;
65164+ const helius = isHeliusEndpoint(endpoint);
65165+
65166+ if (helius) {
65167+ // Requirement 1: For helius-rpc.com, batch only requests that share the same method
65168+ const groupsByMethod = batch.reduce((acc, inflight) => {
65169+ const m = inflight.request.method;
65170+ if (!acc[m]) acc[m] = [];
65171+ acc[m].push(inflight);
65172+ return acc
65173+ }, {});
65174+
65175+ Object.values(groupsByMethod).forEach((group) => {
65176+ // CHUNK_SIZE still applies within each method group
65177+ const chunks = [];
65178+ for (let i = 0; i < Math.ceil(group.length / CHUNK_SIZE); i++) {
65179+ chunks[i] = group.slice(i*CHUNK_SIZE, (i+1)*CHUNK_SIZE);
65180+ }
65181+ chunks.forEach((chunk)=>{
65182+ return this.requestChunk(chunk, endpoint, 1)
65183+ });
65184+ });
65185+ } else {
65186+ // Default behavior (unchanged): chunk regardless of method
65187+ const chunks = [];
65188+ for (let i = 0; i < Math.ceil(batch.length / CHUNK_SIZE); i++) {
65189+ chunks[i] = batch.slice(i*CHUNK_SIZE, (i+1)*CHUNK_SIZE);
65190+ }
65191+ chunks.forEach((chunk)=>{
65192+ return this.requestChunk(chunk, endpoint, 1)
65193+ });
6515865194 }
65159- chunks.forEach((chunk)=>{
65160- // Get the request as an array of requests
65161- chunk.map((inflight) => inflight.request);
65162- return this.requestChunk(chunk, 1)
65163- });
6516465195 }, getConfiguration$1().batchInterval || BATCH_INTERVAL);
6516565196 }
6516665197
@@ -65189,13 +65220,7 @@ const setProvider$1 = (blockchain, provider)=> {
6518965220const setProviderEndpoints$1 = async (blockchain, endpoints, detectFastest = true)=> {
6519065221
6519165222 getAllProviders()[blockchain] = endpoints.map((endpoint, index)=>
65192- new StaticJsonRpcSequentialProvider(endpoint, blockchain, endpoints, ()=>{
65193- if(getAllProviders()[blockchain].length === 1) {
65194- setProviderEndpoints$1(blockchain, endpoints, detectFastest);
65195- } else {
65196- getAllProviders()[blockchain].splice(index, 1);
65197- }
65198- })
65223+ new StaticJsonRpcSequentialProvider(endpoint, blockchain, endpoints)
6519965224 );
6520065225
6520165226 let provider;
@@ -65225,8 +65250,8 @@ const setProviderEndpoints$1 = async (blockchain, endpoints, detectFastest = tru
6522565250 },
6522665251 referrer: "",
6522765252 referrerPolicy: "no-referrer",
65228- body: JSON.stringify({ method: 'getIdentity ', id: 1, jsonrpc: '2.0' }),
65229- signal: _optionalChain$3$2([AbortSignal, 'optionalAccess', _ => _.timeout]) ? AbortSignal.timeout(60000 ) : undefined // 60 -second timeout
65253+ body: JSON.stringify({ method: 'getGenesisHash ', id: 1, jsonrpc: '2.0' }),
65254+ signal: _optionalChain$3$2([AbortSignal, 'optionalAccess', _ => _.timeout]) ? AbortSignal.timeout(10000 ) : undefined // 10 -second timeout
6523065255 });
6523165256 } catch (e) {}
6523265257 if(!_optionalChain$3$2([response, 'optionalAccess', _2 => _2.ok])) { return resolve(999) }
@@ -65446,8 +65471,8 @@ const contractCall = ({ address, api, method, params, provider, block }) => {
6544665471 }
6544765472};
6544865473
65449- const balance$1 = ({ address, provider }) => {
65450- return provider.getBalance(address)
65474+ const balance$1 = ({ address, provider, block }) => {
65475+ return provider.getBalance(address, block )
6545165476};
6545265477
6545365478const transactionCount = ({ address, provider }) => {
@@ -65460,7 +65485,7 @@ const singleRequest$3 = ({ blockchain, address, api, method, params, block, prov
6546065485 } else if (method === 'latestBlockNumber') {
6546165486 return provider.getBlockNumber()
6546265487 } else if (method === 'balance') {
65463- return balance$1({ address, provider })
65488+ return balance$1({ address, provider, block })
6546465489 } else if (method === 'transactionCount') {
6546565490 return transactionCount({ address, provider })
6546665491 }
@@ -65546,14 +65571,7 @@ const singleRequest$2 = async({ blockchain, address, api, method, params, block,
6554665571 }
6554765572
6554865573 } catch (error){
65549- if(providers && error && [
65550- 'Failed to fetch', 'limit reached', '504', '503', '502', '500', '429', '426', '422', '413', '409', '408', '406', '405', '404', '403', '402', '401', '400'
65551- ].some((errorType)=>error.toString().match(errorType))) {
65552- let nextProvider = providers[providers.indexOf(provider)+1] || providers[0];
65553- return singleRequest$2({ blockchain, address, api, method, params, block, provider: nextProvider, providers })
65554- } else {
65555- throw error
65556- }
65574+ throw error
6555765575 }
6555865576};
6555965577
0 commit comments