eth_valkyoth_protocol/transaction/
dynamic_fee.rs1use eth_valkyoth_codec::{DecodeError, DecodeLimits};
2use eth_valkyoth_primitives::{ChainId, Gas, Nonce, Wei};
3
4use super::access_list::{AccessListDecodeError, decode_access_list};
5use super::{
6 AccessList, AccessListTransactionTo, SignatureYParity, TransactionEnvelope,
7 decode_transaction_envelope,
8};
9use crate::transaction::fields::{
10 decode_chain_id as decode_shared_chain_id, decode_to as decode_shared_to,
11 decode_u64_field as decode_shared_u64_field, decode_u256_field as decode_shared_u256_field,
12 next_list as next_shared_list, next_scalar as next_shared_scalar,
13};
14
15mod error;
16
17pub use error::{DynamicFeeTransactionDecodeError, DynamicFeeTransactionDecodeErrorCategory};
18
19pub const DYNAMIC_FEE_TRANSACTION_TYPE: u8 = 0x02;
21pub const DYNAMIC_FEE_TRANSACTION_FIELD_COUNT: usize = 12;
23
24pub type DynamicFeeTransactionTo = AccessListTransactionTo;
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub struct UnvalidatedDynamicFeeTransaction<'a> {
34 pub chain_id: ChainId,
36 pub nonce: Nonce,
38 pub max_priority_fee_per_gas: Wei,
40 pub max_fee_per_gas: Wei,
42 pub gas_limit: Gas,
44 pub to: DynamicFeeTransactionTo,
46 pub value: Wei,
48 pub input: &'a [u8],
50 pub access_list: AccessList<'a>,
52 pub y_parity: SignatureYParity,
54 pub r: [u8; 32],
58 pub s: [u8; 32],
63}
64
65#[non_exhaustive]
67#[derive(Clone, Copy, Debug, Eq, PartialEq)]
68pub enum DynamicFeeTransactionField {
69 Payload,
71 ChainId,
73 Nonce,
75 MaxPriorityFeePerGas,
77 MaxFeePerGas,
79 GasLimit,
81 To,
83 Value,
85 Data,
87 AccessList,
89 SignatureYParity,
91 SignatureR,
93 SignatureS,
95}
96
97pub fn decode_dynamic_fee_transaction<'a>(
99 input: &'a [u8],
100 limits: DecodeLimits,
101) -> Result<UnvalidatedDynamicFeeTransaction<'a>, DynamicFeeTransactionDecodeError> {
102 match decode_transaction_envelope(input, limits)
103 .map_err(DynamicFeeTransactionDecodeError::Envelope)?
104 {
105 TransactionEnvelope::Typed(typed)
106 if typed.transaction_type.get() == DYNAMIC_FEE_TRANSACTION_TYPE =>
107 {
108 decode_dynamic_fee_payload(typed.payload, limits)
109 }
110 TransactionEnvelope::Typed(typed) => {
111 Err(DynamicFeeTransactionDecodeError::WrongTransactionType {
112 type_byte: typed.transaction_type.get(),
113 })
114 }
115 TransactionEnvelope::Legacy(_) => {
116 Err(DynamicFeeTransactionDecodeError::WrongTransactionType { type_byte: 0 })
117 }
118 }
119}
120
121fn decode_dynamic_fee_payload<'a>(
122 payload: &'a [u8],
123 limits: DecodeLimits,
124) -> Result<UnvalidatedDynamicFeeTransaction<'a>, DynamicFeeTransactionDecodeError> {
125 let list = eth_valkyoth_codec::decode_rlp_list(payload, limits)
126 .map_err(|source| field_error(DynamicFeeTransactionField::Payload, source))?;
127 if list.item_count() != DYNAMIC_FEE_TRANSACTION_FIELD_COUNT {
128 return Err(DynamicFeeTransactionDecodeError::WrongFieldCount {
129 expected: DYNAMIC_FEE_TRANSACTION_FIELD_COUNT,
130 found: list.item_count(),
131 });
132 }
133
134 let mut fields = list.items();
135 let chain_id = decode_shared_chain_id(
136 &mut fields,
137 DynamicFeeTransactionField::ChainId,
138 field_error,
139 )?;
140 let nonce = Nonce::new(decode_shared_u64_field(
141 &mut fields,
142 DynamicFeeTransactionField::Nonce,
143 field_error,
144 )?);
145 let max_priority_fee_per_gas = Wei::from_be_bytes(decode_shared_u256_field(
146 &mut fields,
147 DynamicFeeTransactionField::MaxPriorityFeePerGas,
148 field_error,
149 )?);
150 let max_fee_per_gas = Wei::from_be_bytes(decode_shared_u256_field(
151 &mut fields,
152 DynamicFeeTransactionField::MaxFeePerGas,
153 field_error,
154 )?);
155 let gas_limit = Gas::new(decode_shared_u64_field(
156 &mut fields,
157 DynamicFeeTransactionField::GasLimit,
158 field_error,
159 )?);
160 let to = decode_shared_to(
161 next_shared_scalar(&mut fields, DynamicFeeTransactionField::To, field_error)?,
162 |found| DynamicFeeTransactionDecodeError::InvalidToLength { found },
163 )?;
164 let value = Wei::from_be_bytes(decode_shared_u256_field(
165 &mut fields,
166 DynamicFeeTransactionField::Value,
167 field_error,
168 )?);
169 let input =
170 next_shared_scalar(&mut fields, DynamicFeeTransactionField::Data, field_error)?.payload();
171 limits
172 .check_single_allocation_limit(input.len())
173 .map_err(|source| field_error(DynamicFeeTransactionField::Data, source))?;
174 let access_list = decode_access_list(next_shared_list(
175 &mut fields,
176 DynamicFeeTransactionField::AccessList,
177 field_error,
178 )?)
179 .map_err(map_access_list_error)?;
180 let y_parity = SignatureYParity::try_new(decode_shared_u64_field(
181 &mut fields,
182 DynamicFeeTransactionField::SignatureYParity,
183 field_error,
184 )?)
185 .map_err(|error| DynamicFeeTransactionDecodeError::InvalidYParity {
186 value: error.value(),
187 })?;
188 let r = decode_shared_u256_field(
189 &mut fields,
190 DynamicFeeTransactionField::SignatureR,
191 field_error,
192 )?;
193 let s = decode_shared_u256_field(
194 &mut fields,
195 DynamicFeeTransactionField::SignatureS,
196 field_error,
197 )?;
198
199 Ok(UnvalidatedDynamicFeeTransaction {
200 chain_id,
201 nonce,
202 max_priority_fee_per_gas,
203 max_fee_per_gas,
204 gas_limit,
205 to,
206 value,
207 input,
208 access_list,
209 y_parity,
210 r,
211 s,
212 })
213}
214
215const fn field_error(
216 field: DynamicFeeTransactionField,
217 source: DecodeError,
218) -> DynamicFeeTransactionDecodeError {
219 DynamicFeeTransactionDecodeError::FieldDecode { field, source }
220}
221
222const fn map_access_list_error(error: AccessListDecodeError) -> DynamicFeeTransactionDecodeError {
223 match error {
224 AccessListDecodeError::FieldDecode(source) => {
225 field_error(DynamicFeeTransactionField::AccessList, source)
226 }
227 AccessListDecodeError::InvalidAccessListEntryFieldCount { found } => {
228 DynamicFeeTransactionDecodeError::InvalidAccessListEntryFieldCount { found }
229 }
230 AccessListDecodeError::InvalidAccessListAddressLength { found } => {
231 DynamicFeeTransactionDecodeError::InvalidAccessListAddressLength { found }
232 }
233 AccessListDecodeError::InvalidStorageKeyLength { found } => {
234 DynamicFeeTransactionDecodeError::InvalidStorageKeyLength { found }
235 }
236 }
237}