1#![cfg_attr(not(feature = "std"), no_std)]
5
6extern crate alloc;
7
8use alloc::{string::String, vec::Vec};
9use alloy_primitives::{Address, B256, U256};
10use alloy_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
11use core::{mem, ops::Deref};
12
13#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RlpDecodable, RlpEncodable)]
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
19pub struct AccessListItem {
20 pub address: Address,
22 pub storage_keys: Vec<B256>,
24}
25
26impl AccessListItem {
27 #[inline]
29 pub fn size(&self) -> usize {
30 mem::size_of::<Address>() + self.storage_keys.capacity() * mem::size_of::<B256>()
31 }
32}
33
34#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RlpDecodableWrapper, RlpEncodableWrapper)]
36#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38pub struct AccessList(pub Vec<AccessListItem>);
39
40impl From<Vec<AccessListItem>> for AccessList {
41 fn from(list: Vec<AccessListItem>) -> Self {
42 Self(list)
43 }
44}
45
46impl From<AccessList> for Vec<AccessListItem> {
47 fn from(this: AccessList) -> Self {
48 this.0
49 }
50}
51
52impl Deref for AccessList {
53 type Target = Vec<AccessListItem>;
54
55 fn deref(&self) -> &Self::Target {
56 &self.0
57 }
58}
59
60impl AccessList {
61 pub fn flattened(&self) -> Vec<(Address, Vec<U256>)> {
63 self.flatten().collect()
64 }
65
66 pub fn into_flattened(self) -> Vec<(Address, Vec<U256>)> {
68 self.into_flatten().collect()
69 }
70
71 pub fn into_flatten(self) -> impl Iterator<Item = (Address, Vec<U256>)> {
73 self.0.into_iter().map(|item| {
74 (
75 item.address,
76 item.storage_keys.into_iter().map(|slot| U256::from_be_bytes(slot.0)).collect(),
77 )
78 })
79 }
80
81 pub fn flatten(&self) -> impl Iterator<Item = (Address, Vec<U256>)> + '_ {
83 self.0.iter().map(|item| {
84 (
85 item.address,
86 item.storage_keys.iter().map(|slot| U256::from_be_bytes(slot.0)).collect(),
87 )
88 })
89 }
90
91 fn index_of_address(&self, address: Address) -> Option<usize> {
93 self.iter().position(|item| item.address == address)
94 }
95
96 pub fn storage_keys_count(&self) -> usize {
98 self.iter().map(|i| i.storage_keys.len()).sum::<usize>()
99 }
100
101 pub fn contains_storage(&self, address: Address, slot: B256) -> (bool, bool) {
105 self.index_of_address(address)
106 .map_or((false, false), |idx| (true, self.contains_storage_key_at_index(slot, idx)))
107 }
108
109 pub fn contains_address(&self, address: Address) -> bool {
111 self.iter().any(|item| item.address == address)
112 }
113
114 fn contains_storage_key_at_index(&self, slot: B256, index: usize) -> bool {
117 self.get(index).is_some_and(|entry| entry.storage_keys.contains(&slot))
118 }
119
120 pub fn add_address(&mut self, address: Address) -> bool {
123 !self.contains_address(address) && {
124 self.0.push(AccessListItem { address, storage_keys: Vec::new() });
125 true
126 }
127 }
128
129 #[inline]
131 pub fn size(&self) -> usize {
132 self.0.iter().map(AccessListItem::size).sum::<usize>()
134 + self.0.capacity() * mem::size_of::<AccessListItem>()
135 }
136}
137
138#[derive(Clone, Debug, Default, PartialEq, Eq)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
142pub struct AccessListWithGasUsed {
143 pub access_list: AccessList,
145 pub gas_used: U256,
147}
148
149#[derive(Clone, Debug, Default, PartialEq, Eq)]
151#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
152#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
153pub struct AccessListResult {
154 pub access_list: AccessList,
156 pub gas_used: U256,
158 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
160 pub error: Option<String>,
161}
162
163impl AccessListResult {
164 pub fn ensure_ok(self) -> Result<AccessListWithGasUsed, String> {
167 match self.error {
168 Some(err) => Err(err),
169 None => {
170 Ok(AccessListWithGasUsed { access_list: self.access_list, gas_used: self.gas_used })
171 }
172 }
173 }
174
175 #[inline]
177 pub const fn is_err(&self) -> bool {
178 self.error.is_some()
179 }
180}
181
182#[cfg(all(test, feature = "serde"))]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn access_list_serde() {
188 let list = AccessList(vec![
189 AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] },
190 AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] },
191 ]);
192 let json = serde_json::to_string(&list).unwrap();
193 let list2 = serde_json::from_str::<AccessList>(&json).unwrap();
194 assert_eq!(list, list2);
195 }
196
197 #[test]
198 fn access_list_with_gas_used() {
199 let list = AccessListResult {
200 access_list: AccessList(vec![
201 AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] },
202 AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] },
203 ]),
204 gas_used: U256::from(100),
205 error: None,
206 };
207 let json = serde_json::to_string(&list).unwrap();
208 let list2 = serde_json::from_str(&json).unwrap();
209 assert_eq!(list, list2);
210 }
211}