I noticed in the library README for the experimental web3.js SDK that two of the motivations for the API design are:
- Solana developer ‘epicfaace’ wanted first-class support for automatic time-windowed batching in the RPC transport. Support solana-web3.js batching solana#23628.
- Multiple folks have expressed the need for custom retry logic for failed requests or transactions. fix: double the wait time between each 429 induced retry solana#11811 and Allow disabling retry on HTTP 429 with connection config #1041 attempting to modify retry logic to suit their individual use cases.
However, it is somewhat rather unclear on how i.e. batching or retrying can be implemented with the current experimental SDK.
- How do I have the HTTP transport automatically merge together specific multiple JSON-RPC payloads into a batch request?
- How do I synchronously (explicitly) invoke a batch JSON-RPC request with multiple JSON-RPC payloads crafted via. the SDK?
- How do I pass individual timeouts/retry options for a single/batch JSON-RPC request via. the SDK?
I currently manually perform fetch calls myself to perform explicit batch JSON-RPC requests. This, however, is error-prone as it would require types and validation to be handled by myself (i.e. how do I handle the case that a response field overflows Number.MAX_SAFE_INTEGER
?).
export async function getTokenBalances(ownerPublicKey: string) {
const sol = {
jsonrpc: "2.0",
id: 0,
method: "getAccountInfo",
params: [
ownerPublicKey,
{ commitment: "confirmed", encoding: "jsonParsed" },
],
};
const usdc = {
jsonrpc: "2.0",
id: 1,
method: "getTokenAccountsByOwner",
params: [
ownerPublicKey,
{
mint: USDC_TOKEN_PUBLIC_KEY,
},
{
commitment: "confirmed",
encoding: "jsonParsed",
},
],
};
const like = {
jsonrpc: "2.0",
id: 2,
method: "getTokenAccountsByOwner",
params: [
ownerPublicKey,
{
mint: LIKE_TOKEN_PUBLIC_KEY,
},
{
commitment: "confirmed",
encoding: "jsonParsed",
},
],
};
const request = [sol, usdc, like];
const response = await fetch(SOLANA_SERVER_RPC_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error(`RPC request failed: ${response.statusText}`);
}
const json = await response.json();
if (!checkSchema(getTokenBalancesResponse, json)) {
throw new Error(`Malformed RPC response: ${JSON.stringify(json)}`);
}
const balances = {
sol: BigInt(json[0].result.value?.lamports ?? 0),
usdc: json[1].result.value.reduce(
(
acc,
{
account: {
data: {
parsed: { info },
},
},
},
) => {
if (info.state !== "initialized") {
return acc;
}
return acc + BigInt(info.tokenAmount.amount);
},
BigInt(0),
),
like: json[2].result.value.reduce(
(
acc,
{
account: {
data: {
parsed: { info },
},
},
},
) => {
if (info.state !== "initialized") {
return acc;
}
return acc + BigInt(info.tokenAmount.amount);
},
BigInt(0),
),
};
return balances;
}