use crate::SqlColdError;
use alloy::{
consensus::{Receipt as AlloyReceipt, TxType},
eips::{
eip2930::{AccessList, AccessListItem},
eip7702::{Authorization, SignedAuthorization},
},
primitives::{Address, B256, Log, U256},
};
use signet_storage_types::Receipt;
pub(crate) const fn to_i64(v: u64) -> i64 {
v as i64
}
pub(crate) const fn from_i64(v: i64) -> u64 {
v as u64
}
pub(crate) const fn encode_u128(v: u128) -> [u8; 16] {
v.to_be_bytes()
}
pub(crate) fn decode_u128(data: &[u8]) -> Result<u128, SqlColdError> {
let arr: [u8; 16] = data.try_into().map_err(|_| {
SqlColdError::Convert(format!("u128 requires 16 bytes, got {}", data.len()))
})?;
Ok(u128::from_be_bytes(arr))
}
pub(crate) fn decode_u128_required(data: Option<&[u8]>, field: &str) -> Result<u128, SqlColdError> {
data.ok_or_else(|| SqlColdError::Convert(format!("{field} is required"))).and_then(decode_u128)
}
pub(crate) fn decode_access_list_or_empty(data: Option<&[u8]>) -> Result<AccessList, SqlColdError> {
data.map(decode_access_list).transpose().map(|opt| opt.unwrap_or_default())
}
pub(crate) fn encode_access_list(list: &AccessList) -> Vec<u8> {
let items: &[AccessListItem] = &list.0;
let mut buf = Vec::new();
buf.extend_from_slice(&(items.len() as u16).to_be_bytes());
for item in items {
buf.extend_from_slice(item.address.as_slice());
buf.extend_from_slice(&(item.storage_keys.len() as u16).to_be_bytes());
for key in &item.storage_keys {
buf.extend_from_slice(key.as_slice());
}
}
buf
}
fn decode_access_list(data: &[u8]) -> Result<AccessList, SqlColdError> {
if data.len() < 2 {
return Err(SqlColdError::Convert("access_list too short".into()));
}
let count = u16::from_be_bytes([data[0], data[1]]) as usize;
let mut offset = 2;
let mut items = Vec::with_capacity(count);
for _ in 0..count {
if offset + 22 > data.len() {
return Err(SqlColdError::Convert("access_list truncated".into()));
}
let address = Address::from_slice(&data[offset..offset + 20]);
offset += 20;
let key_count = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
offset += 2;
let mut storage_keys = Vec::with_capacity(key_count);
for _ in 0..key_count {
if offset + 32 > data.len() {
return Err(SqlColdError::Convert("access_list keys truncated".into()));
}
storage_keys.push(B256::from_slice(&data[offset..offset + 32]));
offset += 32;
}
items.push(AccessListItem { address, storage_keys });
}
Ok(AccessList(items))
}
pub(crate) fn encode_authorization_list(list: &[SignedAuthorization]) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&(list.len() as u16).to_be_bytes());
for auth in list {
let inner = auth.inner();
buf.extend_from_slice(&inner.chain_id.to_be_bytes::<32>());
buf.extend_from_slice(inner.address.as_slice());
buf.extend_from_slice(&inner.nonce.to_be_bytes());
buf.push(auth.y_parity());
buf.extend_from_slice(&auth.r().to_be_bytes::<32>());
buf.extend_from_slice(&auth.s().to_be_bytes::<32>());
}
buf
}
pub(crate) fn decode_authorization_list(
data: &[u8],
) -> Result<Vec<SignedAuthorization>, SqlColdError> {
if data.is_empty() {
return Ok(Vec::new());
}
if data.len() < 2 {
return Err(SqlColdError::Convert("authorization_list too short".into()));
}
let count = u16::from_be_bytes([data[0], data[1]]) as usize;
let mut offset = 2;
let entry_size = 32 + 20 + 8 + 1 + 32 + 32; let mut result = Vec::with_capacity(count);
for _ in 0..count {
if offset + entry_size > data.len() {
return Err(SqlColdError::Convert("authorization_list truncated".into()));
}
let chain_id = U256::from_be_slice(&data[offset..offset + 32]);
offset += 32;
let address = Address::from_slice(&data[offset..offset + 20]);
offset += 20;
let nonce = u64::from_be_bytes(data[offset..offset + 8].try_into().unwrap());
offset += 8;
let y_parity = data[offset];
offset += 1;
let r = U256::from_be_slice(&data[offset..offset + 32]);
offset += 32;
let s = U256::from_be_slice(&data[offset..offset + 32]);
offset += 32;
let auth = Authorization { chain_id, address, nonce };
result.push(SignedAuthorization::new_unchecked(auth, y_parity, r, s));
}
Ok(result)
}
pub(crate) fn encode_b256_vec(hashes: &[B256]) -> Vec<u8> {
let mut buf = Vec::with_capacity(2 + hashes.len() * 32);
buf.extend_from_slice(&(hashes.len() as u16).to_be_bytes());
for h in hashes {
buf.extend_from_slice(h.as_slice());
}
buf
}
pub(crate) fn decode_b256_vec(data: &[u8]) -> Result<Vec<B256>, SqlColdError> {
if data.len() < 2 {
return Err(SqlColdError::Convert("b256_vec too short".into()));
}
let count = u16::from_be_bytes([data[0], data[1]]) as usize;
if data.len() < 2 + count * 32 {
return Err(SqlColdError::Convert("b256_vec truncated".into()));
}
let result =
(0..count).map(|i| B256::from_slice(&data[2 + i * 32..2 + (i + 1) * 32])).collect();
Ok(result)
}
pub(crate) fn build_receipt(
tx_type: i16,
success: bool,
cumulative_gas_used: i64,
logs: Vec<Log>,
) -> Result<Receipt, SqlColdError> {
let tx_type = TxType::try_from(tx_type as u8)
.map_err(|_| SqlColdError::Convert(format!("invalid tx_type: {tx_type}")))?;
Ok(Receipt {
tx_type,
inner: AlloyReceipt {
status: success.into(),
cumulative_gas_used: from_i64(cumulative_gas_used),
logs,
},
})
}
pub(crate) const EVENT_TRANSACT: i16 = 0;
pub(crate) const EVENT_ENTER: i16 = 1;
pub(crate) const EVENT_ENTER_TOKEN: i16 = 2;