Skip to content

A Chain Reorg-Proof indexing engine that helps aggregate states for EVM contracts in RDBMSs.

Notifications You must be signed in to change notification settings

omahs/chaindexing-rs

 
 

Repository files navigation

Chaindexing

github crates.io diesel-streamer build

An EVM indexing engine that lets you query chain data with SQL.

Mini Comparison with TheGraph

It is a great alternative to theGraph https://thegraph.com/ if you:

  • have a server + relational database setup
  • are NOT indexing thousands of contracts
  • don't want to deal with an additional external system
  • have written your DApp in RUST (Other Languages soon to come!)

Example Usage:

Indexing states of NFTs (NftState) for Bored Ape Yatch Club and Doodle's contracts in a Postgres DB.

  1. Setup state by specifying its RDBMS's(Postgres) table name and migration:
use serde::{Deserialize, Serialize};
use chaindexing::{ContractState, ContractStateMigrations};

#[derive(Clone, Debug, Serialize, Deserialize)]
struct NftState {
    token_id: i32,
    contract_address: String,
    owner_address: String,
}

impl ContractState for NftState {
    fn table_name() -> &'static str {
        "nft_states"
    }
}

struct NftStateMigrations;

impl ContractStateMigrations for NftStateMigrations {
    fn migrations(&self) -> Vec<&'static str> {
        vec![
            "CREATE TABLE IF NOT EXISTS nft_states (
                token_id INTEGER NOT NULL,
                contract_address TEXT NOT NULL,
                owner_address TEXT NOT NULL
            )",
        ]
    }
}
  1. Setup Event Handlers:

For our example, we simply need a handler for Transfer events.

...

use chaindexing::{Contract, EventContext, EventHandler};

struct TransferEventHandler;

#[async_trait::async_trait]
impl EventHandler for TransferEventHandler {
    async fn handle_event<'a>(&self, event_context: EventContext<'a>) {
        let event = &event_context.event;
        // Get event parameters
        let event_params = event.get_params();

        // Extract each parameter as exactly specified in the ABI:
        // "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
        let from = event_params.get("from").unwrap().clone().into_address().unwrap();
        let to = event_params.get("to").unwrap().clone().into_address().unwrap();
        let token_id = event_params.get("tokenId").unwrap().clone().into_uint().unwrap();

        if let Some(nft_state) = NftState::read_one(
            [
                ("token_id".to_owned(), token_id.to_string()),
                ("owner_address".to_owned(), from.to_string()),
            ]
            .into(),
            &event_context,
        )
        .await
        {
            let updates = [("owner_address".to_string(), to.to_string())];

            nft_state.update(updates.into(), &event_context).await;
        } else {
            NftState {
                token_id: token_id.as_u32() as i32,
                contract_address: event.contract_address.clone(),
                owner_address: to.to_string(),
            }
            .create(&event_context)
            .await;
        }
    }
}
  1. Start the indexing background process:
...
use chaindexing::{Chain, Chaindexing, Chains, Config, Contract, PostgresRepo, Repo};

#[tokio::main]
async fn main() {
    // Setup BAYC's contract
    let bayc_contract =  Contract::new("BoredApeYachtClub")
    // add transfer event and it's corresponding handler
    .add_event("event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)", TransferEventHandler)
    // add migration for the state's DB schema
    .add_state_migrations(NftStateMigrations)
    // add contract address for BAYC
    .add_address(
        "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
        &Chain::Mainnet,
        17773490,
    );

    // Setup Doodles' contract
    let doodles_contract =  Contract::new("Doodles")
    .add_event("event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)", TransferEventHandler)
    .add_address(
      "0x8a90CAb2b38dba80c64b7734e58Ee1dB38B8992e",
      &Chain::Mainnet,
      17769635,
    );


    // Setup indexing config
    let config = Config::new(
      // Database
      PostgresRepo::new("postgres://postgres:postgres@localhost/example-db"),
      // All possible chains in your Dapp
      HashMap::from([(
          Chain::Mainnet,
          "https://eth-mainnet.g.alchemy.com/v2/some-secret"
      )]),
    )
    // add BAYC's and Doodles' contracts
    .add_contract(bayc_contract)
    .add_contract(doodles_contract);


    // Start Indexing Process
    Chaindexing::index_states(&config).await.unwrap();
}
  1. Query your DB using any approach/ORM:
  select * from nft_states

About

A Chain Reorg-Proof indexing engine that helps aggregate states for EVM contracts in RDBMSs.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 99.7%
  • Makefile 0.3%