use num_bigint::BigUint;
use substreams::{log, proto};
mod eth;
mod pb;
mod utils;
mod rpc;
#[no_mangle]
pub extern "C" fn map_erc_20_transfer(block_ptr: *mut u8, block_len: usize) {
substreams::register_panic_hook();
let block: pb::eth::Block = proto::decode_ptr(block_ptr, block_len).unwrap();
let mut transfers = pb::erc20::Transfers { transfers: vec![] };
for trx in block.transaction_traces {
for call in trx.calls {
for log in call.clone().logs {
if !utils::is_erc20transfer_event(&log) {
continue;
}
let from_addr = &Vec::from(&log.topics[1][12..]);
let to_addr = &Vec::from(&log.topics[2][12..]);
let amount = &log.data[0..32];
let log_ordinal = log.index as u64;
let transfer_event = pb::erc20::Transfer {
from: eth::address_pretty(from_addr.as_slice()),
to: eth::address_pretty(to_addr.as_slice()),
amount: BigUint::from_bytes_le(amount).to_string(),
balance_change_from: utils::find_erc20_storage_changes(
&call.clone(),
from_addr,
),
balance_change_to: utils::find_erc20_storage_changes(&call.clone(), to_addr),
log_ordinal,
};
transfers.transfers.push(transfer_event);
}
}
}
substreams::output(transfers);
}
#[no_mangle]
pub extern "C" fn block_to_tokens(block_ptr: *mut u8, block_len: usize) {
substreams::register_panic_hook();
let mut tokens = pb::erc20::Tokens { tokens: vec![] };
let blk: pb::eth::Block = proto::decode_ptr(block_ptr, block_len).unwrap();
let initialize_method_hash: &str = "0x1459457a";
for trx in blk.transaction_traces {
for call in trx.calls {
if call.call_type == pb::eth::CallType::Create as i32
|| call.call_type == pb::eth::CallType::Call as i32 && !call.state_reverted
{
let call_input_len = call.input.len();
if call.call_type == pb::eth::CallType::Call as i32
&& (call_input_len < 4
|| !eth::address_pretty(&call.input).starts_with(initialize_method_hash))
{
continue;
}
let contract_address = eth::address_pretty(&call.address);
let caller_address = eth::address_pretty(&call.caller);
if caller_address == "0xca143ce32fe78f1f7019d7d551a6402fc5350c73"
|| caller_address == "0xbcfccbde45ce874adcb698cc183debcf17952812"
{
continue;
}
if call.call_type == pb::eth::CallType::Create as i32 {
let mut code_change_len = 0;
for code_change in &call.code_changes {
code_change_len += code_change.new_code.len()
}
log::println(format!(
"found contract creation: {}, caller {}, code change {}, input {}",
contract_address, caller_address, code_change_len, call_input_len,
));
if code_change_len <= 150 {
log::println(format!(
"skipping too small code to be a token contract: {}",
eth::address_pretty(&call.address)
));
continue;
}
} else if call.call_type == pb::eth::CallType::Call as i32 {
log::println(format!(
"found contract that may be a proxy contract: {}",
caller_address
))
}
if caller_address == "0x0000000000004946c0e9f43f4dee607b0ef1fa1c"
|| caller_address == "0x00000000687f5b66638856396bee28c1db0178d1"
{
continue;
}
let rpc_calls = rpc::create_rpc_calls(&call.address);
let rpc_responses_marshalled: Vec<u8> =
substreams::rpc::eth_call(substreams::proto::encode(&rpc_calls).unwrap());
let rpc_responses_unmarshalled: substreams::pb::eth::RpcResponses =
substreams::proto::decode(&rpc_responses_marshalled).unwrap();
if rpc_responses_unmarshalled.responses[0].failed
|| rpc_responses_unmarshalled.responses[1].failed
|| rpc_responses_unmarshalled.responses[2].failed
{
log::println(format!(
"not a token because of a failure: {}",
eth::address_pretty(&call.address)
));
continue;
};
if !(rpc_responses_unmarshalled.responses[1].raw.len() >= 96)
|| rpc_responses_unmarshalled.responses[0].raw.len() != 32
|| !(rpc_responses_unmarshalled.responses[2].raw.len() >= 96)
{
log::println(format!(
"not a token because response length: {}",
eth::address_pretty(&call.address)
));
continue;
};
log::println(format!(
"found a token: {} {}",
eth::address_pretty(&call.address),
eth::decode_string(rpc_responses_unmarshalled.responses[1].raw.as_ref()),
));
let decoded_address = eth::address_pretty(&call.address);
let decoded_decimals =
eth::decode_uint32(rpc_responses_unmarshalled.responses[0].raw.as_ref());
let decoded_name =
eth::decode_string(rpc_responses_unmarshalled.responses[1].raw.as_ref());
let decoded_symbol =
eth::decode_string(rpc_responses_unmarshalled.responses[2].raw.as_ref());
let token = pb::erc20::Token {
address: decoded_address,
name: decoded_name,
symbol: decoded_symbol,
decimals: decoded_decimals as u64,
};
tokens.tokens.push(token);
}
}
}
substreams::output(tokens);
}