Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
Objects and methods for STRK gas price calculation (#928)
Browse files Browse the repository at this point in the history
* Added objects and methods to compute gas price in STRK.

* fix clippy error.
  • Loading branch information
amosStarkware authored Sep 21, 2023
1 parent bfa8c3a commit 19e54f7
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 1 deletion.
2 changes: 2 additions & 0 deletions crates/blockifier/src/fee.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod errors;
pub mod eth_gas_constants;
pub mod fee_utils;
pub mod gas_usage;
pub mod os_resources;
pub mod os_usage;
pub mod strk_gas_price;
7 changes: 7 additions & 0 deletions crates/blockifier/src/fee/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use thiserror::Error;

#[derive(Debug, Error)]
pub enum StrkGasPriceCalcError {
#[error("No pool states provided.")]
NoPoolStatesError,
}
103 changes: 103 additions & 0 deletions crates/blockifier/src/fee/strk_gas_price.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::cmp::Ordering;

use num_bigint::BigUint;
use num_traits::Zero;

use super::errors::StrkGasPriceCalcError;

#[cfg(test)]
#[path = "strk_gas_price_test.rs"]
pub mod test;

/// Struct representing the current state of a STRK<->ETH AMM pool.
#[derive(Clone, Debug)]
pub struct PoolState {
pub total_wei: BigUint,
pub total_strk: BigUint,
}

impl PoolState {
pub fn tvl_in_wei(&self) -> BigUint {
// Assumption on pool is the two pools have the same total value.
self.total_wei.clone() << 1
}
/// Returns the result of comparing two pool states by STRK / Wei ratio.
pub fn compare_strk_to_wei_ratio(&self, other: &Self) -> Ordering {
// a / b < c / d <=> a * d < c * b. The same is true for the other orders.
let lhs = self.total_strk.clone() * other.total_wei.clone();
let rhs = other.total_strk.clone() * self.total_wei.clone();
lhs.cmp(&rhs)
}
}

/// Struct representing aggregate of STRK<->ETH AMM pools.
/// Converts Wei to STRK at the STRK / Wei ratio of the weighted median pool state.
#[derive(Clone, Debug)]
pub struct PoolStateAggregator {
// Pool states are sorted by STRK / Wei ratio.
pub sorted_pool_states: Vec<PoolState>,
// See PoolStateAggregator::calc_median_values() for more info on median calculation.
pub median_pool_strk_tvl: BigUint,
pub median_pool_wei_tvl: BigUint,
}

impl PoolStateAggregator {
pub fn new(pool_states: &[PoolState]) -> Result<Self, StrkGasPriceCalcError> {
if pool_states.is_empty() {
return Err(StrkGasPriceCalcError::NoPoolStatesError);
}

let mut sorted_pool_states: Vec<PoolState> = pool_states.to_vec();
sorted_pool_states.sort_unstable_by(|pool_state_a, pool_state_b| {
pool_state_a.compare_strk_to_wei_ratio(pool_state_b)
});

let (median_pool_strk_tvl, median_pool_wei_tvl) =
Self::calc_median_values(&sorted_pool_states);

Ok(Self { sorted_pool_states, median_pool_strk_tvl, median_pool_wei_tvl })
}

/// Returns the STRK and Wei TVL of the weighted median pool state:
/// The pool state with a STRK TVL / Wei TVL ratio such that the sum of the weights of the pool
/// states with a smaller ratio is smaller or equal to half the total weight (and the same for
/// pools with a larger ratio). If two such pools exist the average is returned.
/// The pool states are weighted by the total TVL in Wei.
/// This function assumes the given slice is sorted by STRK / Wei ratio.
pub fn calc_median_values(sorted_pool_states: &[PoolState]) -> (BigUint, BigUint) {
let total_weight: BigUint =
sorted_pool_states.iter().map(|state| state.tvl_in_wei()).sum::<BigUint>();

// Find index of weighted median STRK / Wei ratio.
let mut current_weight: BigUint = BigUint::zero();
let mut median_idx = 0;
let equal_weight_partition: bool;
loop {
current_weight += sorted_pool_states[median_idx].tvl_in_wei().clone();
if current_weight.clone() << 1 >= total_weight {
equal_weight_partition = current_weight << 1 == total_weight;
break;
}
median_idx += 1;
}

let median_pool_strk_tvl: BigUint;
let median_pool_wei_tvl: BigUint;
if equal_weight_partition {
median_pool_strk_tvl = (sorted_pool_states[median_idx].total_strk.clone()
+ sorted_pool_states[median_idx + 1].total_strk.clone())
/ BigUint::from(2_u32);
median_pool_wei_tvl = (sorted_pool_states[median_idx].total_wei.clone()
+ sorted_pool_states[median_idx + 1].total_wei.clone())
/ BigUint::from(2_u32);
} else {
median_pool_strk_tvl = sorted_pool_states[median_idx].total_strk.clone();
median_pool_wei_tvl = sorted_pool_states[median_idx].total_wei.clone();
}
(median_pool_strk_tvl, median_pool_wei_tvl)
}

pub fn convert_wei_to_strk(&self, wei_amount: BigUint) -> BigUint {
(wei_amount * self.median_pool_strk_tvl.clone()) / self.median_pool_wei_tvl.clone()
}
}
56 changes: 56 additions & 0 deletions crates/blockifier/src/fee/strk_gas_price_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use num_bigint::BigUint;

use crate::fee::errors::StrkGasPriceCalcError;
use crate::fee::strk_gas_price::{PoolState, PoolStateAggregator};

/// Sanity tests for STRK<->ETH price computation.
#[test]
fn test_convert_wei_to_strk() {
let (wei_1, wei_2, wei_3) =
(BigUint::from(10_u32), BigUint::from(14_u32), BigUint::from(12_u32));
let (strk_1, strk_2, strk_3, strk_4) = (
BigUint::from(50_u32),
BigUint::from(42_u32),
BigUint::from(24_u32),
BigUint::from(150_u32),
);
let state_1 = PoolState { total_wei: wei_1.clone(), total_strk: strk_1 };
let state_2 = PoolState { total_wei: wei_2, total_strk: strk_2 };
let state_3 = PoolState { total_wei: wei_3, total_strk: strk_3 };
let state_4 = PoolState { total_wei: wei_1, total_strk: strk_4 };
let wei_amount = BigUint::from(10_000_000_000_u64);

// Bad flow: ratio computation on empty array.
assert!(matches!(PoolStateAggregator::new(&[]), Err(StrkGasPriceCalcError::NoPoolStatesError)));

// Convert Wei -> STRK with a single pool state.
assert_eq!(
PoolStateAggregator::new(&[state_1.clone()])
.unwrap()
.convert_wei_to_strk(wei_amount.clone()),
(state_1.total_strk.clone() * wei_amount.clone()) / state_1.total_wei.clone()
);

// Convert Wei -> STRK with multiple pool states, no equal weight partition.
assert_eq!(
PoolStateAggregator::new(&[state_3.clone(), state_1.clone()])
.unwrap()
.convert_wei_to_strk(wei_amount.clone()),
(state_3.total_strk.clone() * wei_amount.clone()) / state_3.total_wei.clone()
);
assert_eq!(
PoolStateAggregator::new(&[state_3, state_1.clone(), state_2.clone()])
.unwrap()
.convert_wei_to_strk(wei_amount.clone()),
(state_2.total_strk.clone() * wei_amount.clone()) / state_2.total_wei.clone()
);

// Convert Wei -> STRK with multiple pool states with equal weight partition.
assert_eq!(
PoolStateAggregator::new(&[state_1.clone(), state_4.clone()])
.unwrap()
.convert_wei_to_strk(wei_amount.clone()),
((state_1.total_strk + state_4.total_strk) * wei_amount)
/ (state_1.total_wei + state_4.total_wei)
);
}
1 change: 0 additions & 1 deletion crates/blockifier/src/transaction/transactions_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,6 @@ fn test_declare_tx(
tx_hash: TransactionHash::default(),
contract_class: contract_class.clone(),
});
let fee_type = &account_tx.fee_type();

// Check state before transaction application.
assert_matches!(
Expand Down

0 comments on commit 19e54f7

Please sign in to comment.