Skip to content

Commit

Permalink
fix: send transaction fees (#297)
Browse files Browse the repository at this point in the history
* fix: eth_sendTransaction default fees

* reformatted

* passign eth_sendTransaction errors to client
  • Loading branch information
vbar authored Jun 27, 2024
1 parent e0439f5 commit c1f64c9
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 28 deletions.
46 changes: 45 additions & 1 deletion e2e-tests/test/eth-apis.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from "chai";
import { getTestProvider } from "../helpers/utils";
import { expectThrowsAsync, getTestProvider } from "../helpers/utils";
import { RichAccounts } from "../helpers/constants";
import { ethers } from "ethers";

Expand All @@ -18,3 +18,47 @@ describe("eth_accounts", function () {
expect(accounts).to.deep.equal(richAccounts);
});
});

describe("eth_sendTransaction", function () {
it("Should execute with impersonation", async function () {
// Arrange
const fromAddr = "0xE999bb14881e48934A489cC9B35A4f9449EE87fb";
const toAddr = "0x3355df6d4c9c3035724fd0e3914de96a5a83aaf4";
const transaction = {
to: toAddr,
value: "0x0",
data: "0xa9059cbb000000000000000000000000981f198286e40f9979274e0876636e9144b8fb8e0000000000000000000000000000000000000000000000000000000000989680",
from: fromAddr,
};

// Act
await provider.send("hardhat_impersonateAccount", [fromAddr]);

const hash = await provider.send("eth_sendTransaction", [transaction]);

// Wait for the transaction to be mined and get the receipt
const receipt = await provider.waitForTransaction(hash);

await provider.send("hardhat_stopImpersonatingAccount", [fromAddr]);

// Assert
expect(receipt["from"]).to.equal(fromAddr);
});

it("Should fail without impersonation", async function () {
const action = async () => {
const fromAddr = "0xE999bb14881e48934A489cC9B35A4f9449EE87fb";
const toAddr = "0x3355df6d4c9c3035724fd0e3914de96a5a83aaf4";
const transaction = {
to: toAddr,
value: "0x0",
data: "0xa9059cbb000000000000000000000000981f198286e40f9979274e0876636e9144b8fb8e0000000000000000000000000000000000000000000000000000000000989680",
from: fromAddr,
};

await provider.send("eth_sendTransaction", [transaction]);
};

await expectThrowsAsync(action, "not allowed to perform transactions");
});
});
57 changes: 30 additions & 27 deletions src/node/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use zksync_types::{
l2::L2Tx,
transaction_request::TransactionRequest,
utils::storage_key_for_standard_token_balance,
PackedEthSignature, StorageKey, L2_BASE_TOKEN_ADDRESS,
PackedEthSignature, StorageKey, L2_BASE_TOKEN_ADDRESS, MAX_L1_TRANSACTION_GAS_LIMIT,
};
use zksync_utils::{h256_to_u256, u256_to_h256};
use zksync_web3_decl::{
Expand All @@ -30,8 +30,8 @@ use crate::{
namespaces::{EthNamespaceT, EthTestNodeNamespaceT, RpcResult},
node::{InMemoryNode, TransactionResult, MAX_TX_SIZE, PROTOCOL_VERSION},
utils::{
self, h256_to_u64, into_jsrpc_error, not_implemented, report_into_jsrpc_error,
IntoBoxedFuture,
self, h256_to_u64, into_jsrpc_error, into_jsrpc_error_message, not_implemented,
report_into_jsrpc_error, IntoBoxedFuture,
},
};

Expand Down Expand Up @@ -1390,28 +1390,35 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EthTestNod
&self,
tx: zksync_types::transaction_request::CallRequest,
) -> jsonrpc_core::BoxFuture<jsonrpc_core::Result<zksync_basic_types::H256>> {
let chain_id = match self.get_inner().read() {
Ok(reader) => reader.fork_storage.chain_id,
let (chain_id, l1_gas_price) = match self.get_inner().read() {
Ok(reader) => (
reader.fork_storage.chain_id,
reader.fee_input_provider.l1_gas_price,
),
Err(_) => {
return futures::future::err(into_jsrpc_error(Web3Error::InternalError(
anyhow::Error::msg("Failed to acquire read lock for chain ID retrieval."),
)))
return futures::future::err(into_jsrpc_error_message(
"Failed to acquire read lock for chain ID retrieval.".to_string(),
))
.boxed()
}
};

let mut tx_req = TransactionRequest::from(tx.clone());
// Users might expect a "sensible default"
if tx.gas.is_none() {
tx_req.gas = U256::from(MAX_L1_TRANSACTION_GAS_LIMIT);
}

// EIP-1559 gas fields should be processed separately
if tx.gas_price.is_some() {
if tx.max_fee_per_gas.is_some() || tx.max_priority_fee_per_gas.is_some() {
let error_message = "Transaction contains unsupported fields: max_fee_per_gas or max_priority_fee_per_gas";
return futures::future::err(into_jsrpc_error(Web3Error::InternalError(
anyhow::Error::msg(error_message),
)))
.boxed();
tracing::error!("{}", error_message);
return futures::future::err(into_jsrpc_error_message(error_message.to_string()))
.boxed();
}
} else {
tx_req.gas_price = tx.max_fee_per_gas.unwrap_or_default();
tx_req.gas_price = tx.max_fee_per_gas.unwrap_or(U256::from(l1_gas_price));
tx_req.max_priority_fee_per_gas = tx.max_priority_fee_per_gas;
if tx_req.transaction_type.is_none() {
tx_req.transaction_type = Some(zksync_types::EIP_1559_TX_TYPE.into());
Expand All @@ -1425,8 +1432,9 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EthTestNod
let hash = match tx_req.get_tx_hash(chain_id) {
Ok(result) => result,
Err(e) => {
tracing::error!("Transaction request serialization error: {}", e);
return futures::future::err(into_jsrpc_error(Web3Error::SerializationError(e)))
.boxed()
.boxed();
}
};
let bytes = tx_req.get_signed_bytes(
Expand All @@ -1436,8 +1444,9 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EthTestNod
let mut l2_tx: L2Tx = match L2Tx::from_request(tx_req, MAX_TX_SIZE) {
Ok(tx) => tx,
Err(e) => {
tracing::error!("Transaction serialization error: {}", e);
return futures::future::err(into_jsrpc_error(Web3Error::SerializationError(e)))
.boxed()
.boxed();
}
};

Expand All @@ -1458,16 +1467,14 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EthTestNod
"Initiator address {:?} is not allowed to perform transactions",
l2_tx.common_data.initiator_address
);
return futures::future::err(into_jsrpc_error(Web3Error::InternalError(
anyhow::Error::msg(error_message),
)))
.boxed();
tracing::error!("{}", error_message);
return futures::future::err(into_jsrpc_error_message(error_message)).boxed();
}
}
Err(_) => {
return futures::future::err(into_jsrpc_error(Web3Error::InternalError(
anyhow::Error::msg("Failed to acquire read lock for accounts."),
)))
return futures::future::err(into_jsrpc_error_message(
"Failed to acquire read lock for accounts.".to_string(),
))
.boxed()
}
}
Expand All @@ -1476,11 +1483,7 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EthTestNod
Ok(_) => Ok(l2_tx.hash()).into_boxed_future(),
Err(e) => {
let error_message = format!("Execution error: {}", e);
futures::future::err(into_jsrpc_error(Web3Error::SubmitTransactionError(
error_message,
l2_tx.hash().as_bytes().to_vec(),
)))
.boxed()
futures::future::err(into_jsrpc_error_message(error_message)).boxed()
}
}
}
Expand Down

0 comments on commit c1f64c9

Please sign in to comment.