envio 2.27.6 → 2.28.0-alpha.2
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/package.json +5 -5
- package/rescript.json +3 -0
- package/src/FetchState.res +21 -14
- package/src/FetchState.res.js +14 -5
- package/src/Hasura.res +31 -12
- package/src/Hasura.res.js +31 -13
- package/src/Internal.res +7 -4
- package/src/InternalConfig.res +20 -0
- package/src/InternalConfig.res.js +2 -0
- package/src/Js.shim.ts +11 -0
- package/src/LoadManager.res +12 -6
- package/src/LoadManager.res.js +13 -6
- package/src/Persistence.res +25 -33
- package/src/Persistence.res.js +18 -20
- package/src/PgStorage.res +162 -102
- package/src/PgStorage.res.js +146 -103
- package/src/Prometheus.res +2 -2
- package/src/Prometheus.res.js +2 -3
- package/src/bindings/Pino.res +1 -1
- package/src/bindings/Pino.res.js +2 -1
- package/src/bindings/Postgres.res +1 -1
- package/src/db/EntityHistory.res +18 -17
- package/src/db/EntityHistory.res.js +28 -26
- package/src/db/InternalTable.gen.ts +43 -0
- package/src/db/InternalTable.res +430 -0
- package/src/db/InternalTable.res.js +315 -0
- package/src/vendored/Rest.res +11 -2
- package/src/vendored/Rest.res.js +44 -35
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
open Table
|
|
2
|
+
|
|
3
|
+
//shorthand for punning
|
|
4
|
+
let isPrimaryKey = true
|
|
5
|
+
let isNullable = true
|
|
6
|
+
let isIndex = true
|
|
7
|
+
|
|
8
|
+
module EventSyncState = {
|
|
9
|
+
//Used unsafely in DbFunctions.res so just enforcing the naming here
|
|
10
|
+
let blockTimestampFieldName = "block_timestamp"
|
|
11
|
+
let blockNumberFieldName = "block_number"
|
|
12
|
+
let logIndexFieldName = "log_index"
|
|
13
|
+
let isPreRegisteringDynamicContractsFieldName = "is_pre_registering_dynamic_contracts"
|
|
14
|
+
|
|
15
|
+
// @genType Used for Test DB
|
|
16
|
+
@genType
|
|
17
|
+
type t = {
|
|
18
|
+
@as("chain_id") chainId: int,
|
|
19
|
+
@as("block_number") blockNumber: int,
|
|
20
|
+
@as("log_index") logIndex: int,
|
|
21
|
+
@as("block_timestamp") blockTimestamp: int,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let table = mkTable(
|
|
25
|
+
"event_sync_state",
|
|
26
|
+
~fields=[
|
|
27
|
+
mkField("chain_id", Integer, ~fieldSchema=S.int, ~isPrimaryKey),
|
|
28
|
+
mkField(blockNumberFieldName, Integer, ~fieldSchema=S.int),
|
|
29
|
+
mkField(logIndexFieldName, Integer, ~fieldSchema=S.int),
|
|
30
|
+
mkField(blockTimestampFieldName, Integer, ~fieldSchema=S.int),
|
|
31
|
+
// Keep it in case Hosted Service relies on it to prevent a breaking changes
|
|
32
|
+
mkField(
|
|
33
|
+
isPreRegisteringDynamicContractsFieldName,
|
|
34
|
+
Boolean,
|
|
35
|
+
~default="false",
|
|
36
|
+
~fieldSchema=S.bool,
|
|
37
|
+
),
|
|
38
|
+
],
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
//We need to update values here not delet the rows, since restarting without a row
|
|
42
|
+
//has a different behaviour to restarting with an initialised row with zero values
|
|
43
|
+
let resetCurrentCurrentSyncStateQuery = (~pgSchema) =>
|
|
44
|
+
`UPDATE "${pgSchema}"."${table.tableName}"
|
|
45
|
+
SET ${blockNumberFieldName} = 0,
|
|
46
|
+
${logIndexFieldName} = 0,
|
|
47
|
+
${blockTimestampFieldName} = 0,
|
|
48
|
+
${isPreRegisteringDynamicContractsFieldName} = false;`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module Chains = {
|
|
52
|
+
type field = [
|
|
53
|
+
| #id
|
|
54
|
+
| #start_block
|
|
55
|
+
| #end_block
|
|
56
|
+
| #source_block
|
|
57
|
+
| #first_event_block
|
|
58
|
+
| #buffer_block
|
|
59
|
+
| #ready_at
|
|
60
|
+
| #events_processed
|
|
61
|
+
| #_is_hyper_sync
|
|
62
|
+
| #_latest_processed_block
|
|
63
|
+
| #_num_batches_fetched
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
let fields: array<field> = [
|
|
67
|
+
#id,
|
|
68
|
+
#start_block,
|
|
69
|
+
#end_block,
|
|
70
|
+
#source_block,
|
|
71
|
+
#first_event_block,
|
|
72
|
+
#buffer_block,
|
|
73
|
+
#ready_at,
|
|
74
|
+
#events_processed,
|
|
75
|
+
#_is_hyper_sync,
|
|
76
|
+
#_latest_processed_block,
|
|
77
|
+
#_num_batches_fetched,
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
type t = {
|
|
81
|
+
@as("id") id: int,
|
|
82
|
+
@as("start_block") startBlock: int,
|
|
83
|
+
@as("end_block") endBlock: Js.null<int>,
|
|
84
|
+
@as("source_block") blockHeight: int,
|
|
85
|
+
@as("first_event_block") firstEventBlockNumber: Js.null<int>,
|
|
86
|
+
@as("buffer_block") latestFetchedBlockNumber: int,
|
|
87
|
+
@as("ready_at")
|
|
88
|
+
timestampCaughtUpToHeadOrEndblock: Js.null<Js.Date.t>,
|
|
89
|
+
@as("events_processed") numEventsProcessed: int,
|
|
90
|
+
@as("_latest_processed_block") latestProcessedBlock: Js.null<int>,
|
|
91
|
+
@as("_is_hyper_sync") isHyperSync: bool,
|
|
92
|
+
@as("_num_batches_fetched") numBatchesFetched: int,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let table = mkTable(
|
|
96
|
+
"envio_chains",
|
|
97
|
+
~fields=[
|
|
98
|
+
mkField((#id: field :> string), Integer, ~fieldSchema=S.int, ~isPrimaryKey),
|
|
99
|
+
// Values populated from config
|
|
100
|
+
mkField((#start_block: field :> string), Integer, ~fieldSchema=S.int),
|
|
101
|
+
mkField((#end_block: field :> string), Integer, ~fieldSchema=S.null(S.int), ~isNullable),
|
|
102
|
+
// Block number of the latest block that was fetched from the source
|
|
103
|
+
mkField((#buffer_block: field :> string), Integer, ~fieldSchema=S.int),
|
|
104
|
+
// Block number of the currently active source
|
|
105
|
+
mkField((#source_block: field :> string), Integer, ~fieldSchema=S.int),
|
|
106
|
+
// Block number of the first event that was processed for this chain
|
|
107
|
+
mkField(
|
|
108
|
+
(#first_event_block: field :> string),
|
|
109
|
+
Integer,
|
|
110
|
+
~fieldSchema=S.null(S.int),
|
|
111
|
+
~isNullable,
|
|
112
|
+
),
|
|
113
|
+
// Used to show how much time historical sync has taken, so we need a timezone here (TUI and Hosted Service)
|
|
114
|
+
// null during historical sync, set to current time when sync is complete
|
|
115
|
+
mkField(
|
|
116
|
+
(#ready_at: field :> string),
|
|
117
|
+
TimestampWithNullTimezone,
|
|
118
|
+
~fieldSchema=S.null(Utils.Schema.dbDate),
|
|
119
|
+
~isNullable,
|
|
120
|
+
),
|
|
121
|
+
mkField((#events_processed: field :> string), Integer, ~fieldSchema=S.int), // TODO: In the future it should reference a table with sources
|
|
122
|
+
mkField((#_is_hyper_sync: field :> string), Boolean, ~fieldSchema=S.bool),
|
|
123
|
+
// TODO: Make the data more public facing
|
|
124
|
+
mkField(
|
|
125
|
+
(#_latest_processed_block: field :> string),
|
|
126
|
+
Integer,
|
|
127
|
+
~fieldSchema=S.null(S.int),
|
|
128
|
+
~isNullable,
|
|
129
|
+
),
|
|
130
|
+
mkField((#_num_batches_fetched: field :> string), Integer, ~fieldSchema=S.int),
|
|
131
|
+
],
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
let initialFromConfig = (chainConfig: InternalConfig.chain) => {
|
|
135
|
+
{
|
|
136
|
+
id: chainConfig.id,
|
|
137
|
+
startBlock: chainConfig.startBlock,
|
|
138
|
+
endBlock: chainConfig.endBlock->Js.Null.fromOption,
|
|
139
|
+
blockHeight: 0,
|
|
140
|
+
firstEventBlockNumber: Js.Null.empty,
|
|
141
|
+
latestFetchedBlockNumber: -1,
|
|
142
|
+
timestampCaughtUpToHeadOrEndblock: Js.Null.empty,
|
|
143
|
+
latestProcessedBlock: Js.Null.empty,
|
|
144
|
+
isHyperSync: false,
|
|
145
|
+
numEventsProcessed: 0,
|
|
146
|
+
numBatchesFetched: 0,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let makeInitialValuesQuery = (~pgSchema, ~chainConfigs: array<InternalConfig.chain>) => {
|
|
151
|
+
if chainConfigs->Array.length === 0 {
|
|
152
|
+
None
|
|
153
|
+
} else {
|
|
154
|
+
// Create column names list
|
|
155
|
+
let columnNames = fields->Belt.Array.map(field => `"${(field :> string)}"`)
|
|
156
|
+
|
|
157
|
+
// Create VALUES rows for each chain config
|
|
158
|
+
let valuesRows = chainConfigs->Belt.Array.map(chainConfig => {
|
|
159
|
+
let initialValues = initialFromConfig(chainConfig)
|
|
160
|
+
let values = fields->Belt.Array.map((field: field) => {
|
|
161
|
+
let value =
|
|
162
|
+
initialValues->(Utils.magic: t => dict<unknown>)->Js.Dict.get((field :> string))
|
|
163
|
+
switch Js.typeof(value) {
|
|
164
|
+
| "object" => "NULL"
|
|
165
|
+
| "number" => value->Utils.magic->Belt.Int.toString
|
|
166
|
+
| "boolean" => value->Utils.magic ? "true" : "false"
|
|
167
|
+
| _ => Js.Exn.raiseError("Invalid envio_chains value type")
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
`(${values->Js.Array2.joinWith(", ")})`
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
Some(
|
|
175
|
+
`INSERT INTO "${pgSchema}"."${table.tableName}" (${columnNames->Js.Array2.joinWith(", ")})
|
|
176
|
+
VALUES ${valuesRows->Js.Array2.joinWith(",\n ")};`,
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Fields that should be updated on conflict (excluding static config fields)
|
|
182
|
+
let updateFields: array<field> = [
|
|
183
|
+
#source_block,
|
|
184
|
+
#first_event_block,
|
|
185
|
+
#buffer_block,
|
|
186
|
+
#ready_at,
|
|
187
|
+
#events_processed,
|
|
188
|
+
#_is_hyper_sync,
|
|
189
|
+
#_latest_processed_block,
|
|
190
|
+
#_num_batches_fetched,
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
let makeSingleUpdateQuery = (~pgSchema) => {
|
|
194
|
+
// Generate SET clauses with parameter placeholders
|
|
195
|
+
let setClauses = Belt.Array.mapWithIndex(updateFields, (index, field) => {
|
|
196
|
+
let fieldName = (field :> string)
|
|
197
|
+
let paramIndex = index + 2 // +2 because $1 is for id in WHERE clause
|
|
198
|
+
`"${fieldName}" = $${Belt.Int.toString(paramIndex)}`
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
`UPDATE "${pgSchema}"."${table.tableName}"
|
|
202
|
+
SET ${setClauses->Js.Array2.joinWith(",\n ")}
|
|
203
|
+
WHERE "id" = $1;`
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let setValues = (sql, ~pgSchema, ~chainsData: array<t>) => {
|
|
207
|
+
let query = makeSingleUpdateQuery(~pgSchema)
|
|
208
|
+
|
|
209
|
+
let promises = chainsData->Belt.Array.map(chain => {
|
|
210
|
+
let params = []
|
|
211
|
+
|
|
212
|
+
// Push id first (for WHERE clause)
|
|
213
|
+
let idValue = chain->(Utils.magic: t => dict<unknown>)->Js.Dict.get("id")
|
|
214
|
+
params->Js.Array2.push(idValue)->ignore
|
|
215
|
+
|
|
216
|
+
// Then push all updateable field values (for SET clause)
|
|
217
|
+
updateFields->Js.Array2.forEach(field => {
|
|
218
|
+
let value = chain->(Utils.magic: t => dict<unknown>)->Js.Dict.get((field :> string))
|
|
219
|
+
params->Js.Array2.push(value)->ignore
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
sql->Postgres.preparedUnsafe(query, params->Obj.magic)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
Promise.all(promises)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
module PersistedState = {
|
|
230
|
+
type t = {
|
|
231
|
+
id: int,
|
|
232
|
+
envio_version: string,
|
|
233
|
+
config_hash: string,
|
|
234
|
+
schema_hash: string,
|
|
235
|
+
handler_files_hash: string,
|
|
236
|
+
abi_files_hash: string,
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let table = mkTable(
|
|
240
|
+
"persisted_state",
|
|
241
|
+
~fields=[
|
|
242
|
+
mkField("id", Serial, ~fieldSchema=S.int, ~isPrimaryKey),
|
|
243
|
+
mkField("envio_version", Text, ~fieldSchema=S.string),
|
|
244
|
+
mkField("config_hash", Text, ~fieldSchema=S.string),
|
|
245
|
+
mkField("schema_hash", Text, ~fieldSchema=S.string),
|
|
246
|
+
mkField("handler_files_hash", Text, ~fieldSchema=S.string),
|
|
247
|
+
mkField("abi_files_hash", Text, ~fieldSchema=S.string),
|
|
248
|
+
],
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module EndOfBlockRangeScannedData = {
|
|
253
|
+
type t = {
|
|
254
|
+
chain_id: int,
|
|
255
|
+
block_number: int,
|
|
256
|
+
block_hash: string,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let table = mkTable(
|
|
260
|
+
"end_of_block_range_scanned_data",
|
|
261
|
+
~fields=[
|
|
262
|
+
mkField("chain_id", Integer, ~fieldSchema=S.int, ~isPrimaryKey),
|
|
263
|
+
mkField("block_number", Integer, ~fieldSchema=S.int, ~isPrimaryKey),
|
|
264
|
+
mkField("block_hash", Text, ~fieldSchema=S.string),
|
|
265
|
+
],
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
module RawEvents = {
|
|
270
|
+
// @genType Used for Test DB and internal tests
|
|
271
|
+
@genType
|
|
272
|
+
type t = {
|
|
273
|
+
@as("chain_id") chainId: int,
|
|
274
|
+
@as("event_id") eventId: bigint,
|
|
275
|
+
@as("event_name") eventName: string,
|
|
276
|
+
@as("contract_name") contractName: string,
|
|
277
|
+
@as("block_number") blockNumber: int,
|
|
278
|
+
@as("log_index") logIndex: int,
|
|
279
|
+
@as("src_address") srcAddress: Address.t,
|
|
280
|
+
@as("block_hash") blockHash: string,
|
|
281
|
+
@as("block_timestamp") blockTimestamp: int,
|
|
282
|
+
@as("block_fields") blockFields: Js.Json.t,
|
|
283
|
+
@as("transaction_fields") transactionFields: Js.Json.t,
|
|
284
|
+
params: Js.Json.t,
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
let schema = S.schema(s => {
|
|
288
|
+
chainId: s.matches(S.int),
|
|
289
|
+
eventId: s.matches(S.bigint),
|
|
290
|
+
eventName: s.matches(S.string),
|
|
291
|
+
contractName: s.matches(S.string),
|
|
292
|
+
blockNumber: s.matches(S.int),
|
|
293
|
+
logIndex: s.matches(S.int),
|
|
294
|
+
srcAddress: s.matches(Address.schema),
|
|
295
|
+
blockHash: s.matches(S.string),
|
|
296
|
+
blockTimestamp: s.matches(S.int),
|
|
297
|
+
blockFields: s.matches(S.json(~validate=false)),
|
|
298
|
+
transactionFields: s.matches(S.json(~validate=false)),
|
|
299
|
+
params: s.matches(S.json(~validate=false)),
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
let table = mkTable(
|
|
303
|
+
"raw_events",
|
|
304
|
+
~fields=[
|
|
305
|
+
mkField("chain_id", Integer, ~fieldSchema=S.int),
|
|
306
|
+
mkField("event_id", Numeric, ~fieldSchema=S.bigint),
|
|
307
|
+
mkField("event_name", Text, ~fieldSchema=S.string),
|
|
308
|
+
mkField("contract_name", Text, ~fieldSchema=S.string),
|
|
309
|
+
mkField("block_number", Integer, ~fieldSchema=S.int),
|
|
310
|
+
mkField("log_index", Integer, ~fieldSchema=S.int),
|
|
311
|
+
mkField("src_address", Text, ~fieldSchema=Address.schema),
|
|
312
|
+
mkField("block_hash", Text, ~fieldSchema=S.string),
|
|
313
|
+
mkField("block_timestamp", Integer, ~fieldSchema=S.int),
|
|
314
|
+
mkField("block_fields", JsonB, ~fieldSchema=S.json(~validate=false)),
|
|
315
|
+
mkField("transaction_fields", JsonB, ~fieldSchema=S.json(~validate=false)),
|
|
316
|
+
mkField("params", JsonB, ~fieldSchema=S.json(~validate=false)),
|
|
317
|
+
mkField(
|
|
318
|
+
"db_write_timestamp",
|
|
319
|
+
TimestampWithoutTimezone,
|
|
320
|
+
~default="CURRENT_TIMESTAMP",
|
|
321
|
+
~fieldSchema=S.int,
|
|
322
|
+
),
|
|
323
|
+
mkField("serial", Serial, ~isNullable, ~isPrimaryKey, ~fieldSchema=S.null(S.int)),
|
|
324
|
+
],
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// View names for Hasura integration
|
|
329
|
+
module Views = {
|
|
330
|
+
let metaViewName = "_meta"
|
|
331
|
+
let chainMetadataViewName = "chain_metadata"
|
|
332
|
+
|
|
333
|
+
let makeMetaViewQuery = (~pgSchema) => {
|
|
334
|
+
`CREATE VIEW "${pgSchema}"."${metaViewName}" AS
|
|
335
|
+
SELECT
|
|
336
|
+
"${(#id: Chains.field :> string)}" AS "chainId",
|
|
337
|
+
"${(#start_block: Chains.field :> string)}" AS "startBlock",
|
|
338
|
+
"${(#end_block: Chains.field :> string)}" AS "endBlock",
|
|
339
|
+
"${(#buffer_block: Chains.field :> string)}" AS "bufferBlock",
|
|
340
|
+
"${(#ready_at: Chains.field :> string)}" AS "readyAt",
|
|
341
|
+
"${(#first_event_block: Chains.field :> string)}" AS "firstEventBlock",
|
|
342
|
+
"${(#events_processed: Chains.field :> string)}" AS "eventsProcessed",
|
|
343
|
+
("${(#ready_at: Chains.field :> string)}" IS NOT NULL) AS "isReady"
|
|
344
|
+
FROM "${pgSchema}"."${Chains.table.tableName}"
|
|
345
|
+
ORDER BY "${(#id: Chains.field :> string)}";`
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let makeChainMetadataViewQuery = (~pgSchema) => {
|
|
349
|
+
`CREATE VIEW "${pgSchema}"."${chainMetadataViewName}" AS
|
|
350
|
+
SELECT
|
|
351
|
+
"${(#source_block: Chains.field :> string)}" AS "block_height",
|
|
352
|
+
"${(#id: Chains.field :> string)}" AS "chain_id",
|
|
353
|
+
"${(#end_block: Chains.field :> string)}" AS "end_block",
|
|
354
|
+
"${(#first_event_block: Chains.field :> string)}" AS "first_event_block_number",
|
|
355
|
+
"${(#_is_hyper_sync: Chains.field :> string)}" AS "is_hyper_sync",
|
|
356
|
+
"${(#buffer_block: Chains.field :> string)}" AS "latest_fetched_block_number",
|
|
357
|
+
"${(#_latest_processed_block: Chains.field :> string)}" AS "latest_processed_block",
|
|
358
|
+
"${(#_num_batches_fetched: Chains.field :> string)}" AS "num_batches_fetched",
|
|
359
|
+
"${(#events_processed: Chains.field :> string)}" AS "num_events_processed",
|
|
360
|
+
"${(#start_block: Chains.field :> string)}" AS "start_block",
|
|
361
|
+
"${(#ready_at: Chains.field :> string)}" AS "timestamp_caught_up_to_head_or_endblock"
|
|
362
|
+
FROM "${pgSchema}"."${Chains.table.tableName}";`
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
module DynamicContractRegistry = {
|
|
367
|
+
let name = "dynamic_contract_registry"
|
|
368
|
+
|
|
369
|
+
let makeId = (~chainId, ~contractAddress) => {
|
|
370
|
+
chainId->Belt.Int.toString ++ "-" ++ contractAddress->Address.toString
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// @genType Used for Test DB
|
|
374
|
+
@genType
|
|
375
|
+
type t = {
|
|
376
|
+
id: string,
|
|
377
|
+
@as("chain_id") chainId: int,
|
|
378
|
+
@as("registering_event_block_number") registeringEventBlockNumber: int,
|
|
379
|
+
@as("registering_event_log_index") registeringEventLogIndex: int,
|
|
380
|
+
@as("registering_event_block_timestamp") registeringEventBlockTimestamp: int,
|
|
381
|
+
@as("registering_event_contract_name") registeringEventContractName: string,
|
|
382
|
+
@as("registering_event_name") registeringEventName: string,
|
|
383
|
+
@as("registering_event_src_address") registeringEventSrcAddress: Address.t,
|
|
384
|
+
@as("contract_address") contractAddress: Address.t,
|
|
385
|
+
@as("contract_name") contractName: string,
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
let schema = S.schema(s => {
|
|
389
|
+
id: s.matches(S.string),
|
|
390
|
+
chainId: s.matches(S.int),
|
|
391
|
+
registeringEventBlockNumber: s.matches(S.int),
|
|
392
|
+
registeringEventLogIndex: s.matches(S.int),
|
|
393
|
+
registeringEventContractName: s.matches(S.string),
|
|
394
|
+
registeringEventName: s.matches(S.string),
|
|
395
|
+
registeringEventSrcAddress: s.matches(Address.schema),
|
|
396
|
+
registeringEventBlockTimestamp: s.matches(S.int),
|
|
397
|
+
contractAddress: s.matches(Address.schema),
|
|
398
|
+
contractName: s.matches(S.string),
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
let rowsSchema = S.array(schema)
|
|
402
|
+
|
|
403
|
+
let table = mkTable(
|
|
404
|
+
name,
|
|
405
|
+
~fields=[
|
|
406
|
+
mkField("id", Text, ~isPrimaryKey, ~fieldSchema=S.string),
|
|
407
|
+
mkField("chain_id", Integer, ~fieldSchema=S.int),
|
|
408
|
+
mkField("registering_event_block_number", Integer, ~fieldSchema=S.int),
|
|
409
|
+
mkField("registering_event_log_index", Integer, ~fieldSchema=S.int),
|
|
410
|
+
mkField("registering_event_block_timestamp", Integer, ~fieldSchema=S.int),
|
|
411
|
+
mkField("registering_event_contract_name", Text, ~fieldSchema=S.string),
|
|
412
|
+
mkField("registering_event_name", Text, ~fieldSchema=S.string),
|
|
413
|
+
mkField("registering_event_src_address", Text, ~fieldSchema=Address.schema),
|
|
414
|
+
mkField("contract_address", Text, ~fieldSchema=Address.schema),
|
|
415
|
+
mkField("contract_name", Text, ~fieldSchema=S.string),
|
|
416
|
+
],
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
let entityHistory = table->EntityHistory.fromTable(~schema)
|
|
420
|
+
|
|
421
|
+
external castToInternal: t => Internal.entity = "%identity"
|
|
422
|
+
|
|
423
|
+
let config = {
|
|
424
|
+
name,
|
|
425
|
+
schema,
|
|
426
|
+
rowsSchema,
|
|
427
|
+
table,
|
|
428
|
+
entityHistory,
|
|
429
|
+
}->Internal.fromGenericEntityConfig
|
|
430
|
+
}
|