Skip to content

Commit 37d7fc3

Browse files
agostbiroalcuadrado
andauthoredNov 10, 2023
feat: add eth_getTransactionByHash support (#4577)
Co-authored-by: Patricio Palladino <email@patriciopalladino.com>
1 parent 0647468 commit 37d7fc3

File tree

7 files changed

+367
-59
lines changed

7 files changed

+367
-59
lines changed
 

‎crates/edr_eth/src/access_list.rs

+6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ impl From<Vec<AccessListItem>> for AccessList {
3030
}
3131
}
3232

33+
impl From<AccessList> for Vec<AccessListItem> {
34+
fn from(src: AccessList) -> Vec<AccessListItem> {
35+
src.0
36+
}
37+
}
38+
3339
/// Access list item
3440
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
3541
#[cfg_attr(

‎crates/edr_eth/src/remote/eth.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,13 @@ pub struct Transaction {
7474
pub chain_id: Option<u64>,
7575
/// integer of the transaction type, 0x0 for legacy transactions, 0x1 for
7676
/// access list types, 0x2 for dynamic fees
77-
#[serde(rename = "type", default, with = "crate::serde::u64")]
78-
pub transaction_type: u64,
77+
#[serde(
78+
rename = "type",
79+
default,
80+
skip_serializing_if = "Option::is_none",
81+
with = "crate::serde::optional_u64"
82+
)]
83+
pub transaction_type: Option<u64>,
7984
/// access list
8085
#[serde(default)]
8186
pub access_list: Option<Vec<AccessListItem>>,
@@ -103,7 +108,7 @@ impl Transaction {
103108

104109
/// Returns whether the transaction is a legacy transaction.
105110
pub fn is_legacy(&self) -> bool {
106-
self.transaction_type == 0 && (self.v == 27 || self.v == 28)
111+
self.transaction_type == Some(0) && (self.v == 27 || self.v == 28)
107112
}
108113
}
109114

@@ -147,7 +152,7 @@ impl TryFrom<Transaction> for (SignedTransaction, Address) {
147152
};
148153

149154
let transaction = match value.transaction_type {
150-
0 => {
155+
Some(0) | None => {
151156
if value.is_legacy() {
152157
SignedTransaction::PreEip155Legacy(LegacySignedTransaction {
153158
nonce: value.nonce,
@@ -180,7 +185,7 @@ impl TryFrom<Transaction> for (SignedTransaction, Address) {
180185
})
181186
}
182187
}
183-
1 => SignedTransaction::Eip2930(Eip2930SignedTransaction {
188+
Some(1) => SignedTransaction::Eip2930(Eip2930SignedTransaction {
184189
odd_y_parity: value.odd_y_parity(),
185190
chain_id: value
186191
.chain_id
@@ -199,7 +204,7 @@ impl TryFrom<Transaction> for (SignedTransaction, Address) {
199204
s: value.s,
200205
hash: OnceLock::from(value.hash),
201206
}),
202-
2 => SignedTransaction::Eip1559(Eip1559SignedTransaction {
207+
Some(2) => SignedTransaction::Eip1559(Eip1559SignedTransaction {
203208
odd_y_parity: value.odd_y_parity(),
204209
chain_id: value
205210
.chain_id
@@ -223,7 +228,7 @@ impl TryFrom<Transaction> for (SignedTransaction, Address) {
223228
s: value.s,
224229
hash: OnceLock::from(value.hash),
225230
}),
226-
3 => SignedTransaction::Eip4844(Eip4844SignedTransaction {
231+
Some(3) => SignedTransaction::Eip4844(Eip4844SignedTransaction {
227232
odd_y_parity: value.odd_y_parity(),
228233
chain_id: value
229234
.chain_id
@@ -255,7 +260,7 @@ impl TryFrom<Transaction> for (SignedTransaction, Address) {
255260
s: value.s,
256261
hash: OnceLock::from(value.hash),
257262
}),
258-
r#type => {
263+
Some(r#type) => {
259264
return Err(TransactionConversionError::UnsupportedType(r#type));
260265
}
261266
};

‎crates/edr_eth/src/transaction/signed.rs

+42
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ impl SignedTransaction {
9494
}
9595

9696
/// Retrieves the max fee per gas of the transaction, if any.
97+
pub fn max_fee_per_gas(&self) -> Option<U256> {
98+
match self {
99+
SignedTransaction::PreEip155Legacy(_)
100+
| SignedTransaction::PostEip155Legacy(_)
101+
| SignedTransaction::Eip2930(_) => None,
102+
SignedTransaction::Eip1559(tx) => Some(tx.max_priority_fee_per_gas),
103+
SignedTransaction::Eip4844(tx) => Some(tx.max_priority_fee_per_gas),
104+
}
105+
}
106+
107+
/// Retrieves the max priority fee per gas of the transaction, if any.
97108
pub fn max_priority_fee_per_gas(&self) -> Option<U256> {
98109
match self {
99110
SignedTransaction::PreEip155Legacy(_)
@@ -104,6 +115,28 @@ impl SignedTransaction {
104115
}
105116
}
106117

118+
/// Retrieves the max fee per blob gas of the transaction, if any.
119+
pub fn max_fee_per_blob_gas(&self) -> Option<U256> {
120+
match self {
121+
SignedTransaction::PreEip155Legacy(_)
122+
| SignedTransaction::PostEip155Legacy(_)
123+
| SignedTransaction::Eip2930(_)
124+
| SignedTransaction::Eip1559(_) => None,
125+
SignedTransaction::Eip4844(tx) => Some(tx.max_fee_per_blob_gas),
126+
}
127+
}
128+
129+
/// Retrieves the blob hashes of the transaction, if any.
130+
pub fn blob_hashes(&self) -> Option<Vec<B256>> {
131+
match self {
132+
SignedTransaction::PreEip155Legacy(_)
133+
| SignedTransaction::PostEip155Legacy(_)
134+
| SignedTransaction::Eip2930(_)
135+
| SignedTransaction::Eip1559(_) => None,
136+
SignedTransaction::Eip4844(tx) => Some(tx.blob_hashes.clone()),
137+
}
138+
}
139+
107140
/// Upfront cost of the transaction
108141
pub fn upfront_cost(&self) -> U256 {
109142
self.max_cost().saturating_add(self.value())
@@ -208,6 +241,15 @@ impl SignedTransaction {
208241
},
209242
}
210243
}
244+
245+
pub fn transaction_type(&self) -> u64 {
246+
match self {
247+
SignedTransaction::PreEip155Legacy(_) | SignedTransaction::PostEip155Legacy(_) => 0,
248+
SignedTransaction::Eip2930(_) => 1,
249+
SignedTransaction::Eip1559(_) => 2,
250+
SignedTransaction::Eip4844(_) => 3,
251+
}
252+
}
211253
}
212254

213255
impl rlp::Encodable for SignedTransaction {

‎crates/edr_evm/src/transaction/pending.rs

+5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ impl PendingTransaction {
9191
&self.caller
9292
}
9393

94+
/// Returns the inner [`SignedTransaction`]
95+
pub fn transaction(&self) -> &SignedTransaction {
96+
&self.transaction
97+
}
98+
9499
/// Returns the inner transaction and caller
95100
pub fn into_inner(self) -> (SignedTransaction, Address) {
96101
(self.transaction, self.caller)

‎crates/edr_provider/src/data.rs

+154-15
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl ProviderData {
7272
network_id: config.network_id,
7373
beneficiary: config.coinbase,
7474
// TODO: Add config option (https://github.com/NomicFoundation/edr/issues/111)
75-
min_gas_price: U256::MAX,
75+
min_gas_price: U256::from(1),
7676
prevrandao_generator,
7777
block_time_offset_seconds: block_time_offset_seconds(config)?,
7878
next_block_timestamp: None,
@@ -411,6 +411,51 @@ impl ProviderData {
411411
self.impersonated_accounts.remove(&address)
412412
}
413413

414+
/// Get a transaction by hash from the blockchain or from the mempool if
415+
/// it's not mined yet.
416+
pub async fn transaction_by_hash(
417+
&self,
418+
hash: &B256,
419+
) -> Result<Option<GetTransactionResult>, ProviderError> {
420+
let transaction = if let Some(tx) = self.mem_pool.transaction_by_hash(hash) {
421+
let signed_transaction = tx.pending().transaction().clone();
422+
423+
Some(GetTransactionResult {
424+
signed_transaction,
425+
spec_id: self.blockchain.spec_id(),
426+
block_metadata: None,
427+
})
428+
} else if let Some(tx_block) = self.blockchain.block_by_transaction_hash(hash).await? {
429+
let tx_index = self
430+
.blockchain
431+
.receipt_by_transaction_hash(hash)
432+
.await?
433+
.expect("If the transaction was inserted in a block, it must have a receipt")
434+
.transaction_index;
435+
436+
let tx_index_usize =
437+
usize::try_from(tx_index).expect("Indices cannot be larger than usize::MAX");
438+
let signed_transaction = tx_block.transactions()[tx_index_usize].clone();
439+
440+
let block_metadata = BlockMetadataForTransaction {
441+
base_fee_per_gas: tx_block.header().base_fee_per_gas,
442+
block_hash: *tx_block.hash(),
443+
block_number: tx_block.header().number,
444+
transaction_index: tx_index,
445+
};
446+
447+
Some(GetTransactionResult {
448+
signed_transaction,
449+
spec_id: self.blockchain.spec_id(),
450+
block_metadata: Some(block_metadata),
451+
})
452+
} else {
453+
None
454+
};
455+
456+
Ok(transaction)
457+
}
458+
414459
fn add_pending_transaction(
415460
&mut self,
416461
transaction: PendingTransaction,
@@ -738,23 +783,45 @@ async fn create_blockchain_and_state(
738783
}
739784
}
740785

786+
/// The result returned by requesting a transaction.
787+
#[derive(Debug, Clone, PartialEq, Eq)]
788+
pub struct GetTransactionResult {
789+
/// The signed transaction.
790+
pub signed_transaction: SignedTransaction,
791+
/// Information about the active hardforks.
792+
pub spec_id: SpecId,
793+
/// The block metadata for the transaction. None if the transaction is
794+
/// pending.
795+
pub block_metadata: Option<BlockMetadataForTransaction>,
796+
}
797+
798+
/// Block metadata for a transaction.
799+
#[derive(Debug, Clone, PartialEq, Eq)]
800+
pub struct BlockMetadataForTransaction {
801+
pub base_fee_per_gas: Option<U256>,
802+
pub block_hash: B256,
803+
pub block_number: u64,
804+
pub transaction_index: u64,
805+
}
806+
741807
#[cfg(test)]
742808
mod tests {
809+
use anyhow::Context;
743810
use tempfile::TempDir;
744811

745812
use super::*;
746813
use crate::{test_utils::create_test_config_with_impersonated_accounts, ProviderConfig};
747814

748-
struct NodeTestFixture {
815+
struct ProviderTestFixture {
749816
// We need to keep the tempdir alive for the duration of the test
750817
_cache_dir: TempDir,
751818
config: ProviderConfig,
752819
provider_data: ProviderData,
753820
impersonated_account: Address,
754821
}
755822

756-
impl NodeTestFixture {
757-
pub(crate) async fn new(runtime: &runtime::Handle) -> anyhow::Result<Self> {
823+
impl ProviderTestFixture {
824+
pub(crate) async fn new() -> anyhow::Result<Self> {
758825
let cache_dir = TempDir::new()?;
759826

760827
let impersonated_account = Address::random();
@@ -763,7 +830,8 @@ mod tests {
763830
vec![impersonated_account],
764831
);
765832

766-
let mut provider_data = ProviderData::new(runtime, &config).await?;
833+
let runtime = runtime::Handle::try_current()?;
834+
let mut provider_data = ProviderData::new(&runtime, &config).await?;
767835
provider_data
768836
.impersonated_accounts
769837
.insert(impersonated_account);
@@ -786,7 +854,7 @@ mod tests {
786854
.expect("there are local accounts"),
787855
to: Some(Address::zero()),
788856
gas: Some(100_000),
789-
gas_price: Some(U256::from(1)),
857+
gas_price: Some(U256::from(42_000_000_000_u64)),
790858
value: Some(U256::from(1)),
791859
data: None,
792860
nonce: None,
@@ -813,7 +881,7 @@ mod tests {
813881

814882
#[tokio::test]
815883
async fn test_sign_transaction_request() -> anyhow::Result<()> {
816-
let fixture = NodeTestFixture::new(&runtime::Handle::current()).await?;
884+
let fixture = ProviderTestFixture::new().await?;
817885

818886
let transaction = fixture.signed_dummy_transaction()?;
819887
let recovered_address = transaction.recover()?;
@@ -828,7 +896,7 @@ mod tests {
828896

829897
#[tokio::test]
830898
async fn test_sign_transaction_request_impersonated_account() -> anyhow::Result<()> {
831-
let fixture = NodeTestFixture::new(&runtime::Handle::current()).await?;
899+
let fixture = ProviderTestFixture::new().await?;
832900

833901
let transaction = fixture.impersonated_dummy_transaction()?;
834902

@@ -838,7 +906,7 @@ mod tests {
838906
}
839907

840908
fn test_add_pending_transaction(
841-
fixture: &mut NodeTestFixture,
909+
fixture: &mut ProviderTestFixture,
842910
transaction: PendingTransaction,
843911
) -> anyhow::Result<()> {
844912
let filter_id = fixture.provider_data.new_pending_transaction_filter();
@@ -869,23 +937,23 @@ mod tests {
869937

870938
#[tokio::test]
871939
async fn add_pending_transaction() -> anyhow::Result<()> {
872-
let mut fixture = NodeTestFixture::new(&runtime::Handle::current()).await?;
940+
let mut fixture = ProviderTestFixture::new().await?;
873941
let transaction = fixture.signed_dummy_transaction()?;
874942

875943
test_add_pending_transaction(&mut fixture, transaction)
876944
}
877945

878946
#[tokio::test]
879947
async fn add_pending_transaction_from_impersonated_account() -> anyhow::Result<()> {
880-
let mut fixture = NodeTestFixture::new(&runtime::Handle::current()).await?;
948+
let mut fixture = ProviderTestFixture::new().await?;
881949
let transaction = fixture.impersonated_dummy_transaction()?;
882950

883951
test_add_pending_transaction(&mut fixture, transaction)
884952
}
885953

886954
#[tokio::test]
887955
async fn chain_id() -> anyhow::Result<()> {
888-
let fixture = NodeTestFixture::new(&runtime::Handle::current()).await?;
956+
let fixture = ProviderTestFixture::new().await?;
889957

890958
let chain_id = fixture.provider_data.chain_id().await;
891959
assert_eq!(chain_id, fixture.config.chain_id);
@@ -895,7 +963,7 @@ mod tests {
895963

896964
#[tokio::test]
897965
async fn next_filter_id() -> anyhow::Result<()> {
898-
let mut fixture = NodeTestFixture::new(&runtime::Handle::current()).await?;
966+
let mut fixture = ProviderTestFixture::new().await?;
899967

900968
let mut prev_filter_id = fixture.provider_data.last_filter_id;
901969
for _ in 0..10 {
@@ -909,7 +977,7 @@ mod tests {
909977

910978
#[tokio::test]
911979
async fn set_balance_updates_mem_pool() -> anyhow::Result<()> {
912-
let mut fixture = NodeTestFixture::new(&runtime::Handle::current()).await?;
980+
let mut fixture = ProviderTestFixture::new().await?;
913981

914982
let transaction = {
915983
let mut request = fixture.dummy_transaction_request();
@@ -942,7 +1010,7 @@ mod tests {
9421010

9431011
#[tokio::test]
9441012
async fn set_nonce_updates_mem_pool() -> anyhow::Result<()> {
945-
let mut fixture = NodeTestFixture::new(&runtime::Handle::current()).await?;
1013+
let mut fixture = ProviderTestFixture::new().await?;
9461014

9471015
// Artificially raise the nonce, to ensure the transaction is not rejected
9481016
fixture
@@ -990,4 +1058,75 @@ mod tests {
9901058

9911059
Ok(())
9921060
}
1061+
1062+
#[tokio::test]
1063+
async fn transaction_by_invalid_hash() -> anyhow::Result<()> {
1064+
let fixture = ProviderTestFixture::new().await?;
1065+
1066+
let non_existing_tx = fixture
1067+
.provider_data
1068+
.transaction_by_hash(&B256::zero())
1069+
.await?;
1070+
1071+
assert_eq!(non_existing_tx, None);
1072+
1073+
Ok(())
1074+
}
1075+
1076+
#[tokio::test]
1077+
async fn pending_transaction_by_hash() -> anyhow::Result<()> {
1078+
let mut fixture = ProviderTestFixture::new().await?;
1079+
1080+
let transaction_request = fixture.signed_dummy_transaction()?;
1081+
let transaction_hash = fixture
1082+
.provider_data
1083+
.add_pending_transaction(transaction_request)?;
1084+
1085+
let transaction_result = fixture
1086+
.provider_data
1087+
.transaction_by_hash(&transaction_hash)
1088+
.await?
1089+
.context("transaction not found")?;
1090+
1091+
assert_eq!(
1092+
transaction_result.signed_transaction.hash(),
1093+
&transaction_hash
1094+
);
1095+
1096+
Ok(())
1097+
}
1098+
1099+
#[tokio::test]
1100+
async fn transaction_by_hash() -> anyhow::Result<()> {
1101+
let mut fixture = ProviderTestFixture::new().await?;
1102+
1103+
let transaction_request = fixture.signed_dummy_transaction()?;
1104+
let transaction_hash = fixture
1105+
.provider_data
1106+
.add_pending_transaction(transaction_request)?;
1107+
1108+
let results = fixture.provider_data.mine_and_commit_block(None).await?;
1109+
1110+
// Make sure transaction was mined successfully.
1111+
assert!(results
1112+
.transaction_results
1113+
.first()
1114+
.context("failed to mine transaction")?
1115+
.is_success());
1116+
// Sanity check that the mempool is empty.
1117+
assert_eq!(fixture.provider_data.mem_pool.transactions().count(), 0);
1118+
1119+
let transaction_result = fixture
1120+
.provider_data
1121+
.transaction_by_hash(&transaction_hash)
1122+
.await?
1123+
.context("transaction not found")?;
1124+
1125+
assert_eq!(
1126+
transaction_result.signed_transaction.hash(),
1127+
&transaction_hash
1128+
);
1129+
1130+
Ok(())
1131+
}
9931132
}

‎crates/edr_provider/src/lib.rs

+34-34
Original file line numberDiff line numberDiff line change
@@ -23,43 +23,39 @@ pub use self::{
2323
};
2424

2525
/// A JSON-RPC provider for Ethereum.
26+
///
27+
/// Add a layer in front that handles this
28+
///
29+
/// ```rust,ignore
30+
/// let RpcRequest {
31+
/// version,
32+
/// method: request,
33+
/// id,
34+
/// } = request;
35+
///
36+
/// if version != jsonrpc::Version::V2_0 {
37+
/// return Err(ProviderError::RpcVersion(version));
38+
/// }
39+
///
40+
/// fn to_response(
41+
/// id: jsonrpc::Id,
42+
/// result: Result<serde_json::Value, ProviderError>,
43+
/// ) -> jsonrpc::Response<serde_json::Value> { let data = match result {
44+
/// Ok(result) => jsonrpc::ResponseData::Success { result }, Err(error) =>
45+
/// jsonrpc::ResponseData::Error { error: jsonrpc::Error { code: -32000,
46+
/// message: error.to_string(), data: None, }, }, };
47+
///
48+
/// jsonrpc::Response {
49+
/// jsonrpc: jsonrpc::Version::V2_0,
50+
/// id,
51+
/// data,
52+
/// }
53+
/// }
54+
/// ```
2655
pub struct Provider {
2756
data: Mutex<ProviderData>,
2857
}
2958

30-
// Add a layer in front that handles this
31-
// let RpcRequest {
32-
// version,
33-
// method: request,
34-
// id,
35-
// } = request;
36-
37-
// if version != jsonrpc::Version::V2_0 {
38-
// return Err(ProviderError::RpcVersion(version));
39-
// }
40-
//
41-
// fn to_response(
42-
// id: jsonrpc::Id,
43-
// result: Result<serde_json::Value, ProviderError>,
44-
// ) -> jsonrpc::Response<serde_json::Value> {
45-
// let data = match result {
46-
// Ok(result) => jsonrpc::ResponseData::Success { result },
47-
// Err(error) => jsonrpc::ResponseData::Error {
48-
// error: jsonrpc::Error {
49-
// code: -32000,
50-
// message: error.to_string(),
51-
// data: None,
52-
// },
53-
// },
54-
// };
55-
56-
// jsonrpc::Response {
57-
// jsonrpc: jsonrpc::Version::V2_0,
58-
// id,
59-
// data,
60-
// }
61-
// }
62-
6359
impl Provider {
6460
pub async fn handle_request(
6561
&self,
@@ -155,7 +151,11 @@ async fn handle_eth_request(
155151
EthRequest::GetTransactionByBlockNumberAndIndex(_, _) => {
156152
Err(ProviderError::Unimplemented("".to_string()))
157153
}
158-
EthRequest::GetTransactionByHash(_) => Err(ProviderError::Unimplemented("".to_string())),
154+
EthRequest::GetTransactionByHash(transaction_hash) => {
155+
eth::handle_get_transaction_by_hash(data, transaction_hash)
156+
.await
157+
.and_then(to_json)
158+
}
159159
EthRequest::GetTransactionCount(address, block_spec) => {
160160
eth::handle_get_transaction_count_request(data, address, block_spec)
161161
.await

‎crates/edr_provider/src/requests/eth/transactions.rs

+113-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,117 @@
1-
use edr_eth::{serde::ZeroXPrefixedBytes, transaction::EthTransactionRequest, B256};
1+
use edr_eth::{
2+
remote,
3+
serde::ZeroXPrefixedBytes,
4+
transaction::{EthTransactionRequest, SignedTransaction},
5+
SpecId, B256, U256,
6+
};
27

3-
use crate::{data::ProviderData, ProviderError};
8+
use crate::{
9+
data::{BlockMetadataForTransaction, GetTransactionResult, ProviderData},
10+
ProviderError,
11+
};
12+
13+
const FIRST_HARDFORK_WITH_TRANSACTION_TYPE: SpecId = SpecId::BERLIN;
14+
15+
pub async fn handle_get_transaction_by_hash(
16+
data: &ProviderData,
17+
transaction_hash: B256,
18+
) -> Result<Option<remote::eth::Transaction>, ProviderError> {
19+
data.transaction_by_hash(&transaction_hash)
20+
.await?
21+
.map(get_transaction_result_to_rpc_result)
22+
.transpose()
23+
}
24+
25+
fn get_transaction_result_to_rpc_result(
26+
result: GetTransactionResult,
27+
) -> Result<remote::eth::Transaction, ProviderError> {
28+
fn gas_price_for_post_eip1559(
29+
signed_transaction: &SignedTransaction,
30+
block_metadata: Option<&BlockMetadataForTransaction>,
31+
) -> U256 {
32+
let max_fee_per_gas = signed_transaction
33+
.max_fee_per_gas()
34+
.expect("Transaction must be post EIP-1559 transaction.");
35+
let max_priority_fee_per_gas = signed_transaction
36+
.max_priority_fee_per_gas()
37+
.expect("Transaction must be post EIP-1559 transaction.");
38+
39+
if let Some(block_metadata) = block_metadata {
40+
let base_fee_per_gas = block_metadata.base_fee_per_gas.expect(
41+
"Transaction must have base fee per gas in block metadata if EIP-1559 is active.",
42+
);
43+
let priority_fee_per_gas =
44+
max_priority_fee_per_gas.min(max_fee_per_gas - base_fee_per_gas);
45+
base_fee_per_gas + priority_fee_per_gas
46+
} else {
47+
// We are following Hardhat's behavior of returning the max fee per gas for
48+
// pending transactions.
49+
max_fee_per_gas
50+
}
51+
}
52+
53+
let GetTransactionResult {
54+
signed_transaction,
55+
spec_id,
56+
block_metadata,
57+
} = result;
58+
59+
let gas_price = match &signed_transaction {
60+
SignedTransaction::PreEip155Legacy(tx) => tx.gas_price,
61+
SignedTransaction::PostEip155Legacy(tx) => tx.gas_price,
62+
SignedTransaction::Eip2930(tx) => tx.gas_price,
63+
SignedTransaction::Eip1559(_) | SignedTransaction::Eip4844(_) => {
64+
gas_price_for_post_eip1559(&signed_transaction, block_metadata.as_ref())
65+
}
66+
};
67+
68+
let chain_id = match &signed_transaction {
69+
// Following Hardhat in not returning `chain_id` for `PostEip155Legacy` legacy transactions
70+
// even though the chain id would be recoverable.
71+
SignedTransaction::PreEip155Legacy(_) | SignedTransaction::PostEip155Legacy(_) => None,
72+
SignedTransaction::Eip2930(tx) => Some(tx.chain_id),
73+
SignedTransaction::Eip1559(tx) => Some(tx.chain_id),
74+
SignedTransaction::Eip4844(tx) => Some(tx.chain_id),
75+
};
76+
77+
let show_transaction_type = spec_id >= FIRST_HARDFORK_WITH_TRANSACTION_TYPE;
78+
let is_typed_transaction = signed_transaction.transaction_type() > 0;
79+
let transaction_type = if show_transaction_type || is_typed_transaction {
80+
Some(signed_transaction.transaction_type())
81+
} else {
82+
None
83+
};
84+
85+
let signature = signed_transaction.signature();
86+
87+
Ok(remote::eth::Transaction {
88+
hash: *signed_transaction.hash(),
89+
nonce: signed_transaction.nonce(),
90+
block_hash: block_metadata.as_ref().map(|m| m.block_hash),
91+
block_number: block_metadata.as_ref().map(|m| U256::from(m.block_number)),
92+
transaction_index: block_metadata.map(|m| m.transaction_index),
93+
from: signed_transaction.recover()?,
94+
to: signed_transaction.to(),
95+
value: signed_transaction.value(),
96+
gas_price,
97+
gas: U256::from(signed_transaction.gas_limit()),
98+
input: signed_transaction.data().clone(),
99+
v: signature.v,
100+
// Following Hardhat in always returning `v` instead of `y_parity`.
101+
y_parity: None,
102+
r: signature.r,
103+
s: signature.s,
104+
chain_id,
105+
transaction_type,
106+
access_list: signed_transaction
107+
.access_list()
108+
.map(|access_list| access_list.clone().into()),
109+
max_fee_per_gas: signed_transaction.max_fee_per_gas(),
110+
max_priority_fee_per_gas: signed_transaction.max_priority_fee_per_gas(),
111+
max_fee_per_blob_gas: signed_transaction.max_fee_per_blob_gas(),
112+
blob_versioned_hashes: signed_transaction.blob_hashes(),
113+
})
114+
}
4115

5116
pub fn handle_send_transaction_request(
6117
data: &mut ProviderData,

0 commit comments

Comments
 (0)
Please sign in to comment.