envio 3.6.1 → 3.7.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/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 +9 -2
- package/src/Config.res.mjs +4 -4
- package/src/Core.res +5 -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 +4 -8
- package/src/EventProcessing.res.mjs +4 -4
- package/src/EventUtils.res +3 -47
- package/src/FetchState.res +124 -104
- package/src/FetchState.res.mjs +116 -100
- package/src/HandlerRegister.res +108 -2
- package/src/HandlerRegister.res.mjs +59 -10
- package/src/HandlerRegister.resi +4 -0
- 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 +724 -0
- package/src/MemoryStorage.res.mjs +601 -0
- package/src/PgStorage.res +18 -7
- package/src/PgStorage.res.mjs +14 -11
- 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/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/svm.schema.json +3 -2
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Table from "./db/Table.res.mjs";
|
|
4
|
+
import * as Utils from "./Utils.res.mjs";
|
|
5
|
+
import * as Config from "./Config.res.mjs";
|
|
6
|
+
import * as ChainId from "./ChainId.res.mjs";
|
|
7
|
+
import * as EntityId from "./EntityId.res.mjs";
|
|
8
|
+
import * as Internal from "./Internal.res.mjs";
|
|
9
|
+
import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
|
|
10
|
+
import * as EntityFilter from "./db/EntityFilter.res.mjs";
|
|
11
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
12
|
+
import * as InternalTable from "./db/InternalTable.res.mjs";
|
|
13
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
14
|
+
import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
15
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
16
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
17
|
+
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
18
|
+
|
|
19
|
+
function make() {
|
|
20
|
+
return {
|
|
21
|
+
entities: {},
|
|
22
|
+
entityConfigs: {},
|
|
23
|
+
history: {},
|
|
24
|
+
checkpoints: [],
|
|
25
|
+
chains: {},
|
|
26
|
+
cache: {},
|
|
27
|
+
effectCache: {},
|
|
28
|
+
envioInfo: undefined,
|
|
29
|
+
isInitialized: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function rowKey(scope, entityId) {
|
|
34
|
+
if (scope === "crossChain") {
|
|
35
|
+
return EntityId.toKey(entityId);
|
|
36
|
+
} else {
|
|
37
|
+
return ChainId.toString(scope) + `|` + EntityId.toKey(entityId);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let historyKey = rowKey;
|
|
42
|
+
|
|
43
|
+
function readChainId(entity, field) {
|
|
44
|
+
return entity[field.fieldName];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function copyEntity(entity) {
|
|
48
|
+
return Utils.Dict.shallowCopy(entity);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getEntityDict(state, name) {
|
|
52
|
+
let dict = state.entities[name];
|
|
53
|
+
if (dict !== undefined) {
|
|
54
|
+
return dict;
|
|
55
|
+
}
|
|
56
|
+
let dict$1 = {};
|
|
57
|
+
state.entities[name] = dict$1;
|
|
58
|
+
return dict$1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getHistory(state, name) {
|
|
62
|
+
let rows = state.history[name];
|
|
63
|
+
if (rows !== undefined) {
|
|
64
|
+
return rows;
|
|
65
|
+
}
|
|
66
|
+
let rows$1 = [];
|
|
67
|
+
state.history[name] = rows$1;
|
|
68
|
+
return rows$1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function registerEntities(state, entities) {
|
|
72
|
+
entities.forEach(entityConfig => {
|
|
73
|
+
state.entityConfigs[entityConfig.name] = entityConfig;
|
|
74
|
+
state.entityConfigs[entityConfig.table.tableName] = entityConfig;
|
|
75
|
+
getEntityDict(state, entityConfig.name);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function seedIndexingAddresses(state, chainConfigs) {
|
|
80
|
+
let dict = getEntityDict(state, Config.EnvioAddresses.name);
|
|
81
|
+
chainConfigs.forEach(chainConfig => {
|
|
82
|
+
chainConfig.contracts.forEach(contract => {
|
|
83
|
+
contract.addresses.forEach(address => {
|
|
84
|
+
let entity_id = Config.EnvioAddresses.makeId(chainConfig.id, address);
|
|
85
|
+
let entity_chain_id = chainConfig.id;
|
|
86
|
+
let entity_contract_name = contract.name;
|
|
87
|
+
let entity = {
|
|
88
|
+
id: entity_id,
|
|
89
|
+
chain_id: entity_chain_id,
|
|
90
|
+
registration_block: -1,
|
|
91
|
+
registration_log_index: -1,
|
|
92
|
+
contract_name: entity_contract_name
|
|
93
|
+
};
|
|
94
|
+
dict[entity_id] = entity;
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function toIndexingAddress(dc) {
|
|
101
|
+
return {
|
|
102
|
+
address: Config.EnvioAddresses.getAddress(dc),
|
|
103
|
+
contractName: dc.contract_name,
|
|
104
|
+
registrationBlock: dc.registration_block
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getIndexingAddressesByChain(state) {
|
|
109
|
+
let byChain = {};
|
|
110
|
+
let dcDict = state.entities[Config.EnvioAddresses.name];
|
|
111
|
+
if (dcDict !== undefined) {
|
|
112
|
+
Object.values(dcDict).forEach(entity => {
|
|
113
|
+
let chainIdStr = ChainId.toString(entity.chain_id);
|
|
114
|
+
let arr = byChain[chainIdStr];
|
|
115
|
+
let contracts;
|
|
116
|
+
if (arr !== undefined) {
|
|
117
|
+
contracts = arr;
|
|
118
|
+
} else {
|
|
119
|
+
let arr$1 = [];
|
|
120
|
+
byChain[chainIdStr] = arr$1;
|
|
121
|
+
contracts = arr$1;
|
|
122
|
+
}
|
|
123
|
+
contracts.push(toIndexingAddress(entity));
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return byChain;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function toInitialChainStates(state) {
|
|
130
|
+
let addressesByChain = getIndexingAddressesByChain(state);
|
|
131
|
+
return Object.values(state.chains).map(chain => ({
|
|
132
|
+
id: chain.id,
|
|
133
|
+
startBlock: chain.startBlock,
|
|
134
|
+
endBlock: chain.endBlock,
|
|
135
|
+
maxReorgDepth: chain.maxReorgDepth,
|
|
136
|
+
progressBlockNumber: chain.progressBlockNumber,
|
|
137
|
+
numEventsProcessed: chain.numEventsProcessed,
|
|
138
|
+
firstEventBlockNumber: chain.firstEventBlockNumber,
|
|
139
|
+
timestampCaughtUpToHeadOrEndblock: chain.timestampCaughtUpToHeadOrEndblock,
|
|
140
|
+
indexingAddresses: Stdlib_Option.getOr(addressesByChain[ChainId.toString(chain.id)], []),
|
|
141
|
+
sourceBlockNumber: chain.sourceBlockNumber
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function committedCheckpointId(state) {
|
|
146
|
+
return Stdlib_Array.reduce(state.checkpoints, InternalTable.Checkpoints.initialCheckpointId, (max, cp) => {
|
|
147
|
+
if (cp.id > max) {
|
|
148
|
+
return cp.id;
|
|
149
|
+
} else {
|
|
150
|
+
return max;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function reorgCheckpoints(state) {
|
|
156
|
+
return Stdlib_Array.filterMap(state.checkpoints, cp => {
|
|
157
|
+
let match = cp.blockHash;
|
|
158
|
+
let match$1 = state.chains[ChainId.toString(cp.chainId)];
|
|
159
|
+
if (match === undefined) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (match$1 === undefined) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
let safeBlock = match$1.sourceBlockNumber - match$1.maxReorgDepth | 0;
|
|
166
|
+
if (match$1.maxReorgDepth > 0 && match$1.progressBlockNumber > safeBlock && cp.blockNumber >= safeBlock) {
|
|
167
|
+
return {
|
|
168
|
+
id: cp.id,
|
|
169
|
+
chain_id: cp.chainId,
|
|
170
|
+
block_number: cp.blockNumber,
|
|
171
|
+
block_hash: match
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function toInitialState(state, cleanRun) {
|
|
178
|
+
return {
|
|
179
|
+
cleanRun: cleanRun,
|
|
180
|
+
cache: state.cache,
|
|
181
|
+
chains: toInitialChainStates(state),
|
|
182
|
+
checkpointId: committedCheckpointId(state),
|
|
183
|
+
reorgCheckpoints: reorgCheckpoints(state),
|
|
184
|
+
envioInfo: state.envioInfo
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function handleLoad(state, tableName, filter) {
|
|
189
|
+
let entityConfig = state.entityConfigs[tableName];
|
|
190
|
+
if (entityConfig === undefined) {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
let entityDict = Stdlib_Option.getOr(state.entities[entityConfig.name], {});
|
|
194
|
+
let matched = Object.values(entityDict).filter(entity => EntityFilter.matches(filter, entity));
|
|
195
|
+
let field = Table.getChainIdField(entityConfig.table);
|
|
196
|
+
if (field !== undefined) {
|
|
197
|
+
return matched.map(entity => {
|
|
198
|
+
let copy = Utils.Dict.shallowCopy(entity);
|
|
199
|
+
Utils.Dict.deleteInPlace(copy, field.fieldName);
|
|
200
|
+
return copy;
|
|
201
|
+
});
|
|
202
|
+
} else {
|
|
203
|
+
return matched;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function backfillHistory(state, entityConfig, scope, entityId, rows) {
|
|
208
|
+
let key = rowKey(scope, entityId);
|
|
209
|
+
if (rows.some(row => rowKey(row.scope, row.entityId) === key)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
let entity = Stdlib_Option.flatMap(state.entities[entityConfig.name], dict => dict[rowKey(scope, entityId)]);
|
|
213
|
+
if (entity !== undefined) {
|
|
214
|
+
rows.push({
|
|
215
|
+
entityId: entityId,
|
|
216
|
+
scope: scope,
|
|
217
|
+
checkpointId: InternalTable.Checkpoints.initialCheckpointId,
|
|
218
|
+
action: "SET",
|
|
219
|
+
entity: entity
|
|
220
|
+
});
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function applyRollback(state, targetCheckpointId) {
|
|
226
|
+
state.checkpoints = state.checkpoints.filter(cp => cp.id <= targetCheckpointId);
|
|
227
|
+
Object.entries(state.history).forEach(param => {
|
|
228
|
+
state.history[param[0]] = param[1].filter(row => row.checkpointId <= targetCheckpointId);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function writeBatch(state, batch, rollback, isInReorgThreshold, config, updatedEntities, updatedEffectsCache, chainMetaData) {
|
|
233
|
+
let shouldSaveHistory = Config.shouldSaveHistory(config, isInReorgThreshold);
|
|
234
|
+
if (rollback !== undefined) {
|
|
235
|
+
applyRollback(state, rollback.targetCheckpointId);
|
|
236
|
+
}
|
|
237
|
+
updatedEntities.forEach(param => {
|
|
238
|
+
let scope = param.scope;
|
|
239
|
+
let entityConfig = param.entityConfig;
|
|
240
|
+
let entityDict = getEntityDict(state, entityConfig.name);
|
|
241
|
+
let historyRows = getHistory(state, entityConfig.name);
|
|
242
|
+
let chainIdField = Table.getChainIdField(entityConfig.table);
|
|
243
|
+
param.changes.forEach(change => {
|
|
244
|
+
let entityId = change.entityId;
|
|
245
|
+
if (shouldSaveHistory) {
|
|
246
|
+
backfillHistory(state, entityConfig, scope, entityId, historyRows);
|
|
247
|
+
}
|
|
248
|
+
if (change.type === "SET") {
|
|
249
|
+
let entity = change.entity;
|
|
250
|
+
let match = Internal.chainScopeChainId(scope);
|
|
251
|
+
let storedEntity = chainIdField !== undefined && match !== undefined ? Internal.stampChainId(entity, chainIdField.fieldName, Primitive_option.valFromOption(match)) : entity;
|
|
252
|
+
entityDict[rowKey(scope, entityId)] = storedEntity;
|
|
253
|
+
if (shouldSaveHistory) {
|
|
254
|
+
historyRows.push({
|
|
255
|
+
entityId: entityId,
|
|
256
|
+
scope: scope,
|
|
257
|
+
checkpointId: change.checkpointId,
|
|
258
|
+
action: "SET",
|
|
259
|
+
entity: storedEntity
|
|
260
|
+
});
|
|
261
|
+
return;
|
|
262
|
+
} else {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
Utils.Dict.deleteInPlace(entityDict, rowKey(scope, entityId));
|
|
267
|
+
if (shouldSaveHistory) {
|
|
268
|
+
historyRows.push({
|
|
269
|
+
entityId: entityId,
|
|
270
|
+
scope: scope,
|
|
271
|
+
checkpointId: change.checkpointId,
|
|
272
|
+
action: "DELETE",
|
|
273
|
+
entity: undefined
|
|
274
|
+
});
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
Object.values(batch.progressedChainsById).forEach(chainAfterBatch => {
|
|
280
|
+
let key = ChainId.toString(chainAfterBatch.fetchState.chainId);
|
|
281
|
+
let chain = state.chains[key];
|
|
282
|
+
if (chain !== undefined) {
|
|
283
|
+
chain.progressBlockNumber = chainAfterBatch.progressBlockNumber;
|
|
284
|
+
chain.sourceBlockNumber = chainAfterBatch.sourceBlockNumber;
|
|
285
|
+
chain.numEventsProcessed = chainAfterBatch.totalEventsProcessed;
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
if (chainMetaData !== undefined) {
|
|
290
|
+
Object.entries(chainMetaData).forEach(param => {
|
|
291
|
+
let meta = param[1];
|
|
292
|
+
let chain = state.chains[param[0]];
|
|
293
|
+
if (chain !== undefined) {
|
|
294
|
+
chain.firstEventBlockNumber = Primitive_option.fromNull(meta.first_event_block);
|
|
295
|
+
chain.latestFetchedBlockNumber = meta.buffer_block;
|
|
296
|
+
chain.timestampCaughtUpToHeadOrEndblock = Primitive_option.fromNull(meta.ready_at);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (shouldSaveHistory) {
|
|
302
|
+
for (let i = 0, i_finish = batch.checkpointIds.length; i < i_finish; ++i) {
|
|
303
|
+
state.checkpoints.push({
|
|
304
|
+
id: batch.checkpointIds[i],
|
|
305
|
+
chainId: batch.checkpointChainIds[i],
|
|
306
|
+
blockNumber: batch.checkpointBlockNumbers[i],
|
|
307
|
+
blockHash: Primitive_option.fromNull(batch.checkpointBlockHashes[i]),
|
|
308
|
+
eventsProcessed: batch.checkpointEventsProcessed[i]
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
updatedEffectsCache.forEach(param => {
|
|
313
|
+
let table = param.table;
|
|
314
|
+
let dict = state.effectCache[table.tableName];
|
|
315
|
+
let cacheDict;
|
|
316
|
+
if (dict !== undefined) {
|
|
317
|
+
cacheDict = dict;
|
|
318
|
+
} else {
|
|
319
|
+
let dict$1 = {};
|
|
320
|
+
state.effectCache[table.tableName] = dict$1;
|
|
321
|
+
cacheDict = dict$1;
|
|
322
|
+
}
|
|
323
|
+
param.items.forEach(item => {
|
|
324
|
+
cacheDict[item.id] = item;
|
|
325
|
+
});
|
|
326
|
+
let record = state.cache[table.tableName];
|
|
327
|
+
if (record !== undefined) {
|
|
328
|
+
record.count = Object.keys(cacheDict).length;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function getRollbackData(state, entityConfig, rollbackTargetCheckpointId) {
|
|
335
|
+
let rows = Stdlib_Option.getOr(state.history[entityConfig.name], []);
|
|
336
|
+
let changedKeys = {};
|
|
337
|
+
rows.forEach(row => {
|
|
338
|
+
if (row.checkpointId > rollbackTargetCheckpointId) {
|
|
339
|
+
changedKeys[rowKey(row.scope, row.entityId)] = row;
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
let removals = [];
|
|
344
|
+
let restored = [];
|
|
345
|
+
Object.entries(changedKeys).forEach(param => {
|
|
346
|
+
let changedRow = param[1];
|
|
347
|
+
let key = param[0];
|
|
348
|
+
let preTarget = Stdlib_Array.reduce(rows, undefined, (latest, row) => {
|
|
349
|
+
if (rowKey(row.scope, row.entityId) === key && row.checkpointId <= rollbackTargetCheckpointId && !(latest !== undefined && latest.checkpointId >= row.checkpointId)) {
|
|
350
|
+
return row;
|
|
351
|
+
} else {
|
|
352
|
+
return latest;
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
if (preTarget !== undefined) {
|
|
356
|
+
if (preTarget.action === "SET") {
|
|
357
|
+
let entity = preTarget.entity;
|
|
358
|
+
if (entity !== undefined) {
|
|
359
|
+
restored.push(entity);
|
|
360
|
+
return;
|
|
361
|
+
} else {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
removals.push({
|
|
366
|
+
entityId: preTarget.entityId,
|
|
367
|
+
scope: preTarget.scope
|
|
368
|
+
});
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
removals.push({
|
|
372
|
+
entityId: changedRow.entityId,
|
|
373
|
+
scope: changedRow.scope
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
return [
|
|
377
|
+
removals,
|
|
378
|
+
restored
|
|
379
|
+
];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function getRollbackProgressDiff(state, rollbackTargetCheckpointId) {
|
|
383
|
+
let byChain = {};
|
|
384
|
+
state.checkpoints.forEach(cp => {
|
|
385
|
+
if (cp.id <= rollbackTargetCheckpointId) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
let key = ChainId.toString(cp.chainId);
|
|
389
|
+
let acc = byChain[key];
|
|
390
|
+
if (acc !== undefined) {
|
|
391
|
+
acc.eventsProcessed = acc.eventsProcessed + cp.eventsProcessed | 0;
|
|
392
|
+
acc.newProgressBlockNumber = Math.min(acc.newProgressBlockNumber, cp.blockNumber - 1 | 0);
|
|
393
|
+
} else {
|
|
394
|
+
byChain[key] = {
|
|
395
|
+
chainId: cp.chainId,
|
|
396
|
+
eventsProcessed: cp.eventsProcessed,
|
|
397
|
+
newProgressBlockNumber: cp.blockNumber - 1 | 0
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
return Object.values(byChain).map(acc => ({
|
|
402
|
+
chain_id: acc.chainId,
|
|
403
|
+
events_processed_diff: acc.eventsProcessed.toString(),
|
|
404
|
+
new_progress_block_number: acc.newProgressBlockNumber
|
|
405
|
+
}));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function toStorage(state, config) {
|
|
409
|
+
return {
|
|
410
|
+
name: "memory",
|
|
411
|
+
isInitialized: async () => state.isInitialized,
|
|
412
|
+
initialize: async (chainConfigsOpt, entitiesOpt, $staropt$star, envioInfo) => {
|
|
413
|
+
let chainConfigs = chainConfigsOpt !== undefined ? chainConfigsOpt : [];
|
|
414
|
+
let entities = entitiesOpt !== undefined ? entitiesOpt : [];
|
|
415
|
+
$staropt$star !== undefined;
|
|
416
|
+
registerEntities(state, entities);
|
|
417
|
+
seedIndexingAddresses(state, chainConfigs);
|
|
418
|
+
chainConfigs.forEach(chainConfig => {
|
|
419
|
+
state.chains[ChainId.toString(chainConfig.id)] = {
|
|
420
|
+
id: chainConfig.id,
|
|
421
|
+
startBlock: chainConfig.startBlock,
|
|
422
|
+
endBlock: chainConfig.endBlock,
|
|
423
|
+
maxReorgDepth: chainConfig.maxReorgDepth,
|
|
424
|
+
progressBlockNumber: -1,
|
|
425
|
+
sourceBlockNumber: 0,
|
|
426
|
+
numEventsProcessed: 0,
|
|
427
|
+
firstEventBlockNumber: undefined,
|
|
428
|
+
latestFetchedBlockNumber: 0,
|
|
429
|
+
timestampCaughtUpToHeadOrEndblock: undefined
|
|
430
|
+
};
|
|
431
|
+
});
|
|
432
|
+
state.envioInfo = envioInfo;
|
|
433
|
+
state.isInitialized = true;
|
|
434
|
+
return toInitialState(state, true);
|
|
435
|
+
},
|
|
436
|
+
resumeInitialState: async () => toInitialState(state, false),
|
|
437
|
+
loadOrThrow: async (filter, table) => handleLoad(state, table.tableName, filter),
|
|
438
|
+
ensureQueryIndexes: async (param, param$1) => {},
|
|
439
|
+
ensureSchemaIndexes: async param => {},
|
|
440
|
+
finalizeBackfill: async (param, chainIds, readyAt) => {
|
|
441
|
+
chainIds.forEach(chainId => {
|
|
442
|
+
let chain = state.chains[ChainId.toString(chainId)];
|
|
443
|
+
if (chain !== undefined) {
|
|
444
|
+
chain.timestampCaughtUpToHeadOrEndblock = readyAt;
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
},
|
|
449
|
+
dumpEffectCache: async () => {},
|
|
450
|
+
reset: async () => {
|
|
451
|
+
let clear = dict => {
|
|
452
|
+
Object.keys(dict).forEach(key => Utils.Dict.deleteInPlace(dict, key));
|
|
453
|
+
};
|
|
454
|
+
clear(state.entities);
|
|
455
|
+
clear(state.history);
|
|
456
|
+
clear(state.chains);
|
|
457
|
+
clear(state.effectCache);
|
|
458
|
+
clear(state.cache);
|
|
459
|
+
state.checkpoints = [];
|
|
460
|
+
state.envioInfo = undefined;
|
|
461
|
+
state.isInitialized = false;
|
|
462
|
+
},
|
|
463
|
+
setChainMeta: async chainsData => {
|
|
464
|
+
Object.entries(chainsData).forEach(param => {
|
|
465
|
+
let meta = param[1];
|
|
466
|
+
let chain = state.chains[param[0]];
|
|
467
|
+
if (chain !== undefined) {
|
|
468
|
+
chain.firstEventBlockNumber = Primitive_option.fromNull(meta.first_event_block);
|
|
469
|
+
chain.latestFetchedBlockNumber = meta.buffer_block;
|
|
470
|
+
chain.timestampCaughtUpToHeadOrEndblock = Primitive_option.fromNull(meta.ready_at);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
return undefined;
|
|
475
|
+
},
|
|
476
|
+
pruneStaleCheckpoints: async safeCheckpointId => {
|
|
477
|
+
state.checkpoints = state.checkpoints.filter(cp => cp.id >= safeCheckpointId);
|
|
478
|
+
},
|
|
479
|
+
pruneStaleEntityHistory: async (entityName, param, param$1, safeCheckpointId) => {
|
|
480
|
+
let rows = state.history[entityName];
|
|
481
|
+
if (rows === undefined) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
let newestBelow = {};
|
|
485
|
+
rows.forEach(row => {
|
|
486
|
+
if (row.checkpointId >= safeCheckpointId) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
let key = rowKey(row.scope, row.entityId);
|
|
490
|
+
let current = newestBelow[key];
|
|
491
|
+
if (current !== undefined && current.checkpointId >= row.checkpointId) {
|
|
492
|
+
return;
|
|
493
|
+
} else {
|
|
494
|
+
newestBelow[key] = row;
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
state.history[entityName] = rows.filter(row => {
|
|
499
|
+
let key = rowKey(row.scope, row.entityId);
|
|
500
|
+
if (row.checkpointId >= safeCheckpointId) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
let kept = newestBelow[key];
|
|
504
|
+
if (kept !== undefined) {
|
|
505
|
+
return kept === row;
|
|
506
|
+
} else {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
},
|
|
511
|
+
getRollbackTargetCheckpoint: async (reorgChainId, lastKnownValidBlockNumber) => Stdlib_Array.reduce(state.checkpoints, undefined, (target, cp) => {
|
|
512
|
+
if (Primitive_object.equal(cp.chainId, reorgChainId) && cp.blockNumber <= lastKnownValidBlockNumber && !(target !== undefined && target >= cp.id)) {
|
|
513
|
+
return cp.id;
|
|
514
|
+
} else {
|
|
515
|
+
return target;
|
|
516
|
+
}
|
|
517
|
+
}),
|
|
518
|
+
getRollbackProgressDiff: async rollbackTargetCheckpointId => getRollbackProgressDiff(state, rollbackTargetCheckpointId),
|
|
519
|
+
getRollbackData: async (entityConfig, rollbackTargetCheckpointId) => getRollbackData(state, entityConfig, rollbackTargetCheckpointId),
|
|
520
|
+
writeBatch: async (batch, rollback, isInReorgThreshold, param, param$1, updatedEffectsCache, updatedEntities, chainMetaData, param$2) => writeBatch(state, batch, rollback, isInReorgThreshold, config, updatedEntities, updatedEffectsCache, chainMetaData),
|
|
521
|
+
close: async () => {}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function currentRows(state, entityConfig) {
|
|
526
|
+
return Object.values(Stdlib_Option.getOr(state.entities[entityConfig.name], {}));
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function historyChanges(state, entityConfig) {
|
|
530
|
+
return Stdlib_Option.getOr(state.history[entityConfig.name], []).map(row => {
|
|
531
|
+
let match = row.action;
|
|
532
|
+
if (match === "SET") {
|
|
533
|
+
return {
|
|
534
|
+
type: "SET",
|
|
535
|
+
entityId: row.entityId,
|
|
536
|
+
entity: Stdlib_Option.getOr(row.entity, {}),
|
|
537
|
+
checkpointId: row.checkpointId
|
|
538
|
+
};
|
|
539
|
+
} else {
|
|
540
|
+
return {
|
|
541
|
+
type: "DELETE",
|
|
542
|
+
entityId: row.entityId,
|
|
543
|
+
checkpointId: row.checkpointId
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
}).toSorted((a, b) => {
|
|
547
|
+
let order = Primitive_string.compare(EntityId.toKey(a.entityId), EntityId.toKey(b.entityId));
|
|
548
|
+
if (order !== 0) {
|
|
549
|
+
return order;
|
|
550
|
+
} else {
|
|
551
|
+
return Primitive_float.compare(Number(a.checkpointId), Number(b.checkpointId));
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function checkpointRows(state) {
|
|
557
|
+
return state.checkpoints.map(cp => ({
|
|
558
|
+
id: cp.id,
|
|
559
|
+
chain_id: cp.chainId,
|
|
560
|
+
block_number: cp.blockNumber,
|
|
561
|
+
block_hash: Stdlib_Null.fromOption(cp.blockHash),
|
|
562
|
+
events_processed: cp.eventsProcessed
|
|
563
|
+
}));
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function effectCacheRows(state, tableName) {
|
|
567
|
+
return Object.values(Stdlib_Option.getOr(state.effectCache[tableName], {})).map(item => ({
|
|
568
|
+
id: item.id,
|
|
569
|
+
output: item.output
|
|
570
|
+
}));
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export {
|
|
574
|
+
make,
|
|
575
|
+
rowKey,
|
|
576
|
+
historyKey,
|
|
577
|
+
readChainId,
|
|
578
|
+
copyEntity,
|
|
579
|
+
getEntityDict,
|
|
580
|
+
getHistory,
|
|
581
|
+
registerEntities,
|
|
582
|
+
seedIndexingAddresses,
|
|
583
|
+
toIndexingAddress,
|
|
584
|
+
getIndexingAddressesByChain,
|
|
585
|
+
toInitialChainStates,
|
|
586
|
+
committedCheckpointId,
|
|
587
|
+
reorgCheckpoints,
|
|
588
|
+
toInitialState,
|
|
589
|
+
handleLoad,
|
|
590
|
+
backfillHistory,
|
|
591
|
+
applyRollback,
|
|
592
|
+
writeBatch,
|
|
593
|
+
getRollbackData,
|
|
594
|
+
getRollbackProgressDiff,
|
|
595
|
+
toStorage,
|
|
596
|
+
currentRows,
|
|
597
|
+
historyChanges,
|
|
598
|
+
checkpointRows,
|
|
599
|
+
effectCacheRows,
|
|
600
|
+
}
|
|
601
|
+
/* Table Not a pure module */
|
package/src/PgStorage.res
CHANGED
|
@@ -630,14 +630,19 @@ let classifyWriteError = (~specificError: ref<option<exn>>, ~table: Table.table,
|
|
|
630
630
|
}
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
-
//
|
|
634
|
-
|
|
633
|
+
// Batch set queries, cached per table. The query text bakes in the schema and
|
|
634
|
+
// the chain-id mode, so the cache belongs to the storage instance those came
|
|
635
|
+
// from — `make` creates one and threads it down. A process-wide cache would
|
|
636
|
+
// hand a second storage the first one's schema.
|
|
637
|
+
let makeSetQueryCache = () => Utils.WeakMap.make()
|
|
638
|
+
|
|
635
639
|
let setOrThrow = async (
|
|
636
640
|
sql,
|
|
637
641
|
~items,
|
|
638
642
|
~table: Table.table,
|
|
639
643
|
~itemSchema,
|
|
640
644
|
~pgSchema,
|
|
645
|
+
~setQueryCache,
|
|
641
646
|
~chainIdMode: ChainId.mode=Int32,
|
|
642
647
|
) => {
|
|
643
648
|
if items->Array.length === 0 {
|
|
@@ -953,6 +958,7 @@ let rec writeBatch = async (
|
|
|
953
958
|
~config: Config.t,
|
|
954
959
|
~allEntities: array<Internal.entityConfig>,
|
|
955
960
|
~setEffectCacheOrThrow,
|
|
961
|
+
~setQueryCache,
|
|
956
962
|
~updatedEffectsCache,
|
|
957
963
|
~updatedEntities: array<Persistence.updatedEntity>,
|
|
958
964
|
~sinkPromise: option<promise<option<exn>>>,
|
|
@@ -973,11 +979,8 @@ let rec writeBatch = async (
|
|
|
973
979
|
let rows = batch.items->Array.filterMap(item =>
|
|
974
980
|
switch item {
|
|
975
981
|
| Internal.Event(_) =>
|
|
976
|
-
let
|
|
977
|
-
|
|
978
|
-
->ChainId.toString}-${item
|
|
979
|
-
->Internal.getItemBlockNumber
|
|
980
|
-
->Int.toString}-${item->Internal.getItemLogIndex->Int.toString}`
|
|
982
|
+
let eventItem = item->Internal.castUnsafeEventItem
|
|
983
|
+
let coordinate = `${eventItem.chainId->ChainId.toString}-${eventItem.blockNumber->Int.toString}-${eventItem.logIndex->Int.toString}`
|
|
981
984
|
if seenLogCoordinates->Utils.Set.has(coordinate) {
|
|
982
985
|
None
|
|
983
986
|
} else {
|
|
@@ -1006,6 +1009,7 @@ let rec writeBatch = async (
|
|
|
1006
1009
|
~itemSchema=InternalTable.RawEvents.schema,
|
|
1007
1010
|
~pgSchema,
|
|
1008
1011
|
~chainIdMode,
|
|
1012
|
+
~setQueryCache,
|
|
1009
1013
|
)
|
|
1010
1014
|
}, ~items=rawEvents)
|
|
1011
1015
|
} catch {
|
|
@@ -1161,6 +1165,7 @@ let rec writeBatch = async (
|
|
|
1161
1165
|
~table=entityHistory.table,
|
|
1162
1166
|
~pgSchema,
|
|
1163
1167
|
~chainIdMode,
|
|
1168
|
+
~setQueryCache,
|
|
1164
1169
|
),
|
|
1165
1170
|
)
|
|
1166
1171
|
->ignore
|
|
@@ -1178,6 +1183,7 @@ let rec writeBatch = async (
|
|
|
1178
1183
|
~itemSchema=entityConfig->getRowSchema,
|
|
1179
1184
|
~pgSchema,
|
|
1180
1185
|
~chainIdMode,
|
|
1186
|
+
~setQueryCache,
|
|
1181
1187
|
),
|
|
1182
1188
|
)
|
|
1183
1189
|
}
|
|
@@ -1340,6 +1346,7 @@ let rec writeBatch = async (
|
|
|
1340
1346
|
~escapeTables,
|
|
1341
1347
|
~batch,
|
|
1342
1348
|
~pgSchema,
|
|
1349
|
+
~setQueryCache,
|
|
1343
1350
|
~rollback,
|
|
1344
1351
|
~isInReorgThreshold,
|
|
1345
1352
|
~config,
|
|
@@ -1483,6 +1490,7 @@ let make = (
|
|
|
1483
1490
|
let storageName = "postgres"
|
|
1484
1491
|
|
|
1485
1492
|
let indexManager = IndexManager.make()
|
|
1493
|
+
let setQueryCache = makeSetQueryCache()
|
|
1486
1494
|
|
|
1487
1495
|
let loadCatalogRows = (sql, ~indexName=?) =>
|
|
1488
1496
|
sql
|
|
@@ -2064,6 +2072,8 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
|
|
|
2064
2072
|
~table,
|
|
2065
2073
|
~itemSchema=itemSchema->S.toUnknown,
|
|
2066
2074
|
~pgSchema,
|
|
2075
|
+
~setQueryCache,
|
|
2076
|
+
~chainIdMode,
|
|
2067
2077
|
)
|
|
2068
2078
|
}
|
|
2069
2079
|
|
|
@@ -2347,6 +2357,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
|
|
|
2347
2357
|
sql,
|
|
2348
2358
|
~batch,
|
|
2349
2359
|
~pgSchema,
|
|
2360
|
+
~setQueryCache,
|
|
2350
2361
|
~rollback,
|
|
2351
2362
|
~isInReorgThreshold,
|
|
2352
2363
|
~config,
|