envio 3.6.1 → 3.7.0-subgraph
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/index.d.ts +197 -9
- package/package.json +6 -6
- package/src/Batch.res +64 -25
- package/src/Batch.res.mjs +69 -46
- package/src/ChainFetching.res +13 -17
- package/src/ChainFetching.res.mjs +9 -14
- package/src/ChainState.res +160 -119
- package/src/ChainState.res.mjs +75 -46
- package/src/ChainState.resi +15 -12
- package/src/Config.res +18 -2
- package/src/Config.res.mjs +9 -6
- package/src/Core.res +20 -0
- package/src/Core.res.mjs +11 -0
- package/src/Ecosystem.res +0 -1
- package/src/Envio.res +16 -3
- package/src/EventConfigBuilder.res +155 -40
- package/src/EventConfigBuilder.res.mjs +76 -24
- package/src/EventProcessing.res +10 -14
- package/src/EventProcessing.res.mjs +12 -10
- package/src/EventUtils.res +3 -47
- package/src/FetchState.res +124 -104
- package/src/FetchState.res.mjs +116 -100
- package/src/HandlerLoader.res +20 -8
- package/src/HandlerLoader.res.mjs +11 -2
- package/src/HandlerRegister.res +108 -2
- package/src/HandlerRegister.res.mjs +59 -10
- package/src/HandlerRegister.resi +4 -0
- package/src/InMemoryStore.res +1 -6
- package/src/InMemoryStore.res.mjs +1 -3
- package/src/Internal.res +84 -24
- package/src/Internal.res.mjs +33 -2
- package/src/LazyLoader.res +12 -11
- package/src/LazyLoader.res.mjs +13 -7
- package/src/Main.res +22 -9
- package/src/Main.res.mjs +18 -5
- package/src/MemoryStorage.res +756 -0
- package/src/MemoryStorage.res.mjs +623 -0
- package/src/Persistence.res +4 -2
- package/src/PgStorage.res +24 -8
- package/src/PgStorage.res.mjs +15 -12
- package/src/ReorgDetection.res +0 -222
- package/src/ReorgDetection.res.mjs +0 -163
- package/src/Rollback.res +14 -15
- package/src/Rollback.res.mjs +4 -5
- package/src/SimulateItems.res +2 -2
- package/src/UserContext.res +358 -51
- package/src/UserContext.res.mjs +271 -35
- package/src/Utils.res +3 -0
- package/src/bindings/ClickHouse.res +16 -11
- package/src/bindings/ClickHouse.res.mjs +10 -10
- package/src/bindings/Vitest.res +1 -1
- package/src/sources/BlockStore.res +115 -5
- package/src/sources/BlockStore.res.mjs +25 -0
- package/src/sources/Evm.res +0 -1
- package/src/sources/Evm.res.mjs +0 -1
- package/src/sources/EvmHyperSyncSource.res +19 -84
- package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
- package/src/sources/Fuel.res +24 -4
- package/src/sources/Fuel.res.mjs +23 -3
- package/src/sources/FuelHyperSync.res +8 -3
- package/src/sources/FuelHyperSync.res.mjs +5 -4
- package/src/sources/FuelHyperSync.resi +3 -1
- package/src/sources/FuelHyperSyncClient.res +7 -10
- package/src/sources/FuelHyperSyncSource.res +18 -45
- package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
- package/src/sources/HyperSync.res +49 -229
- package/src/sources/HyperSync.res.mjs +74 -191
- package/src/sources/HyperSync.resi +11 -35
- package/src/sources/HyperSyncClient.res +9 -79
- package/src/sources/HyperSyncClient.res.mjs +2 -6
- package/src/sources/RequestStat.res +5 -0
- package/src/sources/RequestStat.res.mjs +2 -0
- package/src/sources/RpcSource.res +149 -43
- package/src/sources/RpcSource.res.mjs +104 -41
- package/src/sources/SimulateSource.res +8 -5
- package/src/sources/SimulateSource.res.mjs +6 -6
- package/src/sources/Source.res +89 -16
- package/src/sources/Source.res.mjs +50 -1
- package/src/sources/SourceManager.res +255 -50
- package/src/sources/SourceManager.res.mjs +149 -55
- package/src/sources/SourceManager.resi +2 -6
- package/src/sources/Svm.res +0 -7
- package/src/sources/Svm.res.mjs +0 -8
- package/src/sources/SvmHyperSyncClient.res +21 -16
- package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
- package/src/sources/SvmHyperSyncSource.res +25 -117
- package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
- package/src/subgraph/blocks.ts +176 -0
- package/src/subgraph/calls.ts +217 -0
- package/src/subgraph/conformance.ts +99 -0
- package/src/subgraph/division.ts +110 -0
- package/src/subgraph/errors.ts +90 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +1871 -0
- package/src/subgraph/hosts.ts +148 -0
- package/src/subgraph/runtime.ts +845 -0
- package/src/subgraph/scope.ts +63 -0
- package/svm.schema.json +3 -2
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Contract.bind(x).foo()` -> an envio effect over viem.
|
|
3
|
+
*
|
|
4
|
+
* graph-node evaluates a mapping's contract calls against the block the event
|
|
5
|
+
* came from, so the block number is part of the effect's input — and therefore
|
|
6
|
+
* of its cache key, which is what makes a cached call safe to reuse.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
createPublicClient,
|
|
11
|
+
decodeFunctionResult,
|
|
12
|
+
encodeFunctionData,
|
|
13
|
+
fallback,
|
|
14
|
+
http,
|
|
15
|
+
parseAbiParameters,
|
|
16
|
+
type Abi,
|
|
17
|
+
} from "viem";
|
|
18
|
+
import * as Sury from "rescript-schema";
|
|
19
|
+
import { createEffect } from "../Envio.res.mjs";
|
|
20
|
+
import { missingRpcMessage } from "./errors.ts";
|
|
21
|
+
|
|
22
|
+
export type ParsedSignature = {
|
|
23
|
+
name: string;
|
|
24
|
+
inputs: string;
|
|
25
|
+
outputs: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* graph codegen emits `"balanceOf(address):(uint256)"`. Types only, no names —
|
|
30
|
+
* which is all `parseAbiParameters` needs.
|
|
31
|
+
*/
|
|
32
|
+
export function parseSignature(signature: string): ParsedSignature {
|
|
33
|
+
const open = signature.indexOf("(");
|
|
34
|
+
if (open === -1) {
|
|
35
|
+
throw new Error(`Unreadable contract call signature "${signature}"`);
|
|
36
|
+
}
|
|
37
|
+
const name = signature.slice(0, open);
|
|
38
|
+
|
|
39
|
+
let depth = 0;
|
|
40
|
+
let close = -1;
|
|
41
|
+
for (let index = open; index < signature.length; index++) {
|
|
42
|
+
const char = signature[index];
|
|
43
|
+
if (char === "(") depth++;
|
|
44
|
+
if (char === ")") {
|
|
45
|
+
depth--;
|
|
46
|
+
if (depth === 0) {
|
|
47
|
+
close = index;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (close === -1) {
|
|
53
|
+
throw new Error(`Unreadable contract call signature "${signature}"`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const inputs = signature.slice(open + 1, close);
|
|
57
|
+
const rest = signature.slice(close + 1).replace(/^:/, "");
|
|
58
|
+
const outputs = rest.startsWith("(") ? rest.slice(1, -1) : rest;
|
|
59
|
+
|
|
60
|
+
return { name, inputs, outputs };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function abiItemFor(signature: string): Abi {
|
|
64
|
+
const { name, inputs, outputs } = parseSignature(signature);
|
|
65
|
+
return [
|
|
66
|
+
{
|
|
67
|
+
type: "function",
|
|
68
|
+
name,
|
|
69
|
+
stateMutability: "view",
|
|
70
|
+
inputs: inputs.trim() === "" ? [] : [...parseAbiParameters(inputs)],
|
|
71
|
+
outputs: outputs.trim() === "" ? [] : [...parseAbiParameters(outputs)],
|
|
72
|
+
},
|
|
73
|
+
] as Abi;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* A revert is data: the mapping's `try_` sees `{reverted: true}`. A transport
|
|
78
|
+
* failure is not — it throws as the handler error so envio retries, and a flaky
|
|
79
|
+
* RPC never fabricates reverted data.
|
|
80
|
+
*/
|
|
81
|
+
export function isRevert(error: unknown): boolean {
|
|
82
|
+
const parts: string[] = [];
|
|
83
|
+
let current: any = error;
|
|
84
|
+
for (let depth = 0; current && depth < 10; depth++) {
|
|
85
|
+
parts.push(current.name, current.shortMessage, current.details, current.message);
|
|
86
|
+
current = current.cause;
|
|
87
|
+
}
|
|
88
|
+
const text = parts.filter(Boolean).join(" | ");
|
|
89
|
+
|
|
90
|
+
if (/revert|ContractFunctionZeroDataError|invalid opcode|out of gas/i.test(text)) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type CallInput = {
|
|
97
|
+
chainId: number;
|
|
98
|
+
address: string;
|
|
99
|
+
signature: string;
|
|
100
|
+
args: unknown[];
|
|
101
|
+
blockNumber: number;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export type CallOutput = {
|
|
105
|
+
reverted: boolean;
|
|
106
|
+
values: unknown[] | null;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
let clients: Map<string, ReturnType<typeof createPublicClient>> = new Map();
|
|
110
|
+
|
|
111
|
+
function clientFor(rpcUrls: string[]) {
|
|
112
|
+
const key = rpcUrls.join("|");
|
|
113
|
+
let client = clients.get(key);
|
|
114
|
+
if (!client) {
|
|
115
|
+
client = createPublicClient({
|
|
116
|
+
// Retrying is envio's job: a failed call becomes a handler error and
|
|
117
|
+
// the batch is retried with the effect's dedup still in place.
|
|
118
|
+
transport: fallback(rpcUrls.map((url) => http(url, { retryCount: 0 }))),
|
|
119
|
+
});
|
|
120
|
+
clients.set(key, client);
|
|
121
|
+
}
|
|
122
|
+
return client;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Reset between test indexers, which each bring their own endpoints. */
|
|
126
|
+
export function resetClients() {
|
|
127
|
+
clients = new Map();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* BigInt and Bytes cross the effect boundary through Sury, which needs a
|
|
132
|
+
* serialisable shape — so the call is described in plain JSON and the graph-ts
|
|
133
|
+
* values are converted on both sides by the shim.
|
|
134
|
+
*/
|
|
135
|
+
export function makeCallEffect(rpcUrls: string[]) {
|
|
136
|
+
return createEffect(
|
|
137
|
+
{
|
|
138
|
+
name: "envio_subgraph_eth_call",
|
|
139
|
+
// Both sides travel as JSON text: the cache key is then the exact call
|
|
140
|
+
// description, and the cached row is readable.
|
|
141
|
+
input: Sury.string,
|
|
142
|
+
output: Sury.string,
|
|
143
|
+
rateLimit: false,
|
|
144
|
+
cache: true,
|
|
145
|
+
crossChain: false,
|
|
146
|
+
},
|
|
147
|
+
async ({ input: encoded }: { input: string }) => {
|
|
148
|
+
const input = JSON.parse(encoded) as CallInput;
|
|
149
|
+
if (rpcUrls.length === 0) {
|
|
150
|
+
throw new Error(missingRpcMessage(input.signature));
|
|
151
|
+
}
|
|
152
|
+
const abi = abiItemFor(input.signature);
|
|
153
|
+
const { name } = parseSignature(input.signature);
|
|
154
|
+
const data = encodeFunctionData({
|
|
155
|
+
abi,
|
|
156
|
+
functionName: name,
|
|
157
|
+
args: input.args.map(decodeArg),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
let result;
|
|
161
|
+
try {
|
|
162
|
+
result = await clientFor(rpcUrls).call({
|
|
163
|
+
to: input.address as `0x${string}`,
|
|
164
|
+
data,
|
|
165
|
+
blockNumber: BigInt(input.blockNumber),
|
|
166
|
+
});
|
|
167
|
+
} catch (error) {
|
|
168
|
+
if (isRevert(error)) {
|
|
169
|
+
return JSON.stringify({ reverted: true, values: null });
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const decoded = decodeFunctionResult({
|
|
176
|
+
abi,
|
|
177
|
+
functionName: name,
|
|
178
|
+
data: (result.data ?? "0x") as `0x${string}`,
|
|
179
|
+
});
|
|
180
|
+
// graph-ts returns one `ethereum.Value` per ABI output. A single
|
|
181
|
+
// tuple/struct is therefore `[tuple]`, not the flattened fields —
|
|
182
|
+
// generated bindings call `result[0].toTuple()`.
|
|
183
|
+
const outputs = (abi[0] as { outputs?: unknown[] }).outputs ?? [];
|
|
184
|
+
const values = outputs.length === 1 ? [decoded] : Array.isArray(decoded) ? decoded : [decoded];
|
|
185
|
+
return JSON.stringify({ reverted: false, values: values.map(encodeArg) });
|
|
186
|
+
} catch {
|
|
187
|
+
// Output the declared signature can't decode counts as a failed call,
|
|
188
|
+
// which is what lets a mapping retry through a different ABI — the
|
|
189
|
+
// bytes32-vs-string ERC20 name is the case every token subgraph hits.
|
|
190
|
+
return JSON.stringify({ reverted: true, values: null });
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Effect inputs/outputs travel as strings, tagged so the type survives. */
|
|
197
|
+
export function encodeArg(value: unknown): string {
|
|
198
|
+
if (typeof value === "bigint") return `i:${value.toString()}`;
|
|
199
|
+
if (typeof value === "boolean") return `b:${value ? "1" : "0"}`;
|
|
200
|
+
if (Array.isArray(value)) return `a:${JSON.stringify(value.map(encodeArg))}`;
|
|
201
|
+
return `s:${String(value)}`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function decodeArg(value: string): unknown {
|
|
205
|
+
const tag = value.slice(0, 2);
|
|
206
|
+
const body = value.slice(2);
|
|
207
|
+
switch (tag) {
|
|
208
|
+
case "i:":
|
|
209
|
+
return BigInt(body);
|
|
210
|
+
case "b:":
|
|
211
|
+
return body === "1";
|
|
212
|
+
case "a:":
|
|
213
|
+
return (JSON.parse(body) as string[]).map(decodeArg);
|
|
214
|
+
default:
|
|
215
|
+
return body;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
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
|
|
6
|
+
* those two agree, so a gap shows up as green types and a runtime error — the
|
|
7
|
+
* worst feedback loop there is. This file closes it: every export the shim
|
|
8
|
+
* claims to provide has to be assignable to the real one.
|
|
9
|
+
*
|
|
10
|
+
* The declarations come from the real package (see
|
|
11
|
+
* `scripts/generate-graph-ts-types.mjs`). This file is type-checked, never run.
|
|
12
|
+
*
|
|
13
|
+
* A `@ts-expect-error` below is a known gap, and it cleans itself up: closing
|
|
14
|
+
* the gap makes the directive unused, which is itself an error, so the comment
|
|
15
|
+
* has to go with the fix. Adding a new one is a deliberate act.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type * as GraphTs from "./graph-ts-types/index.js";
|
|
19
|
+
import * as Shim from "./graph-ts.ts";
|
|
20
|
+
|
|
21
|
+
// @ts-expect-error graph-ts' BigInt extends Uint8Array and is constructed
|
|
22
|
+
// from a byte length; this one is constructed from the value it holds. No
|
|
23
|
+
// mapping calls `new BigInt(...)` — they go through `BigInt.from*` — and
|
|
24
|
+
// every other member matches. The five below are the same difference,
|
|
25
|
+
// reached through a BigInt in their own signatures.
|
|
26
|
+
const _BigInt: typeof GraphTs.BigInt = Shim.BigInt;
|
|
27
|
+
|
|
28
|
+
// @ts-expect-error constructor takes a BigInt (see above).
|
|
29
|
+
const _BigDecimal: typeof GraphTs.BigDecimal = Shim.BigDecimal;
|
|
30
|
+
|
|
31
|
+
const _Bytes: typeof GraphTs.Bytes = Shim.Bytes;
|
|
32
|
+
|
|
33
|
+
const _ByteArray: typeof GraphTs.ByteArray = Shim.ByteArray;
|
|
34
|
+
|
|
35
|
+
const _Address: typeof GraphTs.Address = Shim.Address;
|
|
36
|
+
|
|
37
|
+
const _TypedMap: typeof GraphTs.TypedMap = Shim.TypedMap;
|
|
38
|
+
|
|
39
|
+
const _TypedMapEntry: typeof GraphTs.TypedMapEntry = Shim.TypedMapEntry;
|
|
40
|
+
|
|
41
|
+
const _Entity: typeof GraphTs.Entity = Shim.Entity;
|
|
42
|
+
|
|
43
|
+
const _Value: typeof GraphTs.Value = Shim.Value;
|
|
44
|
+
|
|
45
|
+
const _ValueKind: typeof GraphTs.ValueKind = Shim.ValueKind;
|
|
46
|
+
|
|
47
|
+
const _store: typeof GraphTs.store = Shim.store;
|
|
48
|
+
|
|
49
|
+
// @ts-expect-error `ethereum.Value` carries a BigInt (see above).
|
|
50
|
+
const _ethereum: typeof GraphTs.ethereum = Shim.ethereum;
|
|
51
|
+
|
|
52
|
+
const _dataSource: typeof GraphTs.dataSource = Shim.dataSource;
|
|
53
|
+
|
|
54
|
+
const _DataSourceTemplate: typeof GraphTs.DataSourceTemplate = Shim.DataSourceTemplate;
|
|
55
|
+
|
|
56
|
+
const _DataSourceContext: typeof GraphTs.DataSourceContext = Shim.DataSourceContext;
|
|
57
|
+
|
|
58
|
+
// @ts-expect-error `log` formats a BigInt (see above).
|
|
59
|
+
const _log: typeof GraphTs.log = Shim.log;
|
|
60
|
+
|
|
61
|
+
const _crypto: typeof GraphTs.crypto = Shim.crypto;
|
|
62
|
+
|
|
63
|
+
// @ts-expect-error `json.toBigInt` returns a BigInt (see above).
|
|
64
|
+
const _json: typeof GraphTs.json = Shim.json;
|
|
65
|
+
|
|
66
|
+
// @ts-expect-error `JSONValue.toBigInt` returns a BigInt (see above).
|
|
67
|
+
const _JSONValue: typeof GraphTs.JSONValue = Shim.JSONValue;
|
|
68
|
+
|
|
69
|
+
const _JSONValueKind: typeof GraphTs.JSONValueKind = Shim.JSONValueKind;
|
|
70
|
+
|
|
71
|
+
const _ipfs: typeof GraphTs.ipfs = Shim.ipfs;
|
|
72
|
+
|
|
73
|
+
const _ens: typeof GraphTs.ens = Shim.ens;
|
|
74
|
+
|
|
75
|
+
export type {};
|
|
76
|
+
void [
|
|
77
|
+
_BigInt,
|
|
78
|
+
_BigDecimal,
|
|
79
|
+
_Bytes,
|
|
80
|
+
_ByteArray,
|
|
81
|
+
_Address,
|
|
82
|
+
_TypedMap,
|
|
83
|
+
_TypedMapEntry,
|
|
84
|
+
_Entity,
|
|
85
|
+
_Value,
|
|
86
|
+
_ValueKind,
|
|
87
|
+
_store,
|
|
88
|
+
_ethereum,
|
|
89
|
+
_dataSource,
|
|
90
|
+
_DataSourceTemplate,
|
|
91
|
+
_DataSourceContext,
|
|
92
|
+
_log,
|
|
93
|
+
_crypto,
|
|
94
|
+
_json,
|
|
95
|
+
_JSONValue,
|
|
96
|
+
_JSONValueKind,
|
|
97
|
+
_ipfs,
|
|
98
|
+
_ens,
|
|
99
|
+
];
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AssemblyScript divides two integers as integers. JavaScript doesn't.
|
|
3
|
+
*
|
|
4
|
+
* `let dayID = timestamp / 86400` is an i32 division in a mapping and a float
|
|
5
|
+
* division here, and the difference travels: Uniswap multiplies the bucket back
|
|
6
|
+
* out and writes it to an `Int` column, where `1588712972.0000002` is a type
|
|
7
|
+
* error rather than a wrong number — but an entity id built the same way would
|
|
8
|
+
* simply be wrong. Nothing in the shim can intercept `/` between two plain
|
|
9
|
+
* numbers, so the operator is rewritten as the mapping is loaded.
|
|
10
|
+
*
|
|
11
|
+
* The rewrite is textual, driven by the TypeScript parser every subgraph project
|
|
12
|
+
* already has: each `a / b` becomes `__envio_idiv(a, b)`, which truncates only
|
|
13
|
+
* when both operands really are integers and otherwise divides as before — an
|
|
14
|
+
* `f64` in AssemblyScript divides as a float too.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { createRequire } from "node:module";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
|
|
20
|
+
type Edit = { at: number; length: number; text: string };
|
|
21
|
+
|
|
22
|
+
let typescript: any | null | undefined;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The project's own TypeScript, which `graph-cli` puts there. Resolving from the
|
|
26
|
+
* project rather than from here keeps the shim free of a parser dependency.
|
|
27
|
+
*/
|
|
28
|
+
export function loadTypeScript(root: string): any | null {
|
|
29
|
+
if (typescript !== undefined) return typescript;
|
|
30
|
+
try {
|
|
31
|
+
const require = createRequire(path.join(path.resolve(root), "package.json"));
|
|
32
|
+
typescript = require("typescript");
|
|
33
|
+
} catch {
|
|
34
|
+
typescript = null;
|
|
35
|
+
}
|
|
36
|
+
return typescript;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function integerDivision(a: unknown, b: unknown): unknown {
|
|
40
|
+
const left = typeof a === "object" && a !== null ? (a as any).valueOf() : a;
|
|
41
|
+
const right = typeof b === "object" && b !== null ? (b as any).valueOf() : b;
|
|
42
|
+
|
|
43
|
+
const integral = (v: unknown) => typeof v === "bigint" || (typeof v === "number" && Number.isInteger(v));
|
|
44
|
+
if (integral(left) && integral(right)) {
|
|
45
|
+
// Only i64 / i64 stays 64-bit; anything narrower is an i32 in AssemblyScript
|
|
46
|
+
// and must come back as a number, or the bigint spreads through every
|
|
47
|
+
// arithmetic that follows.
|
|
48
|
+
if (typeof left === "bigint" && typeof right === "bigint") {
|
|
49
|
+
return left / right;
|
|
50
|
+
}
|
|
51
|
+
return Math.trunc(Number(left) / Number(right));
|
|
52
|
+
}
|
|
53
|
+
return (left as number) / (right as number);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const DIVIDE_HELPER = "__envio_idiv";
|
|
57
|
+
export const RETAG_HELPER = "__envio_retag";
|
|
58
|
+
|
|
59
|
+
/** `changetype<Foo>(x)` → `__envio_retag(Foo, x)` when Foo is a class name. */
|
|
60
|
+
export function rewriteChangetype(source: string): string {
|
|
61
|
+
if (!source.includes("changetype<")) return source;
|
|
62
|
+
return source.replace(
|
|
63
|
+
/changetype\s*<\s*([A-Za-z_][A-Za-z0-9_]*)\s*>\s*\(/g,
|
|
64
|
+
`${RETAG_HELPER}($1, `,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Returns the source unchanged when it divides nothing, or can't be parsed. */
|
|
69
|
+
export function rewriteDivision(source: string): string {
|
|
70
|
+
if (!source.includes("/")) return source;
|
|
71
|
+
const ts = typescript;
|
|
72
|
+
if (!ts) return source;
|
|
73
|
+
|
|
74
|
+
let file: any;
|
|
75
|
+
try {
|
|
76
|
+
file = ts.createSourceFile("mapping.ts", source, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
77
|
+
} catch {
|
|
78
|
+
return source;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const edits: Edit[] = [];
|
|
82
|
+
const visit = (node: any) => {
|
|
83
|
+
if (
|
|
84
|
+
node.kind === ts.SyntaxKind.BinaryExpression &&
|
|
85
|
+
node.operatorToken?.kind === ts.SyntaxKind.SlashToken
|
|
86
|
+
) {
|
|
87
|
+
const left = ts.skipTrivia(source, node.left.pos);
|
|
88
|
+
const operator = ts.skipTrivia(source, node.operatorToken.pos);
|
|
89
|
+
edits.push({ at: left, length: 0, text: `${DIVIDE_HELPER}(` });
|
|
90
|
+
edits.push({ at: operator, length: node.operatorToken.end - operator, text: "," });
|
|
91
|
+
edits.push({ at: node.right.end, length: 0, text: ")" });
|
|
92
|
+
}
|
|
93
|
+
ts.forEachChild(node, visit);
|
|
94
|
+
};
|
|
95
|
+
try {
|
|
96
|
+
ts.forEachChild(file, visit);
|
|
97
|
+
} catch {
|
|
98
|
+
return source;
|
|
99
|
+
}
|
|
100
|
+
if (edits.length === 0) return source;
|
|
101
|
+
|
|
102
|
+
// Applied back to front so an outer division's insertion points stay valid
|
|
103
|
+
// while an inner one is rewritten.
|
|
104
|
+
edits.sort((a, b) => b.at - a.at || b.length - a.length);
|
|
105
|
+
let out = source;
|
|
106
|
+
for (const edit of edits) {
|
|
107
|
+
out = out.slice(0, edit.at) + edit.text + out.slice(edit.at + edit.length);
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const ISSUES_URL = "https://github.com/enviodev/hyperindex/issues";
|
|
2
|
+
|
|
3
|
+
/** A feature we recognise and deliberately don't implement. */
|
|
4
|
+
export function unsupported(feature: string, location: string): Error {
|
|
5
|
+
return new Error(
|
|
6
|
+
`Envio Subgraph doesn't support ${feature} yet.\n` +
|
|
7
|
+
` Found in ${location}.\n` +
|
|
8
|
+
`First, make sure you're on the latest envio version — support may have landed:\n` +
|
|
9
|
+
` pnpm add -D envio@latest\n` +
|
|
10
|
+
`If you're up to date and need this feature, please open an issue (existing\n` +
|
|
11
|
+
`issues welcome a 👍 — demand drives prioritization):\n ${ISSUES_URL}`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Something we don't recognise at all: newer than us, or a typo. */
|
|
16
|
+
export function unknown(thing: string, location: string): Error {
|
|
17
|
+
return new Error(
|
|
18
|
+
`Envio Subgraph doesn't know ${thing}.\n` +
|
|
19
|
+
` Found in ${location}.\n` +
|
|
20
|
+
`This may be a feature newer than this envio version understands, or a typo.\n` +
|
|
21
|
+
`First, make sure you're on the latest envio version:\n` +
|
|
22
|
+
` pnpm add -D envio@latest\n` +
|
|
23
|
+
`If you're up to date and this is a real subgraph feature, please open an\n` +
|
|
24
|
+
`issue so we can add it: ${ISSUES_URL}`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const RPC_ENV_VAR = "ENVIO_SUBGRAPH_RPC";
|
|
29
|
+
|
|
30
|
+
/** §6b: contract calls need an endpoint HyperSync and HyperRPC don't serve. */
|
|
31
|
+
export function missingRpcMessage(callSite: string): string {
|
|
32
|
+
return (
|
|
33
|
+
`This subgraph performs contract calls (${callSite}), which need an\n` +
|
|
34
|
+
`RPC endpoint — HyperSync and HyperRPC serve logs and blocks, not eth_call.\n` +
|
|
35
|
+
`Set one in .env or the environment:\n` +
|
|
36
|
+
` ${RPC_ENV_VAR}=https://...\n` +
|
|
37
|
+
`Advanced (matches envio's rpc config; single entry or array):\n` +
|
|
38
|
+
` ${RPC_ENV_VAR}={"url":"https://...","for":"fallback","headers":{...}}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Names a trap must answer for rather than refuse. AS-compiled mappings never
|
|
44
|
+
* feature-detect, so nothing legitimate reaches these — but `console.log`,
|
|
45
|
+
* `await` and `JSON.stringify` do, and they must not explode.
|
|
46
|
+
*/
|
|
47
|
+
const PASSTHROUGH = new Set([
|
|
48
|
+
"then",
|
|
49
|
+
"toJSON",
|
|
50
|
+
"constructor",
|
|
51
|
+
"valueOf",
|
|
52
|
+
"toString",
|
|
53
|
+
"inspect",
|
|
54
|
+
"nodeType",
|
|
55
|
+
"$$typeof",
|
|
56
|
+
"@@__IMMUTABLE_ITERABLE__@@",
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
function isPassthrough(prop: PropertyKey): boolean {
|
|
60
|
+
return typeof prop === "symbol" || PASSTHROUGH.has(prop as string);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const PROTOTYPE_PASSTHROUGH = PASSTHROUGH;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Full Proxy for a graph-ts namespace object. Future graph-ts APIs are
|
|
67
|
+
* unenumerable, so only a `get` trap can catch them; these are cold paths, so
|
|
68
|
+
* the trap cost doesn't matter.
|
|
69
|
+
*/
|
|
70
|
+
export function strictNamespace<T extends object>(name: string, target: T): T {
|
|
71
|
+
return new Proxy(target, {
|
|
72
|
+
get(target, prop, receiver) {
|
|
73
|
+
if (prop in target || isPassthrough(prop)) {
|
|
74
|
+
return Reflect.get(target, prop, receiver);
|
|
75
|
+
}
|
|
76
|
+
throw unknown(`the graph-ts API ${name}.${String(prop)}`, `a mapping handler`);
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A known-but-refused field: enumerable, so a plain throwing getter is enough. */
|
|
82
|
+
export function refusedGetter(object: object, prop: string, feature: string, location: string) {
|
|
83
|
+
Object.defineProperty(object, prop, {
|
|
84
|
+
enumerable: true,
|
|
85
|
+
configurable: true,
|
|
86
|
+
get() {
|
|
87
|
+
throw unsupported(feature, location);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import '../common/eager-offset';
|
|
2
|
+
import { Bytes } from '../common/collections';
|
|
3
|
+
export declare namespace arweave {
|
|
4
|
+
/**
|
|
5
|
+
* A key-value pair for arbitrary metadata
|
|
6
|
+
*/
|
|
7
|
+
class Tag {
|
|
8
|
+
name: Bytes;
|
|
9
|
+
value: Bytes;
|
|
10
|
+
constructor(name: Bytes, value: Bytes);
|
|
11
|
+
}
|
|
12
|
+
class ProofOfAccess {
|
|
13
|
+
option: string;
|
|
14
|
+
txPath: Bytes;
|
|
15
|
+
dataPath: Bytes;
|
|
16
|
+
chunk: Bytes;
|
|
17
|
+
constructor(option: string, txPath: Bytes, dataPath: Bytes, chunk: Bytes);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* An Arweave block.
|
|
21
|
+
*/
|
|
22
|
+
class Block {
|
|
23
|
+
timestamp: bigint;
|
|
24
|
+
lastRetarget: bigint;
|
|
25
|
+
height: bigint;
|
|
26
|
+
indepHash: Bytes;
|
|
27
|
+
nonce: Bytes;
|
|
28
|
+
previousBlock: Bytes;
|
|
29
|
+
diff: Bytes;
|
|
30
|
+
hash: Bytes;
|
|
31
|
+
txRoot: Bytes;
|
|
32
|
+
txs: Bytes[];
|
|
33
|
+
walletList: Bytes;
|
|
34
|
+
rewardAddr: Bytes;
|
|
35
|
+
tags: Tag[];
|
|
36
|
+
rewardPool: Bytes;
|
|
37
|
+
weaveSize: Bytes;
|
|
38
|
+
blockSize: Bytes;
|
|
39
|
+
cumulativeDiff: Bytes;
|
|
40
|
+
hashListMerkle: Bytes;
|
|
41
|
+
poa: ProofOfAccess;
|
|
42
|
+
constructor(timestamp: bigint, lastRetarget: bigint, height: bigint, indepHash: Bytes, nonce: Bytes, previousBlock: Bytes, diff: Bytes, hash: Bytes, txRoot: Bytes, txs: Bytes[], walletList: Bytes, rewardAddr: Bytes, tags: Tag[], rewardPool: Bytes, weaveSize: Bytes, blockSize: Bytes, cumulativeDiff: Bytes, hashListMerkle: Bytes, poa: ProofOfAccess);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* An Arweave transaction
|
|
46
|
+
*/
|
|
47
|
+
class Transaction {
|
|
48
|
+
format: number;
|
|
49
|
+
id: Bytes;
|
|
50
|
+
lastTx: Bytes;
|
|
51
|
+
owner: Bytes;
|
|
52
|
+
tags: Tag[];
|
|
53
|
+
target: Bytes;
|
|
54
|
+
quantity: Bytes;
|
|
55
|
+
data: Bytes;
|
|
56
|
+
dataSize: Bytes;
|
|
57
|
+
dataRoot: Bytes;
|
|
58
|
+
signature: Bytes;
|
|
59
|
+
reward: Bytes;
|
|
60
|
+
constructor(format: number, id: Bytes, lastTx: Bytes, owner: Bytes, tags: Tag[], target: Bytes, quantity: Bytes, data: Bytes, dataSize: Bytes, dataRoot: Bytes, signature: Bytes, reward: Bytes);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* An Arweave transaction with block ptr
|
|
64
|
+
*/
|
|
65
|
+
class TransactionWithBlockPtr {
|
|
66
|
+
tx: Transaction;
|
|
67
|
+
block: Block;
|
|
68
|
+
constructor(tx: Transaction, block: Block);
|
|
69
|
+
}
|
|
70
|
+
}
|