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}