neo_web3/types/
transaction_request.rs

1use crate::types::{AccessList, Address, Bytes, U256, U64};
2use serde::{Deserialize, Serialize};
3
4/// Call contract request (eth_call / eth_estimateGas)
5///
6/// When using this for `eth_estimateGas`, all the fields
7/// are optional. However, for usage in `eth_call` the
8/// `to` field must be provided.
9#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
10pub struct CallRequest {
11    /// Sender address (None for arbitrary address)
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub from: Option<Address>,
14    /// To address (None allowed for eth_estimateGas)
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub to: Option<Address>,
17    /// Supplied gas (None for sensible default)
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub gas: Option<U256>,
20    /// Gas price (None for sensible default)
21    #[serde(skip_serializing_if = "Option::is_none")]
22    #[serde(rename = "gasPrice")]
23    pub gas_price: Option<U256>,
24    /// Transfered value (None for no transfer)
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub value: Option<U256>,
27    /// Data (None for empty data)
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub data: Option<Bytes>,
30    /// Transaction type, Some(1) for AccessList transaction, None for Legacy
31    #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
32    pub transaction_type: Option<U64>,
33    /// Access list
34    #[serde(rename = "accessList", default, skip_serializing_if = "Option::is_none")]
35    pub access_list: Option<AccessList>,
36    /// Max fee per gas
37    #[serde(rename = "maxFeePerGas", skip_serializing_if = "Option::is_none")]
38    pub max_fee_per_gas: Option<U256>,
39    /// miner bribe
40    #[serde(rename = "maxPriorityFeePerGas", skip_serializing_if = "Option::is_none")]
41    pub max_priority_fee_per_gas: Option<U256>,
42}
43
44impl CallRequest {
45    /// Funtion to return a builder for a Call Request
46    pub fn builder() -> CallRequestBuilder {
47        CallRequestBuilder::new()
48    }
49}
50
51/// Call Request Builder
52#[derive(Clone, Debug, Default)]
53pub struct CallRequestBuilder {
54    call_request: CallRequest,
55}
56
57impl CallRequestBuilder {
58    /// Retuns a Builder with the Call Request set to default
59    pub fn new() -> CallRequestBuilder {
60        CallRequestBuilder {
61            call_request: CallRequest::default(),
62        }
63    }
64
65    /// Set sender address (None for arbitrary address)
66    pub fn from(mut self, from: Address) -> Self {
67        self.call_request.from = Some(from);
68        self
69    }
70
71    /// Set to address (None allowed for eth_estimateGas)
72    pub fn to(mut self, to: Address) -> Self {
73        self.call_request.to = Some(to);
74        self
75    }
76
77    /// Set supplied gas (None for sensible default)
78    pub fn gas(mut self, gas: U256) -> Self {
79        self.call_request.gas = Some(gas);
80        self
81    }
82
83    /// Set transfered value (None for no transfer)
84    pub fn gas_price(mut self, gas_price: U256) -> Self {
85        self.call_request.gas_price = Some(gas_price);
86        self
87    }
88
89    /// Set transfered value (None for no transfer)
90    pub fn value(mut self, value: U256) -> Self {
91        self.call_request.value = Some(value);
92        self
93    }
94
95    /// Set data (None for empty data)
96    pub fn data(mut self, data: Bytes) -> Self {
97        self.call_request.data = Some(data);
98        self
99    }
100
101    /// Set transaction type, Some(1) for AccessList transaction, None for Legacy
102    pub fn transaction_type(mut self, transaction_type: U64) -> Self {
103        self.call_request.transaction_type = Some(transaction_type);
104        self
105    }
106
107    /// Set access list
108    pub fn access_list(mut self, access_list: AccessList) -> Self {
109        self.call_request.access_list = Some(access_list);
110        self
111    }
112
113    /// build the Call Request
114    pub fn build(&self) -> CallRequest {
115        self.call_request.clone()
116    }
117}
118
119/// Send Transaction Parameters
120#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
121pub struct TransactionRequest {
122    /// Sender address
123    pub from: Address,
124    /// Recipient address (None for contract creation)
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub to: Option<Address>,
127    /// Supplied gas (None for sensible default)
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub gas: Option<U256>,
130    /// Gas price (None for sensible default)
131    #[serde(skip_serializing_if = "Option::is_none")]
132    #[serde(rename = "gasPrice")]
133    pub gas_price: Option<U256>,
134    /// Transfered value (None for no transfer)
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub value: Option<U256>,
137    /// Transaction data (None for empty bytes)
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub data: Option<Bytes>,
140    /// Transaction nonce (None for next available nonce)
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub nonce: Option<U256>,
143    /// Min block inclusion (None for include immediately)
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub condition: Option<TransactionCondition>,
146    /// Transaction type, Some(1) for AccessList transaction, None for Legacy
147    #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
148    pub transaction_type: Option<U64>,
149    /// Access list
150    #[serde(rename = "accessList", default, skip_serializing_if = "Option::is_none")]
151    pub access_list: Option<AccessList>,
152    /// Max fee per gas
153    #[serde(rename = "maxFeePerGas", skip_serializing_if = "Option::is_none")]
154    pub max_fee_per_gas: Option<U256>,
155    /// miner bribe
156    #[serde(rename = "maxPriorityFeePerGas", skip_serializing_if = "Option::is_none")]
157    pub max_priority_fee_per_gas: Option<U256>,
158}
159
160impl TransactionRequest {
161    /// Funtion to return a builder for a Transaction Request
162    pub fn builder() -> TransactionRequestBuilder {
163        TransactionRequestBuilder::new()
164    }
165}
166
167/// Transaction Request Builder
168#[derive(Clone, Debug, Default)]
169pub struct TransactionRequestBuilder {
170    transaction_request: TransactionRequest,
171}
172
173impl TransactionRequestBuilder {
174    /// Retuns a Builder with the Transaction Request set to default
175    pub fn new() -> TransactionRequestBuilder {
176        TransactionRequestBuilder {
177            transaction_request: TransactionRequest::default(),
178        }
179    }
180
181    /// Set sender address
182    pub fn from(mut self, from: Address) -> Self {
183        self.transaction_request.from = from;
184        self
185    }
186
187    /// Set recipient address (None for contract creation)
188    pub fn to(mut self, to: Address) -> Self {
189        self.transaction_request.to = Some(to);
190        self
191    }
192
193    /// Set supplied gas (None for sensible default)
194    pub fn gas(mut self, gas: U256) -> Self {
195        self.transaction_request.gas = Some(gas);
196        self
197    }
198
199    /// Set transfered value (None for no transfer)
200    pub fn value(mut self, value: U256) -> Self {
201        self.transaction_request.value = Some(value);
202        self
203    }
204
205    /// Set transaction data (None for empty bytes)
206    pub fn data(mut self, data: Bytes) -> Self {
207        self.transaction_request.data = Some(data);
208        self
209    }
210
211    /// Set transaction nonce (None for next available nonce)
212    pub fn nonce(mut self, nonce: U256) -> Self {
213        self.transaction_request.nonce = Some(nonce);
214        self
215    }
216
217    /// Set min block inclusion (None for include immediately)
218    pub fn condition(mut self, condition: TransactionCondition) -> Self {
219        self.transaction_request.condition = Some(condition);
220        self
221    }
222
223    /// Set transaction type, Some(1) for AccessList transaction, None for Legacy
224    pub fn transaction_type(mut self, transaction_type: U64) -> Self {
225        self.transaction_request.transaction_type = Some(transaction_type);
226        self
227    }
228
229    /// Set access list
230    pub fn access_list(mut self, access_list: AccessList) -> Self {
231        self.transaction_request.access_list = Some(access_list);
232        self
233    }
234
235    /// build the Transaction Request
236    pub fn build(&self) -> TransactionRequest {
237        self.transaction_request.clone()
238    }
239}
240
241/// Represents condition on minimum block number or block timestamp.
242#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
243#[serde(deny_unknown_fields)]
244pub enum TransactionCondition {
245    /// Valid at this minimum block number.
246    #[serde(rename = "block")]
247    Block(u64),
248    /// Valid at given unix time.
249    #[serde(rename = "time")]
250    Timestamp(u64),
251}
252
253#[cfg(test)]
254mod tests {
255    use super::{
256        Address, CallRequest, CallRequestBuilder, TransactionCondition, TransactionRequest, TransactionRequestBuilder,
257    };
258    use hex_literal::hex;
259
260    #[test]
261    fn should_serialize_call_request() {
262        // given
263        let call_request = CallRequest {
264            from: None,
265            to: Some(Address::from_low_u64_be(5)),
266            gas: Some(21_000.into()),
267            gas_price: None,
268            value: Some(5_000_000.into()),
269            data: Some(hex!("010203").into()),
270            transaction_type: None,
271            access_list: None,
272            max_fee_per_gas: None,
273            max_priority_fee_per_gas: None,
274        };
275
276        // when
277        let serialized = serde_json::to_string_pretty(&call_request).unwrap();
278
279        // then
280        assert_eq!(
281            serialized,
282            r#"{
283  "to": "0x0000000000000000000000000000000000000005",
284  "gas": "0x5208",
285  "value": "0x4c4b40",
286  "data": "0x010203"
287}"#
288        );
289    }
290
291    #[test]
292    fn should_deserialize_call_request() {
293        let serialized = r#"{
294  "to": "0x0000000000000000000000000000000000000005",
295  "gas": "0x5208",
296  "value": "0x4c4b40",
297  "data": "0x010203"
298}"#;
299        let deserialized: CallRequest = serde_json::from_str(serialized).unwrap();
300
301        assert_eq!(deserialized.from, None);
302        assert_eq!(deserialized.to, Some(Address::from_low_u64_be(5)));
303        assert_eq!(deserialized.gas, Some(21_000.into()));
304        assert_eq!(deserialized.gas_price, None);
305        assert_eq!(deserialized.value, Some(5_000_000.into()));
306        assert_eq!(deserialized.data, Some(hex!("010203").into()));
307    }
308
309    #[test]
310    fn should_serialize_transaction_request() {
311        // given
312        let tx_request = TransactionRequest {
313            from: Address::from_low_u64_be(5),
314            to: None,
315            gas: Some(21_000.into()),
316            gas_price: None,
317            value: Some(5_000_000.into()),
318            data: Some(hex!("010203").into()),
319            nonce: None,
320            condition: Some(TransactionCondition::Block(5)),
321            transaction_type: None,
322            access_list: None,
323            max_fee_per_gas: None,
324            max_priority_fee_per_gas: None,
325        };
326
327        // when
328        let serialized = serde_json::to_string_pretty(&tx_request).unwrap();
329
330        // then
331        assert_eq!(
332            serialized,
333            r#"{
334  "from": "0x0000000000000000000000000000000000000005",
335  "gas": "0x5208",
336  "value": "0x4c4b40",
337  "data": "0x010203",
338  "condition": {
339    "block": 5
340  }
341}"#
342        );
343    }
344
345    #[test]
346    fn should_deserialize_transaction_request() {
347        let serialized = r#"{
348  "from": "0x0000000000000000000000000000000000000005",
349  "gas": "0x5208",
350  "value": "0x4c4b40",
351  "data": "0x010203",
352  "condition": {
353    "block": 5
354  }
355}"#;
356        let deserialized: TransactionRequest = serde_json::from_str(serialized).unwrap();
357
358        assert_eq!(deserialized.from, Address::from_low_u64_be(5));
359        assert_eq!(deserialized.to, None);
360        assert_eq!(deserialized.gas, Some(21_000.into()));
361        assert_eq!(deserialized.gas_price, None);
362        assert_eq!(deserialized.value, Some(5_000_000.into()));
363        assert_eq!(deserialized.data, Some(hex!("010203").into()));
364        assert_eq!(deserialized.nonce, None);
365        assert_eq!(deserialized.condition, Some(TransactionCondition::Block(5)));
366    }
367
368    #[test]
369    fn should_build_default_call_request() {
370        //given
371        let call_request = CallRequest::default();
372        //when
373        let call_request_builder = CallRequestBuilder::new();
374        //then
375        assert_eq!(call_request_builder.build(), call_request);
376    }
377
378    #[test]
379    fn should_build_call_request() {
380        //given
381        let call_request = CallRequest {
382            from: None,
383            to: Some(Address::from_low_u64_be(5)),
384            gas: Some(21_000.into()),
385            gas_price: None,
386            value: Some(5_000_000.into()),
387            data: Some(hex!("010203").into()),
388            transaction_type: None,
389            access_list: None,
390            max_fee_per_gas: None,
391            max_priority_fee_per_gas: None,
392        };
393        //when
394        let call_request_builder = CallRequestBuilder::new()
395            .to(Address::from_low_u64_be(5))
396            .gas(21_000.into())
397            .value(5_000_000.into())
398            .data(hex!("010203").into())
399            .build();
400        //then
401        assert_eq!(call_request_builder, call_request);
402    }
403
404    #[test]
405    fn should_build_default_transaction_request() {
406        //given
407        let tx_request = TransactionRequest::default();
408        //when
409        let tx_request_builder = TransactionRequestBuilder::new();
410        //then
411        assert_eq!(tx_request_builder.build(), tx_request);
412    }
413
414    #[test]
415    fn should_build_transaction_request() {
416        // given
417        let tx_request = TransactionRequest {
418            from: Address::from_low_u64_be(5),
419            to: None,
420            gas: Some(21_000.into()),
421            gas_price: None,
422            value: Some(5_000_000.into()),
423            data: Some(hex!("010203").into()),
424            nonce: None,
425            condition: Some(TransactionCondition::Block(5)),
426            transaction_type: None,
427            access_list: None,
428            max_fee_per_gas: None,
429            max_priority_fee_per_gas: None,
430        };
431        //when
432        let tx_request_builder = TransactionRequestBuilder::new()
433            .from(Address::from_low_u64_be(5))
434            .gas(21_000.into())
435            .value(5_000_000.into())
436            .data(hex!("010203").into())
437            .condition(TransactionCondition::Block(5));
438        //then
439        assert_eq!(tx_request_builder.build(), tx_request);
440    }
441}