Skip to content

Commit 21fd1f6

Browse files
authored
feat!: add support for eth_getTransactionReceipt response fields (#474)
(XC-412) Add support for the `root` and `cumulativeGasUsed` fields in the response to `eth_getTransactionReceipt`: * The `root` field is mandatory for transactions included pre-Byzantium and represents the post-transaction state root. * The `cumulativeGasUsed` is mandatory for all transactions and represents the sum of gas used by this transaction and all preceding transactions in the same block. This is **not** breaking for the canister Candid API since adding a new field to a returned type is not breaking. BREAKING: This is only breaking for the `evm_rpc_types` crate since it adds new fields to the public `evm_rpc_types::TransactionsReceipt` type.
1 parent bdccb9e commit 21fd1f6

File tree

6 files changed

+84
-4
lines changed

6 files changed

+84
-4
lines changed

candid/evm_rpc.did

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ type Topic = vec text;
284284
type TransactionReceipt = record {
285285
to : opt text;
286286
status : opt nat;
287+
root : opt text;
287288
transactionHash : text;
288289
blockNumber : nat;
289290
from : text;
@@ -295,6 +296,7 @@ type TransactionReceipt = record {
295296
logsBloom : text;
296297
contractAddress : opt text;
297298
gasUsed : nat;
299+
cumulativeGasUsed : nat;
298300
};
299301
type ValidationError = variant {
300302
Custom : text;

evm_rpc_types/src/response/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,18 @@ pub struct TransactionReceipt {
9494
#[serde(rename = "gasUsed")]
9595
pub gas_used: Nat256,
9696

97+
/// The sum of gas used by this transaction and all preceding transactions in the same block.
98+
#[serde(rename = "cumulativeGasUsed")]
99+
pub cumulative_gas_used: Nat256,
100+
97101
/// Either 1 (success) or 0 (failure).
98102
/// Only specified for transactions included after the Byzantium upgrade.
99103
pub status: Option<Nat256>,
100104

105+
/// The post-transaction state root.
106+
/// Only specified for transactions included before the Byzantium upgrade.
107+
pub root: Option<Hex32>,
108+
101109
/// The hash of the transaction
102110
#[serde(rename = "transactionHash")]
103111
pub transaction_hash: Hex32,

src/candid_rpc/cketh_conversion.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,12 @@ pub(super) fn from_transaction_receipt(
120120
block_number: value.block_number.into(),
121121
effective_gas_price: value.effective_gas_price.into(),
122122
gas_used: value.gas_used.into(),
123+
cumulative_gas_used: value.cumulative_gas_used.into(),
123124
status: value.status.map(|v| match v {
124125
crate::rpc_client::json::responses::TransactionStatus::Success => Nat256::from(1_u8),
125126
crate::rpc_client::json::responses::TransactionStatus::Failure => Nat256::from(0_u8),
126127
}),
128+
root: value.root.map(Hash::into_bytes).map(Hex32::from),
127129
transaction_hash: Hex32::from(value.transaction_hash.into_bytes()),
128130
contract_address: value
129131
.contract_address

src/rpc_client/json/responses.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,26 @@ pub struct TransactionReceipt {
1919
#[serde(rename = "blockNumber")]
2020
pub block_number: BlockNumber,
2121

22-
/// The total base charge plus tip paid for each unit of gas
22+
/// The total base charge plus tip paid for each unit of gas.
2323
#[serde(rename = "effectiveGasPrice")]
2424
pub effective_gas_price: WeiPerGas,
2525

26-
/// The amount of gas used by this specific transaction alone
26+
/// The sum of gas used by this transaction and all preceding transactions in the same block.
27+
#[serde(rename = "cumulativeGasUsed")]
28+
pub cumulative_gas_used: GasAmount,
29+
30+
/// The amount of gas used for this specific transaction alone.
2731
#[serde(rename = "gasUsed")]
2832
pub gas_used: GasAmount,
2933

3034
/// Status of the transaction.
35+
/// Only specified for transactions included after the Byzantium upgrade.
3136
pub status: Option<TransactionStatus>,
3237

38+
/// The post-transaction state root.
39+
/// Only specified for transactions included before the Byzantium upgrade.
40+
pub root: Option<Hash>,
41+
3342
/// The hash of the transaction
3443
#[serde(rename = "transactionHash")]
3544
pub transaction_hash: Hash,

src/rpc_client/tests.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,56 @@ mod eth_get_transaction_receipt {
6969
use std::str::FromStr;
7070

7171
#[test]
72-
fn should_deserialize_transaction_receipt() {
72+
fn should_deserialize_pre_byzantium_transaction_receipt() {
73+
const RECEIPT: &str = r#"{
74+
"blockHash": "0x9b3c1d182975fdaa5797879cbc45d6b00a84fb3b13980a107645b2491bcca899",
75+
"blockNumber": "0x3d08ff",
76+
"contractAddress": null,
77+
"cumulativeGasUsed": "0x3a827d",
78+
"effectiveGasPrice": "0x4e3b29200",
79+
"from": "0xcaf8925d8e825ebe0cb3fc34a2be2c8c737c1ecc",
80+
"gasUsed": "0x5208",
81+
"logs": [],
82+
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
83+
"root": "0xc13704a89e282b166ecf5ac93f7f7c1be7141572c1ce4cc8aae28d526d535b4d",
84+
"to": "0x0f1f8a981160a93da959484216b0b8db0ce2cd8e",
85+
"transactionHash": "0x48614dab7303dda8ecfa52c6f583a3e09b3bc1724fb5a1512c361f2b36ed242d",
86+
"transactionIndex": "0x14",
87+
"type": "0x0"
88+
}"#;
89+
90+
let receipt: TransactionReceipt = serde_json::from_str(RECEIPT).unwrap();
91+
92+
assert_eq!(
93+
receipt,
94+
TransactionReceipt {
95+
block_hash: Hash::from_str(
96+
"0x9b3c1d182975fdaa5797879cbc45d6b00a84fb3b13980a107645b2491bcca899"
97+
)
98+
.unwrap(),
99+
block_number: BlockNumber::new(0x3d08ff),
100+
effective_gas_price: WeiPerGas::new(0x4e3b29200),
101+
cumulative_gas_used: GasAmount::new(0x3a827d),
102+
gas_used: GasAmount::new(0x5208),
103+
status: None,
104+
root: Some(Hash::from_str("0xc13704a89e282b166ecf5ac93f7f7c1be7141572c1ce4cc8aae28d526d535b4d").unwrap()),
105+
transaction_hash: Hash::from_str(
106+
"0x48614dab7303dda8ecfa52c6f583a3e09b3bc1724fb5a1512c361f2b36ed242d"
107+
)
108+
.unwrap(),
109+
contract_address: None,
110+
from: "0xcaf8925d8e825ebe0cb3fc34a2be2c8c737c1ecc".parse().unwrap(),
111+
logs: vec![],
112+
logs_bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse().unwrap(),
113+
to: Some("0x0f1f8a981160a93da959484216b0b8db0ce2cd8e".parse().unwrap()),
114+
transaction_index: 0x14_u32.into(),
115+
tx_type: JsonByte::new(0),
116+
}
117+
)
118+
}
119+
120+
#[test]
121+
fn should_deserialize_post_byzantium_transaction_receipt() {
73122
const RECEIPT: &str = r#"{
74123
"transactionHash": "0x0e59bd032b9b22aca5e2784e4cf114783512db00988c716cf17a1cc755a0a93d",
75124
"blockHash": "0x82005d2f17b251900968f01b0ed482cb49b7e1d797342bc504904d442b64dbe4",
@@ -98,8 +147,10 @@ mod eth_get_transaction_receipt {
98147
.unwrap(),
99148
block_number: BlockNumber::new(0x4132ec),
100149
effective_gas_price: WeiPerGas::new(0xfefbee3e),
150+
cumulative_gas_used: GasAmount::new(0x8b2e10),
101151
gas_used: GasAmount::new(0x5208),
102152
status: Some(TransactionStatus::Success),
153+
root: None,
103154
transaction_hash: Hash::from_str(
104155
"0x0e59bd032b9b22aca5e2784e4cf114783512db00988c716cf17a1cc755a0a93d"
105156
)

tests/tests.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ fn should_decode_address() {
597597
fn should_decode_transaction_receipt() {
598598
let value = evm_rpc_types::TransactionReceipt {
599599
status: Some(0x1_u8.into()),
600+
root: None,
600601
transaction_hash: "0xdd5d4b18923d7aae953c7996d791118102e889bea37b48a651157a4890e4746f"
601602
.parse()
602603
.unwrap(),
@@ -617,6 +618,7 @@ fn should_decode_transaction_receipt() {
617618
.unwrap()),
618619
transaction_index: 0xd9_u16.into(),
619620
tx_type: "0x2".parse().unwrap(),
621+
cumulative_gas_used: 0xf02aed_u64.into(),
620622
};
621623
assert_eq!(
622624
Decode!(&Encode!(&value).unwrap(), evm_rpc_types::TransactionReceipt).unwrap(),
@@ -1038,6 +1040,7 @@ fn eth_get_transaction_receipt_should_succeed() {
10381040
raw_body: json!({"jsonrpc":"2.0","id":0,"result":{"blockHash":"0x5115c07eb1f20a9d6410db0916ed3df626cfdab161d3904f45c8c8b65c90d0be","blockNumber":"0x11a85ab","contractAddress":null,"cumulativeGasUsed":"0xf02aed","effectiveGasPrice":"0x63c00ee76","from":"0x0aa8ebb6ad5a8e499e550ae2c461197624c6e667","gasUsed":"0x7d89","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x1","to":"0x356cfd6e6d0000400000003900b415f80669009e","transactionHash":"0xdd5d4b18923d7aae953c7996d791118102e889bea37b48a651157a4890e4746f","transactionIndex":"0xd9","type":"0x2"}}),
10391041
expected: evm_rpc_types::TransactionReceipt {
10401042
status: Some(0x1_u8.into()),
1043+
root: None,
10411044
transaction_hash: "0xdd5d4b18923d7aae953c7996d791118102e889bea37b48a651157a4890e4746f".parse().unwrap(),
10421045
contract_address: None,
10431046
block_number: 0x11a85ab_u64.into(),
@@ -1050,13 +1053,15 @@ fn eth_get_transaction_receipt_should_succeed() {
10501053
to: Some("0x356cfd6e6d0000400000003900b415f80669009e".parse().unwrap()),
10511054
transaction_index: 0xd9_u16.into(),
10521055
tx_type: "0x2".parse().unwrap(),
1056+
cumulative_gas_used: 0xf02aed_u64.into(),
10531057
},
10541058
},
10551059
TestCase { //first transaction after genesis
10561060
request: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
1057-
raw_body: json!({"jsonrpc":"2.0","id":0,"result":{"transactionHash":"0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060","blockHash":"0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd","blockNumber":"0xb443","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasUsed":"0x5208","root":"0x96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957","contractAddress":null,"cumulativeGasUsed":"0x5208","transactionIndex":"0x0","from":"0xa1e4380a3b1f749673e270229993ee55f35663b4","to":"0x5df9b87991262f6ba471f09758cde1c0fc1de734","type":"0x0","effectiveGasPrice":"0x2d79883d2000","logs":[]}}),
1061+
raw_body: json!({"jsonrpc":"2.0","id":0,"result":{"transactionHash":"0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060","blockHash":"0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd","blockNumber":"0xb443","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasUsed":"0x5208","root":"0x96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957","contractAddress":null,"cumulativeGasUsed":"0x5208","transactionIndex":"0x0","from":"0xa1e4380a3b1f749673e270229993ee55f35663b4","to":"0x5df9b87991262f6ba471f09758cde1c0fc1de734","type":"0x0","effectiveGasPrice":"0x2d79883d2000","logs":[],"root":"0x96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957"}}),
10581062
expected: evm_rpc_types::TransactionReceipt {
10591063
status: None,
1064+
root: Some("0x96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957".parse().unwrap()),
10601065
transaction_hash: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060".parse().unwrap(),
10611066
contract_address: None,
10621067
block_number: 0xb443_u64.into(),
@@ -1069,13 +1074,15 @@ fn eth_get_transaction_receipt_should_succeed() {
10691074
to: Some("0x5df9b87991262f6ba471f09758cde1c0fc1de734".parse().unwrap()),
10701075
transaction_index: 0x0_u16.into(),
10711076
tx_type: "0x0".parse().unwrap(),
1077+
cumulative_gas_used: 0x5208_u64.into(),
10721078
},
10731079
},
10741080
TestCase { //contract creation
10751081
request: "0x2b8e12d42a187ace19c64b47fae0955def8859bf966c345102c6d3a52f28308b",
10761082
raw_body: json!({"jsonrpc":"2.0","id":0,"result":{"transactionHash":"0x2b8e12d42a187ace19c64b47fae0955def8859bf966c345102c6d3a52f28308b","blockHash":"0xd050426a753a7cc4833ba15a5dfcef761fd983f5277230ea8dc700eadd307363","blockNumber":"0x12e64fd","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasUsed":"0x69892","contractAddress":"0x6abda0438307733fc299e9c229fd3cc074bd8cc0","cumulativeGasUsed":"0x3009d2","transactionIndex":"0x17","from":"0xe12e9a6661aeaf57abf95fd060bebb223fbee7dd","to":null,"type":"0x2","effectiveGasPrice":"0x17c01a135","logs":[],"status":"0x1"}}),
10771083
expected: evm_rpc_types::TransactionReceipt {
10781084
status: Some(0x1_u8.into()),
1085+
root: None,
10791086
transaction_hash: "0x2b8e12d42a187ace19c64b47fae0955def8859bf966c345102c6d3a52f28308b".parse().unwrap(),
10801087
contract_address: Some("0x6abda0438307733fc299e9c229fd3cc074bd8cc0".parse().unwrap()),
10811088
block_number: 0x12e64fd_u64.into(),
@@ -1088,6 +1095,7 @@ fn eth_get_transaction_receipt_should_succeed() {
10881095
to: None,
10891096
transaction_index: 0x17_u16.into(),
10901097
tx_type: "0x2".parse().unwrap(),
1098+
cumulative_gas_used: 0x3009d2_u64.into(),
10911099
},
10921100
}
10931101
];

0 commit comments

Comments
 (0)