Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Contract Pre Registration #252

Merged
merged 12 commits into from
Oct 11, 2024
22 changes: 18 additions & 4 deletions codegenerator/cli/templates/dynamic/codegen/src/Types.res.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -228,26 +228,34 @@ module HandlerTypes = {
handler: handler<'eventArgs, 'loaderReturn>,
wildcard?: bool,
eventFilters?: SingleOrMultiple.t<'eventFilter>,
preRegisterDynamicContracts?: bool,
}

@genType
type eventConfig<'eventFilter> = {
wildcard?: bool,
eventFilters?: SingleOrMultiple.t<'eventFilter>,
preRegisterDynamicContracts?: bool,
}

module EventOptions = {
type t = {
isWildcard: bool,
topicSelections: array<LogSelection.topicSelection>,
shouldPreRegisterDynamicContracts: bool,
}

let getDefault = (~topic0) => {
isWildcard: false,
topicSelections: [LogSelection.makeTopicSelection(~topic0=[topic0])->Utils.unwrapResultExn],
shouldPreRegisterDynamicContracts: false,
}

let make = (~isWildcard, ~topicSelections: array<LogSelection.topicSelection>) => {
let make = (
~isWildcard,
~topicSelections: array<LogSelection.topicSelection>,
~shouldPreRegisterDynamicContracts,
) => {
let topic0sGrouped = []
let topicSelectionWithFilters = []
topicSelections->Belt.Array.forEach(ts =>
Expand All @@ -270,6 +278,7 @@ module HandlerTypes = {
{
isWildcard,
topicSelections,
shouldPreRegisterDynamicContracts,
}
}
}
Expand Down Expand Up @@ -443,7 +452,7 @@ let makeEventOptions = (
) => {
let module(Event) = eventMod
open Belt
eventConfig->Option.map(({?wildcard, ?eventFilters}) =>
eventConfig->Option.map(({?wildcard, ?eventFilters, ?preRegisterDynamicContracts}) =>
HandlerTypes.EventOptions.make(
~isWildcard=wildcard->Option.getWithDefault(false),
~topicSelections=eventFilters->Option.mapWithDefault(
Expand All @@ -454,6 +463,7 @@ let makeEventOptions = (
],
v => v->Event.getTopicSelection,
),
~shouldPreRegisterDynamicContracts=preRegisterDynamicContracts->Option.getWithDefault(false),
)
)
}
Expand All @@ -466,8 +476,8 @@ let makeGetEventOptions = (
let module(Event) = eventMod
(loaderHandler: HandlerTypes.loaderHandler<Event.eventArgs, 'loaderReturn, Event.eventFilter>) =>
switch loaderHandler {
| {wildcard: ?None, eventFilters: ?None} => None
| {?wildcard, ?eventFilters} =>
| {wildcard: ?None, eventFilters: ?None, preRegisterDynamicContracts: ?None} => None
| {?wildcard, ?eventFilters, ?preRegisterDynamicContracts} =>
let topicSelections =
eventFilters->Option.mapWithDefault(
[
Expand All @@ -480,6 +490,7 @@ let makeGetEventOptions = (
HandlerTypes.EventOptions.make(
~isWildcard=wildcard->Option.getWithDefault(false),
~topicSelections,
~shouldPreRegisterDynamicContracts=preRegisterDynamicContracts->Option.getWithDefault(false),
)->Some
}
}
Expand Down Expand Up @@ -510,6 +521,9 @@ module MakeRegister = (Event: Event) => {
handler,
wildcard: ?eventConfig->Belt.Option.flatMap(c => c.wildcard),
eventFilters: ?eventConfig->Belt.Option.flatMap(c => c.eventFilters),
preRegisterDynamicContracts: ?eventConfig->Belt.Option.flatMap(c =>
c.preRegisterDynamicContracts
),
},
~getEventOptions=makeGetEventOptions(module(Event)),
)
Expand Down
13 changes: 13 additions & 0 deletions codegenerator/cli/templates/static/codegen/src/Config.res
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ type chainConfig = {
chainWorker: module(ChainWorker.S),
}

let shouldPreRegisterDynamicContracts = (chainConfig: chainConfig) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function name is fine, but then change the variable to just preRegister (as the user sees it)

chainConfig.contracts->Array.some(contract => {
contract.events->Array.some(event => {
let module(Event) = event

let {shouldPreRegisterDynamicContracts} =
Event.handlerRegister->Types.HandlerTypes.Register.getEventOptions

shouldPreRegisterDynamicContracts
})
})
}

type historyFlag = FullHistory | MinHistory
type rollbackFlag = RollbackOnReorg | NoRollback
type historyConfig = {rollbackFlag: rollbackFlag, historyFlag: historyFlag}
Expand Down
105 changes: 86 additions & 19 deletions codegenerator/cli/templates/static/codegen/src/EventProcessing.res
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ module EventsProcessed = {
}
}

let updateEventSyncState = (
eventBatchQueueItem: Types.eventBatchQueueItem,
~inMemoryStore: InMemoryStore.t,
~isPreRegisteringDynamicContracts,
) => {
let {event, chain, blockNumber, timestamp: blockTimestamp} = eventBatchQueueItem
let {logIndex} = event
let chainId = chain->ChainMap.Chain.toChainId
let _ = inMemoryStore.eventSyncState->InMemoryTable.set(
chainId,
{
chainId,
blockTimestamp,
blockNumber,
logIndex,
isPreRegisteringDynamicContracts,
},
)
}

type dynamicContractRegistration = {
registeringEventBlockNumber: int,
registeringEventLogIndex: int,
Expand Down Expand Up @@ -72,6 +92,7 @@ let addToDynamicContractRegistrations = (
registeringEventLogIndex,
registeringEventChain: eventBatchQueueItem.chain,
}

{
unprocessedBatch,
registrations: [...registrations, dynamicContractRegistration],
Expand All @@ -85,6 +106,7 @@ let runEventContractRegister = (
~checkContractIsRegistered,
~dynamicContractRegistrations: option<dynamicContractRegistrations>,
~inMemoryStore,
~preRegisterLatestProcessedBlocks=?,
) => {
let {chain, event, blockNumber} = eventBatchQueueItem

Expand Down Expand Up @@ -127,6 +149,20 @@ let runEventContractRegister = (
)->Some
}

switch preRegisterLatestProcessedBlocks {
| Some(latestProcessedBlocks) =>
eventBatchQueueItem->updateEventSyncState(
~inMemoryStore,
~isPreRegisteringDynamicContracts=true,
)
latestProcessedBlocks :=
latestProcessedBlocks.contents->EventsProcessed.updateEventsProcessed(
~chain=eventBatchQueueItem.chain,
~blockNumber=eventBatchQueueItem.blockNumber,
)
| None => ()
}

val->Ok
}
}
Expand Down Expand Up @@ -194,24 +230,6 @@ let addEventToRawEvents = (
inMemoryStore.rawEvents->InMemoryTable.set({chainId, eventId: eventIdStr}, rawEvent)
}

let updateEventSyncState = (
eventBatchQueueItem: Types.eventBatchQueueItem,
~inMemoryStore: InMemoryStore.t,
) => {
let {event, chain, blockNumber, timestamp: blockTimestamp} = eventBatchQueueItem
let {logIndex} = event
let chainId = chain->ChainMap.Chain.toChainId
let _ = inMemoryStore.eventSyncState->InMemoryTable.set(
chainId,
{
chainId,
blockTimestamp,
blockNumber,
logIndex,
},
)
}

let runEventHandler = (
eventBatchQueueItem: Types.eventBatchQueueItem,
~loaderHandler: Types.HandlerTypes.loaderHandler<_>,
Expand Down Expand Up @@ -247,7 +265,10 @@ let runEventHandler = (
->Error
->propogate
| () =>
eventBatchQueueItem->updateEventSyncState(~inMemoryStore)
eventBatchQueueItem->updateEventSyncState(
~inMemoryStore,
~isPreRegisteringDynamicContracts=false,
)
if config.enableRawEvents {
eventBatchQueueItem->addEventToRawEvents(~inMemoryStore)
}
Expand Down Expand Up @@ -303,6 +324,7 @@ let rec registerDynamicContracts = (
~eventsBeforeDynamicRegistrations=[],
~dynamicContractRegistrations: option<dynamicContractRegistrations>=None,
~inMemoryStore,
~preRegisterLatestProcessedBlocks=?,
) => {
switch eventBatch[index] {
| None => (eventsBeforeDynamicRegistrations, dynamicContractRegistrations)->Ok
Expand All @@ -326,6 +348,7 @@ let rec registerDynamicContracts = (
~eventBatchQueueItem,
~dynamicContractRegistrations,
~inMemoryStore,
~preRegisterLatestProcessedBlocks?,
)
| None =>
dynamicContractRegistrations
Expand All @@ -349,6 +372,7 @@ let rec registerDynamicContracts = (
~eventsBeforeDynamicRegistrations,
~dynamicContractRegistrations,
~inMemoryStore,
~preRegisterLatestProcessedBlocks?,
)
| Error(e) => Error(e)
}
Expand Down Expand Up @@ -443,6 +467,49 @@ type batchProcessed = {
latestProcessedBlocks: EventsProcessed.t,
dynamicContractRegistrations: option<dynamicContractRegistrations>,
}

let getDynamicContractRegistrations = (
~eventBatch: array<Types.eventBatchQueueItem>,
~latestProcessedBlocks: EventsProcessed.t,
~checkContractIsRegistered,
) => {
let logger = Logging.createChild(
~params={
"context": "pre-registration",
"batch-size": eventBatch->Array.length,
"first-event-timestamp": eventBatch[0]->Option.map(v => v.timestamp),
},
)
let inMemoryStore = InMemoryStore.make()
let preRegisterLatestProcessedBlocks = ref(latestProcessedBlocks)
open ErrorHandling.ResultPropogateEnv
runAsyncEnv(async () => {
//Register all the dynamic contracts in this batch,
//only continue processing events before the first dynamic contract registration
let (_, dynamicContractRegistrations) =
eventBatch
->registerDynamicContracts(
~checkContractIsRegistered,
~logger,
~inMemoryStore,
~preRegisterLatestProcessedBlocks,
)
->propogate

//We only preregister below the reorg threshold so it can be hardcoded as false
switch await DbFunctions.sql->IO.executeBatch(~inMemoryStore, ~isInReorgThreshold=false) {
| exception exn =>
exn->ErrorHandling.make(~msg="Failed writing batch to database", ~logger)->Error->propogate
| () => ()
}

Ok({
latestProcessedBlocks: preRegisterLatestProcessedBlocks.contents,
dynamicContractRegistrations,
})
})
}

let processEventBatch = (
~eventBatch: array<Types.eventBatchQueueItem>,
~inMemoryStore: InMemoryStore.t,
Expand Down
13 changes: 8 additions & 5 deletions codegenerator/cli/templates/static/codegen/src/Index.res
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ type mainArgs = Yargs.parsedArgs<args>

let makeAppState = (globalState: GlobalState.t): EnvioInkApp.appState => {
open Belt
{
config: globalState.config,
indexerStartTime: globalState.indexerStartTime,
chains: globalState.chainManager.chainFetchers
let chains =
globalState.chainManager.chainFetchers
->ChainMap.values
->Array.map(cf => {
let {numEventsProcessed, fetchState, numBatchesFetched} = cf
Expand Down Expand Up @@ -146,7 +144,12 @@ let makeAppState = (globalState: GlobalState.t): EnvioInkApp.appState => {
},
}: EnvioInkApp.chainData
)
}),
})
{
config: globalState.config,
indexerStartTime: globalState.indexerStartTime,
chains,
isPreRegisteringDynamicContracts: globalState.chainManager->ChainManager.isPreRegisteringDynamicContracts,
}
}

Expand Down
15 changes: 13 additions & 2 deletions codegenerator/cli/templates/static/codegen/src/Utils.res
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ module Dict = {
It's the same as `Js.Dict.get` but it doesn't have runtime overhead to check if the key exists.
*/
external dangerouslyGetNonOption: (dict<'a>, string) => option<'a> = ""

let merge = (a: dict<'a>, b: dict<'a>) => {
let result = Js.Dict.empty()
Js.Dict.entries(a)->Js.Array2.forEach(((key, value)) => {
result->Js.Dict.set(key, value)
})
Js.Dict.entries(b)->Js.Array2.forEach(((key, value)) => {
result->Js.Dict.set(key, value)
})
result
}
}

module Math = {
Expand Down Expand Up @@ -220,8 +231,8 @@ module Schema = {
schema->S.preprocess(s => {
switch s.schema->S.classify {
| Literal(Null(_))
// This is a workaround for Fuel Bytes type
| Unknown => {serializer: _ => %raw(`"null"`)}
| // This is a workaround for Fuel Bytes type
Unknown => {serializer: _ => %raw(`"null"`)}
| Null(_)
| Bool => {
serializer: unknown => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ const chunkBatchQuery = async (sql, entityDataArray, queryToExecute) => {
const commaSeparateDynamicMapQuery = (sql, dynQueryConstructors) =>
sql`${dynQueryConstructors.map(
(constrQuery, i) =>
sql`${constrQuery(sql)}${i === dynQueryConstructors.length - 1 ? sql`` : sql`, `
}`,
sql`${constrQuery(sql)}${
i === dynQueryConstructors.length - 1 ? sql`` : sql`, `
}`,
)}`;

const batchSetItemsInTableCore = (table, sql, rowDataArray) => {
Expand Down Expand Up @@ -101,13 +102,15 @@ module.exports.batchSetEventSyncState = (sql, entityDataArray) => {
"block_number",
"log_index",
"block_timestamp",
"is_pre_registering_dynamic_contracts",
)}
ON CONFLICT(chain_id) DO UPDATE
SET
"chain_id" = EXCLUDED."chain_id",
"block_number" = EXCLUDED."block_number",
"log_index" = EXCLUDED."log_index",
"block_timestamp" = EXCLUDED."block_timestamp";
"block_timestamp" = EXCLUDED."block_timestamp",
"is_pre_registering_dynamic_contracts" = EXCLUDED."is_pre_registering_dynamic_contracts";
`;
};

Expand Down Expand Up @@ -143,7 +146,7 @@ module.exports.batchSetChainMetadata = (sql, entityDataArray) => {
"latest_fetched_block_number" = EXCLUDED."latest_fetched_block_number",
"timestamp_caught_up_to_head_or_endblock" = EXCLUDED."timestamp_caught_up_to_head_or_endblock",
"block_height" = EXCLUDED."block_height";`
.then((res) => { })
.then((res) => {})
.catch((err) => {
console.log("errored", err);
});
Expand All @@ -163,7 +166,7 @@ module.exports.setChainMetadataBlockHeight = (sql, entityDataArray) => {
SET
"chain_id" = EXCLUDED."chain_id",
"block_height" = EXCLUDED."block_height";`
.then((res) => { })
.then((res) => {})
.catch((err) => {
console.log("errored", err);
});
Expand Down
Loading
Loading