envio 3.3.0-alpha.3 → 3.3.0-alpha.4

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.
@@ -500,7 +500,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
500
500
  retry: retry
501
501
  });
502
502
  try {
503
- let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, query.contractNameByAddress, knownHeight, query.partitionId, query.selection, retry, logger$2);
503
+ let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, FetchState.deriveContractNameByAddress(query.addressesByContractName), knownHeight, query.partitionId, query.selection, retry, logger$2);
504
504
  sourceState.lastFailedAt = undefined;
505
505
  responseRef = response;
506
506
  } catch (raw_error) {
@@ -20,7 +20,9 @@ let transactionFields = [
20
20
  "tokenBalances",
21
21
  ]
22
22
 
23
- let transactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
23
+ // One instruction's selected transaction fields → store selection bitmask.
24
+ // Computed per event at config build and cached on the event config.
25
+ let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
24
26
 
25
27
  let make = (~logger: Pino.t): Ecosystem.t => {
26
28
  name: Svm,
@@ -36,7 +38,6 @@ let make = (~logger: Pino.t): Ecosystem.t => {
36
38
  // parse. The schema is a no-op object that always surfaces `None`.
37
39
  onEventBlockFilterSchema: S.object(_ => None),
38
40
  logger,
39
- transactionFieldMask,
40
41
  toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
41
42
  toEventLogger: eventItem => {
42
43
  let instruction =
@@ -30,7 +30,7 @@ let transactionFields = [
30
30
  "tokenBalances"
31
31
  ];
32
32
 
33
- let transactionFieldMask = TransactionStore.makeMaskFn(transactionFields);
33
+ let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields);
34
34
 
35
35
  function make(logger) {
36
36
  return {
@@ -44,7 +44,6 @@ function make(logger) {
44
44
  onEventBlockFilterSchema: S$RescriptSchema.object(param => {}),
45
45
  logger: logger,
46
46
  toEvent: eventItem => eventItem.payload,
47
- transactionFieldMask: transactionFieldMask,
48
47
  toEventLogger: eventItem => {
49
48
  let instruction = eventItem.payload;
50
49
  return Logging.createChildFrom(logger, {
@@ -97,9 +96,9 @@ function makeRPCSource(chain, rpc, sourceForOpt) {
97
96
  export {
98
97
  cleanUpRawEventFieldsInPlace,
99
98
  transactionFields,
100
- transactionFieldMask,
99
+ eventTransactionFieldMask,
101
100
  make,
102
101
  GetFinalizedSlot,
103
102
  makeRPCSource,
104
103
  }
105
- /* transactionFieldMask Not a pure module */
104
+ /* eventTransactionFieldMask Not a pure module */
@@ -19,39 +19,45 @@ let fieldCodes = (fields: array<string>): dict<int> => {
19
19
 
20
20
  let pow2: int => float = %raw(`c => Math.pow(2, c)`)
21
21
 
22
- // Union of an ecosystem's selected transaction fields as a bitmask float (bit
23
- // `code` set ⇔ selected). Built arithmetically to dodge 32-bit JS bitwise ops.
24
- let mask = (eventConfigs: array<Internal.eventConfig>, ~codes: dict<int>): float => {
25
- let selected = Utils.Set.make()
26
- eventConfigs->Array.forEach(eventConfig =>
27
- eventConfig.selectedTransactionFields->Utils.Set.forEach(name =>
28
- switch codes->Utils.Dict.dangerouslyGetNonOption(name) {
29
- | Some(code) => selected->Utils.Set.add(code)->ignore
30
- | None => ()
31
- }
32
- )
22
+ // One event's selected transaction fields as a bitmask float (bit `code` set ⇔
23
+ // selected). Each field appears once, so summing `pow2(code)` sets distinct bits
24
+ // with no overlap; the result stays exact in f64 (codes span 0..31, so a mask is
25
+ // at most 2^32-1).
26
+ let maskFromFields = (selectedTransactionFields: Utils.Set.t<string>, ~codes: dict<int>): float => {
27
+ let mask = ref(0.)
28
+ selectedTransactionFields->Utils.Set.forEach(name =>
29
+ switch codes->Utils.Dict.dangerouslyGetNonOption(name) {
30
+ | Some(code) => mask := mask.contents +. pow2(code)
31
+ | None => ()
32
+ }
33
33
  )
34
- selected->Utils.Set.toArray->Array.reduce(0., (mask, code) => mask +. pow2(code))
34
+ mask.contents
35
35
  }
36
36
 
37
- // Build an ecosystem's mask function from its ordered field-name array. The
38
- // field codes are derived once and closed over.
39
- let makeMaskFn = (fields: array<string>): (array<Internal.eventConfig> => float) => {
37
+ // Build an ecosystem's per-event mask function from its ordered field-name
38
+ // array. The field codes are derived once and closed over.
39
+ let makeMaskFn = (fields: array<string>): (Utils.Set.t<string> => float) => {
40
40
  let codes = fieldCodes(fields)
41
- eventConfigs => eventConfigs->mask(~codes)
41
+ selectedTransactionFields => selectedTransactionFields->maskFromFields(~codes)
42
42
  }
43
43
 
44
+ // Bitwise OR of two per-event masks. Masks fit in 32 bits (≤32 transaction
45
+ // fields), so `>>> 0` recovers the unsigned value that a plain `|` renders
46
+ // negative once bit 31 is set.
47
+ let orMask: (float, float) => float = %raw(`(a, b) => (a | b) >>> 0`)
48
+
44
49
  // Drain another store (a fetch-response page) into this one.
45
50
  @send external merge: (t, t) => unit = "merge"
46
51
 
47
- // Bulk-materialise the fields selected by `mask` (one bit per field code) for
48
- // the given transactions, off the JS thread. Result is aligned with the input.
52
+ // Bulk-materialise transactions off the JS thread, one row per
53
+ // (blockNumbers[i], transactionIndices[i]) key, decoding only the fields set in
54
+ // that row's own masks[i]. Result is aligned with the input.
49
55
  @send
50
56
  external materialize: (
51
57
  t,
52
58
  ~blockNumbers: array<int>,
53
59
  ~transactionIndices: array<int>,
54
- ~mask: float,
60
+ ~masks: array<float>,
55
61
  ) => promise<array<Internal.eventTransaction>> = "materialize"
56
62
 
57
63
  // Drop transactions for blocks at or below the given block (already processed).
@@ -60,69 +66,73 @@ external materialize: (
60
66
  // Drop transactions for blocks above the given block (rolled back).
61
67
  @send external rollback: (t, int) => unit = "rollback"
62
68
 
63
- // Materialise the mask-selected fields for the store-backed items and write the
64
- // resulting transaction onto each item's payload. Items that already carry an
65
- // inline transaction (RPC/simulate/Fuel) are skipped. Store-backed items always
66
- // get a transaction objectthe selected fields, or `{}` when the chain
67
- // selected none so `event.transaction` is never `undefined` (matching the
68
- // inline sources). Deduped per (blockNumber, transactionIndex).
69
- let materializeItems = async (store: t, ~items: array<Internal.item>, ~mask: float) => {
70
- if mask == 0. {
71
- // No fields selected: there's nothing to decode and no reason to group by
72
- // key, but each store-backed item still gets an empty transaction object so
73
- // `event.transaction` is never undefined (matching the inline sources).
74
- items->Array.forEach(item =>
75
- switch item {
76
- | Internal.Event(_) =>
77
- let payload = (item->Internal.castUnsafeEventItem).payload
78
- switch payload->Internal.getPayloadTransaction->Nullable.toOption {
79
- | Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
80
- | None => payload->Internal.setPayloadTransaction(%raw(`{}`))
81
- }
82
- | Internal.Block(_) => ()
83
- }
84
- )
85
- } else {
86
- // Store-backed items arrive in (block, logIndex) order, and a transaction's
87
- // logs are contiguous within a block, so events sharing a (blockNumber,
88
- // transactionIndex) are adjacent. Group them by extending the current run
89
- // rather than hashing a string key per item. A key recurring non-adjacently
90
- // just splits into two groups (one redundant decode) — never incorrect.
91
- let blockNumbers = []
92
- let transactionIndices = []
93
- let payloadGroups = []
69
+ // Materialise each store-backed item's selected transaction fields and write the
70
+ // resulting transaction onto its payload. Every event's mask comes from its own
71
+ // `eventConfig.transactionFieldMask`, so a transaction decodes only the fields
72
+ // the events on it selected a large field (e.g. `input`) never materialises
73
+ // for events that didn't ask for it. Items that already carry an inline
74
+ // transaction (RPC/simulate/Fuel) are skipped. Store-backed items always get a
75
+ // transaction object the selected fields, or `{}` when nothing was selected —
76
+ // so `event.transaction` is never `undefined` (matching the inline sources).
77
+ // Deduped per (blockNumber, transactionIndex); each row's mask is the OR of the
78
+ // masks of the events sharing that transaction.
79
+ let materializeItems = async (store: t, ~items: array<Internal.item>) => {
80
+ // Store-backed items arrive in (block, logIndex) order, and a transaction's
81
+ // logs are contiguous within a block, so events sharing a (blockNumber,
82
+ // transactionIndex) are adjacent regardless of event. Group them by extending
83
+ // the current run rather than hashing a string key per item. A key recurring
84
+ // non-adjacently just splits into two groups (one redundant decode) — never
85
+ // incorrect.
86
+ let blockNumbers = []
87
+ let transactionIndices = []
88
+ let masks = []
89
+ let payloadGroups = []
90
+ let anySelected = ref(false)
94
91
 
95
- items->Array.forEach(item =>
96
- switch item {
97
- | Internal.Event(_) =>
98
- let eventItem = item->Internal.castUnsafeEventItem
99
- switch eventItem.payload->Internal.getPayloadTransaction->Nullable.toOption {
100
- | Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
101
- | None =>
102
- let {blockNumber, transactionIndex} = eventItem
103
- let last = payloadGroups->Array.length - 1
104
- if (
105
- last >= 0 &&
106
- blockNumbers->Array.getUnsafe(last) == blockNumber &&
107
- transactionIndices->Array.getUnsafe(last) == transactionIndex
108
- ) {
109
- payloadGroups->Array.getUnsafe(last)->Array.push(eventItem.payload)
110
- } else {
111
- blockNumbers->Array.push(blockNumber)
112
- transactionIndices->Array.push(transactionIndex)
113
- payloadGroups->Array.push([eventItem.payload])
114
- }
92
+ items->Array.forEach(item =>
93
+ switch item {
94
+ | Internal.Event(_) =>
95
+ let eventItem = item->Internal.castUnsafeEventItem
96
+ switch eventItem.payload->Internal.getPayloadTransaction->Nullable.toOption {
97
+ | Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
98
+ | None =>
99
+ let {blockNumber, transactionIndex} = eventItem
100
+ let mask = eventItem.eventConfig.transactionFieldMask
101
+ if mask != 0. {
102
+ anySelected := true
103
+ }
104
+ let last = payloadGroups->Array.length - 1
105
+ if (
106
+ last >= 0 &&
107
+ blockNumbers->Array.getUnsafe(last) == blockNumber &&
108
+ transactionIndices->Array.getUnsafe(last) == transactionIndex
109
+ ) {
110
+ payloadGroups->Array.getUnsafe(last)->Array.push(eventItem.payload)
111
+ masks->Array.setUnsafe(last, orMask(masks->Array.getUnsafe(last), mask))
112
+ } else {
113
+ blockNumbers->Array.push(blockNumber)
114
+ transactionIndices->Array.push(transactionIndex)
115
+ masks->Array.push(mask)
116
+ payloadGroups->Array.push([eventItem.payload])
115
117
  }
116
- | Internal.Block(_) => ()
117
118
  }
118
- )
119
+ | Internal.Block(_) => ()
120
+ }
121
+ )
119
122
 
120
- if payloadGroups->Utils.Array.notEmpty {
121
- let txs = await store->materialize(~blockNumbers, ~transactionIndices, ~mask)
123
+ if payloadGroups->Utils.Array.notEmpty {
124
+ if anySelected.contents {
125
+ let txs = await store->materialize(~blockNumbers, ~transactionIndices, ~masks)
122
126
  payloadGroups->Array.forEachWithIndex((payloads, i) => {
123
127
  let tx = txs->Array.getUnsafe(i)
124
128
  payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
125
129
  })
130
+ } else {
131
+ // No event selected any field: stamp an empty transaction object so
132
+ // `event.transaction` is never undefined, without a materialize call.
133
+ payloadGroups->Array.forEach(payloads =>
134
+ payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(%raw(`{}`)))
135
+ )
126
136
  }
127
137
  }
128
138
  }
@@ -2,7 +2,6 @@
2
2
 
3
3
  import * as Core from "../Core.res.mjs";
4
4
  import * as Utils from "../Utils.res.mjs";
5
- import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
5
 
7
6
  function make() {
8
7
  return Core.getAddon().TransactionStore.new();
@@ -18,43 +17,35 @@ function fieldCodes(fields) {
18
17
 
19
18
  let pow2 = (c => Math.pow(2, c));
20
19
 
21
- function mask(eventConfigs, codes) {
22
- let selected = new Set();
23
- eventConfigs.forEach(eventConfig => {
24
- eventConfig.selectedTransactionFields.forEach(name => {
25
- let code = codes[name];
26
- if (code !== undefined) {
27
- selected.add(code);
28
- return;
29
- }
30
- });
20
+ function maskFromFields(selectedTransactionFields, codes) {
21
+ let mask = {
22
+ contents: 0
23
+ };
24
+ selectedTransactionFields.forEach(name => {
25
+ let code = codes[name];
26
+ if (code !== undefined) {
27
+ mask.contents = mask.contents + pow2(code);
28
+ return;
29
+ }
31
30
  });
32
- return Stdlib_Array.reduce(Array.from(selected), 0, (mask, code) => mask + pow2(code));
31
+ return mask.contents;
33
32
  }
34
33
 
35
34
  function makeMaskFn(fields) {
36
35
  let codes = fieldCodes(fields);
37
- return eventConfigs => mask(eventConfigs, codes);
36
+ return selectedTransactionFields => maskFromFields(selectedTransactionFields, codes);
38
37
  }
39
38
 
40
- async function materializeItems(store, items, mask) {
41
- if (mask === 0) {
42
- items.forEach(item => {
43
- if (item.kind !== 0) {
44
- return;
45
- }
46
- let payload = item.payload;
47
- let match = payload.transaction;
48
- if (match == null) {
49
- payload.transaction = {};
50
- return;
51
- }
52
- });
53
- return;
54
- }
39
+ let orMask = ((a, b) => (a | b) >>> 0);
40
+
41
+ async function materializeItems(store, items) {
55
42
  let blockNumbers = [];
56
43
  let transactionIndices = [];
44
+ let masks = [];
57
45
  let payloadGroups = [];
46
+ let anySelected = {
47
+ contents: false
48
+ };
58
49
  items.forEach(item => {
59
50
  if (item.kind !== 0) {
60
51
  return;
@@ -65,23 +56,37 @@ async function materializeItems(store, items, mask) {
65
56
  }
66
57
  let transactionIndex = item.transactionIndex;
67
58
  let blockNumber = item.blockNumber;
59
+ let mask = item.eventConfig.transactionFieldMask;
60
+ if (mask !== 0) {
61
+ anySelected.contents = true;
62
+ }
68
63
  let last = payloadGroups.length - 1 | 0;
69
64
  if (last >= 0 && blockNumbers[last] === blockNumber && transactionIndices[last] === transactionIndex) {
70
65
  payloadGroups[last].push(item.payload);
66
+ masks[last] = orMask(masks[last], mask);
71
67
  } else {
72
68
  blockNumbers.push(blockNumber);
73
69
  transactionIndices.push(transactionIndex);
70
+ masks.push(mask);
74
71
  payloadGroups.push([item.payload]);
75
72
  }
76
73
  });
77
74
  if (!Utils.$$Array.notEmpty(payloadGroups)) {
78
75
  return;
79
76
  }
80
- let txs = await store.materialize(blockNumbers, transactionIndices, mask);
81
- payloadGroups.forEach((payloads, i) => {
82
- let tx = txs[i];
77
+ if (anySelected.contents) {
78
+ let txs = await store.materialize(blockNumbers, transactionIndices, masks);
79
+ payloadGroups.forEach((payloads, i) => {
80
+ let tx = txs[i];
81
+ payloads.forEach(payload => {
82
+ payload.transaction = tx;
83
+ });
84
+ });
85
+ return;
86
+ }
87
+ payloadGroups.forEach(payloads => {
83
88
  payloads.forEach(payload => {
84
- payload.transaction = tx;
89
+ payload.transaction = {};
85
90
  });
86
91
  });
87
92
  }
@@ -90,8 +95,9 @@ export {
90
95
  make,
91
96
  fieldCodes,
92
97
  pow2,
93
- mask,
98
+ maskFromFields,
94
99
  makeMaskFn,
100
+ orMask,
95
101
  materializeItems,
96
102
  }
97
103
  /* Core Not a pure module */