cfx_rpc_eth_types/
transaction.rs

1// Copyright 2019-2021 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5// Copyright 2015-2020 Parity Technologies (UK) Ltd.
6// This file is part of OpenEthereum.
7
8// OpenEthereum is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12
13// OpenEthereum is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16// GNU General Public License for more details.
17
18// You should have received a copy of the GNU General Public License
19// along with OpenEthereum.  If not, see <http://www.gnu.org/licenses/>.
20
21use crate::{Bytes, SignedAuthorization};
22use cfx_rpc_cfx_types::Transaction as CfxTransaction;
23use cfx_types::{H160, H256, H512, U256, U64};
24use primitives::{
25    transaction::eth_transaction::eip155_signature, AccessList, Action,
26    SignedTransaction,
27};
28use rlp::Encodable;
29use serde::{Deserialize, Serialize};
30
31/// Transaction
32#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
33#[serde(rename_all = "camelCase")]
34pub struct Transaction {
35    /// transaction type
36    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
37    pub transaction_type: Option<U64>,
38    /// Hash
39    pub hash: H256,
40    /// Nonce
41    pub nonce: U256,
42    /// Block hash
43    pub block_hash: Option<H256>,
44    /// Block number
45    pub block_number: Option<U256>,
46    /// Transaction Index
47    pub transaction_index: Option<U256>,
48    /// Sender
49    pub from: H160,
50    /// Recipient
51    pub to: Option<H160>,
52    /// Transferred value
53    pub value: U256,
54    /// Gas Price
55    pub gas_price: Option<U256>,
56    /// Max fee per gas
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub max_fee_per_gas: Option<U256>,
59    /// Gas
60    pub gas: U256,
61    /// Data
62    pub input: Bytes,
63    /// Creates contract
64    pub creates: Option<H160>,
65    /// Raw transaction data
66    pub raw: Option<Bytes>,
67    /// Public key of the signer.
68    pub public_key: Option<H512>,
69    /// The network id of the transaction, if any.
70    pub chain_id: Option<U64>,
71    /// The standardised V field of the signature (0 or 1). Used by legacy
72    /// transaction
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub standard_v: Option<U256>,
75    /// The standardised V field of the signature.
76    pub v: U64,
77    /// The R field of the signature.
78    pub r: U256,
79    /// The S field of the signature.
80    pub s: U256,
81    // Whether tx is success
82    pub status: Option<U64>,
83    /// Optional access list
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub access_list: Option<AccessList>,
86    /// miner bribe
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub max_priority_fee_per_gas: Option<U256>,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub y_parity: Option<U64>,
91    /* /// Transaction activates at specified block.
92     * pub condition: Option<TransactionCondition>, */
93    /// eip7702 authorization list
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub authorization_list: Option<Vec<SignedAuthorization>>,
96}
97
98impl Transaction {
99    /// Convert `SignedTransaction` into RPC Transaction.
100    pub fn from_signed(
101        t: &SignedTransaction,
102        block_info: (Option<H256>, Option<U256>, Option<U256>),
103        exec_info: (Option<U64>, Option<H160>),
104    ) -> Transaction {
105        let signature = t.signature();
106
107        // for 2718 tx, the v should be equal to the signature.v and y_parity
108        let (v, y_parity) = if t.is_2718() {
109            (U64::from(signature.v()), Some(U64::from(signature.v())))
110        } else {
111            (
112                eip155_signature::add_chain_replay_protection(
113                    signature.v(),
114                    t.chain_id().map(|x| x as u64),
115                )
116                .into(),
117                None,
118            )
119        };
120
121        // for phantom tx, it's r and s are set to 'tx.from', which lead some
122        // txs r and s to 0 which is not valid in some ethereum tools,
123        // so we set them to chain_id
124        let mut r: U256 = signature.r().into();
125        let mut s: U256 = signature.s().into();
126        if r == U256::zero() || s == U256::zero() {
127            let chain_id = t
128                .chain_id()
129                .map(|x| U256::from(x as u64))
130                .expect("should have chain_id");
131            r = chain_id;
132            s = chain_id;
133        }
134
135        Transaction {
136            hash: t.hash(),
137            nonce: *t.nonce(),
138            block_hash: block_info.0,
139            block_number: block_info.1,
140            transaction_index: block_info.2,
141            from: t.sender().address,
142            to: match t.action() {
143                Action::Create => None,
144                Action::Call(ref address) => Some(*address),
145            },
146            value: *t.value(),
147            gas_price: Some(*t.gas_price()),
148            gas: *t.gas(),
149            input: Bytes::new(t.data().clone()),
150            creates: exec_info.1,
151            raw: Some(Bytes::new(t.transaction.transaction.rlp_bytes())),
152            public_key: t.public().map(Into::into),
153            chain_id: t.chain_id().map(|x| U64::from(x as u64)),
154            standard_v: Some(signature.v().into()),
155            v,
156            r,
157            s,
158            status: exec_info.0,
159            access_list: t.access_list().cloned(),
160            max_fee_per_gas: t.after_1559().then_some(*t.gas_price()),
161            max_priority_fee_per_gas: t
162                .after_1559()
163                .then_some(*t.max_priority_gas_price()),
164            y_parity,
165            transaction_type: Some(U64::from(t.type_id())),
166            authorization_list: t.authorization_list().map(|list| {
167                list.iter()
168                    .map(|item| SignedAuthorization::from(item.clone()))
169                    .collect()
170            }),
171        }
172    }
173
174    pub fn deployed_contract_address(t: &SignedTransaction) -> Option<H160> {
175        t.cal_created_address().map(|t| t.address)
176    }
177}
178
179#[derive(Debug, Clone, PartialEq, Serialize)]
180#[serde(rename_all = "camelCase")]
181pub enum WrapTransaction {
182    NativeTransaction(CfxTransaction),
183    EthTransaction(Transaction),
184}