envio 3.15.0-subgraph → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -34
- package/evm.schema.json +3 -3
- package/fuel.schema.json +2 -2
- package/index.d.ts +3 -3
- package/licenses/README.md +1 -1
- package/package.json +9 -10
- package/src/Batch.res.mjs +5 -5
- package/src/BatchProcessing.res +1 -0
- package/src/BatchProcessing.res.mjs +1 -0
- package/src/Bin.res +0 -7
- package/src/Bin.res.mjs +0 -8
- package/src/ChainFetching.res +17 -456
- package/src/ChainFetching.res.mjs +33 -404
- package/src/ChainState.res +48 -83
- package/src/ChainState.res.mjs +83 -115
- package/src/ChainState.resi +0 -26
- package/src/Clock.res +57 -0
- package/src/Clock.res.mjs +72 -0
- package/src/Config.res +7 -16
- package/src/Config.res.mjs +4 -11
- package/src/ContractRegisterContext.res +6 -3
- package/src/ContractRegisterContext.res.mjs +2 -4
- package/src/Core.res +5 -136
- package/src/Core.res.mjs +1 -90
- package/src/CrossChainState.res +2 -8
- package/src/CrossChainState.res.mjs +3 -3
- package/src/Ecosystem.res +4 -7
- package/src/Ecosystem.res.mjs +8 -8
- package/src/EffectState.res +10 -2
- package/src/EffectState.res.mjs +8 -4
- package/src/EffectState.resi +3 -1
- package/src/EntityId.res +2 -7
- package/src/EntityId.res.mjs +1 -8
- package/src/EntityTables.res +4 -1
- package/src/EntityTables.res.mjs +1 -1
- package/src/Env.res +0 -16
- package/src/Env.res.mjs +0 -11
- package/src/EventProcessing.res +1 -38
- package/src/EventProcessing.res.mjs +1 -28
- package/src/FetchState.res +160 -319
- package/src/FetchState.res.mjs +182 -292
- package/src/FinalizeBackfill.res +2 -4
- package/src/FinalizeBackfill.res.mjs +3 -4
- package/src/HandlerLoader.res +15 -39
- package/src/HandlerLoader.res.mjs +8 -27
- package/src/HandlerRegister.res +0 -4
- package/src/HandlerRegister.res.mjs +2 -5
- package/src/HandlerRegister.resi +0 -2
- package/src/InMemoryTable.res +17 -16
- package/src/InMemoryTable.res.mjs +14 -15
- package/src/IndexerState.res +28 -4
- package/src/IndexerState.res.mjs +19 -6
- package/src/IndexerState.resi +4 -0
- package/src/Internal.res +0 -110
- package/src/Internal.res.mjs +1 -115
- package/src/LoadLayer.res +21 -47
- package/src/LoadLayer.res.mjs +47 -72
- package/src/LoadLayer.resi +0 -26
- package/src/Main.res +1 -14
- package/src/Main.res.mjs +3 -4
- package/src/Persistence.res +3 -23
- package/src/Persistence.res.mjs +3 -3
- package/src/Rollback.res +0 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/RunScope.res +0 -3
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +1 -1
- package/src/SimulateItems.res +26 -42
- package/src/SimulateItems.res.mjs +16 -40
- package/src/StagedRows.res +106 -0
- package/src/StagedRows.res.mjs +102 -0
- package/src/Staging.res +32 -14
- package/src/Staging.res.mjs +17 -20
- package/src/Storage.res +277 -998
- package/src/Storage.res.mjs +216 -731
- package/src/Supervisor.res +75 -56
- package/src/Supervisor.res.mjs +55 -26
- package/src/TestIndexer.res +22 -27
- package/src/TestIndexer.res.mjs +20 -17
- package/src/Throttler.res +15 -8
- package/src/Throttler.res.mjs +9 -6
- package/src/Throttler.resi +1 -1
- package/src/UserContext.res +46 -410
- package/src/UserContext.res.mjs +30 -318
- package/src/Utils.res +0 -23
- package/src/Utils.res.mjs +1 -17
- package/src/Writing.res +0 -1
- package/src/Writing.res.mjs +1 -1
- package/src/bindings/ClickHouse.res +0 -322
- package/src/bindings/ClickHouse.res.mjs +0 -338
- package/src/bindings/StorageClient.res +107 -175
- package/src/bindings/StorageClient.res.mjs +19 -8
- package/src/bindings/Vitest.res +0 -26
- package/src/bindings/Vitest.res.mjs +0 -3
- package/src/db/Table.res +24 -150
- package/src/db/Table.res.mjs +38 -272
- package/src/sources/AddressSet.res +0 -2
- package/src/sources/AddressStore.res +0 -3
- package/src/sources/ChainSources.res +2 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +4 -7
- package/src/sources/EvmChain.res.mjs +5 -4
- package/src/sources/EvmHyperSyncSource.res +2 -2
- package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
- package/src/sources/EvmRpcClient.res +13 -3
- package/src/sources/EvmRpcClient.res.mjs +4 -3
- package/src/sources/EvmRpcWs.res +4 -3
- package/src/sources/EvmRpcWs.res.mjs +2 -2
- package/src/sources/HeightFeed.res +31 -10
- package/src/sources/HeightFeed.res.mjs +10 -6
- package/src/sources/HeightStream.res +32 -17
- package/src/sources/HeightStream.res.mjs +10 -10
- package/src/sources/HyperSync.res +1 -2
- package/src/sources/HyperSync.res.mjs +1 -2
- package/src/sources/HyperSyncSSE.res +4 -6
- package/src/sources/HyperSyncSSE.res.mjs +2 -2
- package/src/sources/RpcSource.res +10 -1
- package/src/sources/RpcSource.res.mjs +4 -3
- package/src/sources/Source.res +3 -0
- package/src/sources/SourceManager.res +85 -104
- package/src/sources/SourceManager.res.mjs +18 -37
- package/src/sources/SourceManager.resi +3 -5
- package/src/sources/StartBlockResolver.res +1 -0
- package/src/sources/StartBlockResolver.res.mjs +4 -3
- package/src/sources/SvmHyperSyncSource.res +2 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/svm.schema.json +3 -3
- package/src/EffectCacheLookup.res +0 -98
- package/src/EffectCacheLookup.res.mjs +0 -116
- package/src/PgWriting.res +0 -36
- package/src/PgWriting.res.mjs +0 -34
- package/src/PreloadLookahead.res +0 -184
- package/src/PreloadLookahead.res.mjs +0 -197
- package/src/SubgraphLog.res +0 -19
- package/src/SubgraphLog.res.mjs +0 -26
- package/src/SyncWrites.res +0 -101
- package/src/SyncWrites.res.mjs +0 -98
- package/src/db/IndexDefinition.res +0 -47
- package/src/db/IndexDefinition.res.mjs +0 -60
- package/src/sources/CallSource.res +0 -274
- package/src/sources/CallSource.res.mjs +0 -196
- package/src/subgraph/abi-types.ts +0 -84
- package/src/subgraph/aggregations.ts +0 -745
- package/src/subgraph/as-package.ts +0 -143
- package/src/subgraph/assemblyscript-regex.ts +0 -67
- package/src/subgraph/assemblyscript.ts +0 -731
- package/src/subgraph/blocks.ts +0 -398
- package/src/subgraph/calls-source.ts +0 -702
- package/src/subgraph/calls.ts +0 -258
- package/src/subgraph/conformance.ts +0 -106
- package/src/subgraph/errors.ts +0 -85
- package/src/subgraph/graft.ts +0 -73
- package/src/subgraph/graph-cli-worker.mjs +0 -35
- package/src/subgraph/graph-cli.ts +0 -900
- package/src/subgraph/graph-node-decimal.ts +0 -199
- package/src/subgraph/graph-ts-enums.ts +0 -28
- package/src/subgraph/graph-ts-types/VERSION +0 -2
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +0 -70
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +0 -327
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +0 -233
- package/src/subgraph/graph-ts-types/chain/near.d.ts +0 -253
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +0 -32
- package/src/subgraph/graph-ts-types/common/collections.d.ts +0 -136
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +0 -11
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +0 -30
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +0 -17
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +0 -120
- package/src/subgraph/graph-ts-types/common/value.d.ts +0 -120
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +0 -90
- package/src/subgraph/graph-ts-types/global/global.d.ts +0 -194
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +0 -22
- package/src/subgraph/graph-ts-types/index.d.ts +0 -102
- package/src/subgraph/graph-ts.ts +0 -4280
- package/src/subgraph/hosts.ts +0 -554
- package/src/subgraph/log.ts +0 -28
- package/src/subgraph/receipts.ts +0 -184
- package/src/subgraph/register-slots.ts +0 -93
- package/src/subgraph/rollup.ts +0 -109
- package/src/subgraph/rpc.ts +0 -337
- package/src/subgraph/runtime.ts +0 -2060
- package/src/subgraph/scope.ts +0 -177
- package/src/subgraph/stats.ts +0 -42
- package/src/subgraph/template-instances.ts +0 -66
- package/src/subgraph/toolchain.ts +0 -327
- package/src/subgraph/transient.ts +0 -176
package/src/subgraph/rpc.ts
DELETED
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The one client every RPC read of the subgraph runtime goes through: contract
|
|
3
|
-
* calls, `getBalance`/`hasCode` and block timestamps.
|
|
4
|
-
*
|
|
5
|
-
* Preload runs a batch's handlers at once, and with them every read they make.
|
|
6
|
-
* Unbounded, that is one open connection per read — thousands on a factory's
|
|
7
|
-
* backfill, until the endpoint stops answering and the batch never completes.
|
|
8
|
-
* So requests beyond a fixed number wait for one in flight to finish; each is
|
|
9
|
-
* short, and a queue drains far faster than a refused connection retries.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { HttpStatusError, isRateLimit, isRevert, requestTimeoutMs, withDeadline } from "./transient.ts";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Requests in flight at once, per set of endpoints. A handler's calls run one
|
|
16
|
-
* after another (a decimals call, then a name, then a price), so a batch's
|
|
17
|
-
* throughput is this many divided by the endpoint's latency: at 16 and a
|
|
18
|
-
* second a call — a shared or distant endpoint — a batch of five thousand
|
|
19
|
-
* events took minutes to commit. `ENVIO_SUBGRAPH_RPC_CONCURRENCY` overrides it.
|
|
20
|
-
*/
|
|
21
|
-
const DEFAULT_MAX_IN_FLIGHT = 48;
|
|
22
|
-
|
|
23
|
-
function maxInFlightFromEnv(): number {
|
|
24
|
-
const value = Number(process.env.ENVIO_SUBGRAPH_RPC_CONCURRENCY);
|
|
25
|
-
return Number.isInteger(value) && value > 0 ? value : DEFAULT_MAX_IN_FLIGHT;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Most requests one HTTP request carries as a JSON-RPC batch. Requests that
|
|
30
|
-
* queue while every connection is busy leave together, so a backfill's calls
|
|
31
|
-
* take a round trip per batch rather than per call. `ENVIO_SUBGRAPH_RPC_BATCH`
|
|
32
|
-
* overrides it; 1 sends every request on its own.
|
|
33
|
-
*/
|
|
34
|
-
const DEFAULT_MAX_BATCH = 32;
|
|
35
|
-
|
|
36
|
-
function maxBatchFromEnv(): number {
|
|
37
|
-
const value = Number(process.env.ENVIO_SUBGRAPH_RPC_BATCH);
|
|
38
|
-
return Number.isInteger(value) && value > 0 ? value : DEFAULT_MAX_BATCH;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Runs at most `maxInFlight` requests at once, queueing the rest. A slot is
|
|
43
|
-
* held at most the request deadline: one that never settles would otherwise
|
|
44
|
-
* keep it for good, and a handful of those stall every call behind them with
|
|
45
|
-
* no connection doing anything.
|
|
46
|
-
*/
|
|
47
|
-
export function gate(maxInFlight = maxInFlightFromEnv()) {
|
|
48
|
-
let inFlight = 0;
|
|
49
|
-
const queued: (() => void)[] = [];
|
|
50
|
-
return async <T>(request: () => Promise<T>): Promise<T> => {
|
|
51
|
-
if (inFlight < maxInFlight) inFlight++;
|
|
52
|
-
// A finishing request hands its slot straight over.
|
|
53
|
-
else await new Promise<void>((resolve) => queued.push(resolve));
|
|
54
|
-
try {
|
|
55
|
-
return await withDeadline("The RPC request", request);
|
|
56
|
-
} finally {
|
|
57
|
-
const next = queued.shift();
|
|
58
|
-
if (next) next();
|
|
59
|
-
else inFlight--;
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
type Body = { method: string; params?: unknown };
|
|
65
|
-
type RpcErrorBody = { code: number; message: string; data?: unknown };
|
|
66
|
-
type Answer = { result?: unknown; error?: RpcErrorBody };
|
|
67
|
-
|
|
68
|
-
/** A JSON-RPC error the endpoint answered with: the request's own outcome. */
|
|
69
|
-
export class RpcError extends Error {
|
|
70
|
-
readonly code: number;
|
|
71
|
-
readonly data: unknown;
|
|
72
|
-
constructor(
|
|
73
|
-
readonly url: string,
|
|
74
|
-
method: string,
|
|
75
|
-
error: RpcErrorBody,
|
|
76
|
-
) {
|
|
77
|
-
super(`${method} on ${url} failed: ${error.message}`);
|
|
78
|
-
this.code = error.code;
|
|
79
|
-
this.data = error.data;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** One HTTP request to the endpoint, bounded by the request deadline. */
|
|
84
|
-
async function post(url: string, body: unknown): Promise<unknown> {
|
|
85
|
-
const response = await fetch(url, {
|
|
86
|
-
method: "POST",
|
|
87
|
-
headers: { "content-type": "application/json" },
|
|
88
|
-
body: JSON.stringify(body),
|
|
89
|
-
signal: AbortSignal.timeout(requestTimeoutMs()),
|
|
90
|
-
});
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
const text = await response.text().catch(() => "");
|
|
93
|
-
throw new HttpStatusError(url, response.status, text.slice(0, 200));
|
|
94
|
-
}
|
|
95
|
-
return response.json();
|
|
96
|
-
}
|
|
97
|
-
type Waiting = { body: Body; resolve: (answer: Answer) => void; reject: (error: unknown) => void; missing: number };
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* An endpoint's own words for "this batch is too big" (dRPC's free plan:
|
|
101
|
-
* "Batch of more than 3 requests are not allowed"), said per item.
|
|
102
|
-
*/
|
|
103
|
-
function isBatchLimit(error: Answer["error"]): boolean {
|
|
104
|
-
const message = String(error?.message ?? "");
|
|
105
|
-
return /batch/i.test(message) && /more than|too (many|large|big)|exceed|limit|not allowed|maximum|size/i.test(message);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** The size such a message allows, when it says. */
|
|
109
|
-
function allowedBatch(error: Answer["error"]): number | undefined {
|
|
110
|
-
const match = /more than (\d+)|(?:maximum|max|limit)[^\d]{0,20}(\d+)/i.exec(String(error?.message ?? ""));
|
|
111
|
-
const value = Number(match?.[1] ?? match?.[2]);
|
|
112
|
-
return Number.isInteger(value) && value > 0 ? value : undefined;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Times a batch item may come back without its answer before it fails on its own. */
|
|
116
|
-
const MAX_MISSING = 3;
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* One endpoint, `maxInFlight` HTTP requests at a time, each carrying up to the
|
|
120
|
-
* batch size the endpoint takes. Every request keeps exactly the answer it
|
|
121
|
-
* would get sent alone: items are matched to their answers by id, a JSON-RPC
|
|
122
|
-
* error stays that item's error (a revert reaches only the call that
|
|
123
|
-
* reverted), and HTTP failures fail every item they carried, to be asked
|
|
124
|
-
* again by `untilAnswered`. An endpoint that refuses a batch — HTTP 4xx, an
|
|
125
|
-
* error in place of the array, or a "batch too large" answer per item — gets
|
|
126
|
-
* smaller ones from then on, down to plain single requests.
|
|
127
|
-
*
|
|
128
|
-
* A public endpoint answers 429 past the few requests it serves at once, and
|
|
129
|
-
* at 48 in flight most of every wave was refused, each refused call backing
|
|
130
|
-
* off alone for longer and longer. So the requests in flight, and the calls a
|
|
131
|
-
* batch carries, are halved when the endpoint says "slow down" — once per
|
|
132
|
-
* wave: refusals of requests sent before the last cut don't cut again — and
|
|
133
|
-
* grow back by one per full round of answers (a batch: per answered batch), up
|
|
134
|
-
* to `maxInFlight` and the batch size the endpoint takes.
|
|
135
|
-
*/
|
|
136
|
-
function batchingTransport(url: string, maxInFlight: number, maxBatch: number): (body: Body) => Promise<unknown> {
|
|
137
|
-
// What the endpoint takes in one batch, and what is sent for now.
|
|
138
|
-
let batchCap = maxBatch;
|
|
139
|
-
let batchSize = maxBatch;
|
|
140
|
-
let limit = maxInFlight;
|
|
141
|
-
let wave = 0;
|
|
142
|
-
let answeredSinceGrowth = 0;
|
|
143
|
-
let inFlight = 0;
|
|
144
|
-
let pumpScheduled = false;
|
|
145
|
-
let nextId = 1;
|
|
146
|
-
const queue: Waiting[] = [];
|
|
147
|
-
|
|
148
|
-
const shrink = (to?: number) => {
|
|
149
|
-
batchCap = Math.max(1, Math.min(to ?? Math.floor(batchCap / 2), batchCap - 1));
|
|
150
|
-
batchSize = Math.min(batchSize, batchCap);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const slowDown = (sentIn: number) => {
|
|
154
|
-
if (sentIn !== wave) return;
|
|
155
|
-
wave++;
|
|
156
|
-
limit = Math.max(1, Math.floor(limit / 2));
|
|
157
|
-
batchSize = Math.max(1, Math.floor(batchSize / 2));
|
|
158
|
-
answeredSinceGrowth = 0;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const answered = () => {
|
|
162
|
-
if (limit < maxInFlight && ++answeredSinceGrowth >= limit) {
|
|
163
|
-
limit++;
|
|
164
|
-
answeredSinceGrowth = 0;
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const send = async (items: Waiting[]) => {
|
|
169
|
-
const sentIn = wave;
|
|
170
|
-
if (items.length === 1) {
|
|
171
|
-
const [item] = items;
|
|
172
|
-
try {
|
|
173
|
-
const answer = (await post(url, { jsonrpc: "2.0", id: nextId++, ...item.body })) as Answer;
|
|
174
|
-
if (answer.error && isRateLimit(answer.error)) slowDown(sentIn);
|
|
175
|
-
else answered();
|
|
176
|
-
item.resolve(answer);
|
|
177
|
-
} catch (error) {
|
|
178
|
-
if (isRateLimit(error)) slowDown(sentIn);
|
|
179
|
-
item.reject(error);
|
|
180
|
-
}
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
const ids = items.map(() => nextId++);
|
|
184
|
-
let data: unknown;
|
|
185
|
-
try {
|
|
186
|
-
data = await post(
|
|
187
|
-
url,
|
|
188
|
-
items.map((item, i) => ({ jsonrpc: "2.0", id: ids[i], ...item.body })),
|
|
189
|
-
);
|
|
190
|
-
} catch (error) {
|
|
191
|
-
if (isRateLimit(error)) slowDown(sentIn);
|
|
192
|
-
const status = (error as { status?: unknown }).status;
|
|
193
|
-
if (typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429) {
|
|
194
|
-
shrink();
|
|
195
|
-
queue.unshift(...items);
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
for (const item of items) item.reject(error);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
if (!Array.isArray(data)) {
|
|
202
|
-
// An error in place of the array: the endpoint takes no batch this big.
|
|
203
|
-
shrink();
|
|
204
|
-
queue.unshift(...items);
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
answered();
|
|
208
|
-
if (batchSize < batchCap) batchSize++;
|
|
209
|
-
const byId = new Map<unknown, Answer>();
|
|
210
|
-
for (const answer of data) if (answer && typeof answer === "object") byId.set((answer as any).id, answer as Answer);
|
|
211
|
-
const again: Waiting[] = [];
|
|
212
|
-
items.forEach((item, i) => {
|
|
213
|
-
const answer = byId.get(ids[i]);
|
|
214
|
-
if (answer === undefined) {
|
|
215
|
-
if (++item.missing >= MAX_MISSING) {
|
|
216
|
-
item.reject(new Error(`${url} answered a batch without the answer to ${item.body.method}; asking again.`));
|
|
217
|
-
} else again.push(item);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
if (answer.error && isBatchLimit(answer.error)) {
|
|
221
|
-
shrink(allowedBatch(answer.error));
|
|
222
|
-
again.push(item);
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
if (answer.error && isRateLimit(answer.error)) slowDown(sentIn);
|
|
226
|
-
item.resolve(answer);
|
|
227
|
-
});
|
|
228
|
-
if (again.length > 0) queue.unshift(...again);
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
const schedule = () => {
|
|
232
|
-
if (!pumpScheduled) {
|
|
233
|
-
pumpScheduled = true;
|
|
234
|
-
setImmediate(pump);
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
const pump = () => {
|
|
239
|
-
pumpScheduled = false;
|
|
240
|
-
while (inFlight < limit && queue.length > 0) {
|
|
241
|
-
const items = queue.splice(0, batchSize);
|
|
242
|
-
inFlight++;
|
|
243
|
-
void send(items).finally(() => {
|
|
244
|
-
inFlight--;
|
|
245
|
-
pump();
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
return async (body: Body) => {
|
|
251
|
-
const answer = await new Promise<Answer>((resolve, reject) => {
|
|
252
|
-
queue.push({ body, resolve, reject, missing: 0 });
|
|
253
|
-
// Requests made in the same turn — preload's calls, a batch's
|
|
254
|
-
// timestamps — leave together.
|
|
255
|
-
schedule();
|
|
256
|
-
});
|
|
257
|
-
if (answer.error) throw new RpcError(url, body.method, answer.error);
|
|
258
|
-
return answer.result;
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const toHex = (value: number | bigint) => `0x${BigInt(value).toString(16)}`;
|
|
263
|
-
|
|
264
|
-
/** The reads the runtime makes, over the endpoints in the order given. */
|
|
265
|
-
export type RpcClient = {
|
|
266
|
-
request(body: Body): Promise<any>;
|
|
267
|
-
/** The call's return data. */
|
|
268
|
-
call(args: { to: string; data: string; blockNumber: number | bigint }): Promise<string>;
|
|
269
|
-
getBalance(args: { address: string; blockNumber: number | bigint }): Promise<bigint>;
|
|
270
|
-
getCode(args: { address: string; blockNumber: number | bigint }): Promise<string>;
|
|
271
|
-
/** The block as the endpoint spells it, its numbers hex. */
|
|
272
|
-
getBlock(args: { blockNumber: number | bigint }): Promise<Record<string, any>>;
|
|
273
|
-
getTransactionReceipt(args: { hash: string }): Promise<Record<string, any>>;
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
function makeClient(transports: ((body: Body) => Promise<unknown>)[]): RpcClient {
|
|
277
|
-
// The next endpoint is asked whatever the first answered, except a revert:
|
|
278
|
-
// every node gives that one the same.
|
|
279
|
-
const request = async (body: Body): Promise<any> => {
|
|
280
|
-
let failure: unknown;
|
|
281
|
-
for (const transport of transports) {
|
|
282
|
-
try {
|
|
283
|
-
return await transport(body);
|
|
284
|
-
} catch (error) {
|
|
285
|
-
if (error instanceof RpcError && isRevert(error)) throw error;
|
|
286
|
-
failure = error;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
throw failure;
|
|
290
|
-
};
|
|
291
|
-
return {
|
|
292
|
-
request,
|
|
293
|
-
call: ({ to, data, blockNumber }) =>
|
|
294
|
-
request({ method: "eth_call", params: [{ to, data }, toHex(blockNumber)] }).then(
|
|
295
|
-
(result) => (result ?? "0x") as string,
|
|
296
|
-
),
|
|
297
|
-
getBalance: ({ address, blockNumber }) =>
|
|
298
|
-
request({ method: "eth_getBalance", params: [address, toHex(blockNumber)] }).then((result) =>
|
|
299
|
-
BigInt(result),
|
|
300
|
-
),
|
|
301
|
-
getCode: ({ address, blockNumber }) =>
|
|
302
|
-
request({ method: "eth_getCode", params: [address, toHex(blockNumber)] }).then(
|
|
303
|
-
(result) => (result ?? "0x") as string,
|
|
304
|
-
),
|
|
305
|
-
getBlock: async ({ blockNumber }) => {
|
|
306
|
-
const block = await request({ method: "eth_getBlockByNumber", params: [toHex(blockNumber), false] });
|
|
307
|
-
if (!block) throw new Error(`Block at number ${blockNumber} could not be found.`);
|
|
308
|
-
return block;
|
|
309
|
-
},
|
|
310
|
-
getTransactionReceipt: async ({ hash }) => {
|
|
311
|
-
const receipt = await request({ method: "eth_getTransactionReceipt", params: [hash] });
|
|
312
|
-
if (!receipt) throw new Error(`The receipt of transaction ${hash} could not be found.`);
|
|
313
|
-
return receipt;
|
|
314
|
-
},
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const clients = new Map<string, RpcClient>();
|
|
319
|
-
|
|
320
|
-
export function rpcClient(rpcUrls: string[]): RpcClient {
|
|
321
|
-
const key = rpcUrls.join("|");
|
|
322
|
-
let client = clients.get(key);
|
|
323
|
-
if (!client) {
|
|
324
|
-
const maxInFlight = maxInFlightFromEnv();
|
|
325
|
-
const maxBatch = maxBatchFromEnv();
|
|
326
|
-
// Retrying is `untilAnswered`'s job, with a backoff that outlasts a rate
|
|
327
|
-
// limit.
|
|
328
|
-
client = makeClient(rpcUrls.map((url) => batchingTransport(url, maxInFlight, maxBatch)));
|
|
329
|
-
clients.set(key, client);
|
|
330
|
-
}
|
|
331
|
-
return client;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/** Reset between test indexers, which each bring their own endpoints. */
|
|
335
|
-
export function resetRpcClients() {
|
|
336
|
-
clients.clear();
|
|
337
|
-
}
|