Skip to content

Latest commit

 

History

History
173 lines (135 loc) · 4.47 KB

README.md

File metadata and controls

173 lines (135 loc) · 4.47 KB

Authors

Abstract

The Ethereum JSON-RPC API will be used to retrieve all the events generated by all ERC20, ERC721, and ERC1155 smart contracts. These events will then be used to build a data model that represents the ownership of the users of tokens of all types. The worker service which will run this operation will be written entirely in the Rust programming language.

Models

contract_addresses

smart contract type

token_ownerships

smart contract token id owner quantity

Documentation

UML

Sequence Diagram

Task Concurrency

This worker utilizes two concurrent tasks/worker under the hood in order to process current and upcoming logs. The first one is the latest_block_worker, its only task is to keep track of the latest mined block to be used by the second worker as reference. The second one is the logs_worker, its task is to process/store all the logs in each block.

Pseudo code

let latest_block = Arc::new(Mutex::new(None));

let logs_worker_latest_block = latest_block.clone();

let latest_block_worker = task::spawn(async move {
    loop {
        *latest_block.lock().unwrap() = get_latest_block();
    }
});

let logs_worker = task::spawn(async move {

    let current_block = get_current_block();

    loop {
        let latest_block = *logs_worker_latest_block.lock().unwrap();

        if current_block <= latest_block {
            // process logs
        }
        
        current_block += 1;
    }
});

try_join!(latest_block_worker, logs_worker)

Processing Logs

Fetching Filtered Logs

let erc_20_and_721_transfer_signature = H256::from(keccak256("Transfer(address,address,uint256)".as_bytes()));
let erc_1155_transfer_single_signature = H256::from(keccak256("TransferSingle(address,address,address,uint256,uint256)".as_bytes()));
let erc_1155_transfer_batch_signature = H256::from(keccak256("TransferBatch(addressaddress,address,uint256[],uint256[])".as_bytes()));

let signatures_filter = vec![
    erc_20_and_721_transfer_signature,
    erc_1155_transfer_single_signature,
    erc_1155_transfer_batch_signature,
];

let filter = FilterBuilder::default()
    .from_block(BlockNumber::Number(current_block))
    .to_block(BlockNumber::Number(current_block))
    .topics(Some(signatures_filter), None, None, None)
    .build();

let logs: Vec<Log> = match logs_worker_web3.eth().logs(filter).await {
    Ok(logs) => logs,
    Err(error) => {
        // handle error
    }
};

Utilizing EIP-165

let contract = Contract::from_json(
    logs_worker_web3.eth(),
    log.address,
    include_bytes!("supports_interface_abi.json"),
)
.unwrap();

let erc_721_interface_id: [u8; 4] =
    hex::decode("80ac58cd").unwrap()[0..4].try_into().unwrap();

let supports_interface: bool = match contract
    .query(
        "supportsInterface",
        (erc_721_interface_id,),
        None,
        Options::default(),
        None,
    )
    .await
{
    Ok(value) => value,
    Err(_) => {
        continue;
    }
};

if supports_interface {
    // process
}

Decoding Quantity

let decoded_quantity = match decode(
    &vec![ParamType::Uint(256)],
    &log.data.0,
) {
    Ok(decoded) => match decoded[0] {
        Token::Uint(decoded_quantity) => decoded_quantity,
        _ => {
            // handle
        },
    },
    Err(_) => {
        // handle
    },
};

let quantity = decoded_quantity.as_u128().to_f64().unwrap();

Decoding Token ID

let decoded_token_id = match decode(
    &vec![ParamType::Uint(256)],
    &log.topics[3].as_bytes(),
) {
    Ok(decoded) => match decoded[0] {
        Token::Uint(token_id) => token_id,
        _ => {
            // handle
        },
    },
    Err(_) => {
        // handle
    },
};

let token_id = decoded_token_id.to_string();

Resources

Ethereum Logs

Rust Web3

Rust MongoDB

Tokio Concurrency

EIP-165

CLI

Rust Book