rosetta_tx_ethereum/
lib.rs

1use anyhow::Result;
2use ethabi::token::{LenientTokenizer, Tokenizer};
3use ethers_core::abi::HumanReadableParser;
4use ethers_core::types::{Eip1559TransactionRequest, NameOrAddress, Signature, H160};
5use rosetta_config_ethereum::{EthereumMetadata, EthereumMetadataParams};
6use rosetta_core::crypto::address::Address;
7use rosetta_core::crypto::SecretKey;
8use rosetta_core::{BlockchainConfig, TransactionBuilder};
9use sha3::{Digest, Keccak256};
10
11pub use ethers_core::types::U256;
12
13#[derive(Default)]
14pub struct EthereumTransactionBuilder;
15
16impl TransactionBuilder for EthereumTransactionBuilder {
17    type MetadataParams = EthereumMetadataParams;
18    type Metadata = EthereumMetadata;
19
20    fn transfer(&self, address: &Address, amount: u128) -> Result<Self::MetadataParams> {
21        let destination: H160 = address.address().parse()?;
22        let amount: U256 = amount.into();
23        Ok(EthereumMetadataParams {
24            destination: destination.0.to_vec(),
25            amount: amount.0,
26            data: vec![],
27        })
28    }
29
30    fn method_call(
31        &self,
32        contract: &str,
33        method: &str,
34        params: &[String],
35        amount: u128,
36    ) -> Result<Self::MetadataParams> {
37        let destination: H160 = contract.parse()?;
38        let amount: U256 = amount.into();
39        let function = HumanReadableParser::parse_function(method)?;
40        let mut tokens = Vec::with_capacity(params.len());
41        for (ty, arg) in function.inputs.iter().zip(params) {
42            tokens.push(LenientTokenizer::tokenize(&ty.kind, arg)?);
43        }
44        let bytes = function.encode_input(&tokens)?;
45        Ok(EthereumMetadataParams {
46            destination: destination.0.to_vec(),
47            amount: amount.0,
48            data: bytes,
49        })
50    }
51
52    fn deploy_contract(&self, contract_binary: Vec<u8>) -> Result<Self::MetadataParams> {
53        Ok(EthereumMetadataParams {
54            destination: vec![],
55            amount: [0, 0, 0, 0],
56            data: contract_binary,
57        })
58    }
59
60    fn create_and_sign(
61        &self,
62        config: &BlockchainConfig,
63        metadata_params: &Self::MetadataParams,
64        metadata: &Self::Metadata,
65        secret_key: &SecretKey,
66    ) -> Vec<u8> {
67        let from = secret_key
68            .public_key()
69            .to_address(config.address_format)
70            .address()
71            .parse()
72            .unwrap();
73        let to: Option<NameOrAddress> = if metadata_params.destination.len() >= 20 {
74            Some(H160::from_slice(&metadata_params.destination).into())
75        } else {
76            None
77        };
78        let tx = Eip1559TransactionRequest {
79            from: Some(from),
80            to,
81            gas: Some(U256(metadata.gas_limit)),
82            value: Some(U256(metadata_params.amount)),
83            data: Some(metadata_params.data.clone().into()),
84            nonce: Some(metadata.nonce.into()),
85            access_list: Default::default(),
86            max_priority_fee_per_gas: Some(U256(metadata.max_priority_fee_per_gas)),
87            max_fee_per_gas: Some(U256(metadata.max_fee_per_gas)),
88            chain_id: Some(metadata.chain_id.into()),
89        };
90        let mut hasher = Keccak256::new();
91        hasher.update([0x02]);
92        hasher.update(tx.rlp());
93        let hash = hasher.finalize();
94        let signature = secret_key.sign_prehashed(&hash).unwrap().to_bytes();
95        let rlp = tx.rlp_signed(&Signature {
96            r: U256::from_big_endian(&signature[..32]),
97            s: U256::from_big_endian(&signature[32..64]),
98            v: signature[64] as _,
99        });
100        let mut tx = Vec::with_capacity(rlp.len() + 1);
101        tx.push(0x02);
102        tx.extend(rlp);
103        tx
104    }
105}