Skip to content

Commit

Permalink
refactor: add EthRPC trait (#937)
Browse files Browse the repository at this point in the history
  • Loading branch information
enitrat authored Sep 13, 2024
1 parent 307f330 commit d70cdae
Show file tree
Hide file tree
Showing 8 changed files with 593 additions and 327 deletions.
54 changes: 5 additions & 49 deletions crates/contracts/src/account_contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub mod AccountContract {
use contracts::components::ownable::ownable_component::InternalTrait;
use contracts::components::ownable::ownable_component;
use contracts::errors::KAKAROT_REENTRANCY;
use contracts::kakarot_core::eth_rpc::{IEthRPCDispatcher, IEthRPCDispatcherTrait};
use contracts::kakarot_core::interface::{IKakarotCoreDispatcher, IKakarotCoreDispatcherTrait};
use contracts::storage::StorageBytecode;
use core::cmp::min;
Expand All @@ -69,7 +70,7 @@ pub mod AccountContract {
use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
use super::OutsideExecution;
use utils::constants::{POW_2_32};
use utils::eth_transaction::transaction::{TransactionUnsignedTrait, Transaction};
use utils::eth_transaction::transaction::TransactionUnsignedTrait;
use utils::serialization::{deserialize_signature, deserialize_bytes, serialize_bytes};
use utils::traits::DefaultSignature;

Expand Down Expand Up @@ -256,7 +257,9 @@ pub mod AccountContract {
let address = self.Account_evm_address.read();
verify_eth_signature(unsigned_transaction.hash, signature, address);

let kakarot = IKakarotCoreDispatcher { contract_address: self.ownable.owner() };
let kakarot = IEthRPCDispatcher { contract_address: self.ownable.owner() };
//TODO: refactor this to call eth_send_raw_unsigned_tx. Only the transactions bytes are
//passed.
let (success, return_data, gas_used) = kakarot
.eth_send_transaction(unsigned_transaction.transaction);
let return_data = serialize_bytes(return_data).span();
Expand All @@ -278,51 +281,4 @@ pub mod AccountContract {
array![return_data]
}
}

#[generate_trait]
impl Eip1559TransactionImpl of Eip1559TransactionTrait {
//TODO: refactor into a generic tx validation function.
fn validate_eip1559_tx(ref self: ContractState, tx: Transaction,) -> bool {
// let kakarot = IKakarotCoreDispatcher { contract_address: self.ownable.owner() };
// let block_gas_limit = kakarot.get_block_gas_limit();

// if tx.gas_limit() >= block_gas_limit {
// return false;
// }

// let base_fee = kakarot.get_base_fee();
// let native_token = kakarot.get_native_token();
// let balance = IERC20CamelDispatcher { contract_address: native_token }
// .balanceOf(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 {
// return false;
// }

// // 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 {
// return false;
// }

// 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 {
// return false;
// }

// // 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 {
// return false;
// }

return true;
}
}
}
1 change: 1 addition & 0 deletions crates/contracts/src/kakarot_core.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod eth_rpc;
pub mod interface;
mod kakarot;
pub use interface::{
Expand Down
212 changes: 212 additions & 0 deletions crates/contracts/src/kakarot_core/eth_rpc.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use contracts::account_contract::{IAccountDispatcher, IAccountDispatcherTrait};
use contracts::kakarot_core::interface::IKakarotCore;
use contracts::kakarot_core::kakarot::{KakarotCore, KakarotCore::{KakarotCoreState}};
use core::num::traits::Zero;
use core::starknet::get_tx_info;
use core::starknet::{EthAddress, get_caller_address};
use evm::backend::starknet_backend;
use evm::backend::validation::validate_eth_tx;
use evm::model::{TransactionResult, Address};
use evm::{EVMTrait};
use utils::eth_transaction::transaction::{TransactionTrait, Transaction};

#[starknet::interface]
pub trait IEthRPC<T> {
/// Returns the balance of the specified address.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Arguments
///
/// * `address` - The Ethereum address to get the balance from
///
/// # Returns
///
/// The balance of the address as a u256
fn eth_get_balance(self: @T, address: EthAddress) -> u256;

/// Returns the number of transactions sent from the specified address.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Arguments
///
/// * `address` - The Ethereum address to get the transaction count from
///
/// # Returns
///
/// The transaction count of the address as a u64
fn eth_get_transaction_count(self: @T, address: EthAddress) -> u64;

/// Returns the current chain ID.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Returns
///
/// The chain ID as a u64
fn eth_chain_id(self: @T) -> u64;

/// Executes a new message call immediately without creating a transaction on the block chain.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Arguments
///
/// * `origin` - The address the transaction is sent from
/// * `tx` - The transaction object
///
/// # Returns
///
/// A tuple containing:
/// * A boolean indicating success
/// * The return data as a Span<u8>
/// * The amount of gas used as a u64
fn eth_call(self: @T, origin: EthAddress, tx: Transaction) -> (bool, Span<u8>, u64);

/// Generates and returns an estimate of how much gas is necessary to allow the transaction to
/// complete.
///
/// This is a view-only function that doesn't modify the state.
///
/// # Arguments
///
/// * `origin` - The address the transaction is sent from
/// * `tx` - The transaction object
///
/// # Returns
///
/// A tuple containing:
/// * A boolean indicating success
/// * The return data as a Span<u8>
/// * The estimated gas as a u64
fn eth_estimate_gas(self: @T, origin: EthAddress, tx: Transaction) -> (bool, Span<u8>, u64);

//TODO: make this an internal function. The account contract should call
//eth_send_raw_transaction.
/// Executes a transaction and possibly modifies the state.
///
/// # Arguments
///
/// * `tx` - The transaction object
///
/// # Returns
///
/// A tuple containing:
/// * A boolean indicating success
/// * The return data as a Span<u8>
/// * The amount of gas used as a u64
fn eth_send_transaction(ref self: T, tx: Transaction) -> (bool, Span<u8>, u64);

/// Executes an unsigned transaction.
///
/// This is a modified version of the eth_sendRawTransaction function.
/// Signature validation should be done before calling this function.
///
/// # Arguments
///
/// * `tx_data` - The unsigned transaction data as a Span<u8>
///
/// # Returns
///
/// A tuple containing:
/// * A boolean indicating success
/// * The return data as a Span<u8>
/// * The amount of gas used as a u64
fn eth_send_raw_unsigned_tx(ref self: T, tx_data: Span<u8>) -> (bool, Span<u8>, u64);
}


#[starknet::embeddable]
pub impl EthRPC<
TContractState, impl KakarotState: KakarotCoreState<TContractState>, +Drop<TContractState>
> of IEthRPC<TContractState> {
fn eth_get_balance(self: @TContractState, address: EthAddress) -> u256 {
panic!("unimplemented")
}

fn eth_get_transaction_count(self: @TContractState, address: EthAddress) -> u64 {
panic!("unimplemented")
}

fn eth_chain_id(self: @TContractState) -> u64 {
panic!("unimplemented")
}

fn eth_call(
self: @TContractState, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64) {
let mut kakarot_state = KakarotState::get_state();
if !is_view(@kakarot_state) {
core::panic_with_felt252('fn must be called, not invoked');
};

let origin = Address {
evm: origin, starknet: kakarot_state.compute_starknet_address(origin)
};

let TransactionResult { success, return_data, gas_used, state: _state } =
EVMTrait::process_transaction(
ref kakarot_state, origin, tx, tx.effective_gas_price(Option::None), 0
);

(success, return_data, gas_used)
}

fn eth_estimate_gas(
self: @TContractState, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64) {
panic!("unimplemented")
}

//TODO: make this one internal, and the eth_send_raw_unsigned_tx one public
fn eth_send_transaction(
ref self: TContractState, mut tx: Transaction
) -> (bool, Span<u8>, u64) {
let mut kakarot_state = KakarotState::get_state();
let (gas_price, intrinsic_gas) = validate_eth_tx(@kakarot_state, tx);

let starknet_caller_address = get_caller_address();
let account = IAccountDispatcher { contract_address: starknet_caller_address };
let origin = Address { evm: account.get_evm_address(), starknet: starknet_caller_address };

let TransactionResult { success, return_data, gas_used, mut state } =
EVMTrait::process_transaction(
ref kakarot_state, origin, tx, gas_price, intrinsic_gas
);
starknet_backend::commit(ref state).expect('Committing state failed');
(success, return_data, gas_used)
}

fn eth_send_raw_unsigned_tx(
ref self: TContractState, tx_data: Span<u8>
) -> (bool, Span<u8>, u64) {
panic!("unimplemented")
}
}

trait IEthRPCInternal<T> {
fn eth_send_transaction(
ref self: T, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64);
}

impl EthRPCInternalImpl<TContractState, +Drop<TContractState>> of IEthRPCInternal<TContractState> {
fn eth_send_transaction(
ref self: TContractState, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64) {
panic!("unimplemented")
}
}

fn is_view(self: @KakarotCore::ContractState) -> bool {
let tx_info = get_tx_info().unbox();

// If the account that originated the transaction is not zero, this means we
// are in an invoke transaction instead of a call; therefore, `eth_call` is being
// wrongly called For invoke transactions, `eth_send_transaction` must be used
if !tx_info.account_contract_address.is_zero() {
return false;
}
true
}
11 changes: 0 additions & 11 deletions crates/contracts/src/kakarot_core/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,6 @@ pub trait IKakarotCore<TContractState> {
ref self: TContractState, evm_address: EthAddress
) -> ContractAddress;

/// View entrypoint into the EVM
/// Performs view calls into the blockchain
/// It cannot modify the state of the chain
fn eth_call(
self: @TContractState, origin: EthAddress, tx: Transaction
) -> (bool, Span<u8>, u64);

/// Transaction entrypoint into the EVM
/// Executes an EVM transaction and possibly modifies the state
fn eth_send_transaction(ref self: TContractState, tx: Transaction) -> (bool, Span<u8>, u64);

/// Upgrade the KakarotCore smart contract
/// Using replace_class_syscall
fn upgrade(ref self: TContractState, new_class_hash: ClassHash);
Expand Down
Loading

0 comments on commit d70cdae

Please sign in to comment.