Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVM: Implement estimate_gas #947

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions examples/demo-rollup/tests/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,15 @@ impl TestClient {
contract_address: H160,
set_arg: u32,
) -> PendingTransaction<'_, Http> {
let nonce = self.eth_get_transaction_count(self.from_addr).await;

// Tx without gas_limit should estimate and include it in send_transaction endpoint
// Tx without nonce should fetch and include it in send_transaction endpoint
let req = Eip1559TransactionRequest::new()
.from(self.from_addr)
.to(contract_address)
.chain_id(self.chain_id)
.nonce(nonce)
.data(self.contract.set_call_data(set_arg))
.max_priority_fee_per_gas(10u64)
.max_fee_per_gas(MAX_FEE_PER_GAS)
.gas(900000u64);
.max_fee_per_gas(MAX_FEE_PER_GAS);

let typed_transaction = TypedTransaction::Eip1559(req);

Expand Down Expand Up @@ -148,9 +146,17 @@ impl TestClient {
.chain_id(self.chain_id)
.nonce(nonce)
.data(self.contract.set_call_data(set_arg))
.gas_price(10u64)
.gas(900000u64);
.gas_price(10u64);

let typed_transaction = TypedTransaction::Legacy(req.clone());

// Estimate gas on rpc
let gas = self
.eth_estimate_gas(typed_transaction, Some("latest".to_owned()))
.await;

// Call with the estimated gas
let req = req.gas(gas);
let typed_transaction = TypedTransaction::Legacy(req);

let response = self
Expand Down Expand Up @@ -268,6 +274,16 @@ impl TestClient {
.map_err(|e| e.into())
}

async fn eth_estimate_gas(&self, tx: TypedTransaction, block_number: Option<String>) -> u64 {
let gas: ethereum_types::U64 = self
.http_client
.request("eth_estimateGas", rpc_params![tx, block_number])
.await
.unwrap();

gas.as_u64()
}

async fn execute(self) -> Result<(), Box<dyn std::error::Error>> {
// Nonce should be 0 in genesis
let nonce = self.eth_get_transaction_count(self.from_addr).await;
Expand Down
80 changes: 76 additions & 4 deletions full-node/sov-ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ pub mod experimental {
use jsonrpsee::types::ErrorObjectOwned;
use jsonrpsee::RpcModule;
use reth_primitives::{
Address as RethAddress, TransactionSignedNoHash as RethTransactionSignedNoHash,
Address as RethAddress, TransactionSignedNoHash as RethTransactionSignedNoHash, U128, U256,
};
use reth_rpc_types::{TransactionRequest, TypedTransactionRequest};
use reth_rpc_types::{CallRequest, TransactionRequest, TypedTransactionRequest};
use sov_evm::{CallMessage, Evm, RlpEvmTransaction};
use sov_modules_api::transaction::Transaction;
use sov_modules_api::utils::to_jsonrpsee_error_object;
Expand Down Expand Up @@ -211,6 +211,8 @@ pub mod experimental {

let raw_evm_tx = {
let mut working_set = WorkingSet::<C>::new(ethereum.storage.clone());

// set nonce if none
if transaction_request.nonce.is_none() {
let nonce = evm
.get_transaction_count(from, None, &mut working_set)
Expand All @@ -219,27 +221,43 @@ pub mod experimental {
transaction_request.nonce = Some(nonce);
}

// get current chain id
let chain_id = evm
.chain_id(&mut working_set)
.expect("Failed to get chain id")
.map(|id| id.as_u64())
.unwrap_or(1);

// TODO: implement gas logic after gas estimation (#906) is implemented
// https://github.com/Sovereign-Labs/sovereign-sdk/issues/906
// get call request to estimate gas and gas prices
let (call_request, gas_price, max_fee_per_gas) =
get_call_request_and_params(from, chain_id, &transaction_request);

// estimate gas limit
let gas_limit = U256::from(
evm.eth_estimate_gas(call_request, None, &mut working_set)?
.as_u64(),
);

// get typed transaction request
let transaction_request = match transaction_request.into_typed_request() {
Some(TypedTransactionRequest::Legacy(mut m)) => {
m.chain_id = Some(chain_id);
m.gas_limit = gas_limit;
m.gas_price = gas_price;

TypedTransactionRequest::Legacy(m)
}
Some(TypedTransactionRequest::EIP2930(mut m)) => {
m.chain_id = chain_id;
m.gas_limit = gas_limit;
m.gas_price = gas_price;

TypedTransactionRequest::EIP2930(m)
}
Some(TypedTransactionRequest::EIP1559(mut m)) => {
m.chain_id = chain_id;
m.gas_limit = gas_limit;
m.max_fee_per_gas = max_fee_per_gas;

TypedTransactionRequest::EIP1559(m)
}
Expand All @@ -251,10 +269,12 @@ pub mod experimental {
}
};

// get raw transaction
let transaction = into_transaction(transaction_request).map_err(|_| {
to_jsonrpsee_error_object("Invalid types in transaction request", ETH_RPC_ERROR)
})?;

// sign transaction
let signed_tx = ethereum
.eth_rpc_config
.eth_signer
Expand Down Expand Up @@ -338,4 +358,56 @@ pub mod experimental {
let bytes: [u8; 16] = bytes[16..].try_into()?;
Ok(u128::from_be_bytes(bytes))
}

fn get_call_request_and_params(
from: reth_primitives::H160,
chain_id: u64,
transaction_request: &TransactionRequest,
) -> (CallRequest, U128, U128) {
// TODO: we need an oracle to fetch the gas price of the current chain
// https://github.com/Sovereign-Labs/sovereign-sdk/issues/883
let gas_price = transaction_request.gas_price.unwrap_or_default();
let max_fee_per_gas = transaction_request.max_fee_per_gas.unwrap_or_default();

// TODO: Generate call request better according to the transaction type
// https://github.com/Sovereign-Labs/sovereign-sdk/issues/946
let call_request = CallRequest {
from: Some(from),
to: transaction_request.to,
gas: transaction_request.gas,
gas_price: {
if transaction_request.max_priority_fee_per_gas.is_some() {
// eip 1559
None
} else {
// legacy
Some(U256::from(gas_price))
}
},
max_fee_per_gas: Some(U256::from(max_fee_per_gas)),
value: transaction_request.value,
input: transaction_request.data.clone().into(),
nonce: transaction_request.nonce,
chain_id: Some(chain_id.into()),
access_list: transaction_request.access_list.clone(),
max_priority_fee_per_gas: {
if transaction_request.max_priority_fee_per_gas.is_some() {
// eip 1559
Some(U256::from(
transaction_request
.max_priority_fee_per_gas
.unwrap_or(max_fee_per_gas),
))
} else {
// legacy
None
}
},
transaction_type: None,
blob_versioned_hashes: vec![],
max_fee_per_blob_gas: None,
};

(call_request, gas_price, max_fee_per_gas)
}
}
4 changes: 4 additions & 0 deletions module-system/module-implementations/sov-evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ mod experimental {
Block, BlockEnv, Receipt, SealedBlock, TransactionSignedAndRecovered,
};

// Gas per transaction not creating a contract.
pub(crate) const MIN_TRANSACTION_GAS: u64 = 21_000u64;
pub(crate) const MIN_CREATE_GAS: u64 = 53_000u64;

/// Evm account.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct AccountData {
Expand Down
Loading