arb_storage/
backed_types.rs

1use alloy_primitives::{Address, B256, U256};
2use revm::Database;
3
4use crate::{
5    slot::storage_key_map,
6    state_ops::{read_arbos_storage, write_arbos_storage},
7};
8
9fn compute_slot(base_key: B256, offset: u64) -> U256 {
10    if base_key == B256::ZERO {
11        storage_key_map(&[], offset)
12    } else {
13        storage_key_map(base_key.as_slice(), offset)
14    }
15}
16
17/// Reads a U256 from ArbOS state at the given slot.
18fn read_slot<D: Database>(state: *mut revm::database::State<D>, slot: U256) -> Result<U256, ()> {
19    unsafe {
20        let state = &mut *state;
21        Ok(read_arbos_storage(state, slot))
22    }
23}
24
25/// Writes a U256 to ArbOS state at the given slot.
26fn write_slot<D: Database>(
27    state: *mut revm::database::State<D>,
28    slot: U256,
29    value: U256,
30) -> Result<(), ()> {
31    unsafe {
32        let state = &mut *state;
33        write_arbos_storage(state, slot, value);
34        Ok(())
35    }
36}
37
38/// Storage-backed 64-bit unsigned integer.
39pub struct StorageBackedUint64<D> {
40    pub state: *mut revm::database::State<D>,
41    pub slot: U256,
42}
43
44impl<D: Database> StorageBackedUint64<D> {
45    pub fn new(state: *mut revm::database::State<D>, base_key: B256, offset: u64) -> Self {
46        Self {
47            state,
48            slot: compute_slot(base_key, offset),
49        }
50    }
51
52    pub fn get(&self) -> Result<u64, ()> {
53        let value = read_slot(self.state, self.slot)?;
54        Ok(value.try_into().unwrap_or(0))
55    }
56
57    pub fn set(&self, value: u64) -> Result<(), ()> {
58        write_slot(self.state, self.slot, U256::from(value))
59    }
60}
61
62impl<D> Clone for StorageBackedUint64<D> {
63    fn clone(&self) -> Self {
64        Self {
65            state: self.state,
66            slot: self.slot,
67        }
68    }
69}
70
71unsafe impl<D: Send> Send for StorageBackedUint64<D> {}
72unsafe impl<D: Sync> Sync for StorageBackedUint64<D> {}
73
74/// Storage-backed 256-bit unsigned integer.
75pub struct StorageBackedBigUint<D> {
76    state: *mut revm::database::State<D>,
77    slot: U256,
78}
79
80impl<D: Database> StorageBackedBigUint<D> {
81    pub fn new(state: *mut revm::database::State<D>, base_key: B256, offset: u64) -> Self {
82        Self {
83            state,
84            slot: compute_slot(base_key, offset),
85        }
86    }
87
88    pub fn get(&self) -> Result<U256, ()> {
89        read_slot(self.state, self.slot)
90    }
91
92    pub fn set(&self, value: U256) -> Result<(), ()> {
93        write_slot(self.state, self.slot, value)
94    }
95}
96
97impl<D> Clone for StorageBackedBigUint<D> {
98    fn clone(&self) -> Self {
99        Self {
100            state: self.state,
101            slot: self.slot,
102        }
103    }
104}
105
106unsafe impl<D: Send> Send for StorageBackedBigUint<D> {}
107unsafe impl<D: Sync> Sync for StorageBackedBigUint<D> {}
108
109/// Storage-backed Ethereum address (20 bytes, right-aligned in 32-byte slot).
110pub struct StorageBackedAddress<D> {
111    state: *mut revm::database::State<D>,
112    slot: U256,
113}
114
115impl<D: Database> StorageBackedAddress<D> {
116    pub fn new(state: *mut revm::database::State<D>, base_key: B256, offset: u64) -> Self {
117        Self {
118            state,
119            slot: compute_slot(base_key, offset),
120        }
121    }
122
123    pub fn get(&self) -> Result<Address, ()> {
124        let value = read_slot(self.state, self.slot)?;
125        let bytes = value.to_be_bytes::<32>();
126        let addr_bytes: [u8; 20] = bytes[12..32].try_into().map_err(|_| ())?;
127        Ok(Address::from(addr_bytes))
128    }
129
130    pub fn set(&self, value: Address) -> Result<(), ()> {
131        let mut value_bytes = [0u8; 32];
132        value_bytes[12..32].copy_from_slice(value.as_slice());
133        write_slot(self.state, self.slot, U256::from_be_bytes(value_bytes))
134    }
135}
136
137impl<D> Clone for StorageBackedAddress<D> {
138    fn clone(&self) -> Self {
139        Self {
140            state: self.state,
141            slot: self.slot,
142        }
143    }
144}
145
146unsafe impl<D: Send> Send for StorageBackedAddress<D> {}
147unsafe impl<D: Sync> Sync for StorageBackedAddress<D> {}
148
149/// Storage-backed signed 64-bit integer.
150///
151/// Stores by bit-reinterpreting i64 as u64.
152pub struct StorageBackedInt64<D> {
153    state: *mut revm::database::State<D>,
154    slot: U256,
155}
156
157impl<D: Database> StorageBackedInt64<D> {
158    pub fn new(state: *mut revm::database::State<D>, base_key: B256, offset: u64) -> Self {
159        Self {
160            state,
161            slot: compute_slot(base_key, offset),
162        }
163    }
164
165    pub fn get(&self) -> Result<i64, ()> {
166        let value = read_slot(self.state, self.slot)?;
167        let value_u64: u64 = value.try_into().unwrap_or(0);
168        Ok(value_u64 as i64)
169    }
170
171    pub fn set(&self, value: i64) -> Result<(), ()> {
172        write_slot(self.state, self.slot, U256::from(value as u64))
173    }
174}
175
176impl<D> Clone for StorageBackedInt64<D> {
177    fn clone(&self) -> Self {
178        Self {
179            state: self.state,
180            slot: self.slot,
181        }
182    }
183}
184
185unsafe impl<D: Send> Send for StorageBackedInt64<D> {}
186unsafe impl<D: Sync> Sync for StorageBackedInt64<D> {}
187
188/// Storage-backed signed 256-bit integer using two's complement.
189pub struct StorageBackedBigInt<D> {
190    pub state: *mut revm::database::State<D>,
191    pub slot: U256,
192}
193
194impl<D: Database> StorageBackedBigInt<D> {
195    pub fn new(state: *mut revm::database::State<D>, base_key: B256, offset: u64) -> Self {
196        Self {
197            state,
198            slot: compute_slot(base_key, offset),
199        }
200    }
201
202    /// Returns the raw U256 value (two's complement for negatives).
203    pub fn get_raw(&self) -> Result<U256, ()> {
204        read_slot(self.state, self.slot)
205    }
206
207    /// Returns true if the stored value is negative (high bit set).
208    pub fn is_negative(&self) -> Result<bool, ()> {
209        Ok(self.get_raw()?.bit(255))
210    }
211
212    /// Returns (magnitude, is_negative) decoded from two's complement.
213    pub fn get_signed(&self) -> Result<(U256, bool), ()> {
214        let raw = self.get_raw()?;
215        if raw.bit(255) {
216            let magnitude = (!raw).wrapping_add(U256::from(1));
217            Ok((magnitude, true))
218        } else {
219            Ok((raw, false))
220        }
221    }
222
223    /// Sets a non-negative value.
224    pub fn set(&self, value: U256) -> Result<(), ()> {
225        write_slot(self.state, self.slot, value)
226    }
227
228    /// Sets a negative value by two's complement encoding.
229    pub fn set_negative(&self, magnitude: U256) -> Result<(), ()> {
230        let neg_value = (!magnitude).wrapping_add(U256::from(1));
231        self.set(neg_value)
232    }
233}
234
235impl<D> Clone for StorageBackedBigInt<D> {
236    fn clone(&self) -> Self {
237        Self {
238            state: self.state,
239            slot: self.slot,
240        }
241    }
242}
243
244unsafe impl<D: Send> Send for StorageBackedBigInt<D> {}
245unsafe impl<D: Sync> Sync for StorageBackedBigInt<D> {}
246
247/// Sentinel value for nil addresses: 1 << 255.
248fn nil_address_representation() -> U256 {
249    U256::from(1u64) << 255
250}
251
252/// Storage-backed optional address.
253///
254/// Uses sentinel value (1 << 255) to represent None (NilAddressRepresentation).
255pub struct StorageBackedAddressOrNil<D> {
256    state: *mut revm::database::State<D>,
257    slot: U256,
258}
259
260impl<D: Database> StorageBackedAddressOrNil<D> {
261    pub fn new(state: *mut revm::database::State<D>, base_key: B256, offset: u64) -> Self {
262        Self {
263            state,
264            slot: compute_slot(base_key, offset),
265        }
266    }
267
268    pub fn get(&self) -> Result<Option<Address>, ()> {
269        let value = read_slot(self.state, self.slot)?;
270        if value == nil_address_representation() {
271            return Ok(None);
272        }
273        let bytes = value.to_be_bytes::<32>();
274        let addr_bytes: [u8; 20] = bytes[12..32].try_into().map_err(|_| ())?;
275        Ok(Some(Address::from(addr_bytes)))
276    }
277
278    pub fn set(&self, value: Option<Address>) -> Result<(), ()> {
279        let value_u256 = match value {
280            None => nil_address_representation(),
281            Some(addr) => {
282                let mut bytes = [0u8; 32];
283                bytes[12..32].copy_from_slice(addr.as_slice());
284                U256::from_be_bytes(bytes)
285            }
286        };
287        write_slot(self.state, self.slot, value_u256)
288    }
289}
290
291impl<D> Clone for StorageBackedAddressOrNil<D> {
292    fn clone(&self) -> Self {
293        Self {
294            state: self.state,
295            slot: self.slot,
296        }
297    }
298}
299
300unsafe impl<D: Send> Send for StorageBackedAddressOrNil<D> {}
301unsafe impl<D: Sync> Sync for StorageBackedAddressOrNil<D> {}