Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ use apollo_l1_gas_price_types::errors::{
L1GasPriceClientError,
L1GasPriceProviderError,
};
use apollo_l1_gas_price_types::{MockL1GasPriceProviderClient, PriceInfo};
use apollo_l1_gas_price_types::{
MockExchangeRateOracleClientTrait,
MockL1GasPriceProviderClient,
PriceInfo,
};
use apollo_protobuf::consensus::{
BuildParam,
CommitmentParts,
Expand Down Expand Up @@ -89,6 +93,15 @@ fn expected_l2_gas_info_for_build_proposal_defaults() -> L2GasInfo {
l2_gas_used: GasAmount(0),
}
}

use crate::snip35::{
compute_fee_proposal,
compute_fee_target,
FEE_PROPOSAL_MARGIN_PPT,
ORACLE_L2_GAS_FLOOR_MAX_FRI,
ORACLE_L2_GAS_FLOOR_MIN_FRI,
TARGET_ATTO_USD_PER_L2_GAS,
};
use crate::utils::{apply_fee_transformations, make_gas_price_params};

static TEST_PROPOSAL_COMMITMENT: LazyLock<ProposalCommitment> = LazyLock::new(|| {
Expand Down Expand Up @@ -1544,3 +1557,57 @@ async fn test_first_height_keeps_sync_provided_l2_gas_price() {
expected_price, context.l2_gas_price.0
);
}

/// E2E test: SNIP-35 oracle → fee_proposal → ProposalInit → validate → decision_reached.
/// Verifies the full flow from STRK/USD oracle price to a fee_proposal that appears in the
/// built proposal and passes validation.
#[tokio::test]
async fn snip35_fee_proposal_e2e_flow() {
// STRK/USD rate: $0.50 with 18 decimals.
const STRK_USD_RATE: u128 = 500_000_000_000_000_000;
Comment thread
cursor[bot] marked this conversation as resolved.

let (mut deps, mut network) = create_test_and_network_deps();
deps.setup_deps_for_build(SetupDepsArgs::default());

// Create a mock oracle that returns the known STRK/USD rate.
let mut mock_oracle = MockExchangeRateOracleClientTrait::new();
mock_oracle.expect_fetch_rate().returning(|_| Ok(STRK_USD_RATE));
deps.strk_to_usd_oracle = Some(Arc::new(mock_oracle));

// Build context (SNIP-35 is always enabled with hardcoded constants).
let mut context = deps.build_context();

// Build a proposal.
context.set_height_and_round(BlockNumber(0), 0).await.unwrap();
let _fin_receiver = context.build_proposal(BuildParam::default(), TIMEOUT).await.unwrap();

// Extract ProposalInit from the network.
let (_, mut receiver) = network.outbound_proposal_receiver.next().await.unwrap();
let ProposalPart::Init(init) = receiver.next().await.unwrap() else {
panic!("Expected ProposalPart::Init");
};

// Compute the expected fee_proposal.
// During initiation (< 10 blocks), fee_actual falls back to l2_gas_price (min_gas_price).
let min_gas_price = VersionedConstants::latest_constants().min_gas_price;
let fee_target = compute_fee_target(
TARGET_ATTO_USD_PER_L2_GAS,
STRK_USD_RATE,
ORACLE_L2_GAS_FLOOR_MIN_FRI,
ORACLE_L2_GAS_FLOOR_MAX_FRI,
);
let expected_fee_proposal =
compute_fee_proposal(Some(fee_target), min_gas_price, FEE_PROPOSAL_MARGIN_PPT);

assert_eq!(
init.fee_proposal_fri,
Some(expected_fee_proposal),
"fee_proposal in ProposalInit should match: fee_target={fee_target:?}, \
min_gas_price={min_gas_price:?}, expected={expected_fee_proposal:?}"
);
// Sanity: fee_proposal should be nonzero.
assert!(
init.fee_proposal_fri.expect("fee_proposal must be Some at V0_14_3+").0 > 0,
"fee_proposal should be nonzero when oracle is active"
);
}
Loading