diff --git a/crates/contracts/src/account_contract.cairo b/crates/contracts/src/account_contract.cairo index 5b9df5cec..2c42558f5 100644 --- a/crates/contracts/src/account_contract.cairo +++ b/crates/contracts/src/account_contract.cairo @@ -48,12 +48,14 @@ pub mod AccountContract { use core::starknet::storage_access::{storage_base_address_from_felt252, StorageBaseAddress}; use core::starknet::syscalls::{replace_class_syscall}; use core::starknet::{ - ContractAddress, EthAddress, ClassHash, VALIDATED, get_caller_address, get_tx_info, Store + ContractAddress, EthAddress, ClassHash, VALIDATED, get_caller_address, get_contract_address, + get_tx_info, Store }; use core::traits::TryInto; use openzeppelin::token::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; use super::{IAccountLibraryDispatcher, IAccountDispatcherTrait}; use utils::constants::{POW_2_32}; + use utils::eth_transaction::EthereumTransactionTrait; use utils::eth_transaction::{EthTransactionTrait, TransactionMetadata}; use utils::helpers::SpanExtTrait; use utils::helpers::{ByteArrayExTrait, ResultExTrait}; @@ -210,12 +212,20 @@ pub mod AccountContract { // Increment nonce to match protocol's nonce for EOAs. self.Account_nonce.write(tx_info.nonce.try_into().unwrap() + 1); - //TODO execute checks - let call: @Call = calls[0]; - let calldata = deserialize_bytes(*call.calldata).expect('conversion failed').span(); - - let tx = EthTransactionTrait::decode(calldata).expect('rlp decoding of tx failed'); + let encoded_tx = deserialize_bytes(*call.calldata).expect('conversion failed').span(); + + let tx = EthTransactionTrait::decode(encoded_tx).expect('rlp decoding of tx failed'); + + match tx.try_into_fee_market_transaction() { + Option::Some(tx_fee_infos) => { + let result = self.validate_eip1559_tx(@tx, tx_fee_infos); + if result[0] != @ArrayTrait::new().span() { + return result; + } + }, + Option::None => () + } let (success, return_data, gas_used) = kakarot.eth_send_transaction(tx); let return_data = serialize_bytes(return_data).span(); @@ -254,4 +264,64 @@ pub mod AccountContract { self.Account_nonce.write(nonce); } } + + #[generate_trait] + impl Eip1559TransactionImpl of Eip1559TransactionTrait { + fn validate_eip1559_tx( + ref self: ContractState, + tx: @utils::eth_transaction::EthereumTransaction, + tx_fee_infos: utils::eth_transaction::FeeMarketTransaction + ) -> Array> { + let kakarot = IKakarotCoreDispatcher { contract_address: self.ownable.owner() }; + let block_gas_limit = kakarot.get_block_gas_limit(); + + if tx.gas_limit() >= block_gas_limit { + let error: felt252 = 'tx gas does not fit in block'; + let result: Array> = array![array![error].span()]; + return result; + } + + let base_fee = kakarot.get_base_fee(); + let native_token = kakarot.get_native_token(); + let balance = ERC20ABIDispatcher { contract_address: native_token } + .balance_of(get_contract_address()); + + let max_fee_per_gas = tx_fee_infos.max_fee_per_gas; + let max_priority_fee_per_gas = tx_fee_infos.max_priority_fee_per_gas; + + // ensure that the user was willing to at least pay the base fee + if base_fee >= max_fee_per_gas { + let error: felt252 = 'max fee per gas is too low'; + let result: Array> = array![array![error].span()]; + return result; + } + + // ensure that the max priority fee per gas is not greater than the max fee per gas + if max_priority_fee_per_gas >= max_fee_per_gas { + let error: felt252 = 'priority fee is too high'; + let result: Array> = array![array![error].span()]; + return result; + } + + let max_gas_fee = tx.gas_limit() * max_fee_per_gas; + let tx_cost = max_gas_fee.into() + tx_fee_infos.amount; + + if tx_cost >= balance { + let error: felt252 = 'balance cannot cover tx cost'; + let result: Array> = array![array![error].span()]; + return result; + } + + // priority fee is capped because the base fee is filled first + let possible_priority_fee = max_fee_per_gas - base_fee; + + if max_priority_fee_per_gas >= possible_priority_fee { + let error: felt252 = 'max priority is fee too high'; + let result: Array> = array![array![error].span()]; + return result; + } + + return array![array![].span()]; + } + } } diff --git a/crates/contracts/src/kakarot_core/interface.cairo b/crates/contracts/src/kakarot_core/interface.cairo index c8afcbbda..2e36caf1e 100644 --- a/crates/contracts/src/kakarot_core/interface.cairo +++ b/crates/contracts/src/kakarot_core/interface.cairo @@ -51,6 +51,11 @@ pub trait IKakarotCore { fn set_account_class_hash(ref self: TContractState, new_class_hash: ClassHash); fn register_account(ref self: TContractState, evm_address: EthAddress); + + // Getter for the Block Gas Limit + fn get_block_gas_limit(self: @TContractState) -> u128; + // Getter for the Base Fee + fn get_base_fee(self: @TContractState) -> u128; } #[starknet::interface] diff --git a/crates/contracts/src/kakarot_core/kakarot.cairo b/crates/contracts/src/kakarot_core/kakarot.cairo index 079eb2a44..a7e9f6b71 100644 --- a/crates/contracts/src/kakarot_core/kakarot.cairo +++ b/crates/contracts/src/kakarot_core/kakarot.cairo @@ -224,6 +224,15 @@ pub mod KakarotCore { self.Kakarot_evm_to_starknet_address.write(evm_address, starknet_address); self.emit(AccountDeployed { evm_address, starknet_address }); } + + fn get_block_gas_limit(self: @ContractState) -> u128 { + self.Kakarot_block_gas_limit.read() + } + + + fn get_base_fee(self: @ContractState) -> u128 { + self.Kakarot_base_fee.read() + } } #[generate_trait] diff --git a/crates/contracts/src/tests/test_eoa.cairo b/crates/contracts/src/tests/test_eoa.cairo index 55000a262..d088749fb 100644 --- a/crates/contracts/src/tests/test_eoa.cairo +++ b/crates/contracts/src/tests/test_eoa.cairo @@ -121,9 +121,10 @@ mod test_external_owned_account { #[test] #[should_panic(expected: ('EOA: reentrant call', 'ENTRYPOINT_FAILED'))] fn test___validate__fail__caller_not_0() { - let (_, kakarot_core) = setup_contracts_for_testing(); + let (native_token, kakarot_core) = setup_contracts_for_testing(); let evm_address = evm_address(); let eoa = kakarot_core.deploy_externally_owned_account(evm_address); + fund_account_with_native_token(eoa, native_token, 0xfffffffffffffffffffffffffff); let eoa_contract = IAccountDispatcher { contract_address: eoa }; set_contract_address(other_starknet_address()); @@ -135,9 +136,10 @@ mod test_external_owned_account { #[test] #[should_panic(expected: ('EOA: multicall not supported', 'ENTRYPOINT_FAILED'))] fn test___validate__fail__call_data_len_not_1() { - let (_, kakarot_core) = setup_contracts_for_testing(); + let (native_token, kakarot_core) = setup_contracts_for_testing(); let evm_address = evm_address(); let eoa = kakarot_core.deploy_externally_owned_account(evm_address); + fund_account_with_native_token(eoa, native_token, 0xfffffffffffffffffffffffffff); let eoa_contract = IAccountDispatcher { contract_address: eoa }; set_contract_address(contract_address_const::<0>()); @@ -149,9 +151,10 @@ mod test_external_owned_account { #[test] #[should_panic(expected: ('to is not kakarot core', 'ENTRYPOINT_FAILED'))] fn test___validate__fail__to_address_not_kakarot_core() { - let (_, kakarot_core) = setup_contracts_for_testing(); + let (native_token, kakarot_core) = setup_contracts_for_testing(); let evm_address = evm_address(); let eoa = kakarot_core.deploy_externally_owned_account(evm_address); + fund_account_with_native_token(eoa, native_token, 0xfffffffffffffffffffffffffff); let eoa_contract = IAccountDispatcher { contract_address: eoa }; // to reproduce locally: @@ -181,9 +184,10 @@ mod test_external_owned_account { expected: ("Validate: selector must be eth_send_transaction", 'ENTRYPOINT_FAILED') )] fn test___validate__fail__selector_not_eth_send_transaction() { - let (_, kakarot_core) = setup_contracts_for_testing(); + let (native_token, kakarot_core) = setup_contracts_for_testing(); let evm_address = evm_address(); let eoa = kakarot_core.deploy_externally_owned_account(evm_address); + fund_account_with_native_token(eoa, native_token, 0xfffffffffffffffffffffffffff); let eoa_contract = IAccountDispatcher { contract_address: eoa }; set_chain_id(chain_id().into()); @@ -214,9 +218,11 @@ mod test_external_owned_account { #[test] fn test___validate__legacy_transaction() { - let (_, kakarot_core) = setup_contracts_for_testing(); + let (native_token, kakarot_core) = setup_contracts_for_testing(); let evm_address: EthAddress = 0xaA36F24f65b5F0f2c642323f3d089A3F0f2845Bf_u256.into(); let eoa = kakarot_core.deploy_externally_owned_account(evm_address); + fund_account_with_native_token(eoa, native_token, 0xfffffffffffffffffffffffffff); + let eoa_contract = IAccountDispatcher { contract_address: eoa }; set_chain_id(chain_id().into()); @@ -249,9 +255,11 @@ mod test_external_owned_account { #[test] fn test___validate__eip_2930_transaction() { - let (_, kakarot_core) = setup_contracts_for_testing(); + let (native_token, kakarot_core) = setup_contracts_for_testing(); let evm_address: EthAddress = 0xaA36F24f65b5F0f2c642323f3d089A3F0f2845Bf_u256.into(); let eoa = kakarot_core.deploy_externally_owned_account(evm_address); + fund_account_with_native_token(eoa, native_token, 0xfffffffffffffffffffffffffff); + let eoa_contract = IAccountDispatcher { contract_address: eoa }; set_chain_id(chain_id().into()); @@ -285,9 +293,11 @@ mod test_external_owned_account { #[test] fn test___validate__eip_1559_transaction() { - let (_, kakarot_core) = setup_contracts_for_testing(); + let (native_token, kakarot_core) = setup_contracts_for_testing(); let evm_address: EthAddress = 0xaA36F24f65b5F0f2c642323f3d089A3F0f2845Bf_u256.into(); let eoa = kakarot_core.deploy_externally_owned_account(evm_address); + fund_account_with_native_token(eoa, native_token, 0xfffffffffffffffffffffffffff); + let eoa_contract = IAccountDispatcher { contract_address: eoa }; set_chain_id(chain_id().into());