arbos/l1_pricing/
batch_poster.rs

1use alloy_primitives::{Address, U256};
2use arb_storage::{
3    Storage, StorageBackedAddress, StorageBackedBigInt, StorageBackend, SystemStateBackend,
4};
5use revm::Database;
6
7use super::L1PricingError;
8use crate::address_set::AddressSet;
9
10pub const BATCH_POSTER_TABLE_KEY: &[u8] = &[0];
11pub const POSTER_ADDRS_KEY: &[u8] = &[0];
12pub const POSTER_INFO_KEY: &[u8] = &[1];
13
14pub const TOTAL_FUNDS_DUE_OFFSET: u64 = 0;
15const FUNDS_DUE_OFFSET: u64 = 0;
16pub const PAY_TO_OFFSET: u64 = 1;
17
18pub struct BatchPostersTable<'a, D> {
19    poster_addrs: AddressSet<'a, D>,
20    poster_info: Storage<'a, D>,
21    pub total_funds_due: StorageBackedBigInt,
22}
23
24pub struct BatchPosterState {
25    funds_due: StorageBackedBigInt,
26    pay_to: StorageBackedAddress,
27}
28
29pub struct FundsDueItem {
30    pub address: Address,
31    pub funds_due: U256,
32}
33
34pub fn initialize_batch_posters_table<D: Database, B: StorageBackend>(
35    l1_pricing_storage: &Storage<'_, D>,
36    backend: &mut B,
37    initial_poster: Address,
38) -> Result<(), L1PricingError> {
39    let bpt_storage = l1_pricing_storage.open_sub_storage(BATCH_POSTER_TABLE_KEY);
40    let poster_addrs_storage = bpt_storage.open_sub_storage(POSTER_ADDRS_KEY);
41    let poster_info = bpt_storage.open_sub_storage(POSTER_INFO_KEY);
42
43    let addrs = crate::address_set::open_address_set(poster_addrs_storage);
44    addrs.add(backend, initial_poster)?;
45
46    let bp_storage = poster_info.open_sub_storage(initial_poster.as_slice());
47    let pay_to = StorageBackedAddress::new(bp_storage.base_key(), PAY_TO_OFFSET);
48    pay_to.set(backend, initial_poster)?;
49
50    let funds_due = StorageBackedBigInt::new(bp_storage.base_key(), FUNDS_DUE_OFFSET);
51    funds_due.set(backend, U256::ZERO)?;
52
53    let total_funds_due = StorageBackedBigInt::new(bpt_storage.base_key(), TOTAL_FUNDS_DUE_OFFSET);
54    total_funds_due.set(backend, U256::ZERO)?;
55    Ok(())
56}
57
58pub fn open_batch_posters_table<'a, D>(
59    l1_pricing_storage: &Storage<'a, D>,
60) -> BatchPostersTable<'a, D> {
61    let bpt_storage = l1_pricing_storage.open_sub_storage(BATCH_POSTER_TABLE_KEY);
62    let poster_addrs_storage = bpt_storage.open_sub_storage(POSTER_ADDRS_KEY);
63    let poster_info = bpt_storage.open_sub_storage(POSTER_INFO_KEY);
64
65    let poster_addrs = crate::address_set::open_address_set(poster_addrs_storage);
66    let total_funds_due = StorageBackedBigInt::new(bpt_storage.base_key(), TOTAL_FUNDS_DUE_OFFSET);
67
68    BatchPostersTable {
69        poster_addrs,
70        poster_info,
71        total_funds_due,
72    }
73}
74
75impl<'a, D> BatchPostersTable<'a, D> {
76    pub fn open(l1_pricing_storage: &Storage<'a, D>) -> Self {
77        open_batch_posters_table(l1_pricing_storage)
78    }
79
80    pub fn total_funds_due<B: SystemStateBackend>(
81        &self,
82        backend: &mut B,
83    ) -> Result<U256, L1PricingError> {
84        Ok(self.total_funds_due.get_raw(backend)?)
85    }
86
87    fn internal_open(&self, poster: Address) -> BatchPosterState {
88        let bp_storage = self.poster_info.open_sub_storage(poster.as_slice());
89        BatchPosterState {
90            funds_due: StorageBackedBigInt::new(bp_storage.base_key(), FUNDS_DUE_OFFSET),
91            pay_to: StorageBackedAddress::new(bp_storage.base_key(), PAY_TO_OFFSET),
92        }
93    }
94}
95
96impl<D> BatchPostersTable<'_, D> {
97    pub fn contains_poster<B: SystemStateBackend>(
98        &self,
99        backend: &mut B,
100        poster: Address,
101    ) -> Result<bool, L1PricingError> {
102        Ok(self.poster_addrs.is_member(backend, poster)?)
103    }
104
105    pub fn open_poster<B: StorageBackend>(
106        &self,
107        backend: &mut B,
108        poster: Address,
109        create_if_not_exist: bool,
110    ) -> Result<BatchPosterState, L1PricingError> {
111        let is_poster = self.poster_addrs.is_member(backend, poster)?;
112        if !is_poster {
113            if !create_if_not_exist {
114                return Err(L1PricingError::BatchPosterNotFound);
115            }
116            return self.add_poster(backend, poster, poster);
117        }
118        Ok(self.internal_open(poster))
119    }
120
121    pub fn add_poster<B: StorageBackend>(
122        &self,
123        backend: &mut B,
124        poster_address: Address,
125        pay_to: Address,
126    ) -> Result<BatchPosterState, L1PricingError> {
127        let is_poster = self.poster_addrs.is_member(backend, poster_address)?;
128        if is_poster {
129            return Err(L1PricingError::BatchPosterAlreadyExists);
130        }
131
132        let bp_state = self.internal_open(poster_address);
133        bp_state.funds_due.set(backend, U256::ZERO)?;
134        bp_state.pay_to.set(backend, pay_to)?;
135        self.poster_addrs.add(backend, poster_address)?;
136        Ok(bp_state)
137    }
138
139    pub fn all_posters<B: SystemStateBackend>(
140        &self,
141        backend: &mut B,
142    ) -> Result<Vec<Address>, L1PricingError> {
143        Ok(self.poster_addrs.all_members(backend, u64::MAX)?)
144    }
145
146    pub fn all_posters_capped<B: SystemStateBackend>(
147        &self,
148        backend: &mut B,
149        max: u64,
150    ) -> Result<Vec<Address>, L1PricingError> {
151        Ok(self.poster_addrs.all_members(backend, max)?)
152    }
153
154    pub fn get_funds_due_list<B: SystemStateBackend>(
155        &self,
156        backend: &mut B,
157    ) -> Result<Vec<FundsDueItem>, L1PricingError> {
158        let posters = self.all_posters(backend)?;
159        let mut result = Vec::new();
160        for poster in posters {
161            let state = self.internal_open(poster);
162            let due = state.funds_due(backend)?;
163            if due > U256::ZERO {
164                result.push(FundsDueItem {
165                    address: poster,
166                    funds_due: due,
167                });
168            }
169        }
170        Ok(result)
171    }
172}
173
174impl BatchPosterState {
175    pub fn funds_due<B: SystemStateBackend>(
176        &self,
177        backend: &mut B,
178    ) -> Result<U256, L1PricingError> {
179        Ok(self.funds_due.get_raw(backend)?)
180    }
181
182    pub fn set_funds_due<B: StorageBackend>(
183        &self,
184        backend: &mut B,
185        value: U256,
186        total_funds_due: &StorageBackedBigInt,
187    ) -> Result<(), L1PricingError> {
188        let prev = self.funds_due.get_raw(backend)?;
189        let prev_total = total_funds_due.get_raw(backend)?;
190        let new_total = prev_total.saturating_add(value).saturating_sub(prev);
191        total_funds_due.set(backend, new_total)?;
192        Ok(self.funds_due.set(backend, value)?)
193    }
194
195    pub fn pay_to<B: SystemStateBackend>(
196        &self,
197        backend: &mut B,
198    ) -> Result<Address, L1PricingError> {
199        Ok(self.pay_to.get(backend)?)
200    }
201
202    pub fn set_pay_to<B: StorageBackend>(
203        &self,
204        backend: &mut B,
205        addr: Address,
206    ) -> Result<(), L1PricingError> {
207        Ok(self.pay_to.set(backend, addr)?)
208    }
209}