arbos/programs/
data_pricer.rs1use alloy_primitives::U256;
2use revm::Database;
3
4use arb_storage::{Storage, StorageBackedUint32, StorageBackedUint64};
5
6const DEMAND_OFFSET: u64 = 0;
7const BYTES_PER_SECOND_OFFSET: u64 = 1;
8const LAST_UPDATE_TIME_OFFSET: u64 = 2;
9const MIN_PRICE_OFFSET: u64 = 3;
10const INERTIA_OFFSET: u64 = 4;
11
12pub const ARBITRUM_START_TIME: u64 = 1421388000;
14
15const INITIAL_DEMAND: u32 = 0;
16pub const INITIAL_HOURLY_BYTES: u64 = (1u64 << 40) / (365 * 24);
18const INITIAL_BYTES_PER_SECOND: u32 = (INITIAL_HOURLY_BYTES / (60 * 60)) as u32;
19const INITIAL_LAST_UPDATE_TIME: u64 = ARBITRUM_START_TIME;
20const INITIAL_MIN_PRICE: u32 = 82928201; const INITIAL_INERTIA: u32 = 21360419; const ONE_IN_BIPS: u64 = 10000;
25
26pub struct DataPricer<D> {
28 pub demand: StorageBackedUint32<D>,
29 pub bytes_per_second: StorageBackedUint32<D>,
30 pub last_update_time: StorageBackedUint64<D>,
31 pub min_price: StorageBackedUint32<D>,
32 pub inertia: StorageBackedUint32<D>,
33}
34
35pub fn init_data_pricer<D: Database>(sto: &Storage<D>) {
36 let state = sto.state_ptr();
37 let base_key = sto.base_key();
38 let _ = StorageBackedUint32::new(state, base_key, DEMAND_OFFSET).set(INITIAL_DEMAND);
39 let _ = StorageBackedUint32::new(state, base_key, BYTES_PER_SECOND_OFFSET)
40 .set(INITIAL_BYTES_PER_SECOND);
41 let _ = StorageBackedUint64::new(state, base_key, LAST_UPDATE_TIME_OFFSET)
42 .set(INITIAL_LAST_UPDATE_TIME);
43 let _ = StorageBackedUint32::new(state, base_key, MIN_PRICE_OFFSET).set(INITIAL_MIN_PRICE);
44 let _ = StorageBackedUint32::new(state, base_key, INERTIA_OFFSET).set(INITIAL_INERTIA);
45}
46
47pub fn open_data_pricer<D: Database>(sto: &Storage<D>) -> DataPricer<D> {
48 let state = sto.state_ptr();
49 let base_key = sto.base_key();
50 DataPricer {
51 demand: StorageBackedUint32::new(state, base_key, DEMAND_OFFSET),
52 bytes_per_second: StorageBackedUint32::new(state, base_key, BYTES_PER_SECOND_OFFSET),
53 last_update_time: StorageBackedUint64::new(state, base_key, LAST_UPDATE_TIME_OFFSET),
54 min_price: StorageBackedUint32::new(state, base_key, MIN_PRICE_OFFSET),
55 inertia: StorageBackedUint32::new(state, base_key, INERTIA_OFFSET),
56 }
57}
58
59impl<D: Database> DataPricer<D> {
60 pub fn update_model(&self, temp_bytes: u32, time: u64) -> Result<U256, ()> {
62 let demand = self.demand.get().unwrap_or(0);
63 let bytes_per_second = self.bytes_per_second.get().unwrap_or(0);
64 let last_update_time = self.last_update_time.get().unwrap_or(0);
65 let min_price = self.min_price.get().unwrap_or(0);
66 let inertia = self.inertia.get()?;
67
68 if inertia == 0 {
69 return Ok(U256::ZERO);
70 }
71
72 let passed = (time.saturating_sub(last_update_time)) as u32;
73 let credit = bytes_per_second.saturating_mul(passed);
74 let demand = demand.saturating_sub(credit).saturating_add(temp_bytes);
75
76 self.demand.set(demand)?;
77 self.last_update_time.set(time)?;
78
79 let exponent = ONE_IN_BIPS * (demand as u64) / (inertia as u64);
80 let multiplier = approx_exp_basis_points(exponent, 12);
81 let cost_per_byte = saturating_mul_by_bips(min_price as u64, multiplier);
82 let cost_in_wei = cost_per_byte.saturating_mul(temp_bytes as u64);
83 Ok(U256::from(cost_in_wei))
84 }
85}
86
87fn approx_exp_basis_points(x: u64, terms: u32) -> u64 {
89 if x == 0 {
90 return ONE_IN_BIPS;
91 }
92
93 let mut result = ONE_IN_BIPS;
94 let mut term = ONE_IN_BIPS;
95
96 for k in 1..=terms {
97 term = term * x / (ONE_IN_BIPS * k as u64);
98 result = result.saturating_add(term);
99 if term == 0 {
100 break;
101 }
102 }
103 result
104}
105
106fn saturating_mul_by_bips(value: u64, bips: u64) -> u64 {
108 (value as u128 * bips as u128 / ONE_IN_BIPS as u128).min(u64::MAX as u128) as u64
109}