avalanche_types/evm/eip1559/
mod.rs1use std::io::{self, Error, ErrorKind};
3
4use ethers::prelude::Eip1559TransactionRequest;
5use ethers_core::types::{transaction::eip2718::TypedTransaction, RecoveryMessage, Signature};
6use primitive_types::{H160, H256, U256};
7
8#[derive(Clone, Debug)]
10pub struct Transaction {
11 pub chain_id: u64,
12 pub signer_nonce: Option<U256>,
13 pub max_priority_fee_per_gas: Option<U256>,
14 pub max_fee_per_gas: Option<U256>,
15 pub gas_limit: Option<U256>,
16
17 pub from: H160,
21 pub recipient: Option<H160>,
22
23 pub value: Option<U256>,
24 pub data: Option<Vec<u8>>,
25}
26
27impl Transaction {
28 pub fn new() -> Self {
29 Self {
30 chain_id: 0,
31 signer_nonce: None,
32
33 max_priority_fee_per_gas: None,
34 max_fee_per_gas: None,
35 gas_limit: None,
36
37 from: H160::zero(),
38 recipient: None,
39 value: None,
40 data: None,
41 }
42 }
43
44 #[must_use]
45 pub fn chain_id(mut self, chain_id: impl Into<u64>) -> Self {
46 self.chain_id = chain_id.into();
47 self
48 }
49
50 #[must_use]
51 pub fn signer_nonce(mut self, signer_nonce: impl Into<U256>) -> Self {
52 self.signer_nonce = Some(signer_nonce.into());
53 self
54 }
55
56 #[must_use]
57 pub fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: impl Into<U256>) -> Self {
58 self.max_priority_fee_per_gas = Some(max_priority_fee_per_gas.into());
59 self
60 }
61
62 #[must_use]
63 pub fn max_fee_per_gas(mut self, max_fee_per_gas: impl Into<U256>) -> Self {
64 self.max_fee_per_gas = Some(max_fee_per_gas.into());
65 self
66 }
67
68 #[must_use]
69 pub fn gas_limit(mut self, gas_limit: impl Into<U256>) -> Self {
70 self.gas_limit = Some(gas_limit.into());
71 self
72 }
73
74 #[must_use]
75 pub fn from(mut self, from: impl Into<H160>) -> Self {
76 self.from = from.into();
77 self
78 }
79
80 #[must_use]
81 pub fn recipient(mut self, to: impl Into<H160>) -> Self {
82 self.recipient = Some(to.into());
83 self
84 }
85
86 #[must_use]
87 pub fn value(mut self, value: impl Into<U256>) -> Self {
88 self.value = Some(value.into());
89 self
90 }
91
92 #[must_use]
93 pub fn data(mut self, data: impl Into<Vec<u8>>) -> Self {
94 self.data = Some(data.into());
95 self
96 }
97
98 pub async fn sign_as_typed_transaction(
102 &self,
103 eth_signer: impl ethers_signers::Signer + Clone,
104 ) -> io::Result<ethers_core::types::Bytes> {
105 let mut tx_request = Eip1559TransactionRequest::new()
106 .from(ethers::prelude::H160::from(self.from.as_fixed_bytes()))
107 .chain_id(ethers::prelude::U64::from(self.chain_id));
108
109 if let Some(signer_nonce) = self.signer_nonce {
110 tx_request = tx_request.nonce(signer_nonce);
111 }
112
113 if let Some(to) = &self.recipient {
114 tx_request = tx_request.to(ethers::prelude::H160::from(to.as_fixed_bytes()));
115 }
116
117 if let Some(value) = &self.value {
118 let converted: ethers::prelude::U256 = value.into();
119 tx_request = tx_request.value(converted);
120 }
121
122 if let Some(max_priority_fee_per_gas) = &self.max_priority_fee_per_gas {
123 let converted: ethers::prelude::U256 = max_priority_fee_per_gas.into();
124 tx_request = tx_request.max_priority_fee_per_gas(converted);
125 }
126
127 if let Some(max_fee_per_gas) = &self.max_fee_per_gas {
128 let converted: ethers::prelude::U256 = max_fee_per_gas.into();
129 tx_request = tx_request.max_fee_per_gas(converted);
130 }
131
132 if let Some(gas_limit) = &self.gas_limit {
133 let converted: ethers::prelude::U256 = gas_limit.into();
134 tx_request = tx_request.gas(converted);
135 }
136
137 if let Some(data) = &self.data {
138 tx_request = tx_request.data(data.clone());
139 }
140
141 let tx: TypedTransaction = tx_request.into();
142 let sig = eth_signer.sign_transaction(&tx).await.map_err(|e| {
143 Error::new(
144 ErrorKind::Other,
145 format!("failed to sign_transaction '{}'", e),
146 )
147 })?;
148
149 Ok(tx.rlp_signed(&sig))
150 }
151}
152
153impl Default for Transaction {
154 fn default() -> Self {
155 Self::new()
156 }
157}
158
159pub fn decode_signed_rlp(b: impl AsRef<[u8]>) -> io::Result<(TypedTransaction, Signature)> {
162 let r = rlp::Rlp::new(b.as_ref());
163 TypedTransaction::decode_signed(&r)
164 .map_err(|e| Error::new(ErrorKind::Other, format!("failed decode_signed '{}'", e)))
165}
166
167pub fn decode_and_verify_signed_rlp(
172 b: impl AsRef<[u8]>,
173) -> io::Result<(TypedTransaction, H256, H160, Signature)> {
174 let r = rlp::Rlp::new(b.as_ref());
175 let (decoded_tx, sig) = TypedTransaction::decode_signed(&r)
176 .map_err(|e| Error::new(ErrorKind::Other, format!("failed decode_signed '{}'", e)))?;
177
178 let tx_hash = decoded_tx.sighash();
179 log::debug!("decoded signed transaction hash: 0x{:x}", tx_hash);
180
181 let signer_addr = sig.recover(RecoveryMessage::Hash(tx_hash)).map_err(|e| {
182 Error::new(
183 ErrorKind::Other,
184 format!(
185 "failed to recover signer address from signature and signed transaction hash '{}'",
186 e
187 ),
188 )
189 })?;
190
191 sig.verify(RecoveryMessage::Hash(tx_hash), signer_addr)
192 .map_err(|e| {
193 Error::new(
194 ErrorKind::Other,
195 format!(
196 "failed to verify signature against the signed transaction hash '{}'",
197 e
198 ),
199 )
200 })?;
201 log::info!(
202 "verified signer address '{}' against signature and transaction hash",
203 signer_addr
204 );
205
206 Ok((decoded_tx, tx_hash, signer_addr, sig))
207}
208
209#[test]
211fn test_transaction() {
212 let _ = env_logger::builder()
213 .filter_level(log::LevelFilter::Debug)
214 .is_test(true)
215 .try_init();
216
217 macro_rules! ab {
218 ($e:expr) => {
219 tokio_test::block_on($e)
220 };
221 }
222
223 let k1 = crate::key::secp256k1::private_key::Key::generate().unwrap();
224 let key_info1 = k1.to_info(1234).unwrap();
225 log::info!("created {}", key_info1.h160_address);
226 let k1_signer: ethers_signers::LocalWallet = k1.to_ethers_core_signing_key().into();
227
228 let k2 = crate::key::secp256k1::private_key::Key::generate().unwrap();
229 let key_info2 = k2.to_info(1234).unwrap();
230 log::info!("created {}", key_info2.h160_address);
231
232 let chain_id = random_manager::u64() % 3000;
233 let signer_nonce = U256::from(random_manager::u64() % 10);
234 let gas_limit = U256::from(random_manager::u64() % 10000);
235 let max_fee_per_gas = U256::from(random_manager::u64() % 10000);
236 let value = U256::from(random_manager::u64() % 100000);
237
238 let tx = Transaction::new()
239 .chain_id(chain_id)
240 .from(key_info1.h160_address)
241 .recipient(key_info2.h160_address)
242 .signer_nonce(signer_nonce)
243 .max_fee_per_gas(max_fee_per_gas)
244 .gas_limit(gas_limit)
245 .value(value);
246
247 let signed_bytes = ab!(tx.sign_as_typed_transaction(k1_signer)).unwrap();
248 log::info!("signed_bytes: {}", signed_bytes);
249
250 let (decoded_tx, sig) = decode_signed_rlp(&signed_bytes).unwrap();
251 let (decoded_tx2, _tx_hash, signer_addr, sig2) =
252 decode_and_verify_signed_rlp(&signed_bytes).unwrap();
253
254 assert_eq!(decoded_tx, decoded_tx2);
255 assert_eq!(sig, sig2);
256 assert_eq!(decoded_tx.chain_id().unwrap().as_u64(), chain_id);
257 assert_eq!(*decoded_tx.from().unwrap(), key_info1.h160_address);
258 assert_eq!(signer_addr, key_info1.h160_address);
259 assert_eq!(*decoded_tx.to_addr().unwrap(), key_info2.h160_address);
260 assert_eq!(decoded_tx.nonce().unwrap().as_u64(), signer_nonce.as_u64());
261 assert_eq!(decoded_tx.gas().unwrap().as_u64(), gas_limit.as_u64());
262 assert_eq!(decoded_tx.value().unwrap().as_u64(), value.as_u64());
263}