1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! [EIP-7685]: General purpose execution layer requests
//!
//! Contains traits for encoding and decoding EIP-7685 requests, as well as validation functions.
//!
//! [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685

#[cfg(not(feature = "std"))]
use crate::alloc::{vec, vec::Vec};

use alloy_rlp::BufMut;
use core::{
    fmt,
    fmt::{Display, Formatter},
};

/// [EIP-7685] decoding errors.
///
/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum Eip7685Error {
    /// Rlp error from [`alloy_rlp`].
    RlpError(alloy_rlp::Error),
    /// Got an unexpected request type while decoding.
    UnexpectedType(u8),
    /// There was no request type in the buffer.
    MissingType,
}

impl Display for Eip7685Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::RlpError(err) => write!(f, "{err}"),
            Self::UnexpectedType(t) => write!(f, "Unexpected request type. Got {t}."),
            Self::MissingType => write!(f, "There was no type flag"),
        }
    }
}

impl From<alloy_rlp::Error> for Eip7685Error {
    fn from(err: alloy_rlp::Error) -> Self {
        Self::RlpError(err)
    }
}

impl From<Eip7685Error> for alloy_rlp::Error {
    fn from(err: Eip7685Error) -> Self {
        match err {
            Eip7685Error::RlpError(err) => err,
            Eip7685Error::MissingType => Self::Custom("eip7685 decoding failed: missing type"),
            Eip7685Error::UnexpectedType(_) => {
                Self::Custom("eip7685 decoding failed: unexpected type")
            }
        }
    }
}

/// Decoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that
/// wraps each possible request type.
///
/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685
pub trait Decodable7685: Sized {
    /// Extract the type byte from the buffer, if any. The type byte is the
    /// first byte.
    fn extract_type_byte(buf: &mut &[u8]) -> Option<u8> {
        buf.first().copied()
    }

    /// Decode the appropriate variant, based on the request type.
    ///
    /// This function is invoked by [`Self::decode_7685`] with the type byte, and the tail of the
    /// buffer.
    ///
    /// ## Note
    ///
    /// This should be a simple match block that invokes an inner type's decoder. The decoder is
    /// request type dependent.
    fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result<Self, Eip7685Error>;

    /// Decode an EIP-7685 request into a concrete instance
    fn decode_7685(buf: &mut &[u8]) -> Result<Self, Eip7685Error> {
        Self::extract_type_byte(buf)
            .map(|ty| Self::typed_decode(ty, &mut &buf[1..]))
            .unwrap_or(Err(Eip7685Error::MissingType))
    }
}

/// Encoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that
/// wraps each possible request type.
///
/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685
pub trait Encodable7685: Sized + Send + Sync + 'static {
    /// Return the request type.
    fn request_type(&self) -> u8;

    /// Encode the request according to [EIP-7685] rules.
    ///
    /// First a 1-byte flag specifying the request type, then the encoded payload.
    ///
    /// The encoding of the payload is request-type dependent.
    ///
    /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685
    fn encode_7685(&self, out: &mut dyn BufMut) {
        out.put_u8(self.request_type());
        self.encode_payload_7685(out);
    }

    /// Encode the request payload.
    ///
    /// The encoding for the payload is request type dependent.
    fn encode_payload_7685(&self, out: &mut dyn BufMut);

    /// Encode the request according to [EIP-7685] rules.
    ///
    /// First a 1-byte flag specifying the request type, then the encoded payload.
    ///
    /// The encoding of the payload is request-type dependent.
    ///
    /// This is a convenience method for encoding into a vec, and returning the
    /// vec.
    ///
    /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685
    fn encoded_7685(&self) -> Vec<u8> {
        let mut out = vec![];
        self.encode_7685(&mut out);
        out
    }
}

/// An [EIP-7685] request envelope, blanket implemented for types that impl
/// [`Encodable7685`] and [`Decodable7685`]. This envelope is a wrapper around
/// a request, differentiated by the request type.
///
/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685
pub trait Eip7685RequestEnvelope: Decodable7685 + Encodable7685 {}
impl<T> Eip7685RequestEnvelope for T where T: Decodable7685 + Encodable7685 {}