Skip to content

Commit 7dc790c

Browse files
committed
v13.0.38: fix solana helius rpc queries
1 parent 3f39f67 commit 7dc790c

15 files changed

+408
-336
lines changed

dev.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<script crossorigin src="https://cdn.jsdelivr.net/npm/@depay/react-shadow-dom@5"></script>
1919
<script crossorigin src="https://cdn.jsdelivr.net/npm/@depay/js-verify-signature-web@3"></script>
2020
<script crossorigin src="https://cdn.jsdelivr.net/npm/@depay/web3-blockchains@9.8.10"></script>
21-
<script crossorigin src="https://cdn.jsdelivr.net/npm/@depay/web3-client@11.1.0"></script>
21+
<script crossorigin src="https://cdn.jsdelivr.net/npm/@depay/web3-client@11.1.10"></script>
2222
<script crossorigin src="https://cdn.jsdelivr.net/npm/@depay/web3-tokens@11.1.0"></script>
2323
<script crossorigin src="https://cdn.jsdelivr.net/npm/@depay/react-token-image@6"></script>
2424
<script crossorigin src="https://cdn.jsdelivr.net/npm/@depay/coinbase-wallet-sdk@3"></script>

dist/esm/index.bundle.js

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/esm/index.evm.js

Lines changed: 91 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -64784,21 +64784,19 @@ const MAX_RETRY$1$1 = 5;
6478464784

6478564785
class 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)=> {
6494064941
const 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;
6504065035
const CHUNK_SIZE = 25;
6504165036
const MAX_RETRY = 10;
6504265037

65038+
function isHeliusEndpoint(url) {
65039+
return typeof url === 'string' && url.includes('helius-rpc.com')
65040+
}
65041+
6504365042
class 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)=> {
6518965220
const 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

6545365478
const 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

dist/esm/index.evm.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)