Skip to content

Commit fec5e8d

Browse files
apollo_consensus_orchestrator: wire SNIP-35 fee_proposal into build_proposal
1 parent 8ccdfc9 commit fec5e8d

4 files changed

Lines changed: 85 additions & 4 deletions

File tree

crates/apollo_consensus_orchestrator/src/build_proposal.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ pub(crate) struct ProposalBuildArguments {
7676
pub override_l2_gas_price_fri: Option<u128>,
7777
pub min_l2_gas_price_per_height: Vec<PricePerHeight>,
7878
pub compare_retrospective_block_hash: bool,
79+
/// SNIP-35: proposer's fee_proposal for this block.
80+
pub fee_proposal: GasPrice,
81+
/// SNIP-35: current fee_actual from the sliding window.
82+
pub fee_actual: Option<GasPrice>,
7983
}
8084

8185
type BuildProposalResult<T> = Result<T, BuildProposalError>;
@@ -176,7 +180,7 @@ async fn initiate_build(args: &mut ProposalBuildArguments) -> BuildProposalResul
176180
starknet_version: starknet_api::block::StarknetVersion::LATEST,
177181
// TODO(Asmaa): Put the real value once we have it.
178182
version_constant_commitment: Default::default(),
179-
fee_proposal: GasPrice::default(),
183+
fee_proposal: args.fee_proposal,
180184
};
181185

182186
let retrospective_block_hash = wait_for_retrospective_block_hash(
@@ -319,7 +323,7 @@ async fn get_proposal_content(
319323
info.l2_gas_used,
320324
args.override_l2_gas_price_fri,
321325
&args.min_l2_gas_price_per_height,
322-
None,
326+
args.fee_actual,
323327
);
324328
let fin_payload = ProposalFinPayload {
325329
commitment_parts: CommitmentParts::from(&info),

crates/apollo_consensus_orchestrator/src/fee_market/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ pub(crate) const PPT_DENOMINATOR: u128 = 1000;
3131
/// Number of fee_proposal values used to compute fee_actual (SNIP-35).
3232
pub(crate) const FEE_PROPOSAL_WINDOW_SIZE: usize = 10;
3333

34+
/// Maximum fee_proposal change per block in parts per thousand (SNIP-35: 0.2%).
35+
pub(crate) const FEE_PROPOSAL_MARGIN_PPT: u128 = 2;
36+
37+
/// Target USD cost per L2 gas unit in atto-USD ($3e-9 = 3_000_000_000 atto-USD).
38+
pub(crate) const TARGET_ATTO_USD_PER_L2_GAS: u128 = 3_000_000_000;
39+
40+
/// Hard minimum for the oracle-derived floor (FRI).
41+
pub(crate) const ORACLE_L2_GAS_FLOOR_MIN_FRI: u128 = 8_000_000_000; // 8 gwei, matches MIN_ALLOWED_GAS_PRICE
42+
43+
/// Hard maximum for the oracle-derived floor (FRI).
44+
pub(crate) const ORACLE_L2_GAS_FLOOR_MAX_FRI: u128 = u128::MAX;
45+
3446
/// Fee market information for the next block.
3547
#[derive(Debug, Default, Serialize)]
3648
pub struct FeeMarketInfo {

crates/apollo_consensus_orchestrator/src/sequencer_consensus_context.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ use starknet_api::versioned_constants_logic::VersionedConstantsTrait;
6868
use tokio::task::JoinHandle;
6969
use tokio_util::sync::CancellationToken;
7070
use tokio_util::task::AbortOnDropHandle;
71-
use tracing::{error, error_span, info, instrument, trace, warn, Instrument};
71+
use tracing::{debug, error, error_span, info, instrument, trace, warn, Instrument};
7272

7373
use crate::build_proposal::{build_proposal, BuildProposalError, ProposalBuildArguments};
7474
use crate::cende::{
@@ -79,14 +79,25 @@ use crate::cende::{
7979
};
8080
use crate::fee_market::{
8181
calculate_next_l2_gas_price_for_fin,
82+
compute_fee_actual,
83+
compute_fee_proposal,
84+
compute_fee_target,
8285
FeeMarketInfo,
86+
FEE_PROPOSAL_MARGIN_PPT,
8387
FEE_PROPOSAL_WINDOW_SIZE,
88+
ORACLE_L2_GAS_FLOOR_MAX_FRI,
89+
ORACLE_L2_GAS_FLOOR_MIN_FRI,
90+
TARGET_ATTO_USD_PER_L2_GAS,
8491
};
8592
use crate::metrics::{
8693
record_build_proposal_failure,
8794
record_validate_proposal_failure,
8895
register_metrics,
8996
CONSENSUS_L2_GAS_PRICE,
97+
SNIP35_FEE_ACTUAL,
98+
SNIP35_FEE_PROPOSAL,
99+
SNIP35_FEE_TARGET,
100+
SNIP35_STRK_USD_RATE,
90101
};
91102
use crate::orchestrator_versioned_constants::VersionedConstants;
92103
use crate::utils::{
@@ -347,6 +358,7 @@ impl SequencerConsensusContext {
347358
sequencer,
348359
timestamp: BlockTimestamp(init.timestamp),
349360
l1_da_mode: init.l1_da_mode,
361+
fee_proposal: init.fee_proposal,
350362
// TODO(guy.f): Figure out where/if to get the values below from and fill them.
351363
..Default::default()
352364
};
@@ -365,16 +377,64 @@ impl SequencerConsensusContext {
365377
/// Returns the next L2 gas price without mutating context. Used when building the fin and when
366378
/// updating at decision time.
367379
fn calculate_next_l2_gas_price(&self, height: BlockNumber, l2_gas_used: GasAmount) -> GasPrice {
380+
let fee_actual = self.compute_fee_actual();
368381
calculate_next_l2_gas_price_for_fin(
369382
self.l2_gas_price,
370383
height,
371384
l2_gas_used,
372385
self.config.dynamic_config.override_l2_gas_price_fri,
373386
&self.config.dynamic_config.min_l2_gas_price_per_height,
374-
None,
387+
fee_actual,
375388
)
376389
}
377390

391+
/// SNIP-35: compute fee_actual from the sliding window of fee_proposals.
392+
fn compute_fee_actual(&self) -> Option<GasPrice> {
393+
let proposals: Vec<GasPrice> = self.fee_proposals_window.iter().copied().collect();
394+
compute_fee_actual(&proposals, FEE_PROPOSAL_WINDOW_SIZE)
395+
}
396+
397+
/// SNIP-35: compute the fee_proposal this node should publish.
398+
async fn compute_snip35_fee_proposal(&self, timestamp: u64) -> GasPrice {
399+
// compute_fee_actual returns None for zero median or insufficient data, falling back to
400+
// the current l2_gas_price. This ensures proposer and validator use the same fallback.
401+
let fee_actual = self.compute_fee_actual().unwrap_or(self.l2_gas_price);
402+
SNIP35_FEE_ACTUAL.set_lossy(fee_actual.0);
403+
404+
// The oracle reuses PriceOracleClientTrait configured with a STRK/USD endpoint.
405+
let fee_target = match &self.deps.strk_price_oracle {
406+
Some(oracle) => match oracle.eth_to_fri_rate(timestamp).await {
407+
Ok(rate) if rate > 0 => {
408+
SNIP35_STRK_USD_RATE.set_lossy(rate);
409+
let target = compute_fee_target(
410+
TARGET_ATTO_USD_PER_L2_GAS,
411+
rate,
412+
ORACLE_L2_GAS_FLOOR_MIN_FRI,
413+
ORACLE_L2_GAS_FLOOR_MAX_FRI,
414+
);
415+
SNIP35_FEE_TARGET.set_lossy(target.0);
416+
Some(target)
417+
}
418+
Ok(_) => {
419+
warn!("STRK/USD oracle returned zero rate, freezing fee_proposal");
420+
None
421+
}
422+
Err(e) => {
423+
warn!("STRK/USD oracle error: {e:?}, freezing fee_proposal");
424+
None
425+
}
426+
},
427+
None => {
428+
debug!("No STRK/USD oracle configured, freezing fee_proposal");
429+
None
430+
}
431+
};
432+
433+
let proposal = compute_fee_proposal(fee_target, fee_actual, FEE_PROPOSAL_MARGIN_PPT);
434+
SNIP35_FEE_PROPOSAL.set_lossy(proposal.0);
435+
proposal
436+
}
437+
378438
fn update_l2_gas_price(&mut self, height: BlockNumber, l2_gas_used: GasAmount) {
379439
self.l2_gas_price = self.calculate_next_l2_gas_price(height, l2_gas_used);
380440
let gas_price_u64 = u64::try_from(self.l2_gas_price.0).unwrap_or(u64::MAX);
@@ -573,6 +633,7 @@ impl ConsensusContext for SequencerConsensusContext {
573633
BehaviorMode::Echonet => true,
574634
BehaviorMode::Starknet => false,
575635
};
636+
let fee_proposal = self.compute_snip35_fee_proposal(self.deps.clock.unix_now()).await;
576637
let round = build_param.round;
577638
let args = ProposalBuildArguments {
578639
deps: self.deps.clone(),
@@ -606,6 +667,8 @@ impl ConsensusContext for SequencerConsensusContext {
606667
.config
607668
.dynamic_config
608669
.compare_retrospective_block_hash,
670+
fee_proposal,
671+
fee_actual: self.compute_fee_actual(),
609672
};
610673

611674
let handle = tokio::spawn(

crates/apollo_consensus_orchestrator/src/test_utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,8 @@ impl From<TestProposalBuildArguments> for ProposalBuildArguments {
509509
override_l2_gas_price_fri: args.override_l2_gas_price_fri,
510510
min_l2_gas_price_per_height: args.min_l2_gas_price_per_height,
511511
compare_retrospective_block_hash: args.compare_retrospective_block_hash,
512+
fee_proposal: GasPrice::default(),
513+
fee_actual: None,
512514
}
513515
}
514516
}

0 commit comments

Comments
 (0)