diff --git a/chaindexing/src/chains.rs b/chaindexing/src/chains.rs index de84c31..a1f38ec 100644 --- a/chaindexing/src/chains.rs +++ b/chaindexing/src/chains.rs @@ -1,5 +1,8 @@ +/// Represents the chain network ID for contracts being indexed. +/// For example, ChainId::Mainnet, ChainId::Polygon, etc. pub type ChainId = ethers::types::Chain; +/// Represents chain network for contracts being indexed #[derive(Clone, Debug)] pub struct Chain { pub id: ChainId, @@ -7,6 +10,15 @@ pub struct Chain { } impl Chain { + /// Builds the chain network + /// + /// + /// # Example + /// ``` + /// use chaindexing::{Chain, ChainId}; + /// + /// Chain::new(ChainId::Polygon, "https://polygon-mainnet.g.alchemy.com/v2/..."); + /// ``` pub fn new(id: ChainId, json_rpc_url: &str) -> Self { Self { id, diff --git a/chaindexing/src/config.rs b/chaindexing/src/config.rs index ba8da56..070fff9 100644 --- a/chaindexing/src/config.rs +++ b/chaindexing/src/config.rs @@ -26,17 +26,20 @@ impl std::fmt::Debug for ConfigError { } } +/// Used to configure managing a chaindexing's node heartbeat +/// based on activities from one's Dapp to achieve ergonomic +/// ingesting to cut down RPC drastically #[derive(Clone, Debug)] pub struct OptimizationConfig { pub(crate) node_heartbeat: NodeHeartbeat, - /// Optimization starts after the seconds specified here. - /// This is the typically the estimated time to complete initial indexing - /// i.e. the estimated time in seconds for chaindexing to reach - /// the current block for all chains being indexed. pub(crate) start_after_in_secs: u64, } impl OptimizationConfig { + /// Optimization starts after the seconds specified here. + /// This is the typically the estimated time to complete initial indexing + /// i.e. the estimated time in seconds for chaindexing to reach + /// the current block for all chains being indexed. pub fn new(node_heartbeat: &NodeHeartbeat, start_after_in_secs: u64) -> Self { Self { node_heartbeat: node_heartbeat.clone(), diff --git a/chaindexing/src/contracts.rs b/chaindexing/src/contracts.rs index 75fd643..7286d7e 100644 --- a/chaindexing/src/contracts.rs +++ b/chaindexing/src/contracts.rs @@ -32,9 +32,11 @@ impl ContractEvent { } } +/// Human Readable ABI defined for ingesting events. +/// For example, event Transfer(address indexed from, address indexed to, uint256 indexed tokenId) pub type EventAbi = &'static str; -/// Represents the specification for a given contract. +/// Represents the template/specification/interface for a given contract. #[derive(Clone)] pub struct Contract { pub addresses: Vec, @@ -45,7 +47,7 @@ pub struct Contract { } impl Contract { - /// Builds the specification for a contract. + /// Builds the contract's template/spec/interface. /// /// /// # Example diff --git a/chaindexing/src/events/event.rs b/chaindexing/src/events/event.rs index 9d1e49a..c4f0d7c 100644 --- a/chaindexing/src/events/event.rs +++ b/chaindexing/src/events/event.rs @@ -12,8 +12,8 @@ use uuid::Uuid; use serde::Deserialize; -/// An event aka provider logs are emitted from smart contracts -/// to help infer their state +/// Events, aka. provider logs, are emitted from smart contracts +/// to help infer their states. #[derive(Debug, Deserialize, Clone, Eq, Queryable, Insertable)] #[diesel(table_name = chaindexing_events)] pub struct Event { @@ -99,35 +99,33 @@ impl Event { self.abi.as_str() } + /// Returns the event's block number pub fn get_block_number(&self) -> u64 { self.block_number as u64 } + /// Returns the event's block timestamp pub fn get_block_timestamp(&self) -> u64 { self.block_timestamp as u64 } + /// Returns the event's transaction index pub fn get_transaction_index(&self) -> u32 { self.transaction_index as u32 } + /// Returns the event's log index pub fn get_log_index(&self) -> u32 { self.log_index as u32 } + /// Returns the event's parameters pub fn get_params(&self) -> EventParam { EventParam::new(&self.parameters) } + /// Returns the event's chain id pub fn get_chain_id(&self) -> ChainId { U64::from(self.chain_id).try_into().unwrap() } - pub fn not_removed(&self) -> bool { - !self.removed - } - - pub fn match_contract_address(&self, contract_address: &str) -> bool { - self.contract_address.to_lowercase() == *contract_address.to_lowercase() - } - fn log_params_to_parameters(log_params: &[LogParam]) -> HashMap { log_params.iter().fold(HashMap::new(), |mut parameters, log_param| { parameters.insert(log_param.name.to_string(), log_param.value.clone()); @@ -137,6 +135,9 @@ impl Event { } } +/// Represents the parameters parsed from an event/log. +/// Contains convenient parsers to convert or transform into useful primitives +/// as needed. pub struct EventParam { value: HashMap, } diff --git a/chaindexing/src/handlers/pure_handler.rs b/chaindexing/src/handlers/pure_handler.rs index 1aa65d6..4f1ba52 100644 --- a/chaindexing/src/handlers/pure_handler.rs +++ b/chaindexing/src/handlers/pure_handler.rs @@ -14,12 +14,13 @@ use super::handler_context::HandlerContext; pub trait PureHandler: Send + Sync { /// The human-readable ABI of the event being handled. /// For example, Uniswap's PoolCreated event's name is: - /// PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool) + /// PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool). /// The chain explorer's event section can also be used to easily infer this fn abi(&self) -> &'static str; async fn handle_event<'a, 'b>(&self, context: PureHandlerContext<'a, 'b>); } +/// Event's context in a pure event handler #[derive(Clone)] pub struct PureHandlerContext<'a, 'b> { pub event: Event, diff --git a/chaindexing/src/handlers/side_effect_handler.rs b/chaindexing/src/handlers/side_effect_handler.rs index a7d2290..bc52fc1 100644 --- a/chaindexing/src/handlers/side_effect_handler.rs +++ b/chaindexing/src/handlers/side_effect_handler.rs @@ -13,20 +13,21 @@ use super::handler_context::HandlerContext; /// SideEffectHandlers are event handlers that help handle side-effects for events. /// This is useful for handling events only ONCE and can rely on a non-deterministic /// shared state. Some use-cases are notifications, bridging etc. Chaindexing ensures -/// that the side-effect handlers are called once immutable regardless of resets. -/// However, one can dangerously reset including side effects with the new `reset_including_side_effects` -/// Config API. +/// that the side-effect handlers are called once immutably regardless of resets. +/// However, one can dangerously reset including side effects with the `reset_including_side_effects` +/// exposed in the Config API. pub trait SideEffectHandler: Send + Sync { type SharedState: Send + Sync + Clone + Debug; /// The human-readable ABI of the event being handled. /// For example, Uniswap's PoolCreated event's name is: - /// PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool) - /// The chain explorer's event section can also be used to easily infer this + /// PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool). + /// The chain explorer's event section can also be used to easily infer this. fn abi(&self) -> &'static str; async fn handle_event<'a>(&self, context: SideEffectHandlerContext<'a, Self::SharedState>); } +/// Event's context in a side effect handler #[derive(Clone)] pub struct SideEffectHandlerContext<'a, SharedState: Sync + Send + Clone> { pub event: Event, diff --git a/chaindexing/src/lib.rs b/chaindexing/src/lib.rs index 37b7015..a2fc5c9 100644 --- a/chaindexing/src/lib.rs +++ b/chaindexing/src/lib.rs @@ -1,57 +1,92 @@ -pub mod booting; +// TODO: Add back +// #![warn( +// missing_debug_implementations, +// missing_docs, +// rust_2018_idioms, +// unreachable_pub +// )] + +//! # Chaindexing +//! Index any EVM chain and query in SQL. +//! +//! View working examples here: . mod chain_reorg; mod chains; mod config; mod contracts; -pub mod deferred_futures; mod diesel; -pub mod events; mod handlers; -pub mod ingester; mod nodes; mod pruning; mod repos; mod root; -pub mod states; pub use chains::{Chain, ChainId}; pub use config::{Config, OptimizationConfig}; -pub use contracts::{Contract, ContractAddress, ContractEvent, EventAbi, UnsavedContractAddress}; +pub use contracts::{Contract, ContractAddress, EventAbi}; pub use events::{Event, EventParam}; pub use handlers::{ PureHandler as EventHandler, PureHandlerContext as EventContext, SideEffectHandler, SideEffectHandlerContext as SideEffectContext, }; -pub use ingester::Provider as IngesterProvider; pub use nodes::NodeHeartbeat as Heartbeat; + +pub use ethers::types::{I256, U256}; +use tokio::sync::Mutex; + +/// Houses traits and structs for implementing states that can be indexed. +pub mod states; + +/// Hexadecimal representation of addresses (such as contract addresses) +pub type Address = ethers::types::Address; +/// Represents bytes +pub type Bytes = Vec; +#[cfg(feature = "postgres")] +pub use repos::PostgresRepo; + +#[doc(hidden)] +pub mod booting; +#[doc(hidden)] +pub mod deferred_futures; +#[doc(hidden)] +pub mod events; +#[doc(hidden)] +pub mod ingester; +#[doc(hidden)] +pub use contracts::{ContractEvent, UnsavedContractAddress}; +#[doc(hidden)] +pub use ingester::Provider as IngesterProvider; +#[doc(hidden)] pub use repos::*; +#[doc(hidden)] #[cfg(feature = "postgres")] -pub use repos::{PostgresRepo, PostgresRepoConn, PostgresRepoPool}; +pub use repos::{PostgresRepoConn, PostgresRepoPool}; #[cfg(feature = "postgres")] +#[doc(hidden)] pub type ChaindexingRepo = PostgresRepo; #[cfg(feature = "postgres")] +#[doc(hidden)] pub type ChaindexingRepoPool = PostgresRepoPool; #[cfg(feature = "postgres")] +#[doc(hidden)] pub type ChaindexingRepoConn<'a> = PostgresRepoConn<'a>; #[cfg(feature = "postgres")] +#[doc(hidden)] pub type ChaindexingRepoClient = PostgresRepoClient; #[cfg(feature = "postgres")] +#[doc(hidden)] pub type ChaindexingRepoTxnClient<'a> = PostgresRepoTxnClient<'a>; #[cfg(feature = "postgres")] +#[doc(hidden)] pub use repos::PostgresRepoAsyncConnection as ChaindexingRepoAsyncConnection; -pub use ethers::types::{Address, U256, U256 as BigInt, U256 as Uint}; -use tokio::sync::Mutex; - -pub type Bytes = Vec; - use std::fmt::Debug; use std::sync::Arc; use std::time::Duration; @@ -64,6 +99,7 @@ use crate::nodes::{NodeTask, NodeTasksRunner}; pub(crate) type ChaindexingRepoClientMutex = Arc>; +/// Errors from mis-configurations, database connections, internal errors, etc. pub enum ChaindexingError { Config(ConfigError), } @@ -84,7 +120,7 @@ impl Debug for ChaindexingError { } } -/// Starts processes to ingest and index states as configured +/// Starts processes for ingesting events and indexing states as configured. pub async fn index_states( config: &Config, ) -> Result<(), ChaindexingError> { @@ -129,14 +165,14 @@ pub async fn index_states( Ok(()) } -/// Includes runtime-discovered contract addresses for indexing +/// Includes runtime-discovered contract addresses for indexing. /// /// # Arguments /// -/// * `event_context` - context where the contract was discovered. Indexing starts -/// from this point onwards -/// * `name` - name of the contract specification as defined in the config -/// * `address` - address of the contract +/// * `event_context` - context where the contract was discovered. +/// N/B: Indexing for this contract starts from this point onwards +/// * `name` - name of the contract as defined in the config +/// * `address` - address of discovered contract /// /// # Example /// diff --git a/chaindexing/src/nodes/node_heartbeat.rs b/chaindexing/src/nodes/node_heartbeat.rs index 379b393..c2cd7fb 100644 --- a/chaindexing/src/nodes/node_heartbeat.rs +++ b/chaindexing/src/nodes/node_heartbeat.rs @@ -3,6 +3,9 @@ use std::fmt::Debug; use std::sync::Arc; use tokio::sync::Mutex; +/// A chaindexing node's heartbeat. +/// In a distributed environment, this is useful for managing the indexer's +/// processes such that RPC costs can be reduced based on triggering activities #[derive(Clone, Debug)] pub struct NodeHeartbeat { /// Both in milliseconds diff --git a/chaindexing/src/repos.rs b/chaindexing/src/repos.rs index 5b10f9a..43718b8 100644 --- a/chaindexing/src/repos.rs +++ b/chaindexing/src/repos.rs @@ -1,6 +1,7 @@ #[cfg(feature = "postgres")] mod postgres_repo; +#[doc(hidden)] #[cfg(feature = "postgres")] pub use postgres_repo::{ Conn as PostgresRepoConn, Pool as PostgresRepoPool, PostgresRepo, PostgresRepoAsyncConnection, @@ -9,8 +10,11 @@ pub use postgres_repo::{ mod repo; +#[doc(hidden)] pub use repo::{ExecutesWithRawQuery, HasRawQueryClient, Repo, RepoError}; +#[doc(hidden)] pub(crate) use repo::{LoadsDataWithRawQuery, Migratable, RepoMigrations, SQLikeMigrations}; +#[doc(hidden)] pub mod streams; diff --git a/chaindexing/src/repos/postgres_repo.rs b/chaindexing/src/repos/postgres_repo.rs index 07d5b89..fa4c3a8 100644 --- a/chaindexing/src/repos/postgres_repo.rs +++ b/chaindexing/src/repos/postgres_repo.rs @@ -38,6 +38,7 @@ impl From for RepoError { } } +/// Repo for Postgres databases #[derive(Clone, Debug)] pub struct PostgresRepo { url: String, diff --git a/chaindexing/src/repos/repo.rs b/chaindexing/src/repos/repo.rs index 9bffe0b..6ef691f 100644 --- a/chaindexing/src/repos/repo.rs +++ b/chaindexing/src/repos/repo.rs @@ -14,6 +14,7 @@ use crate::{ ContractAddress, }; +/// Errors from interacting the configured SQL database #[derive(Debug, Display)] pub enum RepoError { NotConnected, diff --git a/chaindexing/src/states.rs b/chaindexing/src/states.rs index 056eb00..dfb4ea3 100644 --- a/chaindexing/src/states.rs +++ b/chaindexing/src/states.rs @@ -1,3 +1,37 @@ +//! # States +//! Any struct that can be serialized and deserialized while implementing +//! any state type, such as ContractState, ChainState, MultiChainState etc. +//! is a valid Chaindexing State +//! +//! ## Example +//! +//! ```rust,no_run +//! use chaindexing::states::{ContractState, StateMigrations}; +//! use serde::{Deserialize, Serialize}; + +//! #[derive(Clone, Debug, Serialize, Deserialize)] +//! pub struct Nft { +//! pub token_id: u32, +//! pub owner_address: String, +//! } + +//! impl ContractState for Nft { +//! fn table_name() -> &'static str { +//! "nfts" +//! } +//! } + +//! pub struct NftMigrations; + +//! impl StateMigrations for NftMigrations { +//! fn migrations(&self) -> &'static [&'static str] { +//! &["CREATE TABLE IF NOT EXISTS nfts ( +//! token_id INTEGER NOT NULL, +//! owner_address TEXT NOT NULL +//! )"] +//! } +//! } +//! ``` pub use migrations::StateMigrations; use std::collections::HashMap; @@ -28,7 +62,7 @@ pub use multi_chain_state::MultiChainState; use state_versions::{StateVersion, StateVersions, STATE_VERSIONS_TABLE_PREFIX}; use state_views::StateViews; -pub async fn backtrack_states<'a>( +pub(crate) async fn backtrack_states<'a>( table_names: &Vec, chain_id: i64, block_number: i64, @@ -45,7 +79,7 @@ pub async fn backtrack_states<'a>( } } -pub async fn prune_state_versions( +pub(crate) async fn prune_state_versions( table_names: &Vec, client: &ChaindexingRepoClient, min_block_number: u64, @@ -68,7 +102,7 @@ pub async fn prune_state_versions( } } -pub fn get_all_table_names(state_migrations: &[Arc]) -> Vec { +pub(crate) fn get_all_table_names(state_migrations: &[Arc]) -> Vec { state_migrations .iter() .flat_map(|state_migration| state_migration.get_table_names()) diff --git a/chaindexing/src/states/migrations.rs b/chaindexing/src/states/migrations.rs index 817e572..706eebc 100644 --- a/chaindexing/src/states/migrations.rs +++ b/chaindexing/src/states/migrations.rs @@ -2,9 +2,8 @@ use std::collections::HashMap; use super::STATE_VERSIONS_TABLE_PREFIX; -// Since contract states are rebuildable from ground up, we can -// easen the type strictness for consumer applications. -// Trait/Callback? this way, consumer apps can statically visualize their migrations +/// Represents the idempotent database migrations required before +/// indexing a state. pub trait StateMigrations: Send + Sync { /// SQL migrations for the state to index. These migrations must be idempotent /// and will require using the 'IF NOT EXISTS` check