Skip to content

Commit

Permalink
feat: EIP1559 checks (#778)
Browse files Browse the repository at this point in the history
* draft

* fund eoa account in tests resolve 1 test failure

* match for fee market tx

* fmt

* address comments

* make eip1559 checks an internal function

* address comments
  • Loading branch information
TAdev0 authored Apr 19, 2024
1 parent 4fbf682 commit 46d7fff
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 13 deletions.
82 changes: 76 additions & 6 deletions crates/contracts/src/account_contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<Span<felt252>> {
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<Span<felt252>> = 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<Span<felt252>> = 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<Span<felt252>> = 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<Span<felt252>> = 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<Span<felt252>> = array![array![error].span()];
return result;
}

return array![array![].span()];
}
}
}
5 changes: 5 additions & 0 deletions crates/contracts/src/kakarot_core/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ pub trait IKakarotCore<TContractState> {
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]
Expand Down
9 changes: 9 additions & 0 deletions crates/contracts/src/kakarot_core/kakarot.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
24 changes: 17 additions & 7 deletions crates/contracts/src/tests/test_eoa.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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>());
Expand All @@ -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:
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down

0 comments on commit 46d7fff

Please sign in to comment.