@aztec/sequencer-client 0.0.1-commit.f5d02921e → 0.0.1-commit.f7ea82942
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/client/sequencer-client.d.ts +3 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +3 -4
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +8 -11
- package/dest/global_variable_builder/fee_predictor.d.ts +37 -0
- package/dest/global_variable_builder/fee_predictor.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_predictor.js +128 -0
- package/dest/global_variable_builder/fee_provider.d.ts +21 -0
- package/dest/global_variable_builder/fee_provider.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_provider.js +58 -0
- package/dest/global_variable_builder/global_builder.d.ts +4 -9
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +3 -41
- package/dest/global_variable_builder/index.d.ts +3 -1
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- package/dest/global_variable_builder/index.js +2 -0
- package/dest/publisher/config.d.ts +1 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +2 -2
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +3 -4
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.d.ts +1 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +0 -1
- package/dest/publisher/sequencer-publisher.d.ts +16 -35
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +60 -92
- package/dest/sequencer/chain_state_overrides.d.ts +25 -0
- package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
- package/dest/sequencer/chain_state_overrides.js +39 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts +27 -22
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +294 -155
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts +34 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.js +72 -0
- package/dest/sequencer/events.d.ts +6 -1
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +7 -10
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +27 -20
- package/dest/sequencer/sequencer.d.ts +5 -2
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +31 -23
- package/dest/sequencer/timetable.d.ts +14 -1
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +45 -36
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +5 -7
- package/src/config.ts +8 -11
- package/src/global_variable_builder/README.md +44 -0
- package/src/global_variable_builder/fee_predictor.ts +172 -0
- package/src/global_variable_builder/fee_provider.ts +75 -0
- package/src/global_variable_builder/global_builder.ts +7 -54
- package/src/global_variable_builder/index.ts +2 -0
- package/src/publisher/config.ts +6 -4
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +3 -1
- package/src/publisher/sequencer-publisher-factory.ts +0 -3
- package/src/publisher/sequencer-publisher.ts +106 -152
- package/src/sequencer/README.md +83 -13
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +365 -205
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/events.ts +5 -0
- package/src/sequencer/metrics.ts +35 -25
- package/src/sequencer/sequencer.ts +44 -26
- package/src/sequencer/timetable.ts +57 -45
|
@@ -4,6 +4,7 @@ import { SequencerState } from './utils.js';
|
|
|
4
4
|
export declare class SequencerTimetable {
|
|
5
5
|
private readonly metrics?;
|
|
6
6
|
private readonly log?;
|
|
7
|
+
private readonly checkpointTiming;
|
|
7
8
|
/**
|
|
8
9
|
* How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
|
|
9
10
|
* assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
|
|
@@ -50,6 +51,13 @@ export declare class SequencerTimetable {
|
|
|
50
51
|
readonly blockDuration: number | undefined;
|
|
51
52
|
/** Maximum number of blocks that can be built in this slot configuration */
|
|
52
53
|
readonly maxNumberOfBlocks: number;
|
|
54
|
+
/** Whether pipelining is enabled (checkpoint finalization deferred to next slot). */
|
|
55
|
+
readonly pipelining: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* How far into the target slot attestation collection can extend when pipelining.
|
|
58
|
+
* Covers validator re-execution (one block duration) plus one-way attestation return.
|
|
59
|
+
*/
|
|
60
|
+
readonly pipeliningAttestationGracePeriod: number;
|
|
53
61
|
constructor(opts: {
|
|
54
62
|
ethereumSlotDuration: number;
|
|
55
63
|
aztecSlotDuration: number;
|
|
@@ -57,7 +65,12 @@ export declare class SequencerTimetable {
|
|
|
57
65
|
p2pPropagationTime?: number;
|
|
58
66
|
blockDurationMs?: number;
|
|
59
67
|
enforce: boolean;
|
|
68
|
+
pipelining?: boolean;
|
|
60
69
|
}, metrics?: SequencerMetrics | undefined, log?: Logger | undefined);
|
|
70
|
+
getCheckpointAssemblyDeadline(): number;
|
|
71
|
+
getCheckpointAttestationDeadline(): number;
|
|
72
|
+
getCheckpointAttestationStartDeadline(): number;
|
|
73
|
+
getCheckpointPublishingDeadline(): number;
|
|
61
74
|
getMaxAllowedTime(state: Extract<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): undefined;
|
|
62
75
|
getMaxAllowedTime(state: Exclude<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): number;
|
|
63
76
|
getMaxAllowedTime(state: SequencerState): number | undefined;
|
|
@@ -85,4 +98,4 @@ export declare class SequencerTimetable {
|
|
|
85
98
|
isLastBlock: boolean;
|
|
86
99
|
};
|
|
87
100
|
}
|
|
88
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
101
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQVdwRCxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLHFCQUFhLGtCQUFrQjtJQWlGM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7SUFqRnZCLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQW1CO0lBRXBEOzs7O09BSUc7SUFDSCxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0M7Ozs7T0FJRztJQUNILFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3Qzs7Ozs7T0FLRztJQUNILFNBQWdCLDBCQUEwQixFQUFFLE1BQU0sQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0gsU0FBZ0IsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBRXpDOzs7T0FHRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBc0I7SUFFOUQsdURBQXVEO0lBQ3ZELFNBQWdCLDRCQUE0QixFQUFFLE1BQU0sQ0FBa0M7SUFFdEYsbUdBQW1HO0lBQ25HLFNBQWdCLGtCQUFrQixFQUFFLE1BQU0sQ0FBQztJQUUzQyxtRkFBbUY7SUFDbkYsU0FBZ0Isc0JBQXNCLEVBQUUsTUFBTSxDQUE0QjtJQUUxRSx3Q0FBd0M7SUFDeEMsU0FBZ0Isb0JBQW9CLEVBQUUsTUFBTSxDQUFDO0lBRTdDLGtGQUFrRjtJQUNsRixTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsNERBQTREO0lBQzVELFNBQWdCLE9BQU8sRUFBRSxPQUFPLENBQUM7SUFFakMsb0dBQW9HO0lBQ3BHLFNBQWdCLGFBQWEsRUFBRSxNQUFNLEdBQUcsU0FBUyxDQUFDO0lBRWxELDRFQUE0RTtJQUM1RSxTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMscUZBQXFGO0lBQ3JGLFNBQWdCLFVBQVUsRUFBRSxPQUFPLENBQUM7SUFFcEM7OztPQUdHO0lBQ0gsU0FBZ0IsZ0NBQWdDLEVBQUUsTUFBTSxDQUFDO0lBRXpELFlBQ0UsSUFBSSxFQUFFO1FBQ0osb0JBQW9CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztRQUMxQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7UUFDekIsa0JBQWtCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDNUIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxPQUFPLENBQUM7UUFDakIsVUFBVSxDQUFDLEVBQUUsT0FBTyxDQUFDO0tBQ3RCLEVBQ2dCLE9BQU8sQ0FBQyw4QkFBa0IsRUFDMUIsR0FBRyxDQUFDLG9CQUFRLEVBd0Q5QjtJQUVNLDZCQUE2QixJQUFJLE1BQU0sQ0FFN0M7SUFFTSxnQ0FBZ0MsSUFBSSxNQUFNLENBRWhEO0lBRU0scUNBQXFDLElBQUksTUFBTSxDQUVyRDtJQUVNLCtCQUErQixJQUFJLE1BQU0sQ0FFL0M7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTRCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { CHECKPOINT_ASSEMBLE_TIME, CHECKPOINT_INITIALIZATION_TIME, DEFAULT_P2P_PROPAGATION_TIME, MIN_EXECUTION_TIME } from '@aztec/stdlib/timetable';
|
|
1
|
+
import { CHECKPOINT_ASSEMBLE_TIME, CHECKPOINT_INITIALIZATION_TIME, DEFAULT_P2P_PROPAGATION_TIME, MIN_EXECUTION_TIME, createCheckpointTimingModel } from '@aztec/stdlib/timetable';
|
|
2
2
|
import { SequencerTooSlowError } from './errors.js';
|
|
3
3
|
import { SequencerState } from './utils.js';
|
|
4
4
|
export class SequencerTimetable {
|
|
5
5
|
metrics;
|
|
6
6
|
log;
|
|
7
|
+
checkpointTiming;
|
|
7
8
|
/**
|
|
8
9
|
* How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
|
|
9
10
|
* assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
|
|
@@ -37,6 +38,11 @@ export class SequencerTimetable {
|
|
|
37
38
|
/** Whether assertTimeLeft will throw if not enough time. */ enforce;
|
|
38
39
|
/** Duration per block when building multiple blocks per slot (undefined = single block per slot) */ blockDuration;
|
|
39
40
|
/** Maximum number of blocks that can be built in this slot configuration */ maxNumberOfBlocks;
|
|
41
|
+
/** Whether pipelining is enabled (checkpoint finalization deferred to next slot). */ pipelining;
|
|
42
|
+
/**
|
|
43
|
+
* How far into the target slot attestation collection can extend when pipelining.
|
|
44
|
+
* Covers validator re-execution (one block duration) plus one-way attestation return.
|
|
45
|
+
*/ pipeliningAttestationGracePeriod;
|
|
40
46
|
constructor(opts, metrics, log){
|
|
41
47
|
this.metrics = metrics;
|
|
42
48
|
this.log = log;
|
|
@@ -46,40 +52,28 @@ export class SequencerTimetable {
|
|
|
46
52
|
this.ethereumSlotDuration = opts.ethereumSlotDuration;
|
|
47
53
|
this.aztecSlotDuration = opts.aztecSlotDuration;
|
|
48
54
|
this.l1PublishingTime = opts.l1PublishingTime;
|
|
49
|
-
this.p2pPropagationTime = opts.p2pPropagationTime ?? DEFAULT_P2P_PROPAGATION_TIME;
|
|
50
55
|
this.blockDuration = opts.blockDurationMs ? opts.blockDurationMs / 1000 : undefined;
|
|
51
56
|
this.enforce = opts.enforce;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
this.pipelining = opts.pipelining ?? false;
|
|
58
|
+
this.checkpointTiming = createCheckpointTimingModel({
|
|
59
|
+
aztecSlotDuration: this.aztecSlotDuration,
|
|
60
|
+
ethereumSlotDuration: this.ethereumSlotDuration,
|
|
61
|
+
blockDuration: this.blockDuration,
|
|
62
|
+
l1PublishingTime: this.l1PublishingTime,
|
|
63
|
+
p2pPropagationTime: opts.p2pPropagationTime ?? DEFAULT_P2P_PROPAGATION_TIME,
|
|
64
|
+
pipelining: this.pipelining
|
|
65
|
+
});
|
|
66
|
+
this.p2pPropagationTime = this.checkpointTiming.p2pPropagationTime;
|
|
67
|
+
this.checkpointAssembleTime = this.checkpointTiming.checkpointAssembleTime;
|
|
68
|
+
this.checkpointInitializationTime = this.checkpointTiming.checkpointInitializationTime;
|
|
69
|
+
this.minExecutionTime = this.checkpointTiming.minExecutionTime;
|
|
63
70
|
// Calculate initialization offset - estimate of time needed for sync + proposer check
|
|
64
71
|
// This is the baseline for all sub-slot deadlines
|
|
65
|
-
this.initializationOffset = this.checkpointInitializationTime;
|
|
66
|
-
|
|
67
|
-
this.
|
|
68
|
-
this.
|
|
69
|
-
|
|
70
|
-
if (!this.blockDuration) {
|
|
71
|
-
this.maxNumberOfBlocks = 1; // Single block per slot
|
|
72
|
-
} else {
|
|
73
|
-
const timeReservedAtEnd = this.blockDuration + // Last sub-slot for validator re-execution
|
|
74
|
-
this.checkpointFinalizationTime; // Checkpoint finalization
|
|
75
|
-
const timeAvailableForBlocks = this.aztecSlotDuration - this.initializationOffset - timeReservedAtEnd;
|
|
76
|
-
this.maxNumberOfBlocks = Math.floor(timeAvailableForBlocks / this.blockDuration);
|
|
77
|
-
}
|
|
78
|
-
// Minimum work to do within a slot for building a block with the minimum time for execution and publishing its checkpoint
|
|
79
|
-
const minWorkToDo = this.initializationOffset + this.minExecutionTime * 2 + // Execution and reexecution
|
|
80
|
-
this.checkpointFinalizationTime;
|
|
81
|
-
const initializeDeadline = this.aztecSlotDuration - minWorkToDo;
|
|
82
|
-
this.initializeDeadline = initializeDeadline;
|
|
72
|
+
this.initializationOffset = this.checkpointTiming.checkpointInitializationTime;
|
|
73
|
+
this.checkpointFinalizationTime = this.checkpointTiming.checkpointFinalizationTime;
|
|
74
|
+
this.pipeliningAttestationGracePeriod = this.checkpointTiming.pipeliningAttestationGracePeriod;
|
|
75
|
+
this.maxNumberOfBlocks = this.checkpointTiming.calculateMaxBlocksPerSlot();
|
|
76
|
+
this.initializeDeadline = this.checkpointTiming.initializeDeadline;
|
|
83
77
|
this.log?.info(`Sequencer timetable initialized with ${this.maxNumberOfBlocks} blocks per slot (${this.enforce ? 'enforced' : 'not enforced'})`, {
|
|
84
78
|
ethereumSlotDuration: this.ethereumSlotDuration,
|
|
85
79
|
aztecSlotDuration: this.aztecSlotDuration,
|
|
@@ -90,14 +84,28 @@ export class SequencerTimetable {
|
|
|
90
84
|
blockAssembleTime: this.checkpointAssembleTime,
|
|
91
85
|
initializeDeadline: this.initializeDeadline,
|
|
92
86
|
enforce: this.enforce,
|
|
93
|
-
|
|
87
|
+
pipelining: this.pipelining,
|
|
88
|
+
pipeliningAttestationGracePeriod: this.pipeliningAttestationGracePeriod,
|
|
89
|
+
minWorkToDo: this.checkpointTiming.minimumBuildSlotWork,
|
|
94
90
|
blockDuration: this.blockDuration,
|
|
95
91
|
maxNumberOfBlocks: this.maxNumberOfBlocks
|
|
96
92
|
});
|
|
97
|
-
if (initializeDeadline <= 0) {
|
|
98
|
-
throw new Error(`Block proposal initialize deadline cannot be negative (got ${initializeDeadline} from total time needed ${
|
|
93
|
+
if (this.initializeDeadline <= 0) {
|
|
94
|
+
throw new Error(`Block proposal initialize deadline cannot be negative (got ${this.initializeDeadline} from total time needed ${this.checkpointTiming.minimumBuildSlotWork} and a slot duration of ${this.aztecSlotDuration}).`);
|
|
99
95
|
}
|
|
100
96
|
}
|
|
97
|
+
getCheckpointAssemblyDeadline() {
|
|
98
|
+
return this.checkpointTiming.checkpointAssemblyDeadline;
|
|
99
|
+
}
|
|
100
|
+
getCheckpointAttestationDeadline() {
|
|
101
|
+
return this.checkpointTiming.checkpointAttestationDeadline;
|
|
102
|
+
}
|
|
103
|
+
getCheckpointAttestationStartDeadline() {
|
|
104
|
+
return this.checkpointTiming.checkpointAttestationStartDeadline;
|
|
105
|
+
}
|
|
106
|
+
getCheckpointPublishingDeadline() {
|
|
107
|
+
return this.checkpointTiming.checkpointPublishingDeadline;
|
|
108
|
+
}
|
|
101
109
|
getMaxAllowedTime(state) {
|
|
102
110
|
switch(state){
|
|
103
111
|
case SequencerState.STOPPED:
|
|
@@ -113,10 +121,11 @@ export class SequencerTimetable {
|
|
|
113
121
|
case SequencerState.WAITING_UNTIL_NEXT_BLOCK:
|
|
114
122
|
return this.initializeDeadline + this.checkpointInitializationTime;
|
|
115
123
|
case SequencerState.ASSEMBLING_CHECKPOINT:
|
|
124
|
+
return this.getCheckpointAssemblyDeadline();
|
|
116
125
|
case SequencerState.COLLECTING_ATTESTATIONS:
|
|
117
|
-
return this.
|
|
126
|
+
return this.getCheckpointAttestationStartDeadline();
|
|
118
127
|
case SequencerState.PUBLISHING_CHECKPOINT:
|
|
119
|
-
return this.
|
|
128
|
+
return this.getCheckpointPublishingDeadline();
|
|
120
129
|
default:
|
|
121
130
|
{
|
|
122
131
|
const _exhaustiveCheck = state;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/sequencer-client",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.f7ea82942",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -26,37 +26,37 @@
|
|
|
26
26
|
"test:integration:run": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --config jest.integration.config.json"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@aztec/aztec.js": "0.0.1-commit.
|
|
30
|
-
"@aztec/bb-prover": "0.0.1-commit.
|
|
31
|
-
"@aztec/blob-client": "0.0.1-commit.
|
|
32
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
33
|
-
"@aztec/constants": "0.0.1-commit.
|
|
34
|
-
"@aztec/epoch-cache": "0.0.1-commit.
|
|
35
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
36
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
37
|
-
"@aztec/l1-artifacts": "0.0.1-commit.
|
|
38
|
-
"@aztec/node-keystore": "0.0.1-commit.
|
|
39
|
-
"@aztec/noir-acvm_js": "0.0.1-commit.
|
|
40
|
-
"@aztec/noir-contracts.js": "0.0.1-commit.
|
|
41
|
-
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.
|
|
42
|
-
"@aztec/noir-types": "0.0.1-commit.
|
|
43
|
-
"@aztec/p2p": "0.0.1-commit.
|
|
44
|
-
"@aztec/protocol-contracts": "0.0.1-commit.
|
|
45
|
-
"@aztec/prover-client": "0.0.1-commit.
|
|
46
|
-
"@aztec/simulator": "0.0.1-commit.
|
|
47
|
-
"@aztec/slasher": "0.0.1-commit.
|
|
48
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
49
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
50
|
-
"@aztec/validator-client": "0.0.1-commit.
|
|
51
|
-
"@aztec/validator-ha-signer": "0.0.1-commit.
|
|
52
|
-
"@aztec/world-state": "0.0.1-commit.
|
|
29
|
+
"@aztec/aztec.js": "0.0.1-commit.f7ea82942",
|
|
30
|
+
"@aztec/bb-prover": "0.0.1-commit.f7ea82942",
|
|
31
|
+
"@aztec/blob-client": "0.0.1-commit.f7ea82942",
|
|
32
|
+
"@aztec/blob-lib": "0.0.1-commit.f7ea82942",
|
|
33
|
+
"@aztec/constants": "0.0.1-commit.f7ea82942",
|
|
34
|
+
"@aztec/epoch-cache": "0.0.1-commit.f7ea82942",
|
|
35
|
+
"@aztec/ethereum": "0.0.1-commit.f7ea82942",
|
|
36
|
+
"@aztec/foundation": "0.0.1-commit.f7ea82942",
|
|
37
|
+
"@aztec/l1-artifacts": "0.0.1-commit.f7ea82942",
|
|
38
|
+
"@aztec/node-keystore": "0.0.1-commit.f7ea82942",
|
|
39
|
+
"@aztec/noir-acvm_js": "0.0.1-commit.f7ea82942",
|
|
40
|
+
"@aztec/noir-contracts.js": "0.0.1-commit.f7ea82942",
|
|
41
|
+
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.f7ea82942",
|
|
42
|
+
"@aztec/noir-types": "0.0.1-commit.f7ea82942",
|
|
43
|
+
"@aztec/p2p": "0.0.1-commit.f7ea82942",
|
|
44
|
+
"@aztec/protocol-contracts": "0.0.1-commit.f7ea82942",
|
|
45
|
+
"@aztec/prover-client": "0.0.1-commit.f7ea82942",
|
|
46
|
+
"@aztec/simulator": "0.0.1-commit.f7ea82942",
|
|
47
|
+
"@aztec/slasher": "0.0.1-commit.f7ea82942",
|
|
48
|
+
"@aztec/stdlib": "0.0.1-commit.f7ea82942",
|
|
49
|
+
"@aztec/telemetry-client": "0.0.1-commit.f7ea82942",
|
|
50
|
+
"@aztec/validator-client": "0.0.1-commit.f7ea82942",
|
|
51
|
+
"@aztec/validator-ha-signer": "0.0.1-commit.f7ea82942",
|
|
52
|
+
"@aztec/world-state": "0.0.1-commit.f7ea82942",
|
|
53
53
|
"lodash.chunk": "^4.2.0",
|
|
54
54
|
"tslib": "^2.4.0",
|
|
55
55
|
"viem": "npm:@aztec/viem@2.38.2"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@aztec/archiver": "0.0.1-commit.
|
|
59
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
58
|
+
"@aztec/archiver": "0.0.1-commit.f7ea82942",
|
|
59
|
+
"@aztec/kv-store": "0.0.1-commit.f7ea82942",
|
|
60
60
|
"@electric-sql/pglite": "^0.3.14",
|
|
61
61
|
"@jest/globals": "^30.0.0",
|
|
62
62
|
"@types/jest": "^30.0.0",
|
|
@@ -14,7 +14,6 @@ import type { P2P } from '@aztec/p2p';
|
|
|
14
14
|
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
15
15
|
import type { L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
16
16
|
import type { ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
17
|
-
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
18
17
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
19
18
|
import { L1Metrics, type TelemetryClient } from '@aztec/telemetry-client';
|
|
20
19
|
import { FullNodeCheckpointsBuilder, NodeKeystoreAdapter, type ValidatorClient } from '@aztec/validator-client';
|
|
@@ -118,11 +117,6 @@ export class SequencerClient {
|
|
|
118
117
|
{ dateProvider: deps.dateProvider },
|
|
119
118
|
));
|
|
120
119
|
|
|
121
|
-
const slashFactoryContract = new SlashFactoryContract(
|
|
122
|
-
publicClient,
|
|
123
|
-
config.l1Contracts.slashFactoryAddress?.toString() ?? EthAddress.ZERO.toString(),
|
|
124
|
-
);
|
|
125
|
-
|
|
126
120
|
const publisherFactory =
|
|
127
121
|
deps.publisherFactory ??
|
|
128
122
|
new SequencerPublisherFactory(config, {
|
|
@@ -130,7 +124,6 @@ export class SequencerClient {
|
|
|
130
124
|
blobClient: deps.blobClient,
|
|
131
125
|
epochCache,
|
|
132
126
|
governanceProposerContract,
|
|
133
|
-
slashFactoryContract,
|
|
134
127
|
rollupContract,
|
|
135
128
|
dateProvider: deps.dateProvider,
|
|
136
129
|
publisherManager,
|
|
@@ -209,6 +202,11 @@ export class SequencerClient {
|
|
|
209
202
|
this.l1Metrics?.stop();
|
|
210
203
|
}
|
|
211
204
|
|
|
205
|
+
/** Triggers an immediate run of the sequencer, bypassing the polling interval. */
|
|
206
|
+
public trigger() {
|
|
207
|
+
return this.sequencer.trigger();
|
|
208
|
+
}
|
|
209
|
+
|
|
212
210
|
public getSequencer(): Sequencer {
|
|
213
211
|
return this.sequencer;
|
|
214
212
|
}
|
package/src/config.ts
CHANGED
|
@@ -83,7 +83,7 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
83
83
|
maxTxsPerCheckpoint: {
|
|
84
84
|
env: 'SEQ_MAX_TX_PER_CHECKPOINT',
|
|
85
85
|
description: 'The maximum number of txs across all blocks in a checkpoint.',
|
|
86
|
-
parseEnv: (val: string) =>
|
|
86
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
87
87
|
},
|
|
88
88
|
minTxsPerBlock: {
|
|
89
89
|
env: 'SEQ_MIN_TX_PER_BLOCK',
|
|
@@ -102,12 +102,12 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
102
102
|
maxL2BlockGas: {
|
|
103
103
|
env: 'SEQ_MAX_L2_BLOCK_GAS',
|
|
104
104
|
description: 'The maximum L2 block gas.',
|
|
105
|
-
parseEnv: (val: string) =>
|
|
105
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
106
106
|
},
|
|
107
107
|
maxDABlockGas: {
|
|
108
108
|
env: 'SEQ_MAX_DA_BLOCK_GAS',
|
|
109
109
|
description: 'The maximum DA block gas.',
|
|
110
|
-
parseEnv: (val: string) =>
|
|
110
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
111
111
|
},
|
|
112
112
|
perBlockAllocationMultiplier: {
|
|
113
113
|
env: 'SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER',
|
|
@@ -124,7 +124,7 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
124
124
|
},
|
|
125
125
|
coinbase: {
|
|
126
126
|
env: 'COINBASE',
|
|
127
|
-
parseEnv: (val: string) =>
|
|
127
|
+
parseEnv: (val: string) => EthAddress.fromString(val),
|
|
128
128
|
description: 'Recipient of block reward.',
|
|
129
129
|
},
|
|
130
130
|
feeRecipient: {
|
|
@@ -153,12 +153,7 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
153
153
|
l1PublishingTime: {
|
|
154
154
|
env: 'SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT',
|
|
155
155
|
description: 'How much time (in seconds) we allow in the slot for publishing the L1 tx (defaults to 1 L1 slot).',
|
|
156
|
-
parseEnv: (val: string) =>
|
|
157
|
-
},
|
|
158
|
-
attestationPropagationTime: {
|
|
159
|
-
env: 'SEQ_ATTESTATION_PROPAGATION_TIME',
|
|
160
|
-
description: 'How many seconds it takes for proposals and attestations to travel across the p2p layer (one-way)',
|
|
161
|
-
...numberConfigHelper(DefaultSequencerConfig.attestationPropagationTime),
|
|
156
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
162
157
|
},
|
|
163
158
|
fakeProcessingDelayPerTxMs: {
|
|
164
159
|
description: 'Used for testing to introduce a fake delay after processing each tx',
|
|
@@ -237,13 +232,15 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
|
|
|
237
232
|
};
|
|
238
233
|
|
|
239
234
|
export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientConfig> = {
|
|
235
|
+
// chainConfigMappings must come first: its l1Contracts only maps rollupAddress,
|
|
236
|
+
// while l1ReaderConfigMappings (spread later) maps all L1 contract addresses.
|
|
237
|
+
...chainConfigMappings,
|
|
240
238
|
...validatorClientConfigMappings,
|
|
241
239
|
...sequencerConfigMappings,
|
|
242
240
|
...keyStoreConfigMappings,
|
|
243
241
|
...l1ReaderConfigMappings,
|
|
244
242
|
...sequencerTxSenderConfigMappings,
|
|
245
243
|
...sequencerPublisherConfigMappings,
|
|
246
|
-
...chainConfigMappings,
|
|
247
244
|
...pipelineConfigMappings,
|
|
248
245
|
...pickConfigMappings(l1ContractsConfigMappings, ['ethereumSlotDuration', 'aztecSlotDuration', 'aztecEpochDuration']),
|
|
249
246
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Fee Prediction
|
|
2
|
+
|
|
3
|
+
The `FeePredictor` predicts min fees for upcoming L2 slots based on the current L1
|
|
4
|
+
oracle state and an assumed mana usage pattern.
|
|
5
|
+
|
|
6
|
+
## Prediction Window
|
|
7
|
+
|
|
8
|
+
The prediction covers `LAG = 2` entries (the next available slot plus 1 more).
|
|
9
|
+
A new oracle update can activate at `startSlot + LAG`, so only the first LAG entries
|
|
10
|
+
are guaranteed stable. Beyond that, L1 fees could change unpredictably.
|
|
11
|
+
|
|
12
|
+
Each entry uses:
|
|
13
|
+
|
|
14
|
+
- The **actual L1 fees** the oracle would return for that slot's timestamp (pre or post,
|
|
15
|
+
depending on whether the slot is before or after `slotOfChange`).
|
|
16
|
+
- A **configurable congestion assumption** (`ManaUsageEstimate`): none (0 mana), target
|
|
17
|
+
(steady state), or limit (worst case, 2x target).
|
|
18
|
+
|
|
19
|
+
The client picks `max(predicted[0..LAG-1])` as their `maxFeesPerGas`.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
predicted[0] → fee at next slot (current oracle + current congestion)
|
|
23
|
+
predicted[1] → fee at next slot + 1 (congestion may change based on usage estimate)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Why LAG and not LIFETIME?
|
|
27
|
+
|
|
28
|
+
The L1 gas oracle has two timing constants:
|
|
29
|
+
|
|
30
|
+
- **LAG = 2 slots**: when new fees are queued, they activate LAG slots later
|
|
31
|
+
- **LIFETIME = 5 slots**: after an oracle update, the next update is rejected until
|
|
32
|
+
`slotOfChange + (LIFETIME - LAG)` slots have passed
|
|
33
|
+
|
|
34
|
+
If the oracle cooldown has already elapsed (i.e., no recent update), a new update can
|
|
35
|
+
be enqueued at any moment. Its new values would activate LAG slots later. This means
|
|
36
|
+
predictions beyond LAG slots could be invalidated by an oracle update that happens
|
|
37
|
+
right after we query.
|
|
38
|
+
|
|
39
|
+
With a LIFETIME-sized window, we'd give a false sense of coverage: the prediction
|
|
40
|
+
would look like it covers 6 slots, but slots beyond LAG could be wrong if an update
|
|
41
|
+
is enqueued after the prediction is computed.
|
|
42
|
+
|
|
43
|
+
By limiting to LAG entries, we guarantee that all predicted L1 fees are the ones that
|
|
44
|
+
will actually be used — no oracle update can change them within this window.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { type L1FeeData, MAX_FEE_ASSET_PRICE_MODIFIER_BPS, type RollupContract } from '@aztec/ethereum/contracts';
|
|
2
|
+
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { times } from '@aztec/foundation/collection';
|
|
4
|
+
import type { DateProvider } from '@aztec/foundation/timer';
|
|
5
|
+
import { getSlotAtNextL1Block, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
6
|
+
import {
|
|
7
|
+
FEE_ORACLE_LAG,
|
|
8
|
+
GasFees,
|
|
9
|
+
MIN_ETH_PER_FEE_ASSET,
|
|
10
|
+
ManaUsageEstimate,
|
|
11
|
+
computeExcessMana,
|
|
12
|
+
computeManaMinFee,
|
|
13
|
+
} from '@aztec/stdlib/gas';
|
|
14
|
+
|
|
15
|
+
/** Cached rollup state for fee prediction. Refreshed once per L1 block. */
|
|
16
|
+
type FeeOracleState = {
|
|
17
|
+
lastSlot: SlotNumber;
|
|
18
|
+
excessMana: bigint;
|
|
19
|
+
ethPerFeeAsset: bigint;
|
|
20
|
+
manaTarget: bigint;
|
|
21
|
+
manaLimit: bigint;
|
|
22
|
+
provingCostPerManaEth: bigint;
|
|
23
|
+
epochDuration: bigint;
|
|
24
|
+
/** Pre-resolved L1 fees for each slot in the prediction window. */
|
|
25
|
+
l1FeesBySlot: L1FeeData[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Predicts min fees for LAG upcoming slots based on the L1 oracle state.
|
|
30
|
+
* A new oracle update can activate at startSlot + LAG, so only the first LAG entries
|
|
31
|
+
* are guaranteed stable. Caches L1 queries per L1 block and recomputes predictions
|
|
32
|
+
* for each mana usage estimate.
|
|
33
|
+
*/
|
|
34
|
+
export class FeePredictor {
|
|
35
|
+
private cachedState: Promise<FeeOracleState> | undefined;
|
|
36
|
+
private cachedL1BlockNumber: bigint | undefined;
|
|
37
|
+
|
|
38
|
+
private readonly slotDuration: number;
|
|
39
|
+
private readonly l1GenesisTime: bigint;
|
|
40
|
+
private readonly ethereumSlotDuration: number;
|
|
41
|
+
|
|
42
|
+
constructor(
|
|
43
|
+
private readonly rollupContract: RollupContract,
|
|
44
|
+
private readonly publicClient: { getBlockNumber: (opts?: { cacheTime?: number }) => Promise<bigint> },
|
|
45
|
+
private readonly dateProvider: DateProvider,
|
|
46
|
+
config: { slotDuration: number; l1GenesisTime: bigint; ethereumSlotDuration: number },
|
|
47
|
+
) {
|
|
48
|
+
this.slotDuration = config.slotDuration;
|
|
49
|
+
this.l1GenesisTime = config.l1GenesisTime;
|
|
50
|
+
this.ethereumSlotDuration = config.ethereumSlotDuration;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Returns predicted min fees for each slot in the prediction window. */
|
|
54
|
+
async getPredictedMinFees(manaUsage: ManaUsageEstimate): Promise<GasFees[]> {
|
|
55
|
+
const state = await this.getState();
|
|
56
|
+
return this.computePredictions(state, manaUsage);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Fetches and caches rollup state. Refreshes when L1 block number advances. */
|
|
60
|
+
private async getState(): Promise<FeeOracleState> {
|
|
61
|
+
const blockNumber = await this.publicClient.getBlockNumber({ cacheTime: 0 });
|
|
62
|
+
if (this.cachedL1BlockNumber === undefined || blockNumber > this.cachedL1BlockNumber) {
|
|
63
|
+
this.cachedL1BlockNumber = blockNumber;
|
|
64
|
+
this.cachedState = this.fetchState(blockNumber);
|
|
65
|
+
}
|
|
66
|
+
return this.cachedState!;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async fetchState(blockNumber: bigint): Promise<FeeOracleState> {
|
|
70
|
+
// Pin all non-constant queries to this L1 block number for a consistent snapshot.
|
|
71
|
+
const opts = { blockNumber };
|
|
72
|
+
|
|
73
|
+
// Cached constants don't need pinning
|
|
74
|
+
const [manaTarget, manaLimit, provingCostPerManaEth, epochDuration] = await Promise.all([
|
|
75
|
+
this.rollupContract.getManaTarget(),
|
|
76
|
+
this.rollupContract.getManaLimit(),
|
|
77
|
+
this.rollupContract.getProvingCostPerMana(),
|
|
78
|
+
this.rollupContract.getEpochDuration(),
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
// First, compute the earliest possible nextSlot independently of the checkpoint, so we can
|
|
82
|
+
// evaluate pruneability at the prediction start timestamp instead of the current L1 block time.
|
|
83
|
+
// This avoids an epoch-boundary edge case where the effective parent differs between now and nextSlot.
|
|
84
|
+
const slotConfig = { slotDuration: this.slotDuration, l1GenesisTime: this.l1GenesisTime };
|
|
85
|
+
const currentSlot = await this.rollupContract.getSlotNumber(opts);
|
|
86
|
+
|
|
87
|
+
const slotAtNextL1Block = getSlotAtNextL1Block(BigInt(this.dateProvider.nowInSeconds()), {
|
|
88
|
+
l1GenesisTime: this.l1GenesisTime,
|
|
89
|
+
slotDuration: this.slotDuration,
|
|
90
|
+
ethereumSlotDuration: this.ethereumSlotDuration,
|
|
91
|
+
});
|
|
92
|
+
const preliminaryNextSlot = SlotNumber(Math.max(currentSlot, slotAtNextL1Block));
|
|
93
|
+
const nextSlotTimestamp = getTimestampForSlot(preliminaryNextSlot, slotConfig);
|
|
94
|
+
|
|
95
|
+
// Resolve the effective checkpoint at the prediction start timestamp
|
|
96
|
+
const lastCheckpoint = await this.rollupContract.getEffectivePendingCheckpoint(nextSlotTimestamp, opts);
|
|
97
|
+
const lastSlot = lastCheckpoint.slotNumber;
|
|
98
|
+
// Refine nextSlot: also account for the slot after the last checkpoint
|
|
99
|
+
const nextSlot = SlotNumber(Math.max(SlotNumber.add(lastSlot, 1), preliminaryNextSlot));
|
|
100
|
+
const feeHeader = lastCheckpoint.feeHeader;
|
|
101
|
+
|
|
102
|
+
const slotCount = FEE_ORACLE_LAG;
|
|
103
|
+
const timestamps = times(slotCount, i => getTimestampForSlot(SlotNumber.add(nextSlot, i), slotConfig));
|
|
104
|
+
const l1FeesBySlot = await Promise.all(timestamps.map(ts => this.rollupContract.getL1FeesAt(ts, opts)));
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
lastSlot,
|
|
108
|
+
excessMana: computeExcessMana(feeHeader.excessMana, feeHeader.manaUsed, manaTarget),
|
|
109
|
+
ethPerFeeAsset: feeHeader.ethPerFeeAsset,
|
|
110
|
+
manaTarget,
|
|
111
|
+
manaLimit,
|
|
112
|
+
provingCostPerManaEth,
|
|
113
|
+
epochDuration: BigInt(epochDuration),
|
|
114
|
+
l1FeesBySlot,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Computes per-slot fee predictions given cached state and a mana usage assumption. */
|
|
119
|
+
private computePredictions(state: FeeOracleState, manaUsage: ManaUsageEstimate): GasFees[] {
|
|
120
|
+
const assumedManaUsed = this.getAssumedManaUsed(state, manaUsage);
|
|
121
|
+
|
|
122
|
+
const result: GasFees[] = [];
|
|
123
|
+
let { excessMana } = state;
|
|
124
|
+
let { ethPerFeeAsset } = state;
|
|
125
|
+
|
|
126
|
+
// Slot 0: current state (next available slot after last checkpoint)
|
|
127
|
+
result.push(this.computeGasFees(state, excessMana, ethPerFeeAsset, state.l1FeesBySlot[0]));
|
|
128
|
+
|
|
129
|
+
// Slots 1..LAG-1: advance excessMana with the assumed mana usage per checkpoint,
|
|
130
|
+
// and decay ethPerFeeAsset by MAX_FEE_ASSET_PRICE_MODIFIER_BPS per slot for conservative estimates.
|
|
131
|
+
// Lower ethPerFeeAsset means higher fees in fee asset terms.
|
|
132
|
+
for (let i = 1; i < state.l1FeesBySlot.length; i++) {
|
|
133
|
+
excessMana = computeExcessMana(excessMana, assumedManaUsed, state.manaTarget);
|
|
134
|
+
const decayed = (ethPerFeeAsset * (10000n - MAX_FEE_ASSET_PRICE_MODIFIER_BPS)) / 10000n;
|
|
135
|
+
ethPerFeeAsset = decayed < MIN_ETH_PER_FEE_ASSET ? MIN_ETH_PER_FEE_ASSET : decayed;
|
|
136
|
+
result.push(this.computeGasFees(state, excessMana, ethPerFeeAsset, state.l1FeesBySlot[i]));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private getAssumedManaUsed(state: FeeOracleState, manaUsage: ManaUsageEstimate): bigint {
|
|
143
|
+
switch (manaUsage) {
|
|
144
|
+
case ManaUsageEstimate.None:
|
|
145
|
+
return 0n;
|
|
146
|
+
case ManaUsageEstimate.Target:
|
|
147
|
+
return state.manaTarget;
|
|
148
|
+
case ManaUsageEstimate.Limit:
|
|
149
|
+
return state.manaLimit;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private computeGasFees(
|
|
154
|
+
state: FeeOracleState,
|
|
155
|
+
excessMana: bigint,
|
|
156
|
+
ethPerFeeAsset: bigint,
|
|
157
|
+
l1Fees: L1FeeData,
|
|
158
|
+
): GasFees {
|
|
159
|
+
return new GasFees(
|
|
160
|
+
0,
|
|
161
|
+
computeManaMinFee({
|
|
162
|
+
l1BaseFee: l1Fees.baseFee,
|
|
163
|
+
l1BlobFee: l1Fees.blobFee,
|
|
164
|
+
manaTarget: state.manaTarget,
|
|
165
|
+
epochDuration: state.epochDuration,
|
|
166
|
+
provingCostPerManaEth: state.provingCostPerManaEth,
|
|
167
|
+
excessMana,
|
|
168
|
+
ethPerFeeAsset,
|
|
169
|
+
}),
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|