use super::RawTransaction;
use ethabi::Token;
use lazy_static::lazy_static;
use log::error;
use std::str::FromStr;
use web3::{
api::Web3,
futures::future::Future,
transports::Http,
types::{Address, BlockNumber, FilterBuilder, Transaction, H160, H256, U256},
};
lazy_static! {
pub static ref TRANSFER_EVENT_FILTER: H256 = {
H256::from_str("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef").unwrap()
};
}
pub fn make_tx(to: Address, value: U256, token_address: Option<Address>) -> RawTransaction {
if let Some(token_address) = token_address {
let mut data = hex::decode("a9059cbb").unwrap();
data.extend(ethabi::encode(&[Token::Address(to), Token::Uint(value)]));
RawTransaction {
to: Some(token_address),
nonce: U256::from(0),
data,
gas: U256::from(0),
gas_price: U256::from(0),
value: U256::zero(),
}
} else {
RawTransaction {
to: Some(to),
nonce: U256::from(0),
data: vec![],
gas: U256::from(0),
gas_price: U256::from(0),
value,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct ERC20Transfer {
pub tx_hash: H256,
pub from: Address,
pub to: Address,
pub amount: U256,
}
pub fn filter_transfer_logs(
web3: Web3<Http>,
contract_address: Address,
from: Option<Address>,
to: Option<Address>,
from_block: BlockNumber,
to_block: BlockNumber,
) -> impl Future<Item = Vec<ERC20Transfer>, Error = ()> {
let from = if let Some(from) = from {
Some(vec![H256::from(from)])
} else {
None
};
let to = if let Some(to) = to {
Some(vec![H256::from(to)])
} else {
None
};
let filter = FilterBuilder::default()
.from_block(from_block)
.to_block(to_block)
.address(vec![contract_address])
.topics(
Some(vec![
*TRANSFER_EVENT_FILTER,
]),
from,
to,
None,
)
.build();
web3.eth()
.logs(filter)
.map_err(move |err| error!("Got error when fetching transfer logs{:?}", err))
.and_then(move |logs| {
let mut ret = Vec::new();
for log in logs {
let indexed = log.topics;
let from = H160::from(indexed[1]);
let to = H160::from(indexed[2]);
let data = log.data;
let amount = U256::from_str(&hex::encode(data.0)).unwrap();
ret.push(ERC20Transfer {
tx_hash: log.transaction_hash.unwrap(),
from,
to,
amount,
});
}
Ok(ret)
})
}
pub fn sent_to_us(tx: Transaction, our_address: Address) -> Option<(Address, U256)> {
if let Some(to) = tx.to {
if to == our_address {
Some((tx.from, tx.value))
} else {
None
}
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_erc20_make_tx() {
let to = "c92be489639a9c61f517bd3b955840fa19bc9b7c".parse().unwrap();
let value = "16345785d8a0000".into();
let token_address =
Some(H160::from_str("B8c77482e45F1F44dE1745F52C74426C631bDD52").unwrap());
let tx = make_tx(to, value, token_address);
assert_eq!(tx.to, token_address);
assert_eq!(tx.value, U256::from(0));
assert_eq!(hex::encode(tx.data), "a9059cbb000000000000000000000000c92be489639a9c61f517bd3b955840fa19bc9b7c000000000000000000000000000000000000000000000000016345785d8a0000")
}
#[test]
fn test_eth_make_tx() {
let to = H160::from_str("c92be489639a9c61f517bd3b955840fa19bc9b7c").unwrap();
let value = "16345785d8a0000".into();
let token_address = None;
let tx = make_tx(to, value, token_address);
assert_eq!(tx.to, Some(to));
assert_eq!(tx.value, value);
let empty: Vec<u8> = Vec::new();
assert_eq!(tx.data, empty);
}
}