example_erc20_gas/handlers/
validation.rs

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
use crate::TOKEN;
use alloy_sol_types::SolValue;
use revm::{
    context::Cfg,
    context_interface::{
        result::InvalidTransaction, Journal, Transaction, TransactionGetter, TransactionType,
    },
    handler::{EthValidation, EthValidationContext, EthValidationError},
    handler_interface::{InitialAndFloorGas, ValidationHandler},
    primitives::{keccak256, U256},
};
use std::cmp::Ordering;

pub struct Erc20Validation<CTX, ERROR> {
    inner: EthValidation<CTX, ERROR>,
}

impl<CTX, ERROR> Erc20Validation<CTX, ERROR> {
    pub fn new() -> Self {
        Self {
            inner: EthValidation::new(),
        }
    }
}

impl<CTX, ERROR> Default for Erc20Validation<CTX, ERROR> {
    fn default() -> Self {
        Self::new()
    }
}

impl<CTX, ERROR> ValidationHandler for Erc20Validation<CTX, ERROR>
where
    CTX: EthValidationContext,
    ERROR: EthValidationError<CTX>,
{
    type Context = CTX;
    type Error = ERROR;

    fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> {
        self.inner.validate_env(context)
    }

    fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> {
        let caller = context.tx().caller();
        let caller_nonce = context.journal().load_account(caller)?.data.info.nonce;
        let token_account = context.journal().load_account(TOKEN)?.data.clone();

        if !context.cfg().is_nonce_check_disabled() {
            let tx_nonce = context.tx().nonce();
            let state_nonce = caller_nonce;
            match tx_nonce.cmp(&state_nonce) {
                Ordering::Less => {
                    return Err(ERROR::from(InvalidTransaction::NonceTooLow {
                        tx: tx_nonce,
                        state: state_nonce,
                    }))
                }
                Ordering::Greater => {
                    return Err(ERROR::from(InvalidTransaction::NonceTooHigh {
                        tx: tx_nonce,
                        state: state_nonce,
                    }))
                }
                _ => (),
            }
        }

        let mut balance_check = U256::from(context.tx().gas_limit())
            .checked_mul(U256::from(context.tx().max_fee_per_gas()))
            .and_then(|gas_cost| gas_cost.checked_add(context.tx().value()))
            .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;

        if context.tx().tx_type() == TransactionType::Eip4844 {
            let tx = context.tx();
            let data_fee = tx.calc_max_data_fee();
            balance_check = balance_check
                .checked_add(data_fee)
                .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
        }

        let account_balance_slot: U256 = keccak256((caller, U256::from(3)).abi_encode()).into();
        let account_balance = token_account
            .storage
            .get(&account_balance_slot)
            .expect("Balance slot not found")
            .present_value();

        if account_balance < balance_check && !context.cfg().is_balance_check_disabled() {
            return Err(InvalidTransaction::LackOfFundForMaxFee {
                fee: Box::new(balance_check),
                balance: Box::new(account_balance),
            }
            .into());
        };

        Ok(())
    }

    fn validate_initial_tx_gas(
        &self,
        context: &Self::Context,
    ) -> Result<InitialAndFloorGas, Self::Error> {
        self.inner.validate_initial_tx_gas(context)
    }
}