ethers_eip2718/
eip1559.rs

1use ethers_primitives::*;
2
3use serde::{Deserialize, Serialize};
4use serde_ethrlp::RlpEncoder;
5
6use super::{keccak256, AccessList, H256};
7
8#[derive(Debug, Serialize, Deserialize, Clone)]
9#[serde(rename_all = "camelCase")]
10pub struct Eip1559TransactionRequest {
11    pub chain_id: U256,
12
13    /// Transaction nonce
14    pub nonce: U256,
15    /// Gas price
16    pub max_priority_fee_per_gas: U256,
17
18    pub max_fee_per_gas: U256,
19    /// Supplied gas
20    pub gas: U256,
21    /// Recipient address (None for contract creation)
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub to: Option<Address>,
24    /// Transferred value
25    pub value: Option<U256>,
26    /// The compiled code of a contract OR the first 4 bytes of the hash of the
27    /// invoked method signature and encoded parameters. For details see Ethereum Contract ABI
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub data: Option<Bytes>,
30
31    pub access_list: AccessList,
32}
33
34impl Eip1559TransactionRequest {
35    /// Generate legacy transaction sign hash.
36    pub fn sign_hash(&self) -> anyhow::Result<H256> {
37        Ok(keccak256(self.rlp()?.0).into())
38    }
39
40    pub fn rlp(&self) -> anyhow::Result<Bytes> {
41        let mut s = RlpEncoder::default();
42        (
43            &self.chain_id,
44            &self.nonce,
45            &self.max_priority_fee_per_gas,
46            &self.max_fee_per_gas,
47            &self.gas,
48            &self.to,
49            &self.value,
50            &self.data,
51            &self.access_list,
52        )
53            .serialize(&mut s)?;
54
55        let mut buff = vec![0x02u8];
56
57        let mut append = s.finalize()?.into();
58
59        buff.append(&mut append);
60
61        Ok(buff.into())
62    }
63
64    /// Returns signed tx rlp encoding stream.
65    pub fn rlp_signed(&self, signature: Eip1559Signature) -> anyhow::Result<Bytes> {
66        let mut s = RlpEncoder::default();
67
68        (
69            &self.chain_id,
70            &self.nonce,
71            &self.max_priority_fee_per_gas,
72            &self.max_fee_per_gas,
73            &self.gas,
74            &self.to,
75            &self.value,
76            &self.data,
77            &self.access_list,
78            signature.v,
79            signature.r,
80            signature.s,
81        )
82            .serialize(&mut s)?;
83
84        let mut buff = vec![0x02u8];
85
86        let mut append = s.finalize()?.into();
87
88        buff.append(&mut append);
89
90        Ok(buff.into())
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use ethers_primitives::Eip1559Signature;
97    use serde_json::json;
98
99    use crate::Eip1559TransactionRequest;
100
101    #[test]
102    fn test_rlp() {
103        let tx = json!({
104          "maxPriorityFeePerGas": "0x0",
105          "maxFeePerGas": "0x0",
106          "gas": "0x0",
107          "nonce": "0x0",
108          "to": null,
109          "value": "0x0",
110          "chainId": "0x1",
111          "type": "0x02",
112          "data": "0x00",
113          "accessList": [
114            {
115              "address": "0x0000000000000000000000000000000000000000",
116              "storageKeys": [
117                "0x0000000000000000000000000000000000000000000000000000000000000000"
118              ]
119            },
120            {
121              "address": "0x0000000000000000000000000000000000000000",
122              "storageKeys": [
123                "0x0000000000000000000000000000000000000000000000000000000000000000"
124              ]
125            }
126          ]
127        });
128
129        let tx: Eip1559TransactionRequest = serde_json::from_value(tx).unwrap();
130
131        assert_eq!(
132            tx.rlp().unwrap().to_string(),
133            "0x02f87a0180808080808000f870f7940000000000000000000000000000000000000000e1a00000000000000000000000000000000000000000000000000000000000000000f7940000000000000000000000000000000000000000e1a00000000000000000000000000000000000000000000000000000000000000000"
134        );
135
136        let sig: Eip1559Signature = "0x007a53fb20b46d9cc2600d8dc3168a698d41c0dec029d46db4ba88ffe359bbe4092536bd58c593edcda36c5f2e35ed4db158b0cab202b6b2648403117e483a9b30".parse().unwrap();
137
138        assert_eq!(
139            tx.rlp_signed(sig).unwrap().to_string(),
140            "0x02f8bd0180808080808000f870f7940000000000000000000000000000000000000000e1a00000000000000000000000000000000000000000000000000000000000000000f7940000000000000000000000000000000000000000e1a0000000000000000000000000000000000000000000000000000000000000000080a07a53fb20b46d9cc2600d8dc3168a698d41c0dec029d46db4ba88ffe359bbe409a02536bd58c593edcda36c5f2e35ed4db158b0cab202b6b2648403117e483a9b30"
141        );
142    }
143}