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/calls.ts
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `Contract.bind(x).foo()` -> an envio effect, encoded and decoded with
|
|
3
|
-
* alloy, as graph-node does.
|
|
4
|
-
*
|
|
5
|
-
* graph-node evaluates a mapping's contract calls against the block the event
|
|
6
|
-
* came from, so the block number is part of the effect's input — and therefore
|
|
7
|
-
* of its cache key, which is what makes a cached call safe to reuse.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import * as Sury from "rescript-schema";
|
|
11
|
-
import { subgraphAbiDecode, subgraphAbiEncodeCall } from "../Core.res.mjs";
|
|
12
|
-
import { createEffect } from "../Envio.res.mjs";
|
|
13
|
-
import { missingRpcMessage } from "./errors.ts";
|
|
14
|
-
import { rpcClient } from "./rpc.ts";
|
|
15
|
-
import { parseSignature } from "./abi-types.ts";
|
|
16
|
-
import { isRevert, untilAnswered } from "./transient.ts";
|
|
17
|
-
import { info } from "./log.ts";
|
|
18
|
-
|
|
19
|
-
export type CallInput = {
|
|
20
|
-
chainId: number;
|
|
21
|
-
address: string;
|
|
22
|
-
signature: string;
|
|
23
|
-
args: unknown[];
|
|
24
|
-
blockNumber: number;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export type CallOutput = {
|
|
28
|
-
reverted: boolean;
|
|
29
|
-
values: unknown[] | null;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* A batch commits only once every contract call its handlers make is in, and a
|
|
34
|
-
* call-heavy backfill's first batch can wait minutes on thousands of them with
|
|
35
|
-
* nothing else to show, which reads as a hang. While calls are
|
|
36
|
-
* outstanding, how many were answered is logged now and then.
|
|
37
|
-
*/
|
|
38
|
-
const progress = { started: 0, answered: 0, reportedAnswered: 0, timer: undefined as NodeJS.Timeout | undefined };
|
|
39
|
-
|
|
40
|
-
function progressIntervalMs(): number {
|
|
41
|
-
const value = Number(process.env.ENVIO_SUBGRAPH_PROGRESS_LOG_MS);
|
|
42
|
-
return Number.isFinite(value) && value > 0 ? value : 30_000;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function reportProgress() {
|
|
46
|
-
const waiting = progress.started - progress.answered;
|
|
47
|
-
if (waiting <= 0) {
|
|
48
|
-
clearInterval(progress.timer);
|
|
49
|
-
progress.timer = undefined;
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const interval = progressIntervalMs();
|
|
53
|
-
const span = interval >= 1000 ? `${Math.round(interval / 1000)}s` : `${interval}ms`;
|
|
54
|
-
info(
|
|
55
|
-
`Envio Subgraph: ${progress.answered - progress.reportedAnswered} contract calls answered in the last ` +
|
|
56
|
-
`${span}, ${waiting} waiting. The batch is written once its handlers' calls are all in.`,
|
|
57
|
-
);
|
|
58
|
-
progress.reportedAnswered = progress.answered;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function trackCall<T>(call: Promise<T>): Promise<T> {
|
|
62
|
-
progress.started++;
|
|
63
|
-
if (!progress.timer) {
|
|
64
|
-
progress.timer = setInterval(reportProgress, progressIntervalMs());
|
|
65
|
-
progress.timer.unref();
|
|
66
|
-
}
|
|
67
|
-
const settle = () => {
|
|
68
|
-
progress.answered++;
|
|
69
|
-
};
|
|
70
|
-
call.then(settle, settle);
|
|
71
|
-
return call;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Functions whose answer a contract does not change once it has given one that
|
|
76
|
-
* isn't empty: token metadata, a pair's tokens, a registry's factory. A mapping
|
|
77
|
-
* asks them again at every block it handles (a token's `decimals()` on every
|
|
78
|
-
* swap of every pair that holds it, a factory's `getPair(a, b)` on every sync)
|
|
79
|
-
* and the effect cache keys a call by block, so each ask was an RPC request.
|
|
80
|
-
* Once one has been answered at block B with a value, any block from B on gets
|
|
81
|
-
* that value without a request. Only a non-empty answer counts: a zero address
|
|
82
|
-
* or an empty string is "not there yet" (`getPair` before the pair exists,
|
|
83
|
-
* `symbol()` before the token is deployed), and a revert is never shared.
|
|
84
|
-
* A proxy that changes its `name()` later is the one case this gets wrong;
|
|
85
|
-
* ENVIO_SUBGRAPH_EXACT_CALLS=1 asks every call at its own block instead.
|
|
86
|
-
*/
|
|
87
|
-
export const STABLE_FUNCTIONS = new Set([
|
|
88
|
-
"name",
|
|
89
|
-
"symbol",
|
|
90
|
-
"decimals",
|
|
91
|
-
"NAME",
|
|
92
|
-
"SYMBOL",
|
|
93
|
-
"DECIMALS",
|
|
94
|
-
"token0",
|
|
95
|
-
"token1",
|
|
96
|
-
"factory",
|
|
97
|
-
"getPair",
|
|
98
|
-
"comptroller",
|
|
99
|
-
"underlying",
|
|
100
|
-
]);
|
|
101
|
-
|
|
102
|
-
type Shared = { block: number; output: string };
|
|
103
|
-
|
|
104
|
-
/** The call without its block: what stays true from the block it was first answered at. */
|
|
105
|
-
function stableKey(input: CallInput): string | undefined {
|
|
106
|
-
if (process.env.ENVIO_SUBGRAPH_EXACT_CALLS === "1") return undefined;
|
|
107
|
-
if (!STABLE_FUNCTIONS.has(parseSignature(input.signature).name)) return undefined;
|
|
108
|
-
// An address argument is the same one whatever its checksum casing.
|
|
109
|
-
const args = input.args.map((arg) => (/^s:0x[0-9a-fA-F]*$/.test(String(arg)) ? String(arg).toLowerCase() : arg));
|
|
110
|
-
return JSON.stringify([input.chainId, input.address.toLowerCase(), input.signature, args]);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const sharedAnswers = new Map<string, Shared>();
|
|
114
|
-
const sharedInFlight = new Map<string, { block: number; settled: Promise<void> }>();
|
|
115
|
-
const SHARED_ANSWERS_LIMIT = 200_000;
|
|
116
|
-
|
|
117
|
-
/** Reset between test indexers, which each bring their own chain. */
|
|
118
|
-
export function resetSharedCalls() {
|
|
119
|
-
sharedAnswers.clear();
|
|
120
|
-
sharedInFlight.clear();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function remember(key: string, block: number, output: string) {
|
|
124
|
-
const answer = JSON.parse(output) as CallOutput;
|
|
125
|
-
// A zero address or an empty string is "not there yet"; a revert says nothing.
|
|
126
|
-
const empty = (answer.values ?? []).every((v) => typeof v === "string" && /^s:(0x0*)?$/i.test(v));
|
|
127
|
-
if (answer.reverted || !answer.values || empty) return;
|
|
128
|
-
const earlier = sharedAnswers.get(key);
|
|
129
|
-
if (earlier && earlier.block <= block) return;
|
|
130
|
-
if (sharedAnswers.size >= SHARED_ANSWERS_LIMIT) sharedAnswers.clear();
|
|
131
|
-
sharedAnswers.set(key, { block, output });
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* A stable function's answer from an earlier block, one still being asked for
|
|
136
|
-
* (waited on, then reused if it turns out to be a value), or `ask`.
|
|
137
|
-
*/
|
|
138
|
-
export async function shareAcrossBlocks(input: CallInput, ask: () => Promise<string>): Promise<string> {
|
|
139
|
-
const key = stableKey(input);
|
|
140
|
-
if (key === undefined) return ask();
|
|
141
|
-
const known = sharedAnswers.get(key);
|
|
142
|
-
if (known && known.block <= input.blockNumber) return known.output;
|
|
143
|
-
|
|
144
|
-
const running = sharedInFlight.get(key);
|
|
145
|
-
if (running && running.block <= input.blockNumber) {
|
|
146
|
-
await running.settled;
|
|
147
|
-
const after = sharedAnswers.get(key);
|
|
148
|
-
if (after && after.block <= input.blockNumber) return after.output;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const asked = ask();
|
|
152
|
-
const entry = { block: input.blockNumber, settled: asked.then(() => undefined, () => undefined) };
|
|
153
|
-
if (!sharedInFlight.has(key) || sharedInFlight.get(key)!.block > input.blockNumber) {
|
|
154
|
-
sharedInFlight.set(key, entry);
|
|
155
|
-
}
|
|
156
|
-
try {
|
|
157
|
-
const output = await asked;
|
|
158
|
-
remember(key, input.blockNumber, output);
|
|
159
|
-
return output;
|
|
160
|
-
} finally {
|
|
161
|
-
if (sharedInFlight.get(key) === entry) sharedInFlight.delete(key);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** An argument as the addon reads it: an integer as its decimal string. */
|
|
166
|
-
function jsonArg(value: unknown): unknown {
|
|
167
|
-
if (typeof value === "bigint") return value.toString();
|
|
168
|
-
if (Array.isArray(value)) return value.map(jsonArg);
|
|
169
|
-
return value;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const hexBytes = (hex: string) => Buffer.from(hex.replace(/^0x/, ""), "hex");
|
|
173
|
-
|
|
174
|
-
async function askNode(rpcUrls: string[], input: CallInput): Promise<string> {
|
|
175
|
-
const { name, inputs, outputs } = parseSignature(input.signature);
|
|
176
|
-
const data = subgraphAbiEncodeCall(name, `(${inputs})`, input.args.map(decodeArg).map(jsonArg));
|
|
177
|
-
|
|
178
|
-
const result = await trackCall(
|
|
179
|
-
untilAnswered(
|
|
180
|
-
`${input.signature} on ${input.address} at block ${input.blockNumber}`,
|
|
181
|
-
async () => {
|
|
182
|
-
try {
|
|
183
|
-
return await rpcClient(rpcUrls).call({ to: input.address, data, blockNumber: input.blockNumber });
|
|
184
|
-
} catch (error) {
|
|
185
|
-
if (isRevert(error)) return null;
|
|
186
|
-
throw error;
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
{ deadline: false },
|
|
190
|
-
),
|
|
191
|
-
);
|
|
192
|
-
if (result === null) {
|
|
193
|
-
return JSON.stringify({ reverted: true, values: null });
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
// graph-ts returns one `ethereum.Value` per ABI output. A single
|
|
198
|
-
// tuple/struct is therefore `[tuple]`, not the flattened fields —
|
|
199
|
-
// generated bindings call `result[0].toTuple()`.
|
|
200
|
-
const values = subgraphAbiDecode(`(${outputs})`, hexBytes(result), true) as unknown[];
|
|
201
|
-
return JSON.stringify({ reverted: false, values: values.map(encodeArg) });
|
|
202
|
-
} catch {
|
|
203
|
-
// Output the declared signature can't decode counts as a failed call,
|
|
204
|
-
// which is what lets a mapping retry through a different ABI — the
|
|
205
|
-
// bytes32-vs-string ERC20 name is the case every token subgraph hits. A
|
|
206
|
-
// function answering no bytes at all is the same: the account has no code.
|
|
207
|
-
return JSON.stringify({ reverted: true, values: null });
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function makeCallEffect(rpcUrls: string[], recall: (encoded: string) => string | undefined = () => undefined) {
|
|
212
|
-
return createEffect(
|
|
213
|
-
{
|
|
214
|
-
name: "envio_subgraph_eth_call",
|
|
215
|
-
// Both sides travel as JSON text: the cache key is then the exact call
|
|
216
|
-
// description, and the cached row is readable.
|
|
217
|
-
input: Sury.string,
|
|
218
|
-
output: Sury.string,
|
|
219
|
-
rateLimit: false,
|
|
220
|
-
cache: true,
|
|
221
|
-
crossChain: false,
|
|
222
|
-
},
|
|
223
|
-
async ({ input: encoded }: { input: string }) => {
|
|
224
|
-
// The register pass asked the same question at fetch time; its answer
|
|
225
|
-
// is the same one, and asking twice doubled a factory's calls.
|
|
226
|
-
const recalled = recall(encoded);
|
|
227
|
-
if (recalled !== undefined) return recalled;
|
|
228
|
-
const input = JSON.parse(encoded) as CallInput;
|
|
229
|
-
if (rpcUrls.length === 0) {
|
|
230
|
-
throw new Error(missingRpcMessage(input.signature));
|
|
231
|
-
}
|
|
232
|
-
return shareAcrossBlocks(input, () => askNode(rpcUrls, input));
|
|
233
|
-
},
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/** Effect inputs/outputs travel as strings, tagged so the type survives. */
|
|
238
|
-
export function encodeArg(value: unknown): string {
|
|
239
|
-
if (typeof value === "bigint") return `i:${value.toString()}`;
|
|
240
|
-
if (typeof value === "boolean") return `b:${value ? "1" : "0"}`;
|
|
241
|
-
if (Array.isArray(value)) return `a:${JSON.stringify(value.map(encodeArg))}`;
|
|
242
|
-
return `s:${String(value)}`;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export function decodeArg(value: string): unknown {
|
|
246
|
-
const tag = value.slice(0, 2);
|
|
247
|
-
const body = value.slice(2);
|
|
248
|
-
switch (tag) {
|
|
249
|
-
case "i:":
|
|
250
|
-
return BigInt(body);
|
|
251
|
-
case "b:":
|
|
252
|
-
return body === "1";
|
|
253
|
-
case "a:":
|
|
254
|
-
return (JSON.parse(body) as string[]).map(decodeArg);
|
|
255
|
-
default:
|
|
256
|
-
return body;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The shim, checked against the types a subgraph developer codes against.
|
|
3
|
-
*
|
|
4
|
-
* A subgraph's mappings are type-checked by `asc` against
|
|
5
|
-
* `@graphprotocol/graph-ts`, and then run by this shim. Nothing else makes the
|
|
6
|
-
* two agree, so a gap shows up as green types and a runtime error — the worst
|
|
7
|
-
* feedback loop there is. This file closes it.
|
|
8
|
-
*
|
|
9
|
-
* The declarations come from the real package (see
|
|
10
|
-
* `scripts/generate-graph-ts-types.mjs`). Every export is walked, and within
|
|
11
|
-
* each class and namespace every static and every instance member, so a gap is
|
|
12
|
-
* named down to the member — `ethereum.Value.fromAddress` — rather than hidden
|
|
13
|
-
* behind a suppression covering a whole namespace. This file is type-checked,
|
|
14
|
-
* never run.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import type * as GraphTs from "./graph-ts-types/index.js";
|
|
18
|
-
import type * as Shim from "./graph-ts.ts";
|
|
19
|
-
|
|
20
|
-
/** A class's statics: a mapped type drops the construct signature (see `Constructs`). */
|
|
21
|
-
type Statics<T> = { [K in keyof T as K extends "prototype" ? never : K]: T[K] };
|
|
22
|
-
|
|
23
|
-
type Agrees<Real, Ours, Path extends string> = [Ours] extends [Real] ? never : Path;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* A public constructor the shim's has to accept, named `Path()` — a mapping
|
|
27
|
-
* can build an `ethereum.Event` itself. A protected one isn't a construct
|
|
28
|
-
* signature, so it is never compared.
|
|
29
|
-
*/
|
|
30
|
-
type Constructs<Real, Ours, Path extends string> = [Real] extends [abstract new (...args: infer Args) => any]
|
|
31
|
-
? [Ours] extends [abstract new (...args: infer OursArgs) => any]
|
|
32
|
-
? // By arity: a shorter parameter list is assignable to a longer one, and
|
|
33
|
-
// would take the arguments a mapping passes without a word. The types
|
|
34
|
-
// themselves can't be compared this way round — graph-ts' `BigInt` is a
|
|
35
|
-
// `Uint8Array`, the shim's isn't — and the members already are.
|
|
36
|
-
number extends OursArgs["length"]
|
|
37
|
-
? never
|
|
38
|
-
: OursArgs["length"] extends Args["length"]
|
|
39
|
-
? never
|
|
40
|
-
: `${Path}()`
|
|
41
|
-
: `${Path}()`
|
|
42
|
-
: never;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Deep enough to reach `ethereum.Value.fromAddress`, and no deeper: a member
|
|
46
|
-
* past that is compared whole, which still catches any disagreement inside it.
|
|
47
|
-
*/
|
|
48
|
-
type Member<Real, Ours, Path extends string, Depth extends unknown[]> = Depth["length"] extends 2
|
|
49
|
-
? Agrees<Real, Ours, Path>
|
|
50
|
-
: // `any` would take every branch below at once.
|
|
51
|
-
0 extends 1 & Real
|
|
52
|
-
? Agrees<Real, Ours, Path>
|
|
53
|
-
: // Bracketed so a union — an enum, `boolean` — is compared whole.
|
|
54
|
-
[Real] extends [(...args: any) => any]
|
|
55
|
-
? Agrees<Real, Ours, Path>
|
|
56
|
-
: // A class, even one whose constructor is protected.
|
|
57
|
-
[Real] extends [{ prototype: infer RealInstance }]
|
|
58
|
-
? [Ours] extends [{ prototype: infer OursInstance }]
|
|
59
|
-
?
|
|
60
|
-
| Constructs<Real, Ours, Path>
|
|
61
|
-
| Gaps<Statics<Real>, Statics<Ours>, `${Path}.`, [...Depth, unknown]>
|
|
62
|
-
| Gaps<RealInstance, OursInstance, `${Path}#`, [...Depth, unknown]>
|
|
63
|
-
: Path
|
|
64
|
-
: [Real] extends [object]
|
|
65
|
-
? Gaps<Real, Ours, `${Path}.`, [...Depth, unknown]>
|
|
66
|
-
: Agrees<Real, Ours, Path>;
|
|
67
|
-
|
|
68
|
-
/** Where `Ours` lacks or disagrees with `Real`, by path. */
|
|
69
|
-
type Gaps<Real, Ours, Path extends string, Depth extends unknown[] = []> = {
|
|
70
|
-
[K in keyof Real & string]-?: K extends keyof Ours
|
|
71
|
-
? Member<Real[K], Ours[K], `${Path}${K}`, Depth>
|
|
72
|
-
: `${Path}${K}`;
|
|
73
|
-
}[keyof Real & string];
|
|
74
|
-
|
|
75
|
-
type Found = Gaps<typeof GraphTs, typeof Shim, "">;
|
|
76
|
-
|
|
77
|
-
/** Every gap the shim knows it has, and why. */
|
|
78
|
-
type KnownGap =
|
|
79
|
-
// Chains envio doesn't index in subgraph mode: their namespaces refuse a read.
|
|
80
|
-
| "arweave.Block"
|
|
81
|
-
| "arweave.ProofOfAccess"
|
|
82
|
-
| "arweave.Tag"
|
|
83
|
-
| "arweave.Transaction"
|
|
84
|
-
| "arweave.TransactionWithBlockPtr"
|
|
85
|
-
// Refused at runtime: a YAML parser isn't shipped with the shim.
|
|
86
|
-
| `yaml.${string}`
|
|
87
|
-
| `YAMLValue${string}`
|
|
88
|
-
| `YAMLTaggedValue${string}`
|
|
89
|
-
// graph-ts declares `CallResult._value` private, which no other declaration
|
|
90
|
-
// can match; the shim's is structurally the same.
|
|
91
|
-
| "ethereum.CallResult.fromValue"
|
|
92
|
-
| "ethereum.SmartContract#tryCall"
|
|
93
|
-
// Constructors only the shim itself calls. graph-ts' BigInt inherits
|
|
94
|
-
// Uint8Array's and is built through its statics; the others are declared
|
|
95
|
-
// without one, and a mapping gets each from a call, a parse or `fromValue`.
|
|
96
|
-
| "BigInt()"
|
|
97
|
-
| "ethereum.CallResult()"
|
|
98
|
-
| "Result()"
|
|
99
|
-
| "JSONValue()";
|
|
100
|
-
|
|
101
|
-
/** A known gap no longer found, whose entry should go. */
|
|
102
|
-
type Closed<Gap> = Gap extends unknown ? ([Extract<Found, Gap>] extends [never] ? Gap : never) : never;
|
|
103
|
-
|
|
104
|
-
// Keyed so a failure lists each path as a missing property.
|
|
105
|
-
export const unlisted: Record<Exclude<Found, KnownGap>, "a gap KnownGap doesn't list"> = {};
|
|
106
|
-
export const closed: Record<Closed<KnownGap>, "a KnownGap entry that no longer applies"> = {};
|
package/src/subgraph/errors.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
const ISSUES_URL = "https://github.com/enviodev/hyperindex/issues";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Subgraph mode ships under its own npm dist-tag (see `publish.yml`), so
|
|
5
|
-
* `envio@latest` would move a project off it.
|
|
6
|
-
*/
|
|
7
|
-
const UPGRADE_COMMAND = "pnpm add -D envio@subgraph";
|
|
8
|
-
|
|
9
|
-
/** A feature we recognise and deliberately don't implement. */
|
|
10
|
-
export function unsupported(feature: string, location: string, why?: string): Error {
|
|
11
|
-
return new Error(
|
|
12
|
-
`Envio Subgraph doesn't support ${feature} yet${why ? `: ${why}` : ""}.\n` +
|
|
13
|
-
` Found in ${location}.\n` +
|
|
14
|
-
`First, make sure you're on the newest Envio Subgraph release — support may have landed:\n` +
|
|
15
|
-
` ${UPGRADE_COMMAND}\n` +
|
|
16
|
-
`If you're up to date and need this feature, please open an issue (existing\n` +
|
|
17
|
-
`issues welcome a 👍 — demand drives prioritization):\n ${ISSUES_URL}`,
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Something we don't recognise at all: newer than us, or a typo. */
|
|
22
|
-
export function unknown(thing: string, location: string): Error {
|
|
23
|
-
return new Error(
|
|
24
|
-
`Envio Subgraph doesn't know ${thing}.\n` +
|
|
25
|
-
` Found in ${location}.\n` +
|
|
26
|
-
`This may be a feature newer than this envio version understands, or a typo.\n` +
|
|
27
|
-
`First, make sure you're on the newest Envio Subgraph release:\n` +
|
|
28
|
-
` ${UPGRADE_COMMAND}\n` +
|
|
29
|
-
`If you're up to date and this is a real subgraph feature, please open an\n` +
|
|
30
|
-
`issue so we can add it: ${ISSUES_URL}`,
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const RPC_ENV_VAR = "ENVIO_SUBGRAPH_RPC";
|
|
35
|
-
|
|
36
|
-
/** §6b: contract calls need an endpoint HyperSync and HyperRPC don't serve. */
|
|
37
|
-
export function missingRpcMessage(callSite: string): string {
|
|
38
|
-
return (
|
|
39
|
-
`This subgraph performs contract calls (${callSite}), which need an\n` +
|
|
40
|
-
`RPC endpoint — HyperSync and HyperRPC serve logs and blocks, not eth_call.\n` +
|
|
41
|
-
`Set one in .env or the environment:\n` +
|
|
42
|
-
` ${RPC_ENV_VAR}=https://...\n` +
|
|
43
|
-
`Advanced (matches envio's rpc config; single entry or array):\n` +
|
|
44
|
-
` ${RPC_ENV_VAR}={"url":"https://...","for":"fallback","headers":{...}}`
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Names a trap must answer for rather than refuse. AS-compiled mappings never
|
|
50
|
-
* feature-detect, so nothing legitimate reaches these — but `console.log`,
|
|
51
|
-
* `await` and `JSON.stringify` do, and they must not explode.
|
|
52
|
-
*/
|
|
53
|
-
const PASSTHROUGH = new Set([
|
|
54
|
-
"then",
|
|
55
|
-
"toJSON",
|
|
56
|
-
"constructor",
|
|
57
|
-
"valueOf",
|
|
58
|
-
"toString",
|
|
59
|
-
"inspect",
|
|
60
|
-
"nodeType",
|
|
61
|
-
"$$typeof",
|
|
62
|
-
"@@__IMMUTABLE_ITERABLE__@@",
|
|
63
|
-
]);
|
|
64
|
-
|
|
65
|
-
function isPassthrough(prop: PropertyKey): boolean {
|
|
66
|
-
return typeof prop === "symbol" || PASSTHROUGH.has(prop as string);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const PROTOTYPE_PASSTHROUGH = PASSTHROUGH;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Full Proxy for a graph-ts namespace object. Future graph-ts APIs are
|
|
73
|
-
* unenumerable, so only a `get` trap can catch them; these are cold paths, so
|
|
74
|
-
* the trap cost doesn't matter.
|
|
75
|
-
*/
|
|
76
|
-
export function strictNamespace<T extends object>(name: string, target: T): T {
|
|
77
|
-
return new Proxy(target, {
|
|
78
|
-
get(target, prop, receiver) {
|
|
79
|
-
if (prop in target || isPassthrough(prop)) {
|
|
80
|
-
return Reflect.get(target, prop, receiver);
|
|
81
|
-
}
|
|
82
|
-
throw unknown(`the graph-ts API ${name}.${String(prop)}`, `a mapping handler`);
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
}
|
package/src/subgraph/graft.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
export type Graft = { base: string; block: number };
|
|
2
|
-
|
|
3
|
-
type Manifest = { startBlocks: number[]; graft: Graft | null };
|
|
4
|
-
|
|
5
|
-
const GATEWAYS = [
|
|
6
|
-
"https://api.thegraph.com/ipfs/api/v0/cat?arg=",
|
|
7
|
-
"https://ipfs.network.thegraph.com/api/v0/cat?arg=",
|
|
8
|
-
];
|
|
9
|
-
const MAX_CHAIN = 8;
|
|
10
|
-
|
|
11
|
-
async function fetchFromGateways(hash: string): Promise<string | null> {
|
|
12
|
-
for (const gateway of GATEWAYS) {
|
|
13
|
-
try {
|
|
14
|
-
const response = await fetch(gateway + hash, {
|
|
15
|
-
method: "POST",
|
|
16
|
-
signal: AbortSignal.timeout(8000),
|
|
17
|
-
});
|
|
18
|
-
if (response.ok) return await response.text();
|
|
19
|
-
} catch {
|
|
20
|
-
// try the next gateway
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** The start blocks and graft of a deployment manifest, read without a YAML parser. */
|
|
27
|
-
export function readManifest(text: string): Manifest {
|
|
28
|
-
const startBlocks = [...text.matchAll(/^\s*startBlock:\s*["']?(\d+)/gm)].map((m) => Number(m[1]));
|
|
29
|
-
const block = /^graft:\s*\n((?:[ \t]+.*\n?)+)/m.exec(text)?.[1] ?? "";
|
|
30
|
-
const base = /^\s*base:\s*["']?(Qm[A-Za-z0-9]+)/m.exec(block)?.[1];
|
|
31
|
-
const at = /^\s*block:\s*["']?(\d+)/m.exec(block)?.[1];
|
|
32
|
-
return { startBlocks, graft: base && at ? { base, block: Number(at) } : null };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* graph-node copies the base deployment's data up to the graft block, and the
|
|
37
|
-
* manifest on IPFS is the only record of where that base started. If it
|
|
38
|
-
* started earlier than any data source here does, a re-index from these start
|
|
39
|
-
* blocks is missing everything in between; the numbers go in the warning.
|
|
40
|
-
* Returns null when the base can't be read (offline, pruned from IPFS).
|
|
41
|
-
*/
|
|
42
|
-
export async function graftBaseNotice(
|
|
43
|
-
graft: Graft,
|
|
44
|
-
ownStartBlocks: number[],
|
|
45
|
-
fetchManifest: (hash: string) => Promise<string | null> = fetchFromGateways,
|
|
46
|
-
): Promise<string | null> {
|
|
47
|
-
let earliest: number | null = null;
|
|
48
|
-
let hash: string | null = graft.base;
|
|
49
|
-
for (let depth = 0; hash && depth < MAX_CHAIN; depth++) {
|
|
50
|
-
const text = await fetchManifest(hash);
|
|
51
|
-
if (text === null) return null;
|
|
52
|
-
const manifest = readManifest(text);
|
|
53
|
-
if (manifest.startBlocks.length > 0) {
|
|
54
|
-
const lowest = Math.min(...manifest.startBlocks);
|
|
55
|
-
earliest = earliest === null ? lowest : Math.min(earliest, lowest);
|
|
56
|
-
}
|
|
57
|
-
hash = manifest.graft?.base ?? null;
|
|
58
|
-
}
|
|
59
|
-
if (earliest === null) return null;
|
|
60
|
-
const own = ownStartBlocks.length > 0 ? Math.min(...ownStartBlocks) : 0;
|
|
61
|
-
if (earliest < own) {
|
|
62
|
-
return (
|
|
63
|
-
`WARNING, DATA IS MISSING: the graft base ${graft.base} (read from IPFS) starts at block ` +
|
|
64
|
-
`${earliest}, but the earliest startBlock here is ${own}. Blocks ${earliest} to ${own - 1} ` +
|
|
65
|
-
`are in the grafted deployment and will not be indexed. Lower the startBlock of the data ` +
|
|
66
|
-
`sources to ${earliest} to rebuild that history.`
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
return (
|
|
70
|
-
`The graft base ${graft.base} (read from IPFS) starts at block ${earliest}, not after this ` +
|
|
71
|
-
`manifest's earliest startBlock ${own}: a re-index covers the same blocks.`
|
|
72
|
-
);
|
|
73
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// Runs one graph-cli command, as its own bin would, on the thread
|
|
2
|
-
// `graph-cli.ts` sized for it. Plain JavaScript: the TypeScript loader envio
|
|
3
|
-
// registers doesn't reach worker threads.
|
|
4
|
-
import { parentPort, workerData } from "node:worker_threads";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { pathToFileURL } from "node:url";
|
|
7
|
-
|
|
8
|
-
const { packageDir, command, argv } = workerData;
|
|
9
|
-
|
|
10
|
-
let ok = true;
|
|
11
|
-
try {
|
|
12
|
-
const commandFile = path.join(packageDir, "dist", "commands", `${command}.js`);
|
|
13
|
-
const exported = await import(pathToFileURL(commandFile).href);
|
|
14
|
-
// graph-cli before 0.93 is CommonJS compiled from TypeScript: an ES import
|
|
15
|
-
// hands back `module.exports`, so the class is its `default`, one level down.
|
|
16
|
-
const Command = [exported.default, exported.default?.default].find(
|
|
17
|
-
(candidate) => typeof candidate?.run === "function",
|
|
18
|
-
);
|
|
19
|
-
if (Command === undefined) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
`Envio Subgraph couldn't find a runnable \`graph ${command}\` in ${commandFile}`,
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
await Command.run(argv, packageDir);
|
|
25
|
-
// A generation failure is reported through the exit code, not a throw.
|
|
26
|
-
ok = process.exitCode === undefined || process.exitCode === 0;
|
|
27
|
-
} catch (error) {
|
|
28
|
-
// oclif ends a successful command early by throwing an exit of 0.
|
|
29
|
-
if (error?.oclif?.exit !== 0) {
|
|
30
|
-
console.error(error?.message ?? error);
|
|
31
|
-
ok = false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
process.exitCode = 0;
|
|
35
|
-
parentPort.postMessage(ok);
|