anychain_ethereum/
transaction.rs

1use crate::util::{adapt2, pad_zeros, restore_sender, trim_leading_zeros};
2use crate::{EthereumAddress, EthereumFormat, EthereumNetwork, EthereumPublicKey};
3use anychain_core::{
4    hex, utilities::crypto::keccak256, Transaction, TransactionError, TransactionId,
5};
6use core::{fmt, marker::PhantomData, str::FromStr};
7use ethabi::{ethereum_types::H160, Function, Param, ParamType, StateMutability, Token};
8use ethereum_types::U256;
9use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
10use serde_json::{json, Value};
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct EthereumTransactionParameters {
14    pub nonce: U256,
15    pub gas_price: U256,
16    pub gas_limit: U256,
17    pub to: EthereumAddress,
18    pub amount: U256,
19    pub data: Vec<u8>,
20}
21
22impl EthereumTransactionParameters {
23    pub fn to_rlp(&self) -> Result<RlpStream, TransactionError> {
24        let to = self
25            .to
26            .to_bytes()
27            .map_err(|e| TransactionError::Message(format!("{}", e)))?;
28
29        let mut rlp = RlpStream::new();
30        rlp.begin_list(9);
31
32        rlp.append(&self.nonce);
33        rlp.append(&self.gas_price);
34        rlp.append(&self.gas_limit);
35        rlp.append(&to);
36        rlp.append(&self.amount);
37        rlp.append(&self.data);
38
39        Ok(rlp)
40    }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub struct EthereumTransactionSignature {
45    pub v: u32,
46    pub r: Vec<u8>,
47    pub s: Vec<u8>,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Hash)]
51pub struct EthereumTransaction<N: EthereumNetwork> {
52    /// The address of the sender
53    pub sender: Option<EthereumAddress>,
54    /// The transaction parameters (gas, gas_price, nonce, data)
55    pub params: EthereumTransactionParameters,
56    /// The transaction signature
57    pub signature: Option<EthereumTransactionSignature>,
58    _network: PhantomData<N>,
59}
60
61impl<N: EthereumNetwork> EthereumTransaction<N> {
62    pub fn restore_sender(&mut self) -> Result<(), TransactionError> {
63        if self.signature.is_none() {
64            return Err(TransactionError::Message(
65                "Signature is missing".to_string(),
66            ));
67        }
68
69        let sig = self.signature.clone().unwrap();
70        self.signature = None;
71
72        let r = sig.r.clone();
73        let s = sig.s.clone();
74
75        let recid = (sig.v - 2 * N::CHAIN_ID - 35) as u8;
76
77        let _sig = [r, s].concat();
78        let msg = self.to_transaction_id()?.txid;
79
80        let sender = restore_sender(msg, _sig, recid)?;
81
82        self.sender = Some(sender);
83        self.signature = Some(sig);
84
85        Ok(())
86    }
87}
88
89impl<N: EthereumNetwork> Transaction for EthereumTransaction<N> {
90    type Address = EthereumAddress;
91    type Format = EthereumFormat;
92    type PublicKey = EthereumPublicKey;
93    type TransactionId = EthereumTransactionId;
94    type TransactionParameters = EthereumTransactionParameters;
95
96    fn new(params: &Self::TransactionParameters) -> Result<Self, TransactionError> {
97        Ok(Self {
98            sender: None,
99            params: params.clone(),
100            signature: None,
101            _network: PhantomData,
102        })
103    }
104
105    fn sign(&mut self, rs: Vec<u8>, recid: u8) -> Result<Vec<u8>, TransactionError> {
106        if rs.len() != 64 {
107            return Err(TransactionError::Message(format!(
108                "Invalid signature length: {}",
109                rs.len()
110            )));
111        }
112        let v = 2 * N::CHAIN_ID + 35 + (recid as u32);
113        let r = rs[..32].to_vec();
114        let s = rs[32..].to_vec();
115        self.signature = Some(EthereumTransactionSignature { v, r, s });
116        self.to_bytes()
117    }
118
119    fn to_bytes(&self) -> Result<Vec<u8>, TransactionError> {
120        match &self.signature {
121            Some(sig) => {
122                let mut rlp = self.params.to_rlp()?;
123                let r = trim_leading_zeros(&sig.r);
124                let s = trim_leading_zeros(&sig.s);
125                rlp.append(&sig.v);
126                rlp.append(&r);
127                rlp.append(&s);
128                Ok(rlp.out().to_vec())
129            }
130            None => {
131                let mut rlp = self.params.to_rlp()?;
132                rlp.append(&N::CHAIN_ID);
133                rlp.append(&0u8);
134                rlp.append(&0u8);
135                Ok(rlp.out().to_vec())
136            }
137        }
138    }
139
140    fn from_bytes(tx: &[u8]) -> Result<Self, TransactionError> {
141        let rlp = Rlp::new(tx);
142
143        let to = adapt2(rlp.val_at::<Vec<u8>>(3))?;
144        let to = hex::encode(to);
145
146        let nonce = adapt2(rlp.val_at::<U256>(0))?;
147        let gas_price = adapt2(rlp.val_at::<U256>(1))?;
148        let gas_limit = adapt2(rlp.val_at::<U256>(2))?;
149        let to = EthereumAddress::from_str(&to)?;
150        let amount = adapt2(rlp.val_at::<U256>(4))?;
151        let data = adapt2(rlp.val_at::<Vec<u8>>(5))?;
152
153        let v = adapt2(rlp.val_at::<u32>(6))?;
154        let mut r = adapt2(rlp.val_at::<Vec<u8>>(7))?;
155        let mut s = adapt2(rlp.val_at::<Vec<u8>>(8))?;
156
157        let params = EthereumTransactionParameters {
158            nonce,
159            gas_price,
160            gas_limit,
161            to,
162            amount,
163            data,
164        };
165
166        let mut tx = EthereumTransaction::<N>::new(&params)?;
167
168        if !r.is_empty() && !s.is_empty() {
169            pad_zeros(&mut r, 32);
170            pad_zeros(&mut s, 32);
171            let sig = EthereumTransactionSignature { v, r, s };
172            tx.signature = Some(sig);
173            tx.restore_sender()?;
174        }
175
176        Ok(tx)
177    }
178
179    fn to_transaction_id(&self) -> Result<Self::TransactionId, TransactionError> {
180        Ok(Self::TransactionId {
181            txid: keccak256(&self.to_bytes()?).to_vec(),
182        })
183    }
184}
185
186impl<N: EthereumNetwork> FromStr for EthereumTransaction<N> {
187    type Err = TransactionError;
188
189    fn from_str(tx: &str) -> Result<Self, Self::Err> {
190        let tx = match &tx[..2] {
191            "0x" => &tx[2..],
192            _ => tx,
193        };
194        Self::from_bytes(&hex::decode(tx)?)
195    }
196}
197
198impl<N: EthereumNetwork> fmt::Display for EthereumTransaction<N> {
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        write!(
201            f,
202            "0x{}",
203            &hex::encode(match self.to_bytes() {
204                Ok(transaction) => transaction,
205                _ => return Err(fmt::Error),
206            })
207        )
208    }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Hash)]
212pub struct Eip1559TransactionParameters {
213    pub chain_id: u32,
214    pub nonce: U256,
215    pub max_priority_fee_per_gas: U256,
216    pub max_fee_per_gas: U256,
217    pub gas_limit: U256,
218    pub to: EthereumAddress,
219    pub amount: U256,
220    pub data: Vec<u8>,
221    pub access_list: Vec<AccessItem>,
222}
223
224impl Eip1559TransactionParameters {
225    pub fn to_rlp(&self, array_len: usize) -> Result<RlpStream, TransactionError> {
226        let to = self
227            .to
228            .to_bytes()
229            .map_err(|e| TransactionError::Message(format!("{}", e)))?;
230
231        let mut rlp = RlpStream::new();
232        rlp.begin_list(array_len);
233
234        rlp.append(&self.chain_id);
235        rlp.append(&self.nonce);
236        rlp.append(&self.max_priority_fee_per_gas);
237        rlp.append(&self.max_fee_per_gas);
238        rlp.append(&self.gas_limit);
239        rlp.append(&to);
240        rlp.append(&self.amount);
241        rlp.append(&self.data);
242        rlp.append_list(&self.access_list);
243
244        Ok(rlp)
245    }
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
249pub struct Eip1559TransactionSignature {
250    pub y_parity: bool,
251    pub r: Vec<u8>,
252    pub s: Vec<u8>,
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
256pub struct AccessItem {
257    pub address: EthereumAddress,
258    pub storage_keys: Vec<Vec<u8>>,
259}
260
261impl Encodable for AccessItem {
262    fn rlp_append(&self, s: &mut RlpStream) {
263        s.begin_list(2);
264        s.append(&self.address.to_bytes().unwrap());
265        s.append_list::<Vec<u8>, Vec<u8>>(&self.storage_keys);
266    }
267}
268
269impl Decodable for AccessItem {
270    fn decode(rlp: &rlp::Rlp) -> Result<Self, DecoderError> {
271        let address = hex::encode(rlp.val_at::<Vec<u8>>(0)?);
272        let address = EthereumAddress::from_str(&address).unwrap();
273        let storage_keys = rlp.list_at::<Vec<u8>>(1)?;
274        Ok(Self {
275            address,
276            storage_keys,
277        })
278    }
279}
280
281#[derive(Debug, Clone, PartialEq, Eq, Hash)]
282pub struct Eip1559Transaction<N: EthereumNetwork> {
283    pub sender: Option<EthereumAddress>,
284    pub params: Eip1559TransactionParameters,
285    pub signature: Option<Eip1559TransactionSignature>,
286    _network: PhantomData<N>,
287}
288
289impl<N: EthereumNetwork> Eip1559Transaction<N> {
290    pub fn restore_sender(&mut self) -> Result<(), TransactionError> {
291        if self.signature.is_none() {
292            return Err(TransactionError::Message(
293                "Signature is missing".to_string(),
294            ));
295        }
296
297        let sig = self.signature.clone().unwrap();
298        self.signature = None;
299
300        let recid = match sig.y_parity {
301            true => 1,
302            false => 0,
303        } as u8;
304
305        let _sig = [sig.r.clone(), sig.s.clone()].concat();
306        let msg = self.to_transaction_id()?.txid;
307
308        let sender = restore_sender(msg, _sig, recid)?;
309
310        self.sender = Some(sender);
311        self.signature = Some(sig);
312
313        Ok(())
314    }
315}
316
317impl<N: EthereumNetwork> Transaction for Eip1559Transaction<N> {
318    type Address = EthereumAddress;
319    type Format = EthereumFormat;
320    type PublicKey = EthereumPublicKey;
321    type TransactionId = EthereumTransactionId;
322    type TransactionParameters = Eip1559TransactionParameters;
323
324    fn new(params: &Self::TransactionParameters) -> Result<Self, TransactionError> {
325        Ok(Self {
326            sender: None,
327            params: params.clone(),
328            signature: None,
329            _network: PhantomData,
330        })
331    }
332
333    fn sign(&mut self, rs: Vec<u8>, recid: u8) -> Result<Vec<u8>, TransactionError> {
334        if rs.len() != 64 {
335            return Err(TransactionError::Message(format!(
336                "Invalid signature length: {}",
337                rs.len()
338            )));
339        }
340        let y_parity = recid == 1;
341        let r = rs[..32].to_vec();
342        let s = rs[32..].to_vec();
343        self.signature = Some(Eip1559TransactionSignature { y_parity, r, s });
344        self.to_bytes()
345    }
346
347    fn to_bytes(&self) -> Result<Vec<u8>, TransactionError> {
348        let rlp = match &self.signature {
349            Some(sig) => {
350                let mut rlp = self.params.to_rlp(12)?;
351                let r = trim_leading_zeros(&sig.r);
352                let s = trim_leading_zeros(&sig.s);
353                rlp.append(&sig.y_parity);
354                rlp.append(&r);
355                rlp.append(&s);
356                rlp.out().to_vec()
357            }
358            None => self.params.to_rlp(9)?.out().to_vec(),
359        };
360        Ok([vec![2u8], rlp].concat())
361    }
362
363    fn from_bytes(tx: &[u8]) -> Result<Self, TransactionError> {
364        let rlp = Rlp::new(&tx[1..]);
365
366        let to = adapt2(rlp.val_at::<Vec<u8>>(5))?;
367        let to = hex::encode(to);
368
369        let chain_id = adapt2(rlp.val_at::<u32>(0))?;
370        let nonce = adapt2(rlp.val_at::<U256>(1))?;
371        let max_priority_fee_per_gas = adapt2(rlp.val_at::<U256>(2))?;
372        let max_fee_per_gas = adapt2(rlp.val_at::<U256>(3))?;
373        let gas_limit = adapt2(rlp.val_at::<U256>(4))?;
374        let to = EthereumAddress::from_str(&to)?;
375        let amount = adapt2(rlp.val_at::<U256>(6))?;
376        let data = adapt2(rlp.val_at::<Vec<u8>>(7))?;
377        let access_list = adapt2(rlp.list_at::<AccessItem>(8))?;
378
379        let y_parity = adapt2(rlp.val_at::<bool>(9))?;
380        let mut r = adapt2(rlp.val_at::<Vec<u8>>(10))?;
381        let mut s = adapt2(rlp.val_at::<Vec<u8>>(11))?;
382
383        let params = Eip1559TransactionParameters {
384            chain_id,
385            nonce,
386            max_priority_fee_per_gas,
387            max_fee_per_gas,
388            gas_limit,
389            to,
390            amount,
391            data,
392            access_list,
393        };
394
395        let mut tx = Eip1559Transaction::<N>::new(&params)?;
396
397        if !r.is_empty() && !s.is_empty() {
398            pad_zeros(&mut r, 32);
399            pad_zeros(&mut s, 32);
400            let sig = Eip1559TransactionSignature { y_parity, r, s };
401            tx.signature = Some(sig);
402            tx.restore_sender()?;
403        }
404
405        Ok(tx)
406    }
407
408    fn to_transaction_id(&self) -> Result<Self::TransactionId, TransactionError> {
409        Ok(Self::TransactionId {
410            txid: keccak256(&self.to_bytes()?).to_vec(),
411        })
412    }
413}
414
415impl<N: EthereumNetwork> FromStr for Eip1559Transaction<N> {
416    type Err = TransactionError;
417
418    fn from_str(tx: &str) -> Result<Self, Self::Err> {
419        let tx = match &tx[..2] {
420            "0x" => &tx[2..],
421            _ => tx,
422        };
423        Self::from_bytes(&hex::decode(tx)?)
424    }
425}
426
427impl<N: EthereumNetwork> fmt::Display for Eip1559Transaction<N> {
428    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
429        write!(
430            f,
431            "0x{}",
432            &hex::encode(match self.to_bytes() {
433                Ok(transaction) => transaction,
434                _ => return Err(fmt::Error),
435            })
436        )
437    }
438}
439
440#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
441pub struct EthereumTransactionId {
442    pub txid: Vec<u8>,
443}
444
445impl TransactionId for EthereumTransactionId {}
446
447impl fmt::Display for EthereumTransactionId {
448    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449        write!(f, "0x{}", hex::encode(&self.txid))
450    }
451}
452
453pub fn encode_transfer(func_name: &str, address: &EthereumAddress, amount: U256) -> Vec<u8> {
454    #[allow(deprecated)]
455    let func = Function {
456        name: func_name.to_string(),
457        inputs: vec![
458            Param {
459                name: "address".to_string(),
460                kind: ParamType::Address,
461                internal_type: None,
462            },
463            Param {
464                name: "amount".to_string(),
465                kind: ParamType::Uint(256),
466                internal_type: None,
467            },
468        ],
469        outputs: vec![],
470        constant: None,
471        state_mutability: StateMutability::Payable,
472    };
473
474    let tokens = vec![
475        Token::Address(H160::from_slice(&address.to_bytes().unwrap())),
476        Token::Uint(amount),
477    ];
478
479    func.encode_input(&tokens).unwrap()
480}
481
482pub fn decode_transfer(data: Vec<u8>) -> Result<Value, TransactionError> {
483    if data.len() < 4 {
484        return Err(TransactionError::Message("Illegal data".to_string()));
485    }
486
487    let selector = &data[..4];
488
489    match selector {
490        // function selector for 'transfer(address,uint256)'
491        [169, 5, 156, 187] => {
492            #[allow(deprecated)]
493            let func = Function {
494                name: "transfer".to_string(),
495                inputs: vec![
496                    Param {
497                        name: "to".to_string(),
498                        kind: ParamType::Address,
499                        internal_type: None,
500                    },
501                    Param {
502                        name: "amount".to_string(),
503                        kind: ParamType::Uint(256),
504                        internal_type: None,
505                    },
506                ],
507                outputs: vec![],
508                constant: None,
509                state_mutability: StateMutability::Payable,
510            };
511            match func.decode_input(&data[4..]) {
512                Ok(tokens) => {
513                    let to = hex::encode(tokens[0].clone().into_address().unwrap().as_bytes());
514                    let amount = tokens[1].clone().into_uint().unwrap();
515                    Ok(json!({
516                        "function": "transfer",
517                        "params": {
518                            "to": to,
519                            "amount": amount.to_string(),
520                        }
521                    }))
522                }
523                Err(e) => Err(TransactionError::Message(e.to_string())),
524            }
525        }
526        _ => Err(TransactionError::Message(
527            "Unsupported contract function".to_string(),
528        )),
529    }
530}
531
532// mod tests {
533//     use super::*;
534//     use crate::Sepolia;
535
536//     #[test]
537//     fn test_legacy_tx() {
538//         let params = EthereumTransactionParameters {
539//             nonce: U256::from_dec_str("6").unwrap(),
540//             gas_price: U256::from_dec_str("20000000000").unwrap(),
541//             gas_limit: U256::from_dec_str("21000").unwrap(),
542//             to: EthereumAddress::from_str("0xf7a63003b8ef116939804b4c2dd49290a39c4d97").unwrap(),
543//             amount: U256::from_dec_str("10000000000000000").unwrap(),
544//             data: vec![],
545//         };
546//         let mut tx = EthereumTransaction::<Sepolia>::new(&params).unwrap();
547//         let msg = tx.to_transaction_id().unwrap().txid;
548//         let msg = libsecp256k1::Message::parse_slice(&msg).unwrap();
549
550//         let sk = "08d586ed207046d6476f92fd4852be3830a9d651fc148d6fa5a6f15b77ba5df0";
551//         let sk = hex::decode(sk).unwrap();
552//         let sk = libsecp256k1::SecretKey::parse_slice(&sk).unwrap();
553
554//         let (sig, recid) = libsecp256k1::sign(&msg, &sk);
555
556//         let sig = sig.serialize().to_vec();
557//         let recid = recid.serialize();
558
559//         let _ = tx.sign(sig, recid);
560
561//         println!("{}", tx);
562//     }
563
564//     #[test]
565//     fn test_eip1559_tx() {
566//         let params = Eip1559TransactionParameters {
567//             chain_id: Sepolia::CHAIN_ID,
568//             nonce: U256::from_dec_str("4").unwrap(),
569//             max_priority_fee_per_gas: U256::from_dec_str("100000000000").unwrap(),
570//             max_fee_per_gas: U256::from_dec_str("200000000000").unwrap(),
571//             gas_limit: U256::from_dec_str("21000").unwrap(),
572//             to: EthereumAddress::from_str("0xf7a63003b8ef116939804b4c2dd49290a39c4d97").unwrap(),
573//             amount: U256::from_dec_str("10000000000000000").unwrap(),
574//             data: vec![],
575//             access_list: vec![],
576//         };
577//         let mut tx = Eip1559Transaction::<Sepolia>::new(&params).unwrap();
578//         let msg = tx.to_transaction_id().unwrap().txid;
579//         let msg = libsecp256k1::Message::parse_slice(&msg).unwrap();
580
581//         let sk = "08d586ed207046d6476f92fd4852be3830a9d651fc148d6fa5a6f15b77ba5df0";
582//         let sk = hex::decode(sk).unwrap();
583//         let sk = libsecp256k1::SecretKey::parse_slice(&sk).unwrap();
584
585//         let (sig, recid) = libsecp256k1::sign(&msg, &sk);
586//         let sig = sig.serialize().to_vec();
587//         let recid = recid.serialize();
588
589//         let _ = tx.sign(sig, recid);
590
591//         println!("{}", tx);
592//     }
593
594//     #[test]
595//     fn test() {
596//         let tx = "0x02f87683aa36a70485174876e800852e90edd00082520894f7a63003b8ef116939804b4c2dd49290a39c4d97872386f26fc1000080c001a077233c9ef0d1a3211f844865172aa31ef716b98f4d82e7c86c2cd7050455e243a0678fdd0f3dd4e0bce65642e24368fa22bb34a8b4542c6bcac55e943c051dbb56";
597//         let tx = Eip1559Transaction::<Sepolia>::from_str(tx).unwrap();
598//         println!("{}", tx);
599//     }
600// }