@aztec/pxe 0.0.1-commit.3100065 → 0.0.1-commit.330febf

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.
Files changed (207) hide show
  1. package/dest/bin/check_oracle_version.js +5 -46
  2. package/dest/bin/index.d.ts +2 -2
  3. package/dest/bin/index.d.ts.map +1 -1
  4. package/dest/bin/index.js +1 -1
  5. package/dest/bin/oracle_version_helpers.d.ts +8 -39
  6. package/dest/bin/oracle_version_helpers.d.ts.map +1 -1
  7. package/dest/bin/oracle_version_helpers.js +10 -239
  8. package/dest/block_synchronizer/block_synchronizer.d.ts +3 -5
  9. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  10. package/dest/block_synchronizer/block_synchronizer.js +5 -18
  11. package/dest/config/package_info.js +1 -1
  12. package/dest/contract/contract_class_service.d.ts +1 -8
  13. package/dest/contract/contract_class_service.d.ts.map +1 -1
  14. package/dest/contract/contract_class_service.js +8 -40
  15. package/dest/contract/helpers.d.ts +1 -1
  16. package/dest/contract/helpers.d.ts.map +1 -1
  17. package/dest/contract/helpers.js +3 -3
  18. package/dest/contract/skip_sync_contracts.d.ts +11 -0
  19. package/dest/contract/skip_sync_contracts.d.ts.map +1 -0
  20. package/dest/contract/skip_sync_contracts.js +18 -0
  21. package/dest/contract_function_simulator/contract_function_simulator.d.ts +1 -8
  22. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  23. package/dest/contract_function_simulator/contract_function_simulator.js +5 -20
  24. package/dest/contract_function_simulator/index.d.ts +3 -3
  25. package/dest/contract_function_simulator/index.d.ts.map +1 -1
  26. package/dest/contract_function_simulator/index.js +1 -2
  27. package/dest/contract_function_simulator/noir-structs/resolved_tx.d.ts +7 -9
  28. package/dest/contract_function_simulator/noir-structs/resolved_tx.d.ts.map +1 -1
  29. package/dest/contract_function_simulator/noir-structs/resolved_tx.js +3 -44
  30. package/dest/contract_function_simulator/noir-structs/tx_effect_data.d.ts +5 -4
  31. package/dest/contract_function_simulator/noir-structs/tx_effect_data.d.ts.map +1 -1
  32. package/dest/contract_function_simulator/noir-structs/tx_effect_data.js +2 -2
  33. package/dest/contract_function_simulator/oracle/oracle_registry.d.ts +4 -3
  34. package/dest/contract_function_simulator/oracle/oracle_registry.d.ts.map +1 -1
  35. package/dest/contract_function_simulator/oracle/oracle_registry.js +15 -6
  36. package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts +87 -35
  37. package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts.map +1 -1
  38. package/dest/contract_function_simulator/oracle/oracle_type_mappings.js +297 -287
  39. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -1
  40. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  41. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +11 -8
  42. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +15 -4
  43. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  44. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +168 -53
  45. package/dest/entrypoints/client/bundle/index.d.ts +2 -1
  46. package/dest/entrypoints/client/bundle/index.d.ts.map +1 -1
  47. package/dest/entrypoints/client/bundle/index.js +1 -0
  48. package/dest/entrypoints/client/bundle/utils.d.ts +1 -1
  49. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  50. package/dest/entrypoints/client/bundle/utils.js +10 -4
  51. package/dest/entrypoints/client/lazy/index.d.ts +2 -1
  52. package/dest/entrypoints/client/lazy/index.d.ts.map +1 -1
  53. package/dest/entrypoints/client/lazy/index.js +1 -0
  54. package/dest/entrypoints/client/lazy/utils.d.ts +1 -1
  55. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  56. package/dest/entrypoints/client/lazy/utils.js +10 -4
  57. package/dest/entrypoints/client/store.d.ts +14 -0
  58. package/dest/entrypoints/client/store.d.ts.map +1 -0
  59. package/dest/entrypoints/client/store.js +42 -0
  60. package/dest/entrypoints/server/index.d.ts +2 -1
  61. package/dest/entrypoints/server/index.d.ts.map +1 -1
  62. package/dest/entrypoints/server/index.js +1 -0
  63. package/dest/entrypoints/server/store.d.ts +18 -0
  64. package/dest/entrypoints/server/store.d.ts.map +1 -0
  65. package/dest/entrypoints/server/store.js +28 -0
  66. package/dest/entrypoints/server/utils.d.ts +1 -1
  67. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  68. package/dest/entrypoints/server/utils.js +8 -2
  69. package/dest/events/event_service.d.ts +13 -6
  70. package/dest/events/event_service.d.ts.map +1 -1
  71. package/dest/events/event_service.js +13 -13
  72. package/dest/logs/log_service.d.ts +15 -6
  73. package/dest/logs/log_service.d.ts.map +1 -1
  74. package/dest/logs/log_service.js +25 -32
  75. package/dest/messages/tx_resolver_service.d.ts +18 -7
  76. package/dest/messages/tx_resolver_service.d.ts.map +1 -1
  77. package/dest/messages/tx_resolver_service.js +13 -7
  78. package/dest/node/benchmarked_node.d.ts +20 -0
  79. package/dest/node/benchmarked_node.d.ts.map +1 -0
  80. package/dest/node/benchmarked_node.js +94 -0
  81. package/dest/node/caching_aztec_node.d.ts +24 -0
  82. package/dest/node/caching_aztec_node.d.ts.map +1 -0
  83. package/dest/node/caching_aztec_node.js +267 -0
  84. package/dest/notes/note_service.d.ts +13 -7
  85. package/dest/notes/note_service.d.ts.map +1 -1
  86. package/dest/notes/note_service.js +10 -10
  87. package/dest/oracle_version.d.ts +2 -2
  88. package/dest/oracle_version.js +5 -5
  89. package/dest/private_kernel/batch_planner.d.ts +1 -1
  90. package/dest/private_kernel/batch_planner.d.ts.map +1 -1
  91. package/dest/private_kernel/batch_planner.js +1 -1
  92. package/dest/private_kernel/private_kernel_execution_prover.d.ts +1 -1
  93. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  94. package/dest/private_kernel/private_kernel_execution_prover.js +45 -5
  95. package/dest/pxe.d.ts +1 -1
  96. package/dest/pxe.d.ts.map +1 -1
  97. package/dest/pxe.js +39 -19
  98. package/dest/storage/contract_store/contract_store.d.ts +1 -1
  99. package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
  100. package/dest/storage/contract_store/contract_store.js +2 -2
  101. package/dest/storage/fact_store/fact_store.d.ts +1 -1
  102. package/dest/storage/fact_store/fact_store.js +1 -1
  103. package/dest/storage/fact_store/index.d.ts +2 -2
  104. package/dest/storage/fact_store/index.d.ts.map +1 -1
  105. package/dest/storage/fact_store/index.js +1 -1
  106. package/dest/storage/fact_store/origin_state.d.ts +4 -2
  107. package/dest/storage/fact_store/origin_state.d.ts.map +1 -1
  108. package/dest/storage/fact_store/origin_state.js +8 -1
  109. package/dest/storage/store_identity.d.ts +35 -0
  110. package/dest/storage/store_identity.d.ts.map +1 -0
  111. package/dest/storage/store_identity.js +44 -0
  112. package/dest/storage/tagging_store/sender_tagging_store.d.ts +41 -10
  113. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  114. package/dest/storage/tagging_store/sender_tagging_store.js +81 -29
  115. package/dest/tagging/constants.d.ts +10 -1
  116. package/dest/tagging/constants.d.ts.map +1 -1
  117. package/dest/tagging/constants.js +19 -0
  118. package/dest/tagging/get_all_logs_by_tags.d.ts +29 -8
  119. package/dest/tagging/get_all_logs_by_tags.d.ts.map +1 -1
  120. package/dest/tagging/get_all_logs_by_tags.js +29 -10
  121. package/dest/tagging/index.d.ts +3 -3
  122. package/dest/tagging/index.d.ts.map +1 -1
  123. package/dest/tagging/index.js +2 -2
  124. package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts +10 -5
  125. package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts.map +1 -1
  126. package/dest/tagging/recipient_sync/sync_tagged_private_logs.js +69 -38
  127. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +6 -3
  128. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
  129. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +23 -38
  130. package/dest/tagging/sender_sync/utils/classify_pending_txs.d.ts +22 -0
  131. package/dest/tagging/sender_sync/utils/classify_pending_txs.d.ts.map +1 -0
  132. package/dest/tagging/sender_sync/utils/classify_pending_txs.js +61 -0
  133. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +15 -5
  134. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  135. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +36 -35
  136. package/dest/tagging/sender_sync/utils/resolve_pending_txs.d.ts +29 -0
  137. package/dest/tagging/sender_sync/utils/resolve_pending_txs.d.ts.map +1 -0
  138. package/dest/tagging/sender_sync/utils/resolve_pending_txs.js +41 -0
  139. package/dest/tagging/sender_sync/utils/test_utils.d.ts +16 -0
  140. package/dest/tagging/sender_sync/utils/test_utils.d.ts.map +1 -0
  141. package/dest/tagging/sender_sync/utils/test_utils.js +24 -0
  142. package/dest/tagging/testing/tag_query_test_utils.d.ts +6 -0
  143. package/dest/tagging/testing/tag_query_test_utils.d.ts.map +1 -0
  144. package/dest/tagging/testing/tag_query_test_utils.js +10 -0
  145. package/package.json +17 -17
  146. package/src/bin/check_oracle_version.ts +5 -56
  147. package/src/bin/index.ts +1 -5
  148. package/src/bin/oracle_version_helpers.ts +11 -292
  149. package/src/block_synchronizer/block_synchronizer.ts +6 -23
  150. package/src/config/package_info.ts +1 -1
  151. package/src/contract/contract_class_service.ts +8 -46
  152. package/src/contract/helpers.ts +3 -3
  153. package/src/contract/skip_sync_contracts.ts +23 -0
  154. package/src/contract_function_simulator/contract_function_simulator.ts +5 -20
  155. package/src/contract_function_simulator/index.ts +23 -3
  156. package/src/contract_function_simulator/noir-structs/resolved_tx.ts +11 -45
  157. package/src/contract_function_simulator/noir-structs/tx_effect_data.ts +4 -3
  158. package/src/contract_function_simulator/oracle/oracle_registry.ts +12 -8
  159. package/src/contract_function_simulator/oracle/oracle_type_mappings.ts +301 -198
  160. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +19 -9
  161. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +184 -56
  162. package/src/entrypoints/client/bundle/index.ts +1 -0
  163. package/src/entrypoints/client/bundle/utils.ts +15 -3
  164. package/src/entrypoints/client/lazy/index.ts +1 -0
  165. package/src/entrypoints/client/lazy/utils.ts +15 -3
  166. package/src/entrypoints/client/store.ts +54 -0
  167. package/src/entrypoints/server/index.ts +1 -0
  168. package/src/entrypoints/server/store.ts +47 -0
  169. package/src/entrypoints/server/utils.ts +21 -7
  170. package/src/events/event_service.ts +24 -14
  171. package/src/logs/log_service.ts +41 -39
  172. package/src/messages/tx_resolver_service.ts +26 -15
  173. package/src/node/benchmarked_node.ts +140 -0
  174. package/src/node/caching_aztec_node.ts +406 -0
  175. package/src/notes/note_service.ts +20 -14
  176. package/src/oracle_version.ts +5 -5
  177. package/src/private_kernel/batch_planner.ts +0 -1
  178. package/src/private_kernel/private_kernel_execution_prover.ts +90 -4
  179. package/src/pxe.ts +40 -21
  180. package/src/storage/contract_store/contract_store.ts +2 -1
  181. package/src/storage/fact_store/fact_store.ts +1 -1
  182. package/src/storage/fact_store/index.ts +1 -0
  183. package/src/storage/fact_store/origin_state.ts +10 -1
  184. package/src/storage/store_identity.ts +72 -0
  185. package/src/storage/tagging_store/sender_tagging_store.ts +113 -41
  186. package/src/tagging/constants.ts +22 -0
  187. package/src/tagging/get_all_logs_by_tags.ts +47 -11
  188. package/src/tagging/index.ts +7 -2
  189. package/src/tagging/recipient_sync/sync_tagged_private_logs.ts +116 -62
  190. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +27 -57
  191. package/src/tagging/sender_sync/utils/classify_pending_txs.ts +91 -0
  192. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +46 -40
  193. package/src/tagging/sender_sync/utils/resolve_pending_txs.ts +64 -0
  194. package/src/tagging/sender_sync/utils/test_utils.ts +46 -0
  195. package/src/tagging/testing/tag_query_test_utils.ts +11 -0
  196. package/dest/contract_function_simulator/aztec_node_read_cache.d.ts +0 -39
  197. package/dest/contract_function_simulator/aztec_node_read_cache.d.ts.map +0 -1
  198. package/dest/contract_function_simulator/aztec_node_read_cache.js +0 -60
  199. package/dest/contract_function_simulator/benchmarked_node.d.ts +0 -9
  200. package/dest/contract_function_simulator/benchmarked_node.d.ts.map +0 -1
  201. package/dest/contract_function_simulator/benchmarked_node.js +0 -77
  202. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +0 -17
  203. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +0 -1
  204. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +0 -60
  205. package/src/contract_function_simulator/aztec_node_read_cache.ts +0 -89
  206. package/src/contract_function_simulator/benchmarked_node.ts +0 -103
  207. package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +0 -66
@@ -19,9 +19,9 @@ import { Fr } from '@aztec/foundation/curves/bn254';
19
19
  import { EthAddress } from '@aztec/foundation/eth-address';
20
20
  import { FieldReader } from '@aztec/foundation/serialize';
21
21
  import { MembershipWitness, type SiblingPath } from '@aztec/foundation/trees';
22
- import { type ACVMField, fromUintArray } from '@aztec/simulator/client';
22
+ import type { ACVMField } from '@aztec/simulator/client';
23
23
  import { FunctionSelector, NoteSelector } from '@aztec/stdlib/abi';
24
- import { PublicDataWrite, RevertCode } from '@aztec/stdlib/avm';
24
+ import { PublicDataWrite } from '@aztec/stdlib/avm';
25
25
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
26
26
  import { BlockHash } from '@aztec/stdlib/block';
27
27
  import type { ContractInstancePreimage, PartialAddress } from '@aztec/stdlib/contract';
@@ -52,7 +52,12 @@ import {
52
52
  TxHash,
53
53
  } from '@aztec/stdlib/tx';
54
54
 
55
- import type { OriginBlock, RetractableFactOrigin } from '../../storage/fact_store/index.js';
55
+ import {
56
+ type OriginBlock,
57
+ type OriginBlockState,
58
+ type RetractableFactOrigin,
59
+ originBlockStateFromNumber,
60
+ } from '../../storage/fact_store/index.js';
56
61
  import { BoundedVec } from '../noir-structs/bounded_vec.js';
57
62
  import type { ContractClassLogData } from '../noir-structs/contract_class_log_data.js';
58
63
  import type { EmbeddedCurvePoint } from '../noir-structs/embedded_curve_point.js';
@@ -118,6 +123,23 @@ export type SlotShape = 'scalar' | { len: number } | 'variable' | { lenFrom: (si
118
123
  * Either side is optional — output-only types omit `deserialization`, input-only types omit `serialization`.
119
124
  */
120
125
  export interface TypeMapping<T = any> {
126
+ /**
127
+ * Discriminant for this mapping's shape: which scalar or combinator it is. The `is*Mapping` guards switch on it, and
128
+ * the combinators tag it (`'array'`, `'option'`, `'struct'`, ...). For a scalar it also names the Noir-declared type
129
+ * (`'field'`, `'u32'`), which is then its {@link label}.
130
+ */
131
+ kind: string;
132
+ /**
133
+ * The wire-structural Noir type string the oracle interface hash is computed from:
134
+ * - a scalar's is its bare `kind` (`'field'`);
135
+ * - a combinator's is built from its inner mappings' labels (`'array(field,4)'`, `'option(u32)'`);
136
+ * - a struct's is a nameless `'{field,u32}'`, with nested struct labels spliced into the parent.
137
+ *
138
+ * The label encodes exactly what the wire does, so anything the wire tolerates (field names, struct-in-struct
139
+ * nesting, a TS wrapper over a Noir primitive like `TX_HASH` over `Field`) shares a label — and mappings sharing a
140
+ * label must be wire-equivalent.
141
+ */
142
+ label: string;
121
143
  serialization?: {
122
144
  /** Convert a typed value to ACVM output slot(s). */
123
145
  fn: (value: T) => (Fr | Fr[])[];
@@ -154,78 +176,91 @@ export function assertReadersConsumed(readers: FieldReader[]): void {
154
176
 
155
177
  // ─── Scalar Type Mappings ────────────────────────────────────────────────────
156
178
 
157
- export const FIELD: TypeMapping<Fr> = {
179
+ /**
180
+ * A leaf mapping whose {@link TypeMapping.label} is just its bare {@link TypeMapping.kind}. Its wire shape is arbitrary
181
+ * (several fields, or a variable-width run); a single-field scalar uses the shorter {@link SCALAR} instead.
182
+ */
183
+ export function LEAF<T>(mapping: Omit<TypeMapping<T>, 'label'>): TypeMapping<T> {
184
+ return { ...mapping, label: mapping.kind };
185
+ }
186
+
187
+ /** A {@link LEAF} occupying a single `'scalar'` wire slot, so callers omit the shape. */
188
+ export function SCALAR<T>(mapping: Omit<TypeMapping<T>, 'label' | 'shape'>): TypeMapping<T> {
189
+ return LEAF({ ...mapping, shape: ['scalar'] });
190
+ }
191
+
192
+ export const FIELD: TypeMapping<Fr> = SCALAR({
193
+ kind: 'field',
158
194
  serialization: { fn: v => [v] },
159
195
  deserialization: { fn: ([reader]) => reader.readField() },
160
- shape: ['scalar'],
161
- };
196
+ });
162
197
 
163
- export const BOOL: TypeMapping<boolean> = {
198
+ export const BOOL: TypeMapping<boolean> = SCALAR({
199
+ kind: 'bool',
164
200
  serialization: { fn: v => [new Fr(v ? 1n : 0n)] },
165
201
  deserialization: { fn: ([reader]) => !reader.readField().isZero() },
166
- shape: ['scalar'],
167
- };
202
+ });
168
203
 
169
- export const U32: TypeMapping<number> = {
204
+ export const U32: TypeMapping<number> = SCALAR({
205
+ kind: 'u32',
170
206
  serialization: { fn: v => [new Fr(v)] },
171
- deserialization: {
172
- fn: ([reader]) => {
173
- const value = reader.readField().toBigInt();
174
- if (value > 0xffffffffn) {
175
- throw new Error(`U32 overflow: value ${value} exceeds u32 max (${0xffffffffn})`);
176
- }
177
- return Number(value);
178
- },
179
- },
180
- shape: ['scalar'],
181
- };
207
+ deserialization: { fn: ([reader]) => Number(uintFromField(reader.readField(), 32)) },
208
+ });
182
209
 
183
- export const BLOCK_NUMBER: TypeMapping<BlockNumber> = {
184
- serialization: { fn: v => [new Fr(v)] },
185
- deserialization: { fn: ([reader]) => BlockNumber(reader.readField().toNumber()) },
186
- shape: ['scalar'],
187
- };
210
+ export const BLOCK_NUMBER: TypeMapping<BlockNumber> = ALIAS(U32, {
211
+ wrap: v => BlockNumber(v),
212
+ unwrap: v => v,
213
+ });
188
214
 
189
- /** A u8 byte: serializes to a single Fr; deserializes from a single Fr to a number in [0, 255]. */
190
- export const BYTE: TypeMapping<number> = {
215
+ export const U8: TypeMapping<number> = SCALAR({
216
+ kind: 'u8',
191
217
  serialization: { fn: byte => [new Fr(byte)] },
192
- deserialization: {
193
- fn: ([reader]) => {
194
- const value = reader.readField().toBigInt();
195
- if (value > 0xffn) {
196
- throw new Error(`BYTE overflow: value ${value} exceeds u8 max (255)`);
197
- }
198
- return Number(value);
199
- },
200
- },
201
- shape: ['scalar'],
202
- };
218
+ deserialization: { fn: ([reader]) => Number(uintFromField(reader.readField(), 8)) },
219
+ });
203
220
 
204
221
  // Noir passes `MessageDelivery` onchain variants here.
205
- export const DELIVERY_MODE: TypeMapping<AppTaggingSecretKind> = {
222
+ export const DELIVERY_MODE: TypeMapping<AppTaggingSecretKind> = SCALAR({
223
+ kind: 'onchain-delivery-mode',
206
224
  deserialization: {
207
- fn: readers => appTaggingSecretKindFromDeliveryMode(BYTE.deserialization!.fn(readers)),
225
+ fn: readers => appTaggingSecretKindFromDeliveryMode(U8.deserialization!.fn(readers)),
208
226
  },
209
- shape: BYTE.shape,
210
- };
227
+ });
211
228
 
212
- export const RESOLVED_TAGGING_STRATEGY: TypeMapping<ResolvedTaggingStrategy> = {
229
+ export const RESOLVED_TAGGING_STRATEGY: TypeMapping<ResolvedTaggingStrategy> = LEAF({
230
+ kind: 'resolved-tagging-strategy',
213
231
  serialization: { fn: resolved => resolvedTaggingStrategyToFields(resolved) },
214
232
  deserialization: {
215
233
  fn: ([kindReader, secretReader]) =>
216
234
  resolvedTaggingStrategyFromFields(kindReader.readField().toNumber(), secretReader.readField()),
217
235
  },
218
236
  shape: ['scalar', 'scalar'],
219
- };
237
+ });
220
238
 
221
- export const BIGINT: TypeMapping<bigint> = {
239
+ export const BIGINT: TypeMapping<bigint> = ALIAS(FIELD, {
240
+ wrap: f => f.toBigInt(),
241
+ unwrap: v => new Fr(v),
242
+ });
243
+
244
+ export const U64: TypeMapping<bigint> = SCALAR({
245
+ kind: 'u64',
222
246
  serialization: { fn: v => [new Fr(v)] },
223
- deserialization: { fn: ([reader]) => reader.readField().toBigInt() },
224
- shape: ['scalar'],
225
- };
247
+ deserialization: { fn: ([reader]) => uintFromField(reader.readField(), 64) },
248
+ });
249
+
250
+ export const U128: TypeMapping<bigint> = SCALAR({
251
+ kind: 'u128',
252
+ serialization: { fn: v => [new Fr(v)] },
253
+ deserialization: { fn: ([reader]) => uintFromField(reader.readField(), 128) },
254
+ });
255
+
256
+ export const LEAF_INDEX: TypeMapping<number> = ALIAS(FIELD, {
257
+ wrap: f => f.toNumber(),
258
+ unwrap: v => new Fr(v),
259
+ });
226
260
 
227
261
  /** Reads every field in the slot as a UTF-8 character code. */
228
- export const STR: TypeMapping<string> = {
262
+ export const STR: TypeMapping<string> = LEAF({
263
+ kind: 'str',
229
264
  serialization: { fn: str => [Array.from(Buffer.from(str, 'utf-8')).map(b => new Fr(b))] },
230
265
  deserialization: {
231
266
  fn: ([reader]) => {
@@ -237,69 +272,64 @@ export const STR: TypeMapping<string> = {
237
272
  },
238
273
  },
239
274
  shape: ['variable'],
240
- };
275
+ });
241
276
 
242
- export const AZTEC_ADDRESS: TypeMapping<AztecAddress> = {
277
+ export const AZTEC_ADDRESS: TypeMapping<AztecAddress> = SCALAR({
278
+ kind: 'aztec-address',
243
279
  serialization: { fn: v => [v.toField()] },
244
280
  deserialization: { fn: ([reader]) => AztecAddress.fromFieldUnsafe(reader.readField()) },
245
- shape: ['scalar'],
246
- };
281
+ });
247
282
 
248
- export const BLOCK_HASH: TypeMapping<BlockHash> = {
249
- serialization: { fn: v => [new Fr(v.toBuffer())] },
250
- deserialization: { fn: ([reader]) => new BlockHash(reader.readField()) },
251
- shape: ['scalar'],
252
- };
283
+ export const BLOCK_HASH: TypeMapping<BlockHash> = ALIAS(FIELD, {
284
+ wrap: f => new BlockHash(f),
285
+ unwrap: v => new Fr(v.toBuffer()),
286
+ });
253
287
 
254
- export const FUNCTION_SELECTOR: TypeMapping<FunctionSelector> = {
288
+ export const FUNCTION_SELECTOR: TypeMapping<FunctionSelector> = SCALAR({
289
+ kind: 'function-selector',
255
290
  serialization: { fn: v => [v.toField()] },
256
291
  deserialization: { fn: ([reader]) => FunctionSelector.fromField(reader.readField()) },
257
- shape: ['scalar'],
258
- };
292
+ });
259
293
 
260
- export const NOTE_SELECTOR: TypeMapping<NoteSelector> = {
261
- serialization: { fn: v => [v.toField()] },
262
- deserialization: { fn: ([reader]) => NoteSelector.fromField(reader.readField()) },
263
- shape: ['scalar'],
264
- };
294
+ export const NOTE_SELECTOR: TypeMapping<NoteSelector> = ALIAS(FIELD, {
295
+ wrap: f => NoteSelector.fromField(f),
296
+ unwrap: v => v.toField(),
297
+ });
265
298
 
266
- export const TX_HASH: TypeMapping<TxHash> = {
267
- serialization: { fn: v => [v.hash] },
268
- deserialization: { fn: ([reader]) => TxHash.fromField(reader.readField()) },
269
- shape: ['scalar'],
270
- };
299
+ export const TX_HASH: TypeMapping<TxHash> = ALIAS(FIELD, {
300
+ wrap: f => TxHash.fromField(f),
301
+ unwrap: v => v.hash,
302
+ });
271
303
 
272
- const TAG: TypeMapping<Tag> = {
273
- serialization: { fn: v => [v.value] },
274
- deserialization: { fn: ([reader]) => new Tag(reader.readField()) },
275
- shape: ['scalar'],
276
- };
304
+ const TAG: TypeMapping<Tag> = ALIAS(FIELD, {
305
+ wrap: f => new Tag(f),
306
+ unwrap: v => v.value,
307
+ });
277
308
 
278
309
  export const POINT: TypeMapping<EmbeddedCurvePoint> = STRUCT([
279
310
  { name: 'x', type: FIELD },
280
311
  { name: 'y', type: FIELD },
281
312
  ]);
282
313
 
283
- const LOG_SOURCE: TypeMapping<LogSource> = {
314
+ const LOG_SOURCE: TypeMapping<LogSource> = SCALAR({
315
+ kind: 'log-source',
284
316
  serialization: { fn: v => [new Fr(v)] },
285
317
  deserialization: { fn: ([reader]) => logSourceFromField(reader.readField()) },
286
- shape: ['scalar'],
287
- };
318
+ });
288
319
 
289
- export const ETH_ADDRESS: TypeMapping<EthAddress> = {
320
+ export const ETH_ADDRESS: TypeMapping<EthAddress> = SCALAR({
321
+ kind: 'eth-address',
290
322
  serialization: { fn: v => [v.toField()] },
291
323
  deserialization: { fn: ([reader]) => EthAddress.fromField(reader.readField()) },
292
- shape: ['scalar'],
293
- };
324
+ });
294
325
 
295
- export const SLOT_NUMBER: TypeMapping<SlotNumber> = {
296
- serialization: { fn: v => [new Fr(v)] },
297
- shape: ['scalar'],
298
- };
326
+ export const SLOT_NUMBER: TypeMapping<SlotNumber> = ALIAS(FIELD, {
327
+ unwrap: v => new Fr(v),
328
+ });
299
329
 
300
330
  const APPEND_ONLY_TREE_SNAPSHOT: TypeMapping<AppendOnlyTreeSnapshot> = STRUCT<AppendOnlyTreeSnapshot>([
301
331
  { name: 'root', type: FIELD },
302
- { name: 'nextAvailableLeafIndex', type: U32 },
332
+ { name: 'nextAvailableLeafIndex', type: LEAF_INDEX },
303
333
  ]);
304
334
 
305
335
  const PARTIAL_STATE_REFERENCE: TypeMapping<PartialStateReference> = STRUCT<PartialStateReference>([
@@ -314,8 +344,8 @@ const STATE_REFERENCE: TypeMapping<StateReference> = STRUCT<StateReference>([
314
344
  ]);
315
345
 
316
346
  const GAS_FEES: TypeMapping<GasFees> = STRUCT<GasFees>([
317
- { name: 'feePerDaGas', type: BIGINT },
318
- { name: 'feePerL2Gas', type: BIGINT },
347
+ { name: 'feePerDaGas', type: U128 },
348
+ { name: 'feePerL2Gas', type: U128 },
319
349
  ]);
320
350
 
321
351
  const GLOBAL_VARIABLES: TypeMapping<GlobalVariables> = STRUCT<GlobalVariables>([
@@ -323,7 +353,7 @@ const GLOBAL_VARIABLES: TypeMapping<GlobalVariables> = STRUCT<GlobalVariables>([
323
353
  { name: 'version', type: FIELD },
324
354
  { name: 'blockNumber', type: BLOCK_NUMBER },
325
355
  { name: 'slotNumber', type: SLOT_NUMBER },
326
- { name: 'timestamp', type: BIGINT },
356
+ { name: 'timestamp', type: U64 },
327
357
  { name: 'coinbase', type: ETH_ADDRESS },
328
358
  { name: 'feeRecipient', type: AZTEC_ADDRESS },
329
359
  { name: 'gasFees', type: GAS_FEES },
@@ -427,11 +457,6 @@ export const CONTRACT_CLASS_LOG: TypeMapping<ContractClassLogData> = STRUCT([
427
457
  { name: 'emittedLength', type: U32 },
428
458
  ]);
429
459
 
430
- const REVERT_CODE: TypeMapping<RevertCode> = {
431
- serialization: { fn: rc => [rc.toField()] },
432
- shape: ['scalar'],
433
- };
434
-
435
460
  const PUBLIC_DATA_WRITE: TypeMapping<PublicDataWrite> = STRUCT<PublicDataWrite>([
436
461
  { name: 'leafSlot', type: FIELD },
437
462
  { name: 'value', type: FIELD },
@@ -459,7 +484,7 @@ const CONTRACT_CLASS_LOGS: TypeMapping<ContractClassLogData[]> = FIXED_ARRAY(
459
484
  );
460
485
 
461
486
  export const TX_EFFECT: TypeMapping<TxEffectData> = STRUCT<TxEffectData>([
462
- { name: 'revertCode', type: REVERT_CODE },
487
+ { name: 'revertCode', type: U8 },
463
488
  { name: 'txHash', type: TX_HASH },
464
489
  { name: 'transactionFee', type: FIELD },
465
490
  { name: 'noteHashes', type: FIXED_ARRAY(FIELD, MAX_NOTE_HASHES_PER_TX) },
@@ -471,7 +496,8 @@ export const TX_EFFECT: TypeMapping<TxEffectData> = STRUCT<TxEffectData>([
471
496
  { name: 'contractClassLogs', type: CONTRACT_CLASS_LOGS },
472
497
  ]);
473
498
 
474
- export const NOTE: TypeMapping<NoteData> = {
499
+ export const NOTE: TypeMapping<NoteData> = LEAF({
500
+ kind: 'note',
475
501
  serialization: {
476
502
  fn: noteData =>
477
503
  packAsHintedNote({
@@ -487,21 +513,23 @@ export const NOTE: TypeMapping<NoteData> = {
487
513
  // A packed note is the note's (variable-count) field items followed by 6 metadata scalars, emitted as one field
488
514
  // output per element. Its length depends on the note, so it is described as a single variable-width run.
489
515
  shape: ['variable'],
490
- };
516
+ });
491
517
 
492
- export const NOTE_VALIDATION_REQUEST: TypeMapping<NoteValidationRequest> = {
518
+ export const NOTE_VALIDATION_REQUEST: TypeMapping<NoteValidationRequest> = LEAF({
519
+ kind: 'note-validation-request',
493
520
  deserialization: {
494
521
  fn: ([reader]) => NoteValidationRequest.fromFields(reader),
495
522
  },
496
523
  shape: ['variable'],
497
- };
524
+ });
498
525
 
499
- export const EVENT_VALIDATION_REQUEST: TypeMapping<EventValidationRequest> = {
526
+ export const EVENT_VALIDATION_REQUEST: TypeMapping<EventValidationRequest> = LEAF({
527
+ kind: 'event-validation-request',
500
528
  deserialization: {
501
529
  fn: ([reader]) => EventValidationRequest.fromFields(reader),
502
530
  },
503
531
  shape: ['variable'],
504
- };
532
+ });
505
533
 
506
534
  export const LOG_RETRIEVAL_REQUEST: TypeMapping<LogRetrievalRequest> = STRUCT<LogRetrievalRequest>([
507
535
  { name: 'contractAddress', type: AZTEC_ADDRESS },
@@ -517,82 +545,88 @@ export const LOG_RETRIEVAL_RESPONSE: TypeMapping<LogRetrievalResponse> = STRUCT<
517
545
  { name: 'uniqueNoteHashesInTx', type: FIXED_BOUNDED_VEC(FIELD, MAX_NOTE_HASHES_PER_TX) },
518
546
  { name: 'firstNullifierInTx', type: FIELD },
519
547
  { name: 'blockNumber', type: BLOCK_NUMBER },
520
- { name: 'blockTimestamp', type: BIGINT },
548
+ { name: 'blockTimestamp', type: U64 },
521
549
  { name: 'blockHash', type: BLOCK_HASH },
522
550
  ]);
523
551
 
524
- // `ResolvedTx.toFields()` packs the whole struct into a single slot: txHash, the uniqueNoteHashesInTx BoundedVec
525
- // (MAX_NOTE_HASHES_PER_TX storage fields + length), firstNullifierInTx, blockNumber and blockHash.
526
- export const RESOLVED_TX: TypeMapping<ResolvedTx> = {
527
- serialization: { fn: resolved => [resolved.toFields()] },
528
- shape: [{ len: MAX_NOTE_HASHES_PER_TX + 5 }],
529
- };
552
+ export const RESOLVED_TX: TypeMapping<ResolvedTx> = STRUCT([
553
+ { name: 'txHash', type: TX_HASH },
554
+ { name: 'uniqueNoteHashesInTx', type: FIXED_BOUNDED_VEC(FIELD, MAX_NOTE_HASHES_PER_TX) },
555
+ { name: 'firstNullifierInTx', type: FIELD },
556
+ { name: 'blockNumber', type: U32 },
557
+ { name: 'blockHash', type: FIELD },
558
+ ]);
530
559
 
531
560
  export const PENDING_TAGGED_LOG: TypeMapping<PendingTaggedLog> = STRUCT([
532
561
  { name: 'log', type: FIXED_BOUNDED_VEC(FIELD, PRIVATE_LOG_SIZE_IN_FIELDS) },
533
562
  { name: 'context', type: RESOLVED_TX },
534
563
  ]);
535
564
 
536
- export const ORIGIN_BLOCK: TypeMapping<OriginBlock> = {
537
- serialization: { fn: ob => [new Fr(ob.blockNumber), ob.blockHash] },
538
- deserialization: {
539
- fn: ([blockNumberReader, blockHashReader]) => ({
540
- blockNumber: blockNumberReader.readField().toNumber(),
541
- blockHash: blockHashReader.readField(),
542
- }),
543
- },
544
- shape: ['scalar', 'scalar'],
545
- };
565
+ export const ORIGIN_BLOCK: TypeMapping<OriginBlock> = STRUCT([
566
+ { name: 'blockNumber', type: U32 },
567
+ { name: 'blockHash', type: FIELD },
568
+ ]);
569
+
570
+ const ORIGIN_BLOCK_STATE: TypeMapping<OriginBlockState> = SCALAR({
571
+ kind: 'origin-block-state',
572
+ serialization: { fn: v => [new Fr(v)] },
573
+ deserialization: { fn: ([reader]) => originBlockStateFromNumber(reader.readField().toNumber()) },
574
+ });
546
575
 
547
576
  /** Read-side origin of a retractable fact, carrying the chain state of its origin block. */
548
- export const RETRACTABLE_FACT_ORIGIN: TypeMapping<RetractableFactOrigin> = {
549
- serialization: { fn: o => [new Fr(o.blockNumber), o.blockHash, new Fr(o.blockState)] },
550
- shape: ['scalar', 'scalar', 'scalar'],
551
- };
577
+ export const RETRACTABLE_FACT_ORIGIN: TypeMapping<RetractableFactOrigin> = STRUCT([
578
+ { name: 'blockNumber', type: U32 },
579
+ { name: 'blockHash', type: FIELD },
580
+ { name: 'blockState', type: ORIGIN_BLOCK_STATE },
581
+ ]);
552
582
 
553
- // `facts` and `payload` each materialize to a single service-slot id, so a Fact occupies: factTypeId, the payload
554
- // array slot, and `OPTION(RETRACTABLE_FACT_ORIGIN)` (its discriminant plus RETRACTABLE_FACT_ORIGIN's three slots).
555
- export const FACT: TypeMapping<Fact> = {
556
- serialization: {
557
- fn: f => [
558
- f.factTypeId,
559
- f.payload.materializeSlot(v => FIELD.serialization!.fn(v).flat() as Fr[]),
560
- ...OPTION(RETRACTABLE_FACT_ORIGIN).serialization!.fn(f.originBlock),
561
- ],
562
- },
563
- shape: ['scalar', 'scalar', 'scalar', 'scalar', 'scalar', 'scalar'],
564
- };
583
+ export const FACT: TypeMapping<Fact> = STRUCT([
584
+ { name: 'factTypeId', type: FIELD },
585
+ { name: 'payload', type: EPHEMERAL_ARRAY(FIELD) },
586
+ { name: 'originBlock', type: OPTION(RETRACTABLE_FACT_ORIGIN) },
587
+ ]);
565
588
 
566
- export const FACT_COLLECTION: TypeMapping<FactCollection> = {
567
- serialization: {
568
- fn: c => [
569
- c.contractAddress.toField(),
570
- c.scope.toField(),
571
- c.factCollectionTypeId,
572
- c.factCollectionId,
573
- c.facts.materializeSlot(v => FACT.serialization!.fn(v).flat() as Fr[]),
574
- ],
575
- },
576
- shape: ['scalar', 'scalar', 'scalar', 'scalar', 'scalar'],
577
- };
589
+ export const FACT_COLLECTION: TypeMapping<FactCollection> = STRUCT([
590
+ { name: 'contractAddress', type: AZTEC_ADDRESS },
591
+ { name: 'scope', type: AZTEC_ADDRESS },
592
+ { name: 'factCollectionTypeId', type: FIELD },
593
+ { name: 'factCollectionId', type: FIELD },
594
+ { name: 'facts', type: EPHEMERAL_ARRAY(FACT) },
595
+ ]);
578
596
 
579
- export const PROVIDED_SECRET: TypeMapping<ProvidedSecret> = {
580
- deserialization: {
581
- fn: ([reader]) => ({
582
- secret: reader.readField(),
583
- mode: appTaggingSecretKindFromDeliveryMode(BYTE.deserialization!.fn([reader])),
584
- }),
585
- },
586
- shape: [{ len: 2 }],
587
- };
597
+ export const PROVIDED_SECRET: TypeMapping<ProvidedSecret> = STRUCT([
598
+ { name: 'secret', type: FIELD },
599
+ { name: 'mode', type: DELIVERY_MODE },
600
+ ]);
588
601
 
589
602
  // ─── Combinator Type Mappings ────────────────────────────────────────────────
590
603
 
591
- function SIBLING_PATH<N extends number>(height: N): TypeMapping<SiblingPath<N>> {
604
+ /**
605
+ * A TS-side alias of `base`: the same Noir type on the wire (it inherits `base`'s `kind`, `label`, and `shape`) but a
606
+ * richer TS value. `wrap` maps a base value to the alias, `unwrap` back; a serialize-only alias passes only `unwrap`.
607
+ */
608
+ export function ALIAS<B, T>(
609
+ base: TypeMapping<B>,
610
+ conversions: { wrap?: (value: B) => T; unwrap?: (value: T) => B },
611
+ ): TypeMapping<T> {
612
+ const { wrap, unwrap } = conversions;
592
613
  return {
614
+ kind: base.kind,
615
+ label: base.label,
616
+ serialization: base.serialization && unwrap ? { fn: value => base.serialization!.fn(unwrap(value)) } : undefined,
617
+ deserialization:
618
+ base.deserialization && wrap ? { fn: readers => wrap(base.deserialization!.fn(readers)) } : undefined,
619
+ shape: base.shape,
620
+ };
621
+ }
622
+
623
+ function SIBLING_PATH<N extends number>(height: N): TypeMapping<SiblingPath<N>> {
624
+ return LEAF({
625
+ // On the wire (and in Noir) a sibling path is a plain `[Field; height]`, so it shares the fixed-array kind.
626
+ kind: `array(field,${height})`,
593
627
  serialization: { fn: sp => [sp.toFields()] },
594
628
  shape: [{ len: height }],
595
- };
629
+ });
596
630
  }
597
631
 
598
632
  export function MEMBERSHIP_WITNESS<N extends number>(height: N): TypeMapping<MembershipWitness<N>> {
@@ -602,9 +636,10 @@ export function MEMBERSHIP_WITNESS<N extends number>(height: N): TypeMapping<Mem
602
636
  ]);
603
637
  }
604
638
 
605
- export function ARRAY<T>(inner: TypeMapping<T>): TypeMapping<T[]> & { kind: 'array'; inner: TypeMapping<T> } {
639
+ export function ARRAY<T>(inner: TypeMapping<T>): ArrayMapping<T> {
606
640
  return {
607
641
  kind: 'array',
642
+ label: `array(${inner.label})`,
608
643
  inner,
609
644
  serialization: inner.serialization ? { fn: values => [packElements(inner, values)] } : undefined,
610
645
  deserialization: inner.deserialization
@@ -623,13 +658,11 @@ export function ARRAY<T>(inner: TypeMapping<T>): TypeMapping<T[]> & { kind: 'arr
623
658
  * zero-padded to exactly `length * elementWidth` fields, and deserializes all `length` elements back. An absent
624
659
  * element is the zero encoding, so the padding is derived from the shape.
625
660
  */
626
- export function FIXED_ARRAY<T>(
627
- element: TypeMapping<T>,
628
- length: number,
629
- ): TypeMapping<T[]> & { kind: 'fixed-array'; inner: TypeMapping<T>; length: number } {
661
+ export function FIXED_ARRAY<T>(element: TypeMapping<T>, length: number): FixedArrayMapping<T> {
630
662
  const elementWidth = fieldWidth(element.shape);
631
663
  return {
632
664
  kind: 'fixed-array',
665
+ label: `array(${element.label},${length})`,
633
666
  inner: element,
634
667
  length,
635
668
  serialization: element.serialization
@@ -649,17 +682,16 @@ export function FIXED_ARRAY<T>(
649
682
  *
650
683
  * Both directions are derived from `element`: bidirectional iff `element` has both serialization and deserialization.
651
684
  *
652
- * @example Serializing `BoundedVec.from({ data: [0x41, 0x42], maxLength: 4 })` with `BOUNDED_VEC(BYTE)`:
685
+ * @example Serializing `BoundedVec.from({ data: [0x41, 0x42], maxLength: 4 })` with `BOUNDED_VEC(U8)`:
653
686
  * ```
654
687
  * slot 0: [Fr(0x41), Fr(0x42), Fr(0), Fr(0)] // data padded to maxLength
655
688
  * slot 1: Fr(2) // actual length
656
689
  * ```
657
690
  */
658
- export function BOUNDED_VEC<T>(
659
- inner: TypeMapping<T>,
660
- ): TypeMapping<BoundedVec<T>> & { kind: 'bounded-vec'; inner: TypeMapping<T> } {
691
+ export function BOUNDED_VEC<T>(inner: TypeMapping<T>): BoundedVecMapping<T> {
661
692
  return {
662
693
  kind: 'bounded-vec',
694
+ label: `bounded-vec(${inner.label})`,
663
695
  inner,
664
696
  serialization: inner.serialization
665
697
  ? {
@@ -680,8 +712,23 @@ export function BOUNDED_VEC<T>(
680
712
  fn: ([storageReader, lengthReader]) => {
681
713
  // slot 0 is the padded storage, slot 1 the actual length. Parse only the first `length` elements out of
682
714
  // storage, then drain the trailing zero-padding so the storage reader is fully consumed.
683
- const maxLength = storageReader.remainingFields() / fieldWidth(inner.shape);
715
+ const elementWidth = fieldWidth(inner.shape);
716
+ const storageFields = storageReader.remainingFields();
717
+ if (storageFields % elementWidth !== 0) {
718
+ throw new Error(
719
+ `Malformed BoundedVec: storage array holds ${storageFields} field(s), which is not a whole number of ` +
720
+ `${elementWidth}-field elements`,
721
+ );
722
+ }
723
+ // The BoundedVec's maximum length is determined by the size of the storage array, so its length is bounded
724
+ // by it.
725
+ const maxLength = storageFields / elementWidth;
684
726
  const length = lengthReader.readField().toNumber();
727
+ if (length > maxLength) {
728
+ throw new Error(
729
+ `Malformed BoundedVec: length ${length} exceeds the ${maxLength} element(s) its storage array holds`,
730
+ );
731
+ }
685
732
  const elements = unpackElements(inner, storageReader, length);
686
733
  storageReader.skip(storageReader.remainingFields());
687
734
  return BoundedVec.from<T>({ data: elements, maxLength });
@@ -698,13 +745,11 @@ export function BOUNDED_VEC<T>(
698
745
  * (zero-padded) followed by the actual length, with no length prefix, so the width is statically known. Serialize-only.
699
746
  * Throws if the input exceeds `maxLength`.
700
747
  */
701
- export function FIXED_BOUNDED_VEC<T>(
702
- element: TypeMapping<T>,
703
- maxLength: number,
704
- ): TypeMapping<T[]> & { kind: 'fixed-bounded-vec'; inner: TypeMapping<T>; maxLength: number } {
748
+ export function FIXED_BOUNDED_VEC<T>(element: TypeMapping<T>, maxLength: number): FixedBoundedVecMapping<T> {
705
749
  const width = fieldWidth(element.shape);
706
750
  return {
707
751
  kind: 'fixed-bounded-vec',
752
+ label: `bounded-vec(${element.label},${maxLength})`,
708
753
  inner: element,
709
754
  maxLength,
710
755
  serialization: element.serialization
@@ -740,9 +785,10 @@ export function FIXED_BOUNDED_VEC<T>(
740
785
  * slot 1: Fr(0) // zero-filled from AZTEC_ADDRESS.shape
741
786
  * ```
742
787
  */
743
- export function OPTION<T>(inner: TypeMapping<T>): TypeMapping<Option<T>> & { kind: 'option'; inner: TypeMapping<T> } {
788
+ export function OPTION<T>(inner: TypeMapping<T>): OptionMapping<T> {
744
789
  return {
745
790
  kind: 'option',
791
+ label: `option(${inner.label})`,
746
792
  inner,
747
793
  serialization: inner.serialization
748
794
  ? {
@@ -770,32 +816,14 @@ export function OPTION<T>(inner: TypeMapping<T>): TypeMapping<Option<T>> & { kin
770
816
  };
771
817
  }
772
818
 
773
- /**
774
- * A fixed packed uint buffer (Noir `[u8; length]` at `bitSize` 8): one slot of `length` packed uint values ↔ `Buffer`.
775
- * `length` is the field count, which equals the byte count at `bitSize` 8.
776
- */
777
- export function BUFFER(bitSize: number, length: number): TypeMapping<Buffer> {
778
- return {
779
- serialization: {
780
- fn: buf => [Array.from(buf).map(b => new Fr(b))],
781
- },
782
- deserialization: {
783
- fn: ([reader]) =>
784
- fromUintArray(
785
- reader.readFieldArray(length).map(f => f.toString()),
786
- bitSize,
787
- ),
788
- },
789
- shape: [{ len: length }],
790
- };
791
- }
792
-
793
- export function EPHEMERAL_ARRAY<T>(element: TypeMapping<T>): TypeMapping<EphemeralArray<T>> {
819
+ export function EPHEMERAL_ARRAY<T>(element: TypeMapping<T>): EphemeralArrayMapping<T> {
794
820
  // EphemeralArray.readAll hands each row's flat fields in as a single reader; reconstruct the element's per-slot
795
821
  // readers from its shape, deserialize, and assert the row was fully consumed so a row with trailing fields is
796
822
  // rejected.
797
823
  const rowElement: TypeMapping<T> | undefined = element.deserialization
798
824
  ? {
825
+ kind: element.kind,
826
+ label: element.label,
799
827
  deserialization: {
800
828
  fn: ([rowReader]) => deserializeElement(element, rowReader.readFieldArray(rowReader.remainingFields())),
801
829
  },
@@ -805,6 +833,9 @@ export function EPHEMERAL_ARRAY<T>(element: TypeMapping<T>): TypeMapping<Ephemer
805
833
  }
806
834
  : undefined;
807
835
  return {
836
+ kind: 'ephemeral-array',
837
+ label: `ephemeral-array(${element.label})`,
838
+ inner: element,
808
839
  serialization: element.serialization
809
840
  ? { fn: ea => [ea.materializeSlot(v => serializeElement(element, v))] }
810
841
  : undefined,
@@ -824,11 +855,10 @@ export type StructField<TName extends string = string, T = any> = { name: TName;
824
855
  * `shape`. `T` is the struct's TS value type and must match the field layout — serialization reads each field by name
825
856
  * off the value, deserialization returns the decoded bag as `T`; convert in the handler, not here, when `T` differs.
826
857
  */
827
- export function STRUCT<T>(
828
- fields: readonly StructField[],
829
- ): TypeMapping<T> & { kind: 'struct'; fields: readonly StructField[] } {
858
+ export function STRUCT<T>(fields: readonly StructField[]): StructMapping<T> {
830
859
  return {
831
860
  kind: 'struct',
861
+ label: `{${structFieldLabels(fields)}}`,
832
862
  fields,
833
863
  serialization: fields.every(f => f.type.serialization)
834
864
  ? {
@@ -856,6 +886,79 @@ export function STRUCT<T>(
856
886
  };
857
887
  }
858
888
 
889
+ /**
890
+ * The composite `TypeMapping`s (`ARRAY`/`BOUNDED_VEC`/`OPTION`/`STRUCT`/`FIXED_ARRAY`/`FIXED_BOUNDED_VEC`/
891
+ * `EPHEMERAL_ARRAY`), discriminated by `kind`. The combinators attach `kind` plus the inner mapping(s) at construction;
892
+ * the registry erases params to the base `TypeMapping`, so the guards below recover the structure for recursion.
893
+ */
894
+ export type ArrayMapping<T = any> = TypeMapping<T[]> & { kind: 'array'; inner: TypeMapping<T> };
895
+ export type BoundedVecMapping<T = any> = TypeMapping<BoundedVec<T>> & { kind: 'bounded-vec'; inner: TypeMapping<T> };
896
+ export type OptionMapping<T = any> = TypeMapping<Option<T>> & { kind: 'option'; inner: TypeMapping<T> };
897
+ export type StructMapping<T = any> = TypeMapping<T> & { kind: 'struct'; fields: readonly StructField[] };
898
+ export type FixedArrayMapping<T = any> = TypeMapping<T[]> & {
899
+ kind: 'fixed-array';
900
+ inner: TypeMapping<T>;
901
+ length: number;
902
+ };
903
+ export type FixedBoundedVecMapping<T = any> = TypeMapping<T[]> & {
904
+ kind: 'fixed-bounded-vec';
905
+ inner: TypeMapping<T>;
906
+ maxLength: number;
907
+ };
908
+ export type EphemeralArrayMapping<T = any> = TypeMapping<EphemeralArray<T>> & {
909
+ kind: 'ephemeral-array';
910
+ inner: TypeMapping<T>;
911
+ };
912
+ export type CompositeMapping =
913
+ | ArrayMapping
914
+ | BoundedVecMapping
915
+ | OptionMapping
916
+ | StructMapping
917
+ | FixedArrayMapping
918
+ | FixedBoundedVecMapping
919
+ | EphemeralArrayMapping;
920
+
921
+ export function isArrayMapping(type: TypeMapping<any>): type is ArrayMapping {
922
+ return type.kind === 'array';
923
+ }
924
+ export function isBoundedVecMapping(type: TypeMapping<any>): type is BoundedVecMapping {
925
+ return type.kind === 'bounded-vec';
926
+ }
927
+ export function isOptionMapping(type: TypeMapping<any>): type is OptionMapping {
928
+ return type.kind === 'option';
929
+ }
930
+ export function isStructMapping(type: TypeMapping<any>): type is StructMapping {
931
+ return type.kind === 'struct';
932
+ }
933
+ export function isFixedArrayMapping(type: TypeMapping<any>): type is FixedArrayMapping {
934
+ return type.kind === 'fixed-array';
935
+ }
936
+ export function isFixedBoundedVecMapping(type: TypeMapping<any>): type is FixedBoundedVecMapping {
937
+ return type.kind === 'fixed-bounded-vec';
938
+ }
939
+ export function isEphemeralArrayMapping(type: TypeMapping<any>): type is EphemeralArrayMapping {
940
+ return type.kind === 'ephemeral-array';
941
+ }
942
+
943
+ /**
944
+ * The comma-joined labels of a struct's fields, with nested struct labels spliced into the parent: nesting affects
945
+ * neither the wire (struct slots concatenate) nor the interface, so the label treats nested and flat declarations as
946
+ * the same type.
947
+ */
948
+ function structFieldLabels(fields: readonly StructField[]): string {
949
+ return fields.map(f => (isStructMapping(f.type) ? structFieldLabels(f.type.fields) : f.type.label)).join(',');
950
+ }
951
+
952
+ /** The field's value as a bigint, asserted to fit a `bits`-wide uint. */
953
+ function uintFromField(field: Fr, bits: number): bigint {
954
+ const max = 2n ** BigInt(bits) - 1n;
955
+ const value = field.toBigInt();
956
+ if (value > max) {
957
+ throw new Error(`u${bits} overflow: value ${value} exceeds max (${max})`);
958
+ }
959
+ return value;
960
+ }
961
+
859
962
  /** Number of InputSlots a deserializable type spans, derived from its {@link TypeMapping.shape}. */
860
963
  export function slotsOf(mapping: TypeMapping): number {
861
964
  return mapping.shape.length;