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 contains_storage(&self, address: Address, slot: B256) -> (bool, bool) {
100 self.index_of_address(address)
101 .map_or((false, false), |idx| (true, self.contains_storage_key_at_index(slot, idx)))
102 }
103
104 pub fn contains_address(&self, address: Address) -> bool {
106 self.iter().any(|item| item.address == address)
107 }
108
109 fn contains_storage_key_at_index(&self, slot: B256, index: usize) -> bool {
112 self.get(index).is_some_and(|entry| entry.storage_keys.contains(&slot))
113 }
114
115 pub fn add_address(&mut self, address: Address) -> bool {
118 !self.contains_address(address) && {
119 self.0.push(AccessListItem { address, storage_keys: Vec::new() });
120 true
121 }
122 }
123
124 #[inline]
126 pub fn size(&self) -> usize {
127 self.0.iter().map(AccessListItem::size).sum::<usize>()
129 + self.0.capacity() * mem::size_of::<AccessListItem>()
130 }
131}
132
133#[derive(Clone, Debug, Default, PartialEq, Eq)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
137pub struct AccessListWithGasUsed {
138 pub access_list: AccessList,
140 pub gas_used: U256,
142}
143
144#[derive(Clone, Debug, Default, PartialEq, Eq)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
148pub struct AccessListResult {
149 pub access_list: AccessList,
151 pub gas_used: U256,
153 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
155 pub error: Option<String>,
156}
157
158impl AccessListResult {
159 pub fn ensure_ok(self) -> Result<AccessListWithGasUsed, String> {
162 match self.error {
163 Some(err) => Err(err),
164 None => {
165 Ok(AccessListWithGasUsed { access_list: self.access_list, gas_used: self.gas_used })
166 }
167 }
168 }
169
170 #[inline]
172 pub const fn is_err(&self) -> bool {
173 self.error.is_some()
174 }
175}
176
177#[cfg(all(test, feature = "serde"))]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn access_list_serde() {
183 let list = AccessList(vec![
184 AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] },
185 AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] },
186 ]);
187 let json = serde_json::to_string(&list).unwrap();
188 let list2 = serde_json::from_str::<AccessList>(&json).unwrap();
189 assert_eq!(list, list2);
190 }
191
192 #[test]
193 fn access_list_with_gas_used() {
194 let list = AccessListResult {
195 access_list: AccessList(vec![
196 AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] },
197 AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] },
198 ]),
199 gas_used: U256::from(100),
200 error: None,
201 };
202 let json = serde_json::to_string(&list).unwrap();
203 let list2 = serde_json::from_str(&json).unwrap();
204 assert_eq!(list, list2);
205 }
206}