From 2223bd513fd3ebcef4d8f8e9ab5f4f8ea4cf1f42 Mon Sep 17 00:00:00 2001 From: Rodolfo Araujo Date: Tue, 8 Dec 2020 19:26:05 -0300 Subject: [PATCH 1/4] Adding documentation. --- src/erc20.rs | 11 ++++++++++- src/error.rs | 9 ++++++++- src/lib.rs | 4 ++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/erc20.rs b/src/erc20.rs index ceb6398..6cf7706 100644 --- a/src/erc20.rs +++ b/src/erc20.rs @@ -10,19 +10,27 @@ use std::{ TryFrom, TryInto, }, + str::FromStr, }; use web3::types::H160; -use std::str::FromStr; +/// ERC20 method operation #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum ERC20Method { + /// Returns the amount which `spender` is still allowed to withdraw from `owner`. Allowance, + /// Allows `spender` to withdraw from your account multiple times, up to the `value` amount. If this function is called again it overwrites the current allowance with `value`. Approve, + /// Returns the account balance of another account with address `owner`. BalanceOf, + /// Returns the total token supply. TotalSupply, + /// Transfers `value` amount of tokens to address `to`, and MUST fire the Transfer event. The function SHOULD throw if the message caller’s account balance does not have enough tokens to spend. Transfer, + /// Transfers `value` amount of tokens from address `from` to address `to`, and MUST fire the Transfer event. TransferFrom, + /// In case it is not identified an ERC20 operation. Unidentified, } @@ -75,6 +83,7 @@ impl From> for ERC20Method { } } +/// Known ERC20 contract addresses. #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum ContractAddress { diff --git a/src/error.rs b/src/error.rs index 0655b08..b18fc38 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,20 @@ +//! Expected errors. + use serde::{ Deserialize, Serialize, }; +/// Possible transaction errors. #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum ERC20Error { + /// Returned when the transaction is not a Ethereum transfer neither an ERC20 transfer. NoTransferTransaction, + /// Unexpected size for the input. UnexpectedSize, + /// The end of the input was found before expected. UnexpectedEndOfData, + // Returned when the type or value used is not expected for the operation. UnexpectedType, -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index ef1cc55..0273d6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,7 @@ +//! A simple implementation for parsing ERC20 transactions +#![warn(missing_docs)] +// #![warn(missing_doc_code_examples)] + extern crate serde; extern crate hex; From 780aca6b2a7ed2e3eab94d5596179f5415018385 Mon Sep 17 00:00:00 2001 From: Rodolfo Araujo Date: Tue, 8 Dec 2020 19:50:34 -0300 Subject: [PATCH 2/4] Adding documentation. --- src/erc20.rs | 2 ++ src/transaction.rs | 13 ++++++++ src/transfer.rs | 14 +++++++++ src/util.rs | 74 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 80 insertions(+), 23 deletions(-) diff --git a/src/erc20.rs b/src/erc20.rs index 6cf7706..bdda370 100644 --- a/src/erc20.rs +++ b/src/erc20.rs @@ -1,3 +1,5 @@ +///! ERC20 specific information. + use crate::ERC20Error; use maplit::hashmap; use serde::{ diff --git a/src/transaction.rs b/src/transaction.rs index f7b5437..afcc0e6 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,3 +1,5 @@ +///! web3 transaction specific operations. + use crate::{ erc20::ERC20Method, error::ERC20Error, @@ -21,12 +23,17 @@ use web3::types::{ U256, }; +/// Identifies an Ethereum transaction as a transfer, contract invocation, creation, or other. #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum ParsedTransaction { + /// Ether transfer transaction. EthereumTransfer(Transaction), + /// Smart contract invocation transaction. ContractInvocation(TransactionContractInvocation), + /// Smart contract creation transaction. ContractCreation(Transaction), + /// Unidentified transaction. Other(Transaction), } @@ -48,9 +55,12 @@ impl From for ParsedTransaction { } } +/// Smart contract invocation transaction. #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub enum TransactionContractInvocation { + /// ERC20 contract invocation. ERC20(ERC20Method, Transaction), + /// Any smart contract invocation that is not a ERC20 contract. Other(Transaction), } @@ -64,6 +74,7 @@ impl From for TransactionContractInvocation { } } +/// Transaction and transaction type information for asset transfers. #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct TransactionAndTransferType { @@ -107,6 +118,8 @@ impl TryFrom for TransactionAndTransferType { } impl TransactionAndTransferType { + /// Gets information from the transaction. + /// The `from`, `to`, and `value` regardless if it is an ERC20 or Ether transfer. pub fn get_from_to_value(&self) -> Result<(H160, H160, U256), ERC20Error> { let from_v: H160; let to_v: H160; diff --git a/src/transfer.rs b/src/transfer.rs index f0edba4..5b3ee05 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -1,3 +1,5 @@ +///! Ethereum transfer abstraction. + use serde::{ Deserialize, Serialize, @@ -13,21 +15,33 @@ use web3::types::{ U256, }; +/// Type of the asset transfer. #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum TransferType { + /// Indicates an Ether transfer. Ethereum, + /// Indicates an ERC20 transfer. ERC20, } +/// Asset transfer abstraction. pub trait Transfer { + /// Returns the sender of the transfer. fn from(&self) -> H160; + /// Returns the recipient of the transfer. fn to(&self) -> H160; + /// Returns the ERC20 contract address for ERC20 transfers. fn contract(&self) -> Option; + /// Returns the value of the transfer. fn value(&self) -> U256; + /// Returns the transaction hash for the transfer. fn tx_hash(&self) -> H256; + /// Returns the block hash for the transfer, if available. fn block_hash(&self) -> Option; + /// Returns the block number for the transfer, if available. fn block_number(&self) -> Option; + /// Returns the transaction index for the transfer, if available. fn transaction_index(&self) -> Option; } diff --git a/src/util.rs b/src/util.rs index 4f7790b..834d8fd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,5 @@ +///! Useful methods and abstractions + use crate::ERC20Error; use hex::FromHexError; use web3::types::{ @@ -7,28 +9,7 @@ use web3::types::{ U256, }; -macro_rules! string_to { - ($name:ident, $num_type:ty, $size:expr) => { - pub fn $name(text: String) -> Result<$num_type, FromHexError> { - let value_vec = hex::decode(text)?; - if value_vec.len() != $size { - return Err(FromHexError::InvalidStringLength); - } - let mut value_array: [u8; $size] = Default::default(); - for i in 0..$size { - value_array[i] = value_vec[i]; - } - - Ok(<$num_type>::from(value_array)) - } - }; -} - -string_to!(string_to_h160, H160, 20); -string_to!(string_to_h256, H256, 32); - -string_to!(string_to_u256, U256, 32); - +/// Converts `Bytes` and `Vec` to H160, H256, and U256. pub struct BytesToFixedNumber { data: Vec, index: usize, @@ -52,6 +33,12 @@ impl From for BytesToFixedNumber { } impl BytesToFixedNumber { + /// Returns the next vector for the specified size. + /// + /// # Arguments + /// + /// * `size` - The size requested for the next vector. + /// pub fn next_vec(&mut self, size: usize) -> Result, ERC20Error> { if self.index + size > self.data.len() { return Err(ERC20Error::UnexpectedEndOfData); @@ -64,6 +51,12 @@ impl BytesToFixedNumber { Ok(resp) } + /// Skips a specified number of bytes. + /// + /// # Arguments + /// + /// * `size` - The number of bytes to skip. + /// pub fn skip(&mut self, size: usize) -> Result<(), ERC20Error> { if self.index + size > self.data.len() { return Err(ERC20Error::UnexpectedEndOfData); @@ -72,11 +65,13 @@ impl BytesToFixedNumber { Ok(()) } + /// Returns the next H160. pub fn next_h160(&mut self) -> Result { self.skip((256 - 160) / 8)?; self.next_h160_not_padded() } + /// Returns the next H160 with no padding to 32 bytes. pub fn next_h160_not_padded(&mut self) -> Result { let vec_resp = self.next_vec(20)?; let mut the_vec: [u8; 20] = [0; 20]; @@ -86,6 +81,7 @@ impl BytesToFixedNumber { Ok(the_vec.into()) } + /// Returns the next H256. pub fn next_h256(&mut self) -> Result { let vec_resp = self.next_vec(32)?; let mut the_vec: [u8; 32] = [0; 32]; @@ -95,6 +91,7 @@ impl BytesToFixedNumber { Ok(the_vec.into()) } + /// Returns the next U256. pub fn next_u256(&mut self) -> Result { let vec_resp = self.next_vec(32)?; let mut the_vec: [u8; 32] = [0; 32]; @@ -105,17 +102,30 @@ impl BytesToFixedNumber { } } +/// Converts H160, H256, and U256 into `Vec` which can be used to create a `Bytes`. pub struct FixedNumberToBytes { data: Vec, } impl FixedNumberToBytes { + /// Pushes a vector of bytes to the tail of the current byte array. + /// + /// # Arguments + /// + /// * `vec` - Vector with the bytes to be added. + /// pub fn push_vec(&mut self, vec: &Vec) { for it in vec { self.data.push(*it); } } + /// Pushes a H160 to the tail of the current byte array. + /// + /// # Arguments + /// + /// * `value` - H160 to be pushed. + /// pub fn push_h160(&mut self, value: &H160) { for _ in 0..((256 - 160) / 8) { self.data.push(0); @@ -123,18 +133,36 @@ impl FixedNumberToBytes { self.push_h160_not_padded(value); } + /// Pushes a H160 to the tail of the current byte array, with no padding to 32 bytes. + /// + /// # Arguments + /// + /// * `value` - H160 to be pushed. + /// pub fn push_h160_not_padded(&mut self, value: &H160) { for it in value.0.iter() { self.data.push(*it); } } + /// Pushes a H256 to the tail of the current byte array. + /// + /// # Arguments + /// + /// * `value` - H256 to be pushed. + /// pub fn push_h256(&mut self, value: &H256) { for it in value.0.iter() { self.data.push(*it); } } + /// Pushes an U256 to the tail of the current byte array. + /// + /// # Arguments + /// + /// * `value` - U256 to be pushed. + /// pub fn push_u256(&mut self, value: &U256) { for i in 0..(256 / 8) { self.data.push(value.byte(i)); @@ -148,4 +176,4 @@ impl From for FixedNumberToBytes { data: data.0 } } -} \ No newline at end of file +} From f8445bea87eafd39366abc834f10b71cb0dbd05a Mon Sep 17 00:00:00 2001 From: Rodolfo Araujo Date: Tue, 8 Dec 2020 19:55:16 -0300 Subject: [PATCH 3/4] Adding documentation. --- src/error.rs | 2 +- src/lib.rs | 7 ++++++- src/transfer.rs | 4 ++++ src/util.rs | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index b18fc38..c768c8f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,6 +15,6 @@ pub enum ERC20Error { UnexpectedSize, /// The end of the input was found before expected. UnexpectedEndOfData, - // Returned when the type or value used is not expected for the operation. + /// Returned when the type or value used is not expected for the operation. UnexpectedType, } diff --git a/src/lib.rs b/src/lib.rs index 0273d6a..4a3f146 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,19 @@ -//! A simple implementation for parsing ERC20 transactions #![warn(missing_docs)] // #![warn(missing_doc_code_examples)] +//! A simple implementation for parsing ERC20 transactions + extern crate serde; extern crate hex; mod error; +/// A set of useful methods and abstractions. pub mod util; +/// Ethereum transfer abstraction. pub mod transfer; +/// ERC20 specific information. pub mod erc20; +/// web3 transaction specific operations. pub mod transaction; #[cfg(test)] mod transaction_test; diff --git a/src/transfer.rs b/src/transfer.rs index 5b3ee05..fc464c4 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -46,6 +46,7 @@ pub trait Transfer { } impl dyn Transfer { + /// The kind of transfer. pub fn kind(&self) -> TransferType { match self.contract() { None => TransferType::Ethereum, @@ -53,12 +54,15 @@ impl dyn Transfer { } } + /// Checks if it is an Ether transfer. pub fn is_ethereum(&self) -> bool { self.kind() == TransferType::Ethereum } + /// Checks if it is an ERC20 transfer. pub fn is_erc20(&self) -> bool { !self.is_ethereum() } + /// Retrieves the transaction id. #[allow(dead_code)] fn transaction_id(&self) -> TransactionId { if let Some(block_num) = self.block_number() { diff --git a/src/util.rs b/src/util.rs index 834d8fd..c3b3618 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,4 @@ -///! Useful methods and abstractions +///! A set of useful methods and abstractions. use crate::ERC20Error; use hex::FromHexError; From 423476cdff00fdf3d8a023f1ed04953cecf5207d Mon Sep 17 00:00:00 2001 From: Rodolfo Araujo Date: Tue, 8 Dec 2020 20:53:58 -0300 Subject: [PATCH 4/4] Fixing util and adding ContractAddress conversion. --- Cargo.toml | 2 +- src/erc20.rs | 63 +++++++++++++++++++++++++++-------------- src/lib.rs | 2 ++ src/transaction_test.rs | 22 ++++++++++---- src/util.rs | 15 ++++++---- src/util_test.rs | 50 ++++++++++++++++++++++++++++++++ 6 files changed, 121 insertions(+), 33 deletions(-) create mode 100644 src/util_test.rs diff --git a/Cargo.toml b/Cargo.toml index d520a86..41a764d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "erc20" -version = "0.2.0" +version = "0.1.1" authors = ["Rodolfo Araujo "] edition = "2018" license-file = "LICENSE" diff --git a/src/erc20.rs b/src/erc20.rs index bdda370..dd38d64 100644 --- a/src/erc20.rs +++ b/src/erc20.rs @@ -126,28 +126,33 @@ pub enum ContractAddress { Unidentified(H160), } +impl ContractAddress { + fn contract_and_address() -> HashMap { + hashmap! { + ContractAddress::TUSD => H160::from_str("0000000000085d4780B73119b644AE5ecd22b376").unwrap(), + ContractAddress::LINK => H160::from_str("514910771af9ca656af840dff83e8264ecf986ca").unwrap(), + ContractAddress::BNB => H160::from_str("B8c77482e45F1F44dE1745F52C74426C631bDD52").unwrap(), + ContractAddress::USDC => H160::from_str("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(), + ContractAddress::WBTC => H160::from_str("2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(), + ContractAddress::cDAI => H160::from_str("5d3a536E4D6DbD6114cc1Ead35777bAB948E3643").unwrap(), + ContractAddress::OKB => H160::from_str("75231f58b43240c9718dd58b4967c5114342a86c").unwrap(), + ContractAddress::CRO => H160::from_str("a0b73e1ff0b80914ab6fe0444e65848c4c34450b").unwrap(), + ContractAddress::WFIL => H160::from_str("6e1A19F235bE7ED8E3369eF73b196C07257494DE").unwrap(), + ContractAddress::BAT => H160::from_str("0d8775f648430679a709e98d2b0cb6250d2887ef").unwrap(), + ContractAddress::BUSD => H160::from_str("4fabb145d64652a948d72533023f6e7a623c7c53").unwrap(), + ContractAddress::USDT => H160::from_str("dac17f958d2ee523a2206206994597c13d831ec7").unwrap(), + ContractAddress::LEO => H160::from_str("2af5d2ad76741191d15dfe7bf6ac92d4bd912ca3").unwrap(), + ContractAddress::VEN => H160::from_str("d850942ef8811f2a866692a623011bde52a462c1").unwrap(), + ContractAddress::DAI => H160::from_str("6b175474e89094c44da98b954eedeac495271d0f").unwrap(), + ContractAddress::UNI => H160::from_str("1f9840a85d5af5bf1d1762f925bdaddc4201f984").unwrap(), + } + } +} + impl From for ContractAddress { fn from(address: H160) -> Self { - let contract_and_address: HashMap = hashmap! { - ContractAddress::TUSD => "0000000000085d4780B73119b644AE5ecd22b376", - ContractAddress::LINK => "514910771af9ca656af840dff83e8264ecf986ca", - ContractAddress::BNB => "B8c77482e45F1F44dE1745F52C74426C631bDD52", - ContractAddress::USDC => "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - ContractAddress::WBTC => "2260fac5e5542a773aa44fbcfedf7c193bc2c599", - ContractAddress::cDAI => "5d3a536E4D6DbD6114cc1Ead35777bAB948E3643", - ContractAddress::OKB => "75231f58b43240c9718dd58b4967c5114342a86c", - ContractAddress::CRO => "a0b73e1ff0b80914ab6fe0444e65848c4c34450b", - ContractAddress::WFIL => "6e1A19F235bE7ED8E3369eF73b196C07257494DE", - ContractAddress::BAT => "0d8775f648430679a709e98d2b0cb6250d2887ef", - ContractAddress::BUSD => "4fabb145d64652a948d72533023f6e7a623c7c53", - ContractAddress::USDT => "dac17f958d2ee523a2206206994597c13d831ec7", - ContractAddress::LEO => "2af5d2ad76741191d15dfe7bf6ac92d4bd912ca3", - ContractAddress::VEN => "d850942ef8811f2a866692a623011bde52a462c1", - ContractAddress::DAI => "6b175474e89094c44da98b954eedeac495271d0f", - ContractAddress::UNI => "1f9840a85d5af5bf1d1762f925bdaddc4201f984", - }; - for (contract, address_str) in contract_and_address { - if H160::from_str(address_str).unwrap() == address { + for (contract, address_v) in Self::contract_and_address() { + if address_v == address { return contract; } } @@ -155,6 +160,20 @@ impl From for ContractAddress { } } +impl From for H160 { + fn from(contract_address: ContractAddress) -> Self { + for (contract, address) in ContractAddress::contract_and_address() { + if contract_address == contract { + return address; + } + } + match contract_address { + ContractAddress::Unidentified(address) => address, + _ => panic!("Unexpected contract {:?}", contract_address), + } + } +} + #[test] fn creating_address() { let tusd_address = H160::from_str("0000000000085d4780B73119b644AE5ecd22b376").unwrap(); @@ -163,6 +182,6 @@ fn creating_address() { let contract_address: ContractAddress = tusd_address.into(); assert_eq!(ContractAddress::TUSD, contract_address); - // let tusd_from_contract: H160 = contract_address.into(); - // assert_eq!(tusd_address, tusd_from_contract); + let tusd_from_contract: H160 = contract_address.into(); + assert_eq!(tusd_address, tusd_from_contract); } diff --git a/src/lib.rs b/src/lib.rs index 4a3f146..c3d8429 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ extern crate hex; mod error; /// A set of useful methods and abstractions. pub mod util; +#[cfg(test)] +mod util_test; /// Ethereum transfer abstraction. pub mod transfer; /// ERC20 specific information. diff --git a/src/transaction_test.rs b/src/transaction_test.rs index 43025c2..d8e8bf0 100644 --- a/src/transaction_test.rs +++ b/src/transaction_test.rs @@ -1,13 +1,25 @@ -use crate::{transfer::Transfer, transaction::TransactionAndTransferType, util::string_to_h256, ERC20Error}; -use std::convert::TryInto; -use web3::types::{Bytes, Transaction, H160}; +use crate::{ + transfer::Transfer, + transaction::TransactionAndTransferType, + ERC20Error, +}; +use std::{ + convert::TryInto, + str::FromStr, +}; +use web3::types::{ + Bytes, + H160, + H256, + Transaction, +}; #[test] fn parse_no_transfer_transaction() { let serialized_str = "a9059cbb0000000000000000000000006748f50f686bfbca6fe8ad62b22228b87f31ff2b00000000000000000000000000000000000000000000003635c9adc5dea00000"; let transaction = Transaction { - hash: string_to_h256("43a5d6d13b6a9dca381e3f4b4677a4b9e5d9f80d1a5b6cfa2b1404fab733bcee".to_string()).unwrap(), + hash: H256::from_str("43a5d6d13b6a9dca381e3f4b4677a4b9e5d9f80d1a5b6cfa2b1404fab733bcee").unwrap(), nonce: Default::default(), block_hash: None, block_number: None, @@ -31,7 +43,7 @@ fn parse_erc20() { let serialized_str = "a9059cbb0000000000000000000000006748f50f686bfbca6fe8ad62b22228b87f31ff2b00000000000000000000000000000000000000000000003635c9adc5dea00000"; let transaction = Transaction { - hash: string_to_h256("43a5d6d13b6a9dca381e3f4b4677a4b9e5d9f80d1a5b6cfa2b1404fab733bcee".to_string()).unwrap(), + hash: H256::from_str("43a5d6d13b6a9dca381e3f4b4677a4b9e5d9f80d1a5b6cfa2b1404fab733bcee").unwrap(), nonce: Default::default(), block_hash: None, block_number: None, diff --git a/src/util.rs b/src/util.rs index c3b3618..5dbc8d7 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,7 +1,6 @@ ///! A set of useful methods and abstractions. use crate::ERC20Error; -use hex::FromHexError; use web3::types::{ Bytes, H160, @@ -164,16 +163,22 @@ impl FixedNumberToBytes { /// * `value` - U256 to be pushed. /// pub fn push_u256(&mut self, value: &U256) { - for i in 0..(256 / 8) { + for i in (0..(256 / 8)).rev() { self.data.push(value.byte(i)); } } } -impl From for FixedNumberToBytes { - fn from(data: Bytes) -> Self { +impl Default for FixedNumberToBytes { + fn default() -> Self { Self { - data: data.0 + data: Vec::new(), } } } + +impl From for Vec { + fn from(data: FixedNumberToBytes) -> Self { + data.data + } +} diff --git a/src/util_test.rs b/src/util_test.rs new file mode 100644 index 0000000..2c1408e --- /dev/null +++ b/src/util_test.rs @@ -0,0 +1,50 @@ +use crate::util::{ + BytesToFixedNumber, + FixedNumberToBytes, +}; +use web3::types::{ + H160, + U256, +}; + +#[test] +fn bytes_to_fixed_number() { + let bytes_str = "a9059cbb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"; + let bytes_vec = hex::decode(bytes_str); + assert!(bytes_vec.is_ok()); + let bytes_vec = bytes_vec.unwrap(); + let mut decoder: BytesToFixedNumber = bytes_vec.into(); + + let method = decoder.next_vec(4); + assert!(method.is_ok()); + let method = method.unwrap(); + assert_eq!(hex::decode("a9059cbb").unwrap(), method); + + let to_address = decoder.next_h160(); + assert!(to_address.is_ok()); + let to_address = to_address.unwrap(); + assert_eq!(H160::from_low_u64_be(1), to_address); + + let value = decoder.next_u256(); + assert!(value.is_ok()); + let value = value.unwrap(); + assert_eq!(U256::from(2), value); +} + +#[test] +fn fixed_number_to_bytes() { + let mut encoder: FixedNumberToBytes = Default::default(); + + encoder.push_vec(&hex::decode("a9059cbb").unwrap()); + encoder.push_h160(&H160::from_low_u64_be(1)); + encoder.push_u256(&U256::from(2)); + + let encoded_vec: Vec = encoder.into(); + + let bytes_str = "a9059cbb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"; + let bytes_vec = hex::decode(bytes_str); + assert!(bytes_vec.is_ok()); + let bytes_vec = bytes_vec.unwrap(); + + assert_eq!(bytes_vec, encoded_vec); +}