@aztec/stdlib 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 (231) hide show
  1. package/dest/abi/abi.d.ts +32 -7
  2. package/dest/abi/abi.d.ts.map +1 -1
  3. package/dest/abi/abi.js +11 -2
  4. package/dest/abi/contract_artifact.d.ts +1 -14
  5. package/dest/abi/contract_artifact.d.ts.map +1 -1
  6. package/dest/abi/contract_artifact.js +35 -36
  7. package/dest/abi/decoder.d.ts +12 -5
  8. package/dest/abi/decoder.d.ts.map +1 -1
  9. package/dest/abi/decoder.js +18 -21
  10. package/dest/abi/function_call.d.ts +17 -8
  11. package/dest/abi/function_call.d.ts.map +1 -1
  12. package/dest/abi/function_call.js +28 -9
  13. package/dest/avm/avm.d.ts +212 -211
  14. package/dest/avm/avm.d.ts.map +1 -1
  15. package/dest/avm/avm.js +3 -1
  16. package/dest/avm/index.d.ts +1 -6
  17. package/dest/avm/index.d.ts.map +1 -1
  18. package/dest/avm/index.js +0 -5
  19. package/dest/block/l2_block_source.d.ts +1 -1
  20. package/dest/block/l2_block_source.d.ts.map +1 -1
  21. package/dest/block/l2_block_source.js +6 -2
  22. package/dest/block/l2_block_stream/interfaces.d.ts +7 -5
  23. package/dest/block/l2_block_stream/interfaces.d.ts.map +1 -1
  24. package/dest/block/l2_block_stream/l2_block_stream.d.ts +2 -1
  25. package/dest/block/l2_block_stream/l2_block_stream.d.ts.map +1 -1
  26. package/dest/block/l2_block_stream/l2_block_stream.js +47 -20
  27. package/dest/block/test/l2_tips_store_test_suite.d.ts +1 -1
  28. package/dest/block/test/l2_tips_store_test_suite.d.ts.map +1 -1
  29. package/dest/block/test/l2_tips_store_test_suite.js +9 -4
  30. package/dest/config/node-rpc-config.d.ts +1 -1
  31. package/dest/config/node-rpc-config.d.ts.map +1 -1
  32. package/dest/config/node-rpc-config.js +1 -1
  33. package/dest/contract/artifact_hash.d.ts +2 -2
  34. package/dest/contract/artifact_hash.d.ts.map +1 -1
  35. package/dest/contract/artifact_hash.js +10 -2
  36. package/dest/contract/contract_class.d.ts +1 -1
  37. package/dest/contract/contract_class.d.ts.map +1 -1
  38. package/dest/contract/contract_class.js +3 -2
  39. package/dest/contract/index.d.ts +1 -2
  40. package/dest/contract/index.d.ts.map +1 -1
  41. package/dest/contract/index.js +0 -1
  42. package/dest/interfaces/api_limit.d.ts +6 -1
  43. package/dest/interfaces/api_limit.d.ts.map +1 -1
  44. package/dest/interfaces/api_limit.js +3 -0
  45. package/dest/interfaces/aztec-node-admin.d.ts +2 -1
  46. package/dest/interfaces/aztec-node-admin.d.ts.map +1 -1
  47. package/dest/interfaces/block-builder.d.ts +7 -2
  48. package/dest/interfaces/block-builder.d.ts.map +1 -1
  49. package/dest/interfaces/configs.d.ts +7 -1
  50. package/dest/interfaces/configs.d.ts.map +1 -1
  51. package/dest/interfaces/configs.js +1 -0
  52. package/dest/interfaces/merkle_tree_operations.d.ts +8 -3
  53. package/dest/interfaces/merkle_tree_operations.d.ts.map +1 -1
  54. package/dest/interfaces/private_kernel_prover.d.ts +64 -3
  55. package/dest/interfaces/private_kernel_prover.d.ts.map +1 -1
  56. package/dest/interfaces/proving-job.d.ts +1 -1
  57. package/dest/interfaces/server.d.ts +1 -2
  58. package/dest/interfaces/server.d.ts.map +1 -1
  59. package/dest/interfaces/server.js +0 -1
  60. package/dest/interfaces/tx_provider.d.ts +5 -1
  61. package/dest/interfaces/tx_provider.d.ts.map +1 -1
  62. package/dest/kernel/hints/index.d.ts +1 -2
  63. package/dest/kernel/hints/index.d.ts.map +1 -1
  64. package/dest/kernel/hints/index.js +0 -1
  65. package/dest/kernel/index.d.ts +5 -1
  66. package/dest/kernel/index.d.ts.map +1 -1
  67. package/dest/kernel/index.js +4 -0
  68. package/dest/kernel/private_kernel_circuit_public_inputs.d.ts +1 -9
  69. package/dest/kernel/private_kernel_circuit_public_inputs.d.ts.map +1 -1
  70. package/dest/kernel/private_kernel_circuit_public_inputs.js +3 -7
  71. package/dest/kernel/private_kernel_init_4_circuit_private_inputs.d.ts +24 -0
  72. package/dest/kernel/private_kernel_init_4_circuit_private_inputs.d.ts.map +1 -0
  73. package/dest/kernel/private_kernel_init_4_circuit_private_inputs.js +37 -0
  74. package/dest/kernel/private_kernel_init_5_circuit_private_inputs.d.ts +25 -0
  75. package/dest/kernel/private_kernel_init_5_circuit_private_inputs.d.ts.map +1 -0
  76. package/dest/kernel/private_kernel_init_5_circuit_private_inputs.js +39 -0
  77. package/dest/kernel/private_kernel_inner_4_circuit_private_inputs.d.ts +18 -0
  78. package/dest/kernel/private_kernel_inner_4_circuit_private_inputs.d.ts.map +1 -0
  79. package/dest/kernel/private_kernel_inner_4_circuit_private_inputs.js +27 -0
  80. package/dest/kernel/private_kernel_inner_5_circuit_private_inputs.d.ts +19 -0
  81. package/dest/kernel/private_kernel_inner_5_circuit_private_inputs.d.ts.map +1 -0
  82. package/dest/kernel/private_kernel_inner_5_circuit_private_inputs.js +29 -0
  83. package/dest/kernel/private_kernel_prover_output.d.ts +4 -1
  84. package/dest/kernel/private_kernel_prover_output.d.ts.map +1 -1
  85. package/dest/kernel/private_kernel_prover_output.js +4 -1
  86. package/dest/kernel/private_kernel_simulated_output.d.ts +1 -7
  87. package/dest/kernel/private_kernel_simulated_output.d.ts.map +1 -1
  88. package/dest/kernel/private_kernel_simulated_output.js +2 -1
  89. package/dest/kernel/utils/index.d.ts +1 -2
  90. package/dest/kernel/utils/index.d.ts.map +1 -1
  91. package/dest/kernel/utils/index.js +0 -1
  92. package/dest/logs/index.d.ts +3 -1
  93. package/dest/logs/index.d.ts.map +1 -1
  94. package/dest/logs/index.js +2 -0
  95. package/dest/logs/logs_query.d.ts +2 -2
  96. package/dest/logs/message_context.d.ts +17 -0
  97. package/dest/logs/message_context.d.ts.map +1 -0
  98. package/dest/logs/message_context.js +9 -0
  99. package/dest/logs/pending_tagged_log.d.ts +11 -0
  100. package/dest/logs/pending_tagged_log.d.ts.map +1 -0
  101. package/dest/logs/pending_tagged_log.js +4 -0
  102. package/dest/logs/shared_secret_derivation.d.ts +12 -2
  103. package/dest/logs/shared_secret_derivation.d.ts.map +1 -1
  104. package/dest/logs/shared_secret_derivation.js +18 -0
  105. package/dest/noir/index.d.ts +3 -3
  106. package/dest/noir/index.d.ts.map +1 -1
  107. package/dest/p2p/index.d.ts +1 -2
  108. package/dest/p2p/index.d.ts.map +1 -1
  109. package/dest/p2p/index.js +0 -1
  110. package/dest/stats/stats.d.ts +2 -2
  111. package/dest/stats/stats.d.ts.map +1 -1
  112. package/dest/tests/factories.d.ts +1 -27
  113. package/dest/tests/factories.d.ts.map +1 -1
  114. package/dest/tests/factories.js +0 -30
  115. package/dest/tx/hashed_values.d.ts +7 -1
  116. package/dest/tx/hashed_values.d.ts.map +1 -1
  117. package/dest/tx/hashed_values.js +9 -1
  118. package/dest/tx/index.d.ts +1 -2
  119. package/dest/tx/index.d.ts.map +1 -1
  120. package/dest/tx/index.js +0 -1
  121. package/dest/tx/offchain_effect.js +1 -1
  122. package/dest/tx/profiling.d.ts +1 -1
  123. package/dest/tx/profiling.d.ts.map +1 -1
  124. package/dest/tx/profiling.js +2 -0
  125. package/dest/tx/simulated_tx.d.ts +1 -1
  126. package/dest/tx/simulated_tx.d.ts.map +1 -1
  127. package/dest/tx/simulated_tx.js +7 -2
  128. package/dest/tx/tx.d.ts +8 -1
  129. package/dest/tx/tx.d.ts.map +1 -1
  130. package/dest/tx/tx.js +9 -3
  131. package/dest/validators/errors.d.ts +16 -1
  132. package/dest/validators/errors.d.ts.map +1 -1
  133. package/dest/validators/errors.js +12 -0
  134. package/dest/world-state/world_state_revision.d.ts +1 -18
  135. package/dest/world-state/world_state_revision.d.ts.map +1 -1
  136. package/dest/world-state/world_state_revision.js +0 -20
  137. package/package.json +8 -8
  138. package/src/abi/abi.ts +32 -4
  139. package/src/abi/contract_artifact.ts +40 -35
  140. package/src/abi/decoder.ts +20 -24
  141. package/src/abi/function_call.ts +19 -7
  142. package/src/avm/avm.ts +4 -1
  143. package/src/avm/index.ts +0 -5
  144. package/src/block/l2_block_source.ts +14 -2
  145. package/src/block/l2_block_stream/interfaces.ts +6 -4
  146. package/src/block/l2_block_stream/l2_block_stream.ts +58 -21
  147. package/src/block/test/l2_tips_store_test_suite.ts +5 -0
  148. package/src/config/node-rpc-config.ts +2 -1
  149. package/src/contract/artifact_hash.ts +15 -3
  150. package/src/contract/contract_class.ts +3 -2
  151. package/src/contract/index.ts +0 -1
  152. package/src/gas/README.md +108 -36
  153. package/src/interfaces/api_limit.ts +5 -0
  154. package/src/interfaces/block-builder.ts +6 -1
  155. package/src/interfaces/configs.ts +6 -0
  156. package/src/interfaces/merkle_tree_operations.ts +8 -2
  157. package/src/interfaces/private_kernel_prover.ts +94 -1
  158. package/src/interfaces/server.ts +0 -1
  159. package/src/interfaces/tx_provider.ts +4 -0
  160. package/src/kernel/hints/index.ts +0 -1
  161. package/src/kernel/index.ts +4 -0
  162. package/src/kernel/private_kernel_circuit_public_inputs.ts +0 -7
  163. package/src/kernel/private_kernel_init_4_circuit_private_inputs.ts +52 -0
  164. package/src/kernel/private_kernel_init_5_circuit_private_inputs.ts +55 -0
  165. package/src/kernel/private_kernel_inner_4_circuit_private_inputs.ts +39 -0
  166. package/src/kernel/private_kernel_inner_5_circuit_private_inputs.ts +42 -0
  167. package/src/kernel/private_kernel_prover_output.ts +4 -0
  168. package/src/kernel/private_kernel_reset_dimensions.ts +1 -1
  169. package/src/kernel/private_kernel_simulated_output.ts +0 -7
  170. package/src/kernel/utils/index.ts +0 -1
  171. package/src/logs/index.ts +2 -0
  172. package/src/logs/logs_query.ts +2 -2
  173. package/src/logs/message_context.ts +18 -0
  174. package/src/logs/pending_tagged_log.ts +12 -0
  175. package/src/logs/shared_secret_derivation.ts +18 -1
  176. package/src/noir/index.ts +2 -1
  177. package/src/p2p/index.ts +0 -1
  178. package/src/stats/stats.ts +4 -0
  179. package/src/tests/factories.ts +0 -38
  180. package/src/tx/hashed_values.ts +11 -6
  181. package/src/tx/index.ts +0 -1
  182. package/src/tx/offchain_effect.ts +1 -1
  183. package/src/tx/profiling.ts +2 -0
  184. package/src/tx/simulated_tx.ts +13 -2
  185. package/src/tx/tx.ts +19 -3
  186. package/src/validators/errors.ts +17 -0
  187. package/src/world-state/world_state_revision.ts +0 -33
  188. package/dest/avm/contract_storage_read.d.ts +0 -58
  189. package/dest/avm/contract_storage_read.d.ts.map +0 -1
  190. package/dest/avm/contract_storage_read.js +0 -65
  191. package/dest/avm/contract_storage_update_request.d.ts +0 -65
  192. package/dest/avm/contract_storage_update_request.d.ts.map +0 -1
  193. package/dest/avm/contract_storage_update_request.js +0 -80
  194. package/dest/avm/public_call_stack_item_compressed.d.ts +0 -43
  195. package/dest/avm/public_call_stack_item_compressed.d.ts.map +0 -1
  196. package/dest/avm/public_call_stack_item_compressed.js +0 -70
  197. package/dest/avm/public_data_update_request.d.ts +0 -62
  198. package/dest/avm/public_data_update_request.d.ts.map +0 -1
  199. package/dest/avm/public_data_update_request.js +0 -67
  200. package/dest/avm/public_inner_call_request.d.ts +0 -50
  201. package/dest/avm/public_inner_call_request.d.ts.map +0 -1
  202. package/dest/avm/public_inner_call_request.js +0 -74
  203. package/dest/contract/deployment_info.d.ts +0 -24
  204. package/dest/contract/deployment_info.d.ts.map +0 -1
  205. package/dest/contract/deployment_info.js +0 -3
  206. package/dest/interfaces/prover-coordination.d.ts +0 -15
  207. package/dest/interfaces/prover-coordination.d.ts.map +0 -1
  208. package/dest/interfaces/prover-coordination.js +0 -1
  209. package/dest/kernel/hints/tree_leaf_read_request.d.ts +0 -14
  210. package/dest/kernel/hints/tree_leaf_read_request.d.ts.map +0 -1
  211. package/dest/kernel/hints/tree_leaf_read_request.js +0 -33
  212. package/dest/kernel/utils/optional_number.d.ts +0 -31
  213. package/dest/kernel/utils/optional_number.d.ts.map +0 -1
  214. package/dest/kernel/utils/optional_number.js +0 -42
  215. package/dest/p2p/interface.d.ts +0 -5
  216. package/dest/p2p/interface.d.ts.map +0 -1
  217. package/dest/p2p/interface.js +0 -1
  218. package/dest/tx/validator/empty_validator.d.ts +0 -5
  219. package/dest/tx/validator/empty_validator.d.ts.map +0 -1
  220. package/dest/tx/validator/empty_validator.js +0 -7
  221. package/src/avm/contract_storage_read.ts +0 -77
  222. package/src/avm/contract_storage_update_request.ts +0 -93
  223. package/src/avm/public_call_stack_item_compressed.ts +0 -113
  224. package/src/avm/public_data_update_request.ts +0 -106
  225. package/src/avm/public_inner_call_request.ts +0 -85
  226. package/src/contract/deployment_info.ts +0 -25
  227. package/src/interfaces/prover-coordination.ts +0 -17
  228. package/src/kernel/hints/tree_leaf_read_request.ts +0 -35
  229. package/src/kernel/utils/optional_number.ts +0 -50
  230. package/src/p2p/interface.ts +0 -4
  231. package/src/tx/validator/empty_validator.ts +0 -7
@@ -5,7 +5,7 @@ import { z } from 'zod';
5
5
 
6
6
  import { AztecAddress } from '../aztec-address/index.js';
7
7
  import { schemas } from '../schemas/index.js';
8
- import { type AbiType, AbiTypeSchema, FunctionType } from './abi.js';
8
+ import { type AbiType, AbiTypeSchema, FunctionType, getFunctionReturnType } from './abi.js';
9
9
  import { FunctionSelector } from './function_selector.js';
10
10
 
11
11
  /** A request to call a function on a contract. */
@@ -25,8 +25,8 @@ export class FunctionCall {
25
25
  public isStatic: boolean,
26
26
  /** The encoded args */
27
27
  public args: Fr[],
28
- /** The return type for decoding */
29
- public returnTypes: AbiType[],
28
+ /** The type the call returns, for decoding, or undefined if it returns nothing */
29
+ public returnType?: AbiType,
30
30
  ) {}
31
31
 
32
32
  static getFields(fields: FieldsOf<FunctionCall>) {
@@ -38,7 +38,7 @@ export class FunctionCall {
38
38
  fields.hideMsgSender,
39
39
  fields.isStatic,
40
40
  fields.args,
41
- fields.returnTypes,
41
+ fields.returnType,
42
42
  ] as const;
43
43
  }
44
44
 
@@ -56,9 +56,22 @@ export class FunctionCall {
56
56
  isStatic: z.boolean(),
57
57
  hideMsgSender: z.boolean(),
58
58
  args: z.array(schemas.Fr),
59
- returnTypes: z.array(AbiTypeSchema),
59
+ returnType: AbiTypeSchema.optional(),
60
+ // Old consumers send `returnTypes`, not `returnType`. So we accept both for now, until we can safely remove
61
+ // the old version.
62
+ returnTypes: z.array(AbiTypeSchema).optional(),
60
63
  })
61
- .transform(FunctionCall.from);
64
+ .transform(({ returnType, returnTypes, ...fields }) =>
65
+ FunctionCall.from({ ...fields, returnType: getFunctionReturnType({ returnType, returnTypes }) }),
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Serializes the deprecated `returnTypes` alongside `returnType`, so that peers that still require the old field can
71
+ * parse calls sent by this version.
72
+ */
73
+ public toJSON() {
74
+ return { ...this, returnTypes: this.returnType ? [this.returnType] : [] };
62
75
  }
63
76
 
64
77
  public isPublicStatic(): boolean {
@@ -78,7 +91,6 @@ export class FunctionCall {
78
91
  hideMsgSender: false,
79
92
  isStatic: false,
80
93
  args: [],
81
- returnTypes: [],
82
94
  });
83
95
  }
84
96
  }
package/src/avm/avm.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { DEFAULT_MAX_DEBUG_LOG_MEMORY_READS } from '@aztec/constants';
2
1
  import { Fr } from '@aztec/foundation/curves/bn254';
3
2
  import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc';
4
3
 
@@ -1384,6 +1383,10 @@ export class PublicTxResult {
1384
1383
  }
1385
1384
  }
1386
1385
 
1386
+ // Excessive memory reads due to debug logging can open a DOS vector to nodes that perform public tx simulation for users.
1387
+ // This default limit (4MB) can be configured further by node runners via the `RPC_SIMULATE_PUBLIC_MAX_DEBUG_LOG_MEMORY_READS` environment variable.
1388
+ export const DEFAULT_MAX_DEBUG_LOG_MEMORY_READS = 125000;
1389
+
1387
1390
  export class CollectionLimitsConfig {
1388
1391
  constructor(
1389
1392
  public readonly maxDebugLogMemoryReads: number,
package/src/avm/index.ts CHANGED
@@ -3,10 +3,5 @@ export * from './avm_accumulated_data.js';
3
3
  export * from './avm_circuit_public_inputs.js';
4
4
  export * from './revert_code.js';
5
5
  export * from './public_data_write.js';
6
- export * from './public_data_update_request.js';
7
- export * from './contract_storage_update_request.js';
8
- export * from './contract_storage_read.js';
9
- export * from './public_inner_call_request.js';
10
- export * from './public_call_stack_item_compressed.js';
11
6
  export * from './avm_proving_request.js';
12
7
  export * from './message_pack.js';
@@ -19,6 +19,7 @@ import type { CheckpointData, ProposedCheckpointData, ProposedCheckpointInput }
19
19
  import type { CheckpointInfo } from '../checkpoint/checkpoint_info.js';
20
20
  import type { PublishedCheckpoint } from '../checkpoint/published_checkpoint.js';
21
21
  import type { L1RollupConstants } from '../epoch-helpers/index.js';
22
+ import { MAX_RPC_CHECKPOINTS_DATA_LEN } from '../interfaces/api_limit.js';
22
23
  import type { L2ToL1MembershipWitness } from '../messaging/l2_to_l1_membership.js';
23
24
  import { CheckpointHeader } from '../rollup/checkpoint_header.js';
24
25
  import type { IndexedTxEffect } from '../tx/indexed_tx_effect.js';
@@ -89,9 +90,20 @@ export const CheckpointQuerySchema: z.ZodType<CheckpointQuery, unknown> = z.unio
89
90
  z.object({ tag: z.union([z.literal('checkpointed'), z.literal('proven'), z.literal('finalized')]) }).strict(),
90
91
  ]);
91
92
 
93
+ const checkpointsQueryLimitSchema = () =>
94
+ z
95
+ .number()
96
+ .int()
97
+ .min(1)
98
+ .max(MAX_RPC_CHECKPOINTS_DATA_LEN, {
99
+ message: `limit must be at most ${MAX_RPC_CHECKPOINTS_DATA_LEN}`,
100
+ });
101
+
92
102
  export const CheckpointsQuerySchema: z.ZodType<CheckpointsQuery, unknown> = z.union([
93
- z.object({ from: CheckpointNumberSchema, limit: z.number().int().min(1) }).strict(),
94
- z.object({ fromSlot: SlotNumberSchema, limit: z.number().int().min(1), reverse: z.boolean().optional() }).strict(),
103
+ z.object({ from: CheckpointNumberSchema, limit: checkpointsQueryLimitSchema() }).strict(),
104
+ z
105
+ .object({ fromSlot: SlotNumberSchema, limit: checkpointsQueryLimitSchema(), reverse: z.boolean().optional() })
106
+ .strict(),
95
107
  z.object({ epoch: EpochNumberSchema }).strict(),
96
108
  ]);
97
109
 
@@ -1,5 +1,6 @@
1
1
  import type { BlockNumber } from '@aztec/foundation/branded-types';
2
2
 
3
+ import type { BlockHeader } from '../../tx/block_header.js';
3
4
  import type { L2Block } from '../l2_block.js';
4
5
  import type { CheckpointId, L2BlockId, L2TipId, L2Tips, LocalL2Tips } from '../l2_block_source.js';
5
6
 
@@ -81,13 +82,14 @@ export type L2BlockStreamEvent =
81
82
  }
82
83
  | /**
83
84
  * Reports the new proposed tip of the chain. Emitted once per sync pass when the source's proposed tip differs
84
- * from the pre-pass local one (downloads, a prune, or a thin tip movement). Carries only the block id; in block
85
- * mode the corresponding payloads arrive via preceding `blocks-added` events, while in tips-only mode this is the
86
- * sole signal that the proposed tip moved. Consumers that only track the proposed tip can ignore `blocks-added`
87
- * entirely and anchor on this event instead.
85
+ * from the pre-pass local one (downloads, a prune, or a thin tip movement). In block mode the corresponding
86
+ * payloads also arrive via preceding `blocks-added` events, while in tips-only mode this is the sole signal that
87
+ * the proposed tip moved. Consumers that only track the proposed tip can ignore `blocks-added` entirely and
88
+ * anchor on this event instead.
88
89
  */ {
89
90
  type: 'chain-proposed';
90
91
  block: L2BlockId;
92
+ header: BlockHeader;
91
93
  }
92
94
  | /**
93
95
  * Reports a new checkpointed tip. Emitted at most once per sync pass when the source's checkpointed tip
@@ -3,6 +3,8 @@ import { AbortError } from '@aztec/foundation/error';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import { RunningPromise } from '@aztec/foundation/running-promise';
5
5
 
6
+ import type { BlockHeader } from '../../tx/block_header.js';
7
+ import { BlockHash } from '../block_hash.js';
6
8
  import type { L2Block } from '../l2_block.js';
7
9
  import { type L2BlockId, type L2BlockSource, type LocalL2Tips, makeL2BlockId } from '../l2_block_source.js';
8
10
  import {
@@ -100,6 +102,13 @@ export class L2BlockStream {
100
102
  // Baseline for the chain-proposed event; captured before the local store mutates during the pass.
101
103
  const prePassProposed = localTips.proposed;
102
104
 
105
+ // In tips-only mode there is no download loop to supply the chain-proposed event's header, so it must be
106
+ // fetched. Start the read now so it runs in parallel with the walk-back below.
107
+ const prefetch: ProposedHeaderPrefetch | undefined =
108
+ this.opts.tipsOnly && localBlockIdDiffers(prePassProposed, sourceTips.proposed)
109
+ ? { hash: sourceTips.proposed.hash, header: this.getHeaderFromSource(sourceTips.proposed.hash) }
110
+ : undefined;
111
+
103
112
  // Walk back to find a reorg, floored at the local finalized tip (a legitimate reorg can never reach it, since
104
113
  // finalized means the proving tx is itself L1-finalized). Seed the cache with ONLY the proposed tip: a stale
105
114
  // tier seed at a reorged height equals the local old-fork hash, faking agreement and stopping the walk above the
@@ -169,17 +178,29 @@ export class L2BlockStream {
169
178
  }
170
179
 
171
180
  // Pass atomicity: a prune mid-download leaves the source unable to serve the planned blocks, so the snapshot's
172
- // tier tips may reference blocks the consumer never saw — skip tier reconciliation in that case. Tips-only mode
173
- // has no download plan (the snapshot is one atomic getL2Tips read), so it always reconciles.
174
- if (!this.opts.tipsOnly && !(await this.downloadBlocks(latestBlockNumber, sourceTips))) {
175
- return;
176
- }
181
+ // tier tips may reference blocks the consumer never saw — downloadBlocks aborts the pass in that case.
182
+ // Tips-only mode has no download plan (the snapshot is one atomic getL2Tips read), so it always reconciles.
183
+ const deliveredProposedHeader = this.opts.tipsOnly
184
+ ? undefined
185
+ : await this.downloadBlocks(latestBlockNumber, sourceTips);
177
186
 
178
187
  // End-of-pass reconciliation: chain-proposed fires against the pre-pass baseline (a post-prune re-read would
179
188
  // equal the source tip and suppress it), then the tiers highest-to-lowest so the finalized <= proven <=
180
189
  // checkpointed <= proposed invariant holds mid-pass.
181
190
  if (localBlockIdDiffers(prePassProposed, sourceTips.proposed)) {
182
- await this.emitEvent({ type: 'chain-proposed', block: sourceTips.proposed });
191
+ const availableHeader =
192
+ prefetch?.hash === sourceTips.proposed.hash ? await prefetch.header : deliveredProposedHeader;
193
+ const header = availableHeader ?? (await this.getHeaderFromSource(sourceTips.proposed.hash));
194
+ if (header === undefined) {
195
+ // Abort the pass rather than skip just this event: the later tier events must not get ahead of a
196
+ // proposed tip the consumer never received. Nothing advanced, so the next pass retries.
197
+ this.log.warn(`No header for the proposed tip; aborting this sync pass`, {
198
+ blockNumber: sourceTips.proposed.number,
199
+ blockHash: sourceTips.proposed.hash,
200
+ });
201
+ throw new AbortError();
202
+ }
203
+ await this.emitEvent({ type: 'chain-proposed', block: sourceTips.proposed, header });
183
204
  }
184
205
 
185
206
  const reconcileTips = pruned ? await this.localData.getL2Tips() : localTips;
@@ -222,13 +243,15 @@ export class L2BlockStream {
222
243
  }
223
244
 
224
245
  /**
225
- * Downloads every block from the post-prune cursor through the source's proposed tip, emitting `blocks-added`
226
- * events. The return value gates tier-cursor advancement (pass atomicity): tier tips may only be reconciled when
227
- * the plan that backs them ran to the proposed tip.
228
- * @returns `true` if the plan completed (caught up, or delivered the proposed tip with a matching hash); `false` if
229
- * the source no longer has a promised block, or served a fork at the proposed height mid-pass.
246
+ * Delivers every block from the agreed-on point through the advertised proposed tip as `blocks-added` events,
247
+ * aborting the pass when the source's own answers prove that tip is no longer real.
248
+ * @returns The delivered tip's header, so the caller can avoid a re-fetch; undefined when there was nothing to
249
+ * download (already at the tip, or startingBlock past it).
230
250
  */
231
- private async downloadBlocks(latestBlockNumber: BlockNumber, sourceTips: LocalL2Tips): Promise<boolean> {
251
+ private async downloadBlocks(
252
+ latestBlockNumber: BlockNumber,
253
+ sourceTips: LocalL2Tips,
254
+ ): Promise<BlockHeader | undefined> {
232
255
  // The post-prune cursor: the highest block number both sides agree on. Block downloads resume from here.
233
256
  let nextBlockNumber = latestBlockNumber + 1;
234
257
 
@@ -258,12 +281,12 @@ export class L2BlockStream {
258
281
  if (blocks.length === 0) {
259
282
  // The source no longer has a block the snapshot promised: the snapshot is provably stale, so report the plan
260
283
  // incomplete and skip reconciliation this pass.
261
- this.log.warn(`Block source returned no blocks for a promised range; skipping reconciliation this pass`, {
284
+ this.log.warn(`Block source returned no blocks for a promised range; aborting this sync pass`, {
262
285
  from: nextBlockNumber,
263
286
  limit,
264
287
  sourceProposed: sourceTips.proposed.number,
265
288
  });
266
- return false;
289
+ throw new AbortError();
267
290
  }
268
291
  await this.emitEvent({ type: 'blocks-added', blocks });
269
292
  lastDeliveredBlock = blocks.at(-1)!;
@@ -271,22 +294,23 @@ export class L2BlockStream {
271
294
  }
272
295
 
273
296
  if (lastDeliveredBlock === undefined) {
274
- // Loop never ran: caught up before the plan started, or startingBlock past the tip (A-1061). Trivially complete.
275
- return true;
297
+ // Loop never ran (caught up before the plan started, or startingBlock past the tip, see A-1061), so nothing
298
+ // was downloaded that could disprove the snapshot.
299
+ return undefined;
276
300
  }
277
301
 
278
- // Complete iff the block delivered at the proposed height carries the snapshot's proposed hash; a different hash
279
- // means a same-height fork swap happened mid-pass, so the snapshot is stale.
302
+ // The block delivered at the proposed height must carry the snapshot's proposed hash; a different hash means a
303
+ // same-height fork swap happened mid-pass, so the snapshot is stale.
280
304
  const deliveredHash = (await lastDeliveredBlock.hash()).toString();
281
305
  if (deliveredHash !== sourceTips.proposed.hash) {
282
- this.log.warn(`Delivered proposed-block hash differs from snapshot; skipping reconciliation this pass`, {
306
+ this.log.warn(`Delivered proposed-block hash differs from snapshot; aborting this sync pass`, {
283
307
  blockNumber: lastDeliveredBlock.number,
284
308
  deliveredHash,
285
309
  snapshotHash: sourceTips.proposed.hash,
286
310
  });
287
- return false;
311
+ throw new AbortError();
288
312
  }
289
- return true;
313
+ return lastDeliveredBlock.header;
290
314
  }
291
315
 
292
316
  /**
@@ -342,6 +366,16 @@ export class L2BlockStream {
342
366
  .then(hash => hash?.toString());
343
367
  }
344
368
 
369
+ private getHeaderFromSource(hash: string): Promise<BlockHeader | undefined> {
370
+ return this.l2BlockSource
371
+ .getBlockData({ hash: BlockHash.fromString(hash) })
372
+ .then(data => data?.header)
373
+ .catch(err => {
374
+ this.log.error(`Failed to read header for block`, err, { blockHash: hash });
375
+ return undefined;
376
+ });
377
+ }
378
+
345
379
  private async emitEvent(event: L2BlockStreamEvent) {
346
380
  this.log.debug(
347
381
  `Emitting ${event.type} (${
@@ -359,6 +393,9 @@ export class L2BlockStream {
359
393
  }
360
394
  }
361
395
 
396
+ /** A speculative proposed-tip header read, tagged with the hash it targets so a moved tip is detectable. */
397
+ type ProposedHeaderPrefetch = { hash: string; header: Promise<BlockHeader | undefined> };
398
+
362
399
  class BlockHashCache {
363
400
  private readonly cache: Map<number, string> = new Map();
364
401
 
@@ -10,6 +10,7 @@ import {
10
10
  type L2TipId,
11
11
  } from '@aztec/stdlib/block';
12
12
  import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
13
+ import { BlockHeader } from '@aztec/stdlib/tx';
13
14
 
14
15
  import { expect } from 'vitest';
15
16
 
@@ -132,6 +133,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
132
133
  await tipsStore.handleBlockStreamEvent({
133
134
  type: 'chain-proposed',
134
135
  block: { number: BlockNumber(5), hash: new Fr(500).toString() },
136
+ header: BlockHeader.empty(),
135
137
  });
136
138
 
137
139
  const tips = await tipsStore.getL2Tips();
@@ -145,10 +147,12 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
145
147
  await tipsStore.handleBlockStreamEvent({
146
148
  type: 'chain-proposed',
147
149
  block: { number: BlockNumber(4), hash: new Fr(400).toString() },
150
+ header: BlockHeader.empty(),
148
151
  });
149
152
  await tipsStore.handleBlockStreamEvent({
150
153
  type: 'chain-proposed',
151
154
  block: { number: BlockNumber(9), hash: new Fr(900).toString() },
155
+ header: BlockHeader.empty(),
152
156
  });
153
157
 
154
158
  // Both recorded heights resolve; the heights between them were never seen and stay undefined.
@@ -165,6 +169,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
165
169
  await tipsStore.handleBlockStreamEvent({
166
170
  type: 'chain-proposed',
167
171
  block: { number: BlockNumber(9), hash: new Fr(900).toString() },
172
+ header: BlockHeader.empty(),
168
173
  });
169
174
 
170
175
  // Record sparse witnesses at heights below the tip (e.g. a consumer materializing per-height state).
@@ -1,6 +1,7 @@
1
- import { DEFAULT_MAX_DEBUG_LOG_MEMORY_READS } from '@aztec/constants';
2
1
  import { type ConfigMappingsType, numberConfigHelper } from '@aztec/foundation/config';
3
2
 
3
+ import { DEFAULT_MAX_DEBUG_LOG_MEMORY_READS } from '../avm/avm.js';
4
+
4
5
  export const nodeRpcConfigMappings: ConfigMappingsType<NodeRPCConfig> = {
5
6
  rpcSimulatePublicMaxGasLimit: {
6
7
  env: 'RPC_SIMULATE_PUBLIC_MAX_GAS_LIMIT',
@@ -6,7 +6,13 @@ import { MerkleTree, MerkleTreeCalculator } from '@aztec/foundation/trees';
6
6
 
7
7
  import deterministicStringify from 'json-stringify-deterministic';
8
8
 
9
- import { type ContractArtifact, type FunctionArtifact, FunctionSelector, FunctionType } from '../abi/index.js';
9
+ import {
10
+ type ContractArtifact,
11
+ type FunctionArtifact,
12
+ FunctionSelector,
13
+ FunctionType,
14
+ getFunctionReturnType,
15
+ } from '../abi/index.js';
10
16
 
11
17
  const VERSION = 1;
12
18
 
@@ -104,8 +110,14 @@ export async function computeFunctionArtifactHash(
104
110
  return sha256Fr(Buffer.concat([numToUInt8(VERSION), selector.toBuffer(), metadataHash.toBuffer(), bytecodeHash]));
105
111
  }
106
112
 
107
- export function computeFunctionMetadataHash(fn: FunctionArtifact) {
108
- return sha256Fr(Buffer.from(deterministicStringify(fn.returnTypes), 'utf8'));
113
+ export function computeFunctionMetadataHash(fn: Pick<FunctionArtifact, 'returnType' | 'returnTypes'>) {
114
+ // Wrap the type in a list (empty when the function returns nothing) so the hash preimage is identical to
115
+ // what it was when this hash consumed the deprecated `returnTypes` list. This hash is part of the
116
+ // artifact hash preimage, which contract class ids commit to - changing it would move the computed
117
+ // class id and address of every contract, and break artifact validation against already-registered
118
+ // classes.
119
+ const returnType = getFunctionReturnType(fn);
120
+ return sha256Fr(Buffer.from(deterministicStringify(returnType ? [returnType] : []), 'utf8'));
109
121
  }
110
122
 
111
123
  function getLogger() {
@@ -1,4 +1,5 @@
1
- import { vkAsFieldsMegaHonk } from '@aztec/foundation/crypto/keys';
1
+ import { CircuitKind } from '@aztec/bb.js';
2
+ import { vkAsFields } from '@aztec/foundation/crypto/keys';
2
3
  import { Fr } from '@aztec/foundation/curves/bn254';
3
4
 
4
5
  import { type ContractArtifact, type FunctionArtifact, FunctionSelector, FunctionType } from '../abi/index.js';
@@ -60,5 +61,5 @@ export async function computeVerificationKeyHash(f: FunctionArtifact) {
60
61
  if (!f.verificationKey) {
61
62
  throw new Error(`Private function ${f.name} must have a verification key`);
62
63
  }
63
- return hashVK(await vkAsFieldsMegaHonk(Buffer.from(f.verificationKey, 'base64')));
64
+ return hashVK(await vkAsFields(Buffer.from(f.verificationKey, 'base64'), CircuitKind.App));
64
65
  }
@@ -9,5 +9,4 @@ export * from './private_function.js';
9
9
  export * from './interfaces/index.js';
10
10
  export * from './contract_function_dao.js';
11
11
  export * from './partial_address.js';
12
- export * from './deployment_info.js';
13
12
  export * from './complete_address.js';
package/src/gas/README.md CHANGED
@@ -1,47 +1,69 @@
1
1
  # Aztec Gas and Fee Model
2
2
 
3
3
  The minimum fee per mana and its components are computed on L1 in
4
- `l1-contracts/src/core/libraries/rollup/FeeLib.sol`. This document describes the
5
- formulas, the oracle lag/lifetime mechanism, and the TypeScript types in this directory.
4
+ `l1-contracts/src/core/libraries/rollup/FeeLib.sol` (`fee_math.ts` in this directory is a
5
+ TypeScript port of those formulas, used to predict fees a few slots ahead). This document
6
+ describes the formulas, the oracle lag/lifetime mechanism, how a transaction's fee is
7
+ derived from them, the gas and data limits, and the TypeScript types in this directory.
6
8
 
7
9
  ## Mana
8
10
 
9
- Aztec uses **mana** as its unit of work (analogous to Ethereum gas). Transactions consume
10
- mana in two dimensions: **DA** (data availability) and **L2** (execution). The total fee
11
- is `gasUsed * feePerMana` summed across both dimensions.
11
+ Aztec meters work as gas in two dimensions: **DA** (data availability, i.e. blob data
12
+ published to L1) and **L2** (execution). The L2 dimension is called **mana** (analogous to
13
+ Ethereum gas): block headers track `totalManaUsed`, and the fee model below prices one unit
14
+ of mana. The total fee is `gasUsed * feePerGas` summed across both dimensions.
15
+
16
+ ### The DA dimension is priced at zero
17
+
18
+ Only the L2 dimension currently carries a price. When building checkpoint global variables,
19
+ the sequencer sets `feePerDaGas = 0` and sets `feePerL2Gas` to the L1-computed minimum fee
20
+ per mana (`sequencer-client/src/global_variable_builder/global_builder.ts` and
21
+ `fee_provider.ts` in the same directory). The cost of publishing data is still recovered:
22
+ the blob gas a checkpoint pays on L1 is part of the *sequencer cost* component of the mana
23
+ fee below. DA gas remains fully metered and limited (see
24
+ [Gas and Data Limits](#gas-and-data-limits)) — it bounds how much data a tx or checkpoint
25
+ may publish — it just contributes nothing to the fee today.
12
26
 
13
27
  ## Fee Components
14
28
 
15
- The minimum fee per mana has four components:
29
+ The minimum fee per mana has four components. All are computed in ETH (wei) per mana and
30
+ converted to the fee asset at the end (see [Fee Asset Price](#fee-asset-price)).
16
31
 
17
32
  ### Sequencer Cost
18
33
 
19
34
  L1 cost to propose a checkpoint (calldata gas + blob data), amortized over `manaTarget`:
20
35
 
21
36
  ```
22
- sequencerCost = ((L1_GAS_PER_CHECKPOINT_PROPOSED * baseFee)
37
+ sequencerCost = ceil(((L1_GAS_PER_CHECKPOINT_PROPOSED * baseFee)
23
38
  + (BLOBS_PER_CHECKPOINT * BLOB_GAS_PER_BLOB * blobFee))
24
- / manaTarget
39
+ / manaTarget)
25
40
  ```
26
41
 
42
+ Note that `BLOBS_PER_CHECKPOINT` here is FeeLib's own constant (3), not the protocol blob
43
+ capacity of the same name (6) — see the note under [Key Constants](#key-constants).
44
+
27
45
  ### Prover Cost
28
46
 
29
47
  L1 cost to verify an epoch proof, amortized over epoch duration and `manaTarget`, plus a
30
48
  governance-set proving cost that compensates for off-chain proof generation:
31
49
 
32
50
  ```
33
- proverCost = (L1_GAS_PER_EPOCH_VERIFIED * baseFee / epochDuration) / manaTarget
51
+ proverCost = ceil(ceil((L1_GAS_PER_EPOCH_VERIFIED * baseFee) / epochDuration) / manaTarget)
34
52
  + provingCostPerMana
35
53
  ```
36
54
 
55
+ Updates to `provingCostPerMana` are rate-limited on L1 (`FeeLib.updateProvingCostPerMana`):
56
+ at most one update every 30 days, each moving the value by at most ×1.5 (or ÷1.5), with a
57
+ floor of 2 wei per mana.
58
+
37
59
  ### Congestion Cost
38
60
 
39
61
  An exponential surcharge when the network is congested (inspired by EIP-1559; the
40
62
  implementation uses the `fakeExponential` Taylor series approximation from EIP-4844):
41
63
 
42
64
  ```
43
- baseCost = sequencerCost + proverCost
44
- congestionCost = baseCost * congestionMultiplier / MINIMUM_CONGESTION_MULTIPLIER - baseCost
65
+ baseCost = sequencerCost + proverCost
66
+ congestionCost = floor(baseCost * congestionMultiplier / MINIMUM_CONGESTION_MULTIPLIER) - baseCost
45
67
  ```
46
68
 
47
69
  When there is no congestion the multiplier equals `MINIMUM_CONGESTION_MULTIPLIER` (1e9)
@@ -50,11 +72,15 @@ and congestion cost is zero.
50
72
  ### Congestion Multiplier
51
73
 
52
74
  ```
53
- excessMana = max(0, prevExcessMana + prevManaUsed - manaTarget)
54
- congestionMultiplier = fakeExponential(MINIMUM_CONGESTION_MULTIPLIER, excessMana, denominator)
75
+ excessMana = max(0, prevExcessMana + prevManaUsed - manaTarget)
76
+ denominator = manaTarget * 854,700,854 / 1e8 ≈ 8.547 * manaTarget
77
+ congestionMultiplier = fakeExponential(MINIMUM_CONGESTION_MULTIPLIER,
78
+ min(excessMana, 100 * denominator), denominator)
55
79
  ```
56
80
 
57
- Each additional `manaTarget` of excess mana increases the multiplier by ~12.5%.
81
+ Each additional `manaTarget` of excess mana multiplies the fee by `e^(1/8.547) ≈ 1.124`,
82
+ i.e. ~12.5%. The exponent is capped at 100 (multiplier ≤ ~2.7e43 × the minimum) to keep
83
+ the Taylor series from overflowing.
58
84
 
59
85
  ### Total
60
86
 
@@ -62,6 +88,11 @@ Each additional `manaTarget` of excess mana increases the multiplier by ~12.5%.
62
88
  minFeePerMana = sequencerCost + proverCost + congestionCost
63
89
  ```
64
90
 
91
+ Each component is converted from ETH to the fee asset individually (rounding up) before
92
+ summing. The sum is capped at `type(uint128).max` (`FeeLib.summedMinFee`) so it always fits
93
+ the proposal header's `feePerL2Gas` field — without the cap, extreme congestion could
94
+ produce a fee no valid header can represent, halting the chain.
95
+
65
96
  ## L1 Gas Oracle: Lag and Lifetime
66
97
 
67
98
  The oracle feeds Ethereum's `baseFee` and `blobFee` into the fee model using a two-phase
@@ -70,9 +101,9 @@ The oracle feeds Ethereum's `baseFee` and `blobFee` into the fee model using a t
70
101
  - **LAG = 2 slots** — when new L1 fees are observed, they activate `LAG` slots later
71
102
  (`slotOfChange = currentSlot + LAG`). This gives mempool transactions time to land
72
103
  before fees change.
73
- - **LIFETIME = 5 slots** — after an oracle update, the next update is rejected until
74
- `slotOfChange + (LIFETIME - LAG)` = 3 more slots have passed. This rate-limits how
75
- frequently L1 fee data can change.
104
+ - **LIFETIME = 5 slots** — after an oracle update, further updates are ignored
105
+ (`updateL1GasFeeOracle` returns without effect) until `slotOfChange + (LIFETIME - LAG)`
106
+ = 3 more slots have passed. This rate-limits how frequently L1 fee data can change.
76
107
 
77
108
  Fee resolution at a given timestamp:
78
109
 
@@ -84,6 +115,12 @@ else → use post (new fees)
84
115
  **Net effect**: L1 fee changes reach L2 with a 2-slot delay and can update at most once
85
116
  every 5 slots.
86
117
 
118
+ Because queued values only activate `LAG` slots later, the min fee for the next `LAG`
119
+ slots is fully determined by current on-chain state. `fee_math.ts` ports the FeeLib
120
+ formulas so the sequencer can predict min fees over that window (`FeePredictor` in
121
+ `sequencer-client/src/global_variable_builder`), under a configurable mana-usage
122
+ assumption (`ManaUsageEstimate`: none / target / limit).
123
+
87
124
  ### Worked Example
88
125
 
89
126
  Suppose the oracle is updated at slot 10 with new L1 fees. Here is the timeline:
@@ -117,34 +154,68 @@ Key observations:
117
154
 
118
155
  ## Fee Asset Price
119
156
 
120
- Fees are computed in ETH internally but converted to the fee asset (Fee Juice) via
121
- `ethPerFeeAsset` (1e12 precision). The price updates at most ±1% (±100 bps) per
122
- checkpoint:
157
+ Fees are computed in ETH (wei) internally and converted to the fee asset (Fee Juice) via
158
+ `ethPerFeeAsset` (1e12 precision), rounding up (`PriceLib.toFeeAsset` in
159
+ `l1-contracts/src/core/libraries/compressed-data/fees/FeeConfig.sol`).
160
+
161
+ Each checkpoint proposal carries a fee-asset price modifier (`OracleInput`) chosen by the
162
+ proposer, bounded to ±1% (±100 bps) per checkpoint:
123
163
 
124
164
  ```
125
165
  newPrice = currentPrice * (10000 + modifierBps) / 10000
126
166
  ```
127
167
 
168
+ The result is clamped to [`MIN_ETH_PER_FEE_ASSET` = 100, `MAX_ETH_PER_FEE_ASSET` = 1e14],
169
+ i.e. 1e-10 to 100 ETH per fee asset. The floor of 100 guarantees a ±1% step always moves
170
+ the integer price by at least 1.
171
+
172
+ ## Transaction Fees
173
+
174
+ The checkpoint's `gasFees` (the L1-computed min fee per mana, with DA priced at zero) act
175
+ as a base fee. Senders declare `GasSettings`: gas limits, teardown gas limits,
176
+ `maxFeesPerGas`, and `maxPriorityFeesPerGas`. A tx is only includable if `maxFeesPerGas`
177
+ covers the checkpoint's `gasFees` in both dimensions. The effective fee adds an
178
+ EIP-1559-style priority fee on top of the base, capped by the max
179
+ (`computeEffectiveGasFees` in `stdlib/src/fees/transaction_fee.ts`):
180
+
181
+ ```
182
+ effectiveFeePerGas = gasFees + min(maxPriorityFeePerGas, maxFeesPerGas - gasFees) (per dimension)
183
+ transactionFee = billedGas.daGas * effectiveFeePerDaGas + billedGas.l2Gas * effectiveFeePerL2Gas
184
+ ```
185
+
186
+ `billedGas` is actual consumption except for the teardown phase, which is billed at the
187
+ declared `teardownGasLimits` rather than actual usage — the fee is charged during teardown
188
+ itself, before actual teardown consumption is known.
189
+
128
190
  ## Maximum Fee Change Rate
129
191
 
130
- | Component | Bound |
131
- | ---------------------- | ------------------------------------------------------- |
132
- | L1 base fee / blob fee | At most once every 5 slots (oracle LIFETIME) |
133
- | Fee asset price | ±1% per checkpoint |
192
+ | Component | Bound |
193
+ | ---------------------- | -------------------------------------------------------- |
194
+ | L1 base fee / blob fee | At most once every 5 slots (oracle LIFETIME) |
195
+ | Fee asset price | ±1% per checkpoint |
196
+ | Proving cost per mana | At most ×1.5 (or ÷1.5) per update, one update per 30 days |
134
197
  | Congestion multiplier | Depends on excess mana accumulation/drain per checkpoint |
135
- | Sequencer/prover costs | Scale linearly with L1 fees |
198
+ | Sequencer/prover costs | Scale linearly with L1 fees |
136
199
 
137
200
  ## Key Constants
138
201
 
139
- | Constant | Value |
140
- | ------------------------------ | -------------- |
141
- | `L1_GAS_PER_CHECKPOINT_PROPOSED` | 300,000 |
142
- | `L1_GAS_PER_EPOCH_VERIFIED` | 3,600,000 |
143
- | `BLOBS_PER_CHECKPOINT` | 3 |
144
- | `BLOB_GAS_PER_BLOB` | 2^17 |
145
- | `MINIMUM_CONGESTION_MULTIPLIER` | 1e9 |
146
- | `LAG` | 2 slots |
147
- | `LIFETIME` | 5 slots |
202
+ | Constant | Value |
203
+ | ------------------------------- | -------------- |
204
+ | `L1_GAS_PER_CHECKPOINT_PROPOSED` | 300,000 |
205
+ | `L1_GAS_PER_EPOCH_VERIFIED` | 3,600,000 |
206
+ | `BLOBS_PER_CHECKPOINT` (FeeLib) | 3 |
207
+ | `BLOB_GAS_PER_BLOB` | 2^17 |
208
+ | `MINIMUM_CONGESTION_MULTIPLIER` | 1e9 |
209
+ | `LAG` | 2 slots |
210
+ | `LIFETIME` | 5 slots |
211
+ | `MIN_ETH_PER_FEE_ASSET` | 100 (1e-10 ETH) |
212
+ | `MAX_ETH_PER_FEE_ASSET` | 1e14 (100 ETH) |
213
+
214
+ ⚠️ Name clash: `FeeLib.sol` (and its port `fee_math.ts`) defines `BLOBS_PER_CHECKPOINT = 3`,
215
+ used only to price the sequencer's blob costs. The protocol constant of the same name in
216
+ `@aztec/constants` is **6** — the actual blob capacity of a checkpoint, used throughout the
217
+ limits section below. The FeeLib value is a holdover from the pre-checkpoint
218
+ `BLOBS_PER_BLOCK`, so the fee model prices half of a full checkpoint's blobs.
148
219
 
149
220
  ## Gas and Data Limits
150
221
 
@@ -196,7 +267,7 @@ advertises them in `NodeInfo.txsLimits` (a required field); wallets read it and
196
267
  `GasSettings.fallback` as the default gas limits when sending without explicit limits, and they are enforced
197
268
  by `GasLimitsValidator` (clamped to the per-tx protocol maxima) at three points: RPC tx acceptance
198
269
  (`aztec-node/src/aztec-node/server.ts`), gossip validation (`p2p/src/services/libp2p/libp2p_service.ts`),
199
- and pending-pool admission (`p2p/src/client/factory.ts`). They are deliberately *not* enforced at reqresp or
270
+ and pending-pool admission (`p2p/src/client/factory.ts`). They are deliberately *not* enforced at req/resp or
200
271
  block-proposal validation — admission is relay policy, not block validity.
201
272
 
202
273
  ### Per-block builder budgets
@@ -240,7 +311,8 @@ The outermost limits, enforced as proposal validity in `validateCheckpointLimits
240
311
 
241
312
  ## TypeScript Types
242
313
 
243
- - **`Gas`** — mana quantity in two dimensions (`daGas`, `l2Gas`).
314
+ - **`Gas`** — gas quantity in two dimensions (`daGas`, `l2Gas`).
244
315
  - **`GasFees`** — per-unit price in each dimension (`feePerDaGas`, `feePerL2Gas`).
245
316
  - **`GasSettings`** — sender-chosen fee parameters: gas limits, teardown limits, max fees, priority fees.
246
317
  - **`GasUsed`** — actual consumption after execution. Note: `billedGas` uses the teardown gas *limit*, not actual usage.
318
+ - **`fee_math.ts`** — TypeScript port of the FeeLib formulas, used for fee prediction over the oracle LAG window.