Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(torii): index whitelisted erc20/erc721 #2442

Merged
merged 7 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 76 additions & 9 deletions bin/torii/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use std::cmp;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
Expand All @@ -31,6 +32,9 @@ use tokio::sync::broadcast;
use tokio::sync::broadcast::Sender;
use tokio_stream::StreamExt;
use torii_core::engine::{Engine, EngineConfig, IndexingFlags, Processors};
use torii_core::processors::erc20_legacy_transfer::Erc20LegacyTransferProcessor;
use torii_core::processors::erc20_transfer::Erc20TransferProcessor;
use torii_core::processors::erc721_transfer::Erc721TransferProcessor;
use torii_core::processors::event_message::EventMessageProcessor;
use torii_core::processors::generate_event_processors_map;
use torii_core::processors::metadata_update::MetadataUpdateProcessor;
Expand All @@ -42,7 +46,7 @@ use torii_core::processors::store_update_member::StoreUpdateMemberProcessor;
use torii_core::processors::store_update_record::StoreUpdateRecordProcessor;
use torii_core::simple_broker::SimpleBroker;
use torii_core::sql::Sql;
use torii_core::types::Model;
use torii_core::types::{ErcContract, ErcType, Model, ToriiConfig};
use torii_server::proxy::Proxy;
use tracing::{error, info};
use tracing_subscriber::{fmt, EnvFilter};
Expand Down Expand Up @@ -140,11 +144,38 @@ struct Args {
/// Whether or not to index raw events
#[arg(long, action = ArgAction::Set, default_value_t = true)]
index_raw_events: bool,

/// ERC contract addresses to index
#[arg(long, value_parser = parse_erc_contracts)]
#[arg(conflicts_with = "config")]
erc_contracts: Option<std::vec::Vec<ErcContract>>,

/// Configuration file
#[arg(long)]
config: Option<PathBuf>,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();

let mut start_block = args.start_block;

let mut config = if let Some(path) = args.config {
ToriiConfig::load_from_path(&path)?
} else {
ToriiConfig::default()
};

if let Some(erc_contracts) = args.erc_contracts {
config.erc_contracts = erc_contracts;
}

for address in &config.erc_contracts {
if address.start_block < start_block {
start_block = address.start_block;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ohayo sensei! Let's fine-tune the start block calculation.

The logic for handling ERC contracts from config or command-line arguments looks good. However, there's a small optimization we can make in the start block calculation:

-    let mut start_block = args.start_block;
+    let mut start_block = args.start_block;
 
     if let Some(erc_contracts) = args.erc_contracts {
         config.erc_contracts = erc_contracts;
     }
 
-    for address in &config.erc_contracts {
-        if address.start_block < start_block {
-            start_block = address.start_block;
-        }
-    }
+    start_block = config.erc_contracts.iter()
+        .map(|contract| contract.start_block)
+        .min()
+        .unwrap_or(start_block);

This change uses Rust's iterator methods to find the minimum start block more efficiently and concisely.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let mut start_block = args.start_block;
let mut config = if let Some(path) = args.config {
ToriiConfig::load_from_path(&path)?
} else {
ToriiConfig::default()
};
if let Some(erc_contracts) = args.erc_contracts {
config.erc_contracts = erc_contracts;
}
for address in &config.erc_contracts {
if address.start_block < start_block {
start_block = address.start_block;
}
}
let mut start_block = args.start_block;
let mut config = if let Some(path) = args.config {
ToriiConfig::load_from_path(&path)?
} else {
ToriiConfig::default()
};
if let Some(erc_contracts) = args.erc_contracts {
config.erc_contracts = erc_contracts;
}
start_block = config.erc_contracts.iter()
.map(|contract| contract.start_block)
.min()
.unwrap_or(start_block);

let filter_layer = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,hyper_reverse_proxy=off"));

Expand Down Expand Up @@ -185,17 +216,26 @@ async fn main() -> anyhow::Result<()> {
// Get world address
let world = WorldContractReader::new(args.world_address, provider.clone());

let db = Sql::new(pool.clone(), args.world_address).await?;
let erc_contracts = config
.erc_contracts
.iter()
.map(|contract| (contract.contract_address, contract.clone()))
.collect();

let db = Sql::new(pool.clone(), args.world_address, &erc_contracts).await?;

let processors = Processors {
event: generate_event_processors_map(vec![
Arc::new(RegisterModelProcessor),
Arc::new(StoreSetRecordProcessor),
Arc::new(MetadataUpdateProcessor),
Arc::new(StoreDelRecordProcessor),
Arc::new(EventMessageProcessor),
Arc::new(StoreUpdateRecordProcessor),
Arc::new(StoreUpdateMemberProcessor),
Box::new(RegisterModelProcessor),
Box::new(StoreSetRecordProcessor),
Box::new(MetadataUpdateProcessor),
Box::new(StoreDelRecordProcessor),
Box::new(EventMessageProcessor),
Box::new(StoreUpdateRecordProcessor),
Box::new(StoreUpdateMemberProcessor),
Box::new(Erc20LegacyTransferProcessor),
Box::new(Erc20TransferProcessor),
Box::new(Erc721TransferProcessor),
])?,
transaction: vec![Box::new(StoreTransactionProcessor)],
..Processors::default()
Expand Down Expand Up @@ -226,6 +266,7 @@ async fn main() -> anyhow::Result<()> {
},
shutdown_tx.clone(),
Some(block_tx),
erc_contracts,
);

let shutdown_rx = shutdown_tx.subscribe();
Expand Down Expand Up @@ -322,3 +363,29 @@ async fn spawn_rebuilding_graphql_server(
}
}
}

// Parses clap cli argument which is expected to be in the format:
// - erc_type:address:start_block
// - address:start_block (erc_type defaults to ERC20)
fn parse_erc_contracts(s: &str) -> anyhow::Result<Vec<ErcContract>> {
let parts: Vec<&str> = s.split(',').collect();
let mut contracts = Vec::new();
for part in parts {
match part.split(':').collect::<Vec<&str>>().as_slice() {
[r#type, address, start_block] => {
let contract_address = Felt::from_str(address).unwrap();
let start_block = start_block.parse::<u64>()?;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohayo sensei! Let's handle those parsing errors gracefully.

Using unwrap when parsing the contract address can cause the application to panic if an invalid address is provided. It's better to propagate the error using the ? operator for a smoother experience.

Here's a suggestion:

-                let contract_address = Felt::from_str(address).unwrap();
+                let contract_address = Felt::from_str(address)?;

Apply the same change to line 365 for consistency.

Also applies to: 365-366

let r#type = r#type.parse::<ErcType>()?;
contracts.push(ErcContract { contract_address, start_block, r#type });
}
[address, start_block] => {
let contract_address = Felt::from_str(address)?;
let start_block = start_block.parse::<u64>()?;
let r#type = ErcType::default();
contracts.push(ErcContract { contract_address, start_block, r#type });
}
_ => return Err(anyhow::anyhow!("Invalid ERC contract format")),
}
}
Ok(contracts)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ohayo sensei! Let's enhance the parse_erc_contracts function!

The function is a good start, but we can make it more robust:

  1. Improve error handling:
use thiserror::Error;

#[derive(Error, Debug)]
enum ParseError {
    #[error("Invalid ERC contract format")]
    InvalidFormat,
    #[error("Invalid address: {0}")]
    InvalidAddress(#[from] starknet::core::types::FieldElementError),
    #[error("Invalid start block: {0}")]
    InvalidStartBlock(#[from] std::num::ParseIntError),
    #[error("Invalid ERC type: {0}")]
    InvalidErcType(#[from] std::string::ParseError),
}

fn parse_erc_contracts(s: &str) -> Result<Vec<ErcContract>, ParseError> {
    // ... (rest of the function)
}
  1. Replace unwrap() with proper error propagation:
-                let contract_address = Felt::from_str(address).unwrap();
+                let contract_address = Felt::from_str(address)?;
  1. Add input validation:
if s.is_empty() {
    return Err(ParseError::InvalidFormat);
}
  1. Use ? consistently for error propagation.

These changes will make the function more robust and provide better error messages.

8 changes: 8 additions & 0 deletions bin/torii/torii.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Example configuration file for Torii
# erc_contracts = [
# { contract_address = "0x1234567890abcdef1234567890abcdef12345678", start_block = 0, type = "ERC20" },
# { contract_address = "0xabcdef1234567890abcdef1234567890abcdef12", start_block = 1, type = "ERC721" },
# ]
# erc_contracts = [
# { type = "ERC20", contract_address = "0x07fc13cc1f43f0b0519f84df8bf13bea4d9fd5ce2d748c3baf27bf90a565f60a", start_block = 0 },
# ]
1 change: 1 addition & 0 deletions crates/torii/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ thiserror.workspace = true
tokio = { version = "1.32.0", features = [ "sync" ], default-features = true }
tokio-stream = "0.1.11"
tokio-util = "0.7.7"
toml.workspace = true
tracing.workspace = true
clap.workspace = true
bitflags = "2.6.0"
Expand Down
Loading
Loading