chaindexing-0.1.12 has been yanked.
Chaindexing
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 Yacht Club and Doodle's contracts in a Postgres DB.
- 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
)",
]
}
}
- Setup Event Handlers:
For our example, we simply need a handler for Transfer events.
...
use chaindexing::{Contract, EventContext, EventHandler};
use chaindexing::utils::address_to_string;
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;
let event_params = event.get_params();
let from = address_to_string(&event_params.get("from").unwrap().clone().into_address().unwrap());
let to = address_to_string(&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)];
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,
}
.create(&event_context)
.await;
}
}
}
- Start the indexing background process:
...
use chaindexing::{Chain, Chaindexing, Chains, Config, Contract, PostgresRepo, Repo};
#[tokio::main]
async fn main() {
let bayc_contract = Contract::new("BoredApeYachtClub")
.add_event("event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)", TransferEventHandler)
.add_state_migrations(NftStateMigrations)
.add_address(
"0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
&Chain::Mainnet,
17773490,
);
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,
);
let config = Config::new(
PostgresRepo::new("postgres://postgres:postgres@localhost/example-db"),
HashMap::from([(
Chain::Mainnet,
"https://eth-mainnet.g.alchemy.com/v2/some-secret"
)]),
)
.add_contract(bayc_contract)
.add_contract(doodles_contract);
Chaindexing::index_states(&config).await.unwrap();
}
- Query your DB using any approach/ORM:
select * from nft_states