use alloc::{borrow::Cow, vec::Vec};
use alloy_primitives::{keccak256, Bytes, Sealable, Sealed, B256};
use alloy_rlp::{Buf, BufMut, Header, EMPTY_STRING_CODE};
use auto_impl::auto_impl;
use core::fmt;
const TX_TYPE_BYTE_MAX: u8 = 0x7f;
pub const LEGACY_TX_TYPE_ID: u8 = 0;
pub const EIP2930_TX_TYPE_ID: u8 = 1;
pub const EIP1559_TX_TYPE_ID: u8 = 2;
pub const EIP4844_TX_TYPE_ID: u8 = 3;
pub const EIP7702_TX_TYPE_ID: u8 = 4;
#[derive(Clone, Copy, Debug)]
#[non_exhaustive] pub enum Eip2718Error {
RlpError(alloy_rlp::Error),
UnexpectedType(u8),
}
pub type Eip2718Result<T, E = Eip2718Error> = core::result::Result<T, E>;
impl fmt::Display for Eip2718Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RlpError(err) => write!(f, "{err}"),
Self::UnexpectedType(t) => write!(f, "Unexpected type flag. Got {t}."),
}
}
}
impl From<alloy_rlp::Error> for Eip2718Error {
fn from(err: alloy_rlp::Error) -> Self {
Self::RlpError(err)
}
}
impl From<Eip2718Error> for alloy_rlp::Error {
fn from(err: Eip2718Error) -> Self {
match err {
Eip2718Error::RlpError(err) => err,
Eip2718Error::UnexpectedType(_) => Self::Custom("Unexpected type flag"),
}
}
}
impl core::error::Error for Eip2718Error {}
pub trait Decodable2718: Sized {
fn extract_type_byte(buf: &mut &[u8]) -> Option<u8> {
buf.first().copied().filter(|b| *b <= TX_TYPE_BYTE_MAX)
}
fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self>;
fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self>;
fn decode_2718(buf: &mut &[u8]) -> Eip2718Result<Self> {
Self::extract_type_byte(buf)
.map(|ty| {
buf.advance(1);
Self::typed_decode(ty, buf)
})
.unwrap_or_else(|| Self::fallback_decode(buf))
}
fn decode_2718_exact(bytes: &[u8]) -> Eip2718Result<Self> {
let mut buf = bytes;
let tx = Self::decode_2718(&mut buf)?;
if !buf.is_empty() {
return Err(Eip2718Error::RlpError(alloy_rlp::Error::UnexpectedLength));
}
Ok(tx)
}
fn network_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
let mut h_decode = *buf;
let h = Header::decode(&mut h_decode)?;
if h.list {
return Self::fallback_decode(buf);
}
*buf = h_decode;
let remaining_len = buf.len();
if remaining_len == 0 || remaining_len < h.payload_length {
return Err(alloy_rlp::Error::InputTooShort.into());
}
let ty = buf.get_u8();
let tx = Self::typed_decode(ty, buf)?;
let bytes_consumed = remaining_len - buf.len();
if bytes_consumed != h.payload_length && h_decode[0] > EMPTY_STRING_CODE {
return Err(alloy_rlp::Error::UnexpectedLength.into());
}
Ok(tx)
}
}
impl<T: Decodable2718 + Sealable> Decodable2718 for Sealed<T> {
fn extract_type_byte(buf: &mut &[u8]) -> Option<u8> {
T::extract_type_byte(buf)
}
fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
T::typed_decode(ty, buf).map(Self::new)
}
fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
T::fallback_decode(buf).map(Self::new)
}
fn decode_2718(buf: &mut &[u8]) -> Eip2718Result<Self> {
T::decode_2718(buf).map(Self::new)
}
fn network_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
T::network_decode(buf).map(Self::new)
}
}
#[auto_impl(&)]
pub trait Encodable2718: Typed2718 + Sized + Send + Sync {
fn type_flag(&self) -> Option<u8> {
match self.ty() {
LEGACY_TX_TYPE_ID => None,
ty => Some(ty),
}
}
fn encode_2718_len(&self) -> usize;
fn encode_2718(&self, out: &mut dyn BufMut);
fn encoded_2718(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.encode_2718_len());
self.encode_2718(&mut out);
out
}
fn trie_hash(&self) -> B256 {
keccak256(self.encoded_2718())
}
#[auto_impl(keep_default_for(&))]
fn seal(self) -> Sealed<Self> {
let hash = self.trie_hash();
Sealed::new_unchecked(self, hash)
}
#[auto_impl(keep_default_for(&))]
fn into_encoded(self) -> WithEncoded<Self> {
WithEncoded::from_2718_encodable(self)
}
fn network_len(&self) -> usize {
let mut payload_length = self.encode_2718_len();
if !self.is_legacy() {
payload_length += Header { list: false, payload_length }.length();
}
payload_length
}
fn network_encode(&self, out: &mut dyn BufMut) {
if !self.is_legacy() {
Header { list: false, payload_length: self.encode_2718_len() }.encode(out);
}
self.encode_2718(out);
}
}
impl<T: Encodable2718> Encodable2718 for Sealed<T> {
fn encode_2718_len(&self) -> usize {
self.inner().encode_2718_len()
}
fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
self.inner().encode_2718(out);
}
fn trie_hash(&self) -> B256 {
self.hash()
}
}
impl<T: Encodable2718 + Clone> Encodable2718 for Cow<'_, T> {
fn encode_2718_len(&self) -> usize {
(**self).encode_2718_len()
}
fn encode_2718(&self, out: &mut dyn BufMut) {
(**self).encode_2718(out)
}
fn trie_hash(&self) -> B256 {
(**self).trie_hash()
}
}
pub trait Eip2718Envelope: Decodable2718 + Encodable2718 {}
impl<T> Eip2718Envelope for T where T: Decodable2718 + Encodable2718 {}
#[auto_impl::auto_impl(&)]
pub trait Typed2718 {
fn ty(&self) -> u8;
fn is_type(&self, ty: u8) -> bool {
self.ty() == ty
}
fn is_legacy(&self) -> bool {
self.ty() == LEGACY_TX_TYPE_ID
}
fn is_eip2930(&self) -> bool {
self.ty() == EIP2930_TX_TYPE_ID
}
fn is_eip1559(&self) -> bool {
self.ty() == EIP1559_TX_TYPE_ID
}
fn is_eip4844(&self) -> bool {
self.ty() == EIP4844_TX_TYPE_ID
}
fn is_eip7702(&self) -> bool {
self.ty() == EIP7702_TX_TYPE_ID
}
}
impl<T: Typed2718> Typed2718 for Sealed<T> {
fn ty(&self) -> u8 {
self.inner().ty()
}
}
impl<T: Typed2718 + Clone> Typed2718 for Cow<'_, T> {
fn ty(&self) -> u8 {
(**self).ty()
}
}
#[cfg(feature = "serde")]
impl<T: Typed2718> Typed2718 for alloy_serde::WithOtherFields<T> {
#[inline]
fn ty(&self) -> u8 {
self.inner.ty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WithEncoded<T>(Bytes, pub T);
impl<T> From<(Bytes, T)> for WithEncoded<T> {
fn from(value: (Bytes, T)) -> Self {
Self(value.0, value.1)
}
}
impl<T> WithEncoded<T> {
pub const fn new(bytes: Bytes, value: T) -> Self {
Self(bytes, value)
}
pub const fn encoded_bytes(&self) -> &Bytes {
&self.0
}
pub fn into_encoded_bytes(self) -> Bytes {
self.0
}
pub const fn value(&self) -> &T {
&self.1
}
pub fn into_value(self) -> T {
self.1
}
pub fn transform<F: From<T>>(self) -> WithEncoded<F> {
WithEncoded(self.0, self.1.into())
}
pub fn split(self) -> (Bytes, T) {
(self.0, self.1)
}
pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> WithEncoded<U> {
WithEncoded(self.0, op(self.1))
}
}
impl<T> AsRef<Self> for WithEncoded<T> {
fn as_ref(&self) -> &Self {
self
}
}
impl<T: Encodable2718> WithEncoded<T> {
pub fn from_2718_encodable(value: T) -> Self {
Self(value.encoded_2718().into(), value)
}
}
impl<T> WithEncoded<Option<T>> {
pub fn transpose(self) -> Option<WithEncoded<T>> {
self.1.map(|v| WithEncoded(self.0, v))
}
}
impl<L: Encodable2718, R: Encodable2718> Encodable2718 for either::Either<L, R> {
fn encode_2718_len(&self) -> usize {
match self {
Self::Left(l) => l.encode_2718_len(),
Self::Right(r) => r.encode_2718_len(),
}
}
fn encode_2718(&self, out: &mut dyn BufMut) {
match self {
Self::Left(l) => l.encode_2718(out),
Self::Right(r) => r.encode_2718(out),
}
}
}
impl<L: Typed2718, R: Typed2718> Typed2718 for either::Either<L, R> {
fn ty(&self) -> u8 {
match self {
Self::Left(l) => l.ty(),
Self::Right(r) => r.ty(),
}
}
}
pub trait IsTyped2718 {
fn is_type(type_id: u8) -> bool;
}
impl<L, R> IsTyped2718 for either::Either<L, R>
where
L: IsTyped2718,
R: IsTyped2718,
{
fn is_type(type_id: u8) -> bool {
L::is_type(type_id) || R::is_type(type_id)
}
}
impl<L, R> Decodable2718 for either::Either<L, R>
where
L: Decodable2718 + IsTyped2718,
R: Decodable2718,
{
fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
if L::is_type(ty) {
let envelope = L::typed_decode(ty, buf)?;
Ok(Self::Left(envelope))
} else {
let other = R::typed_decode(ty, buf)?;
Ok(Self::Right(other))
}
}
fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
if buf.is_empty() {
return Err(Eip2718Error::RlpError(alloy_rlp::Error::InputTooShort));
}
L::fallback_decode(buf).map(Self::Left)
}
}