@@ -26,7 +26,11 @@ use apollo_l1_gas_price_types::errors::{
2626 L1GasPriceProviderError ,
2727 PriceOracleClientError ,
2828} ;
29- use apollo_l1_gas_price_types:: { MockL1GasPriceProviderClient , PriceInfo } ;
29+ use apollo_l1_gas_price_types:: {
30+ MockL1GasPriceProviderClient ,
31+ MockPriceOracleClientTrait ,
32+ PriceInfo ,
33+ } ;
3034use apollo_protobuf:: consensus:: {
3135 BuildParam ,
3236 CommitmentParts ,
@@ -88,6 +92,15 @@ fn expected_l2_gas_info_for_build_proposal_defaults() -> L2GasInfo {
8892 l2_gas_used : GasAmount ( 0 ) ,
8993 }
9094}
95+
96+ use crate :: snip35:: {
97+ compute_fee_proposal,
98+ compute_fee_target,
99+ FEE_PROPOSAL_MARGIN_PPT ,
100+ ORACLE_L2_GAS_FLOOR_MAX_FRI ,
101+ ORACLE_L2_GAS_FLOOR_MIN_FRI ,
102+ TARGET_ATTO_USD_PER_L2_GAS ,
103+ } ;
91104use crate :: utils:: { apply_fee_transformations, make_gas_price_params} ;
92105
93106const TEST_PROPOSAL_COMMITMENT : ProposalCommitment = ProposalCommitment ( PARTIAL_BLOCK_HASH . 0 ) ;
@@ -1534,3 +1547,53 @@ async fn test_first_height_keeps_sync_provided_l2_gas_price() {
15341547 expected_price, context. l2_gas_price. 0
15351548 ) ;
15361549}
1550+
1551+ /// E2E test: SNIP-35 oracle → fee_proposal → ProposalInit → validate → decision_reached.
1552+ /// Verifies the full flow from STRK/USD oracle price to a fee_proposal that appears in the
1553+ /// built proposal and passes validation.
1554+ #[ tokio:: test]
1555+ async fn snip35_fee_proposal_e2e_flow ( ) {
1556+ // STRK/USD rate: $0.50 with 18 decimals.
1557+ const STRK_USD_RATE : u128 = 500_000_000_000_000_000 ;
1558+
1559+ let ( mut deps, mut network) = create_test_and_network_deps ( ) ;
1560+ deps. setup_deps_for_build ( SetupDepsArgs :: default ( ) ) ;
1561+
1562+ // Create a mock oracle that returns the known STRK/USD rate.
1563+ let mut mock_oracle = MockPriceOracleClientTrait :: new ( ) ;
1564+ mock_oracle. expect_eth_to_fri_rate ( ) . returning ( |_| Ok ( STRK_USD_RATE ) ) ;
1565+ deps. strk_price_oracle = Some ( Arc :: new ( mock_oracle) ) ;
1566+
1567+ // Build context (SNIP-35 is always enabled with hardcoded constants).
1568+ let mut context = deps. build_context ( ) ;
1569+
1570+ // Build a proposal.
1571+ context. set_height_and_round ( BlockNumber ( 0 ) , 0 ) . await . unwrap ( ) ;
1572+ let _fin_receiver = context. build_proposal ( BuildParam :: default ( ) , TIMEOUT ) . await . unwrap ( ) ;
1573+
1574+ // Extract ProposalInit from the network.
1575+ let ( _, mut receiver) = network. outbound_proposal_receiver . next ( ) . await . unwrap ( ) ;
1576+ let ProposalPart :: Init ( init) = receiver. next ( ) . await . unwrap ( ) else {
1577+ panic ! ( "Expected ProposalPart::Init" ) ;
1578+ } ;
1579+
1580+ // Compute the expected fee_proposal.
1581+ // During initiation (< 10 blocks), fee_actual falls back to l2_gas_price (min_gas_price).
1582+ let min_gas_price = VersionedConstants :: latest_constants ( ) . min_gas_price ;
1583+ let fee_target = compute_fee_target (
1584+ TARGET_ATTO_USD_PER_L2_GAS ,
1585+ STRK_USD_RATE ,
1586+ ORACLE_L2_GAS_FLOOR_MIN_FRI ,
1587+ ORACLE_L2_GAS_FLOOR_MAX_FRI ,
1588+ ) ;
1589+ let expected_fee_proposal =
1590+ compute_fee_proposal ( Some ( fee_target) , min_gas_price, FEE_PROPOSAL_MARGIN_PPT ) ;
1591+
1592+ assert_eq ! (
1593+ init. fee_proposal, expected_fee_proposal,
1594+ "fee_proposal in ProposalInit should match: fee_target={fee_target:?}, \
1595+ min_gas_price={min_gas_price:?}, expected={expected_fee_proposal:?}"
1596+ ) ;
1597+ // Sanity: fee_proposal should be nonzero.
1598+ assert ! ( init. fee_proposal. 0 > 0 , "fee_proposal should be nonzero when oracle is active" ) ;
1599+ }
0 commit comments