Skip to content

Commit

Permalink
feat(torii): indexing cartridge controllers (#2959)
Browse files Browse the repository at this point in the history
* feat(torii): indexing cartridge controllers & cartrdige paymaster contracts

* processor and migration

* rename

* use adderss for paralle

* fmt

* use udc to retrieve events

* read paymaster accounts

* use cartridge magic instead of deployer address

* comments

* c

* logs

* chore

* format address in hex

* unused event id

* rename

* fix build
  • Loading branch information
Larkooo authored Feb 1, 2025
1 parent f4b733d commit a9e65a3
Show file tree
Hide file tree
Showing 16 changed files with 208 additions and 12 deletions.
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.

14 changes: 14 additions & 0 deletions crates/torii/cli/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,15 @@ pub struct IndexingOptions {
#[serde(default)]
pub world_block: u64,

/// Whether or not to index Cartridge controllers.
#[arg(
long = "indexing.controllers",
default_value_t = false,
help = "Whether or not to index Cartridge controllers."
)]
#[serde(default)]
pub controllers: bool,

/// Whether or not to read models from the block number they were registered in.
/// If false, models will be read from the latest block.
#[arg(
Expand All @@ -194,6 +203,7 @@ impl Default for IndexingOptions {
max_concurrent_tasks: DEFAULT_MAX_CONCURRENT_TASKS,
namespaces: vec![],
world_block: 0,
controllers: false,
strict_model_reader: false,
}
}
Expand Down Expand Up @@ -238,6 +248,10 @@ impl IndexingOptions {
self.world_block = other.world_block;
}

if !self.controllers {
self.controllers = other.controllers;
}

if !self.strict_model_reader {
self.strict_model_reader = other.strict_model_reader;
}
Expand Down
1 change: 1 addition & 0 deletions crates/torii/indexer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ipfs-api-backend-hyper.workspace = true
tokio-util.workspace = true
tracing.workspace = true
torii-sqlite.workspace = true
lazy_static.workspace = true

[dev-dependencies]
dojo-test-utils.workspace = true
Expand Down
5 changes: 4 additions & 1 deletion crates/torii/indexer/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use torii_sqlite::{Cursors, Sql};
use tracing::{debug, error, info, trace, warn};

use crate::constants::LOG_TARGET;
use crate::processors::controller::ControllerProcessor;
use crate::processors::erc20_legacy_transfer::Erc20LegacyTransferProcessor;
use crate::processors::erc20_transfer::Erc20TransferProcessor;
use crate::processors::erc721_legacy_transfer::Erc721LegacyTransferProcessor;
Expand All @@ -39,6 +40,7 @@ use crate::processors::register_event::RegisterEventProcessor;
use crate::processors::register_model::RegisterModelProcessor;
use crate::processors::store_del_record::StoreDelRecordProcessor;
use crate::processors::store_set_record::StoreSetRecordProcessor;
use crate::processors::store_transaction::StoreTransactionProcessor;
use crate::processors::store_update_member::StoreUpdateMemberProcessor;
use crate::processors::store_update_record::StoreUpdateRecordProcessor;
use crate::processors::upgrade_event::UpgradeEventProcessor;
Expand All @@ -62,7 +64,7 @@ impl<P: Provider + Send + Sync + std::fmt::Debug + 'static> Default for Processo
fn default() -> Self {
Self {
block: vec![],
transaction: vec![],
transaction: vec![Box::new(StoreTransactionProcessor)],
// We shouldn't have a catch all for now since the world doesn't forward raw events
// anymore.
catch_all_event: Box::new(RawEventProcessor) as Box<dyn EventProcessor<P>>,
Expand Down Expand Up @@ -105,6 +107,7 @@ impl<P: Provider + Send + Sync + std::fmt::Debug + 'static> Processors<P> {
Box::new(Erc721LegacyTransferProcessor) as Box<dyn EventProcessor<P>>,
],
),
(ContractType::UDC, vec![Box::new(ControllerProcessor) as Box<dyn EventProcessor<P>>]),
];

for (contract_type, processors) in event_processors {
Expand Down
124 changes: 124 additions & 0 deletions crates/torii/indexer/src/processors/controller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use std::hash::{DefaultHasher, Hash, Hasher};

use anyhow::{Error, Result};
use async_trait::async_trait;
use dojo_world::contracts::world::WorldContractReader;
use lazy_static::lazy_static;
use starknet::core::types::Event;
use starknet::core::utils::parse_cairo_short_string;
use starknet::macros::felt;
use starknet::providers::Provider;
use starknet_crypto::Felt;
use torii_sqlite::Sql;
use tracing::info;

use super::{EventProcessor, EventProcessorConfig};
use crate::task_manager::{TaskId, TaskPriority};

pub(crate) const LOG_TARGET: &str = "torii_indexer::processors::controller";

#[derive(Default, Debug)]
pub struct ControllerProcessor;

lazy_static! {
// https://x.cartridge.gg/
pub(crate) static ref CARTRIDGE_MAGIC: [Felt; 22] = [
felt!("0x68"),
felt!("0x74"),
felt!("0x74"),
felt!("0x70"),
felt!("0x73"),
felt!("0x3a"),
felt!("0x2f"),
felt!("0x2f"),
felt!("0x78"),
felt!("0x2e"),
felt!("0x63"),
felt!("0x61"),
felt!("0x72"),
felt!("0x74"),
felt!("0x72"),
felt!("0x69"),
felt!("0x64"),
felt!("0x67"),
felt!("0x65"),
felt!("0x2e"),
felt!("0x67"),
felt!("0x67"),
];
}

#[async_trait]
impl<P> EventProcessor<P> for ControllerProcessor
where
P: Provider + Send + Sync + std::fmt::Debug,
{
fn event_key(&self) -> String {
"ContractDeployed".to_string()
}

fn validate(&self, event: &Event) -> bool {
// ContractDeployed event has no keys and contains username in data
event.keys.len() == 1 && !event.data.is_empty()
}

fn task_priority(&self) -> TaskPriority {
3
}

fn task_identifier(&self, event: &Event) -> TaskId {
let mut hasher = DefaultHasher::new();
// the contract address is the first felt in data
event.data[0].hash(&mut hasher);
hasher.finish()
}

async fn process(
&self,
_world: &WorldContractReader<P>,
db: &mut Sql,
_block_number: u64,
block_timestamp: u64,
_event_id: &str,
event: &Event,
_config: &EventProcessorConfig,
) -> Result<(), Error> {
// Address is the first felt in data
let address = event.data[0];

let calldata = event.data[5..].to_vec();
// our calldata has to be more than 25 felts.
if calldata.len() < 25 {
return Ok(());
}
// check for this sequence of felts
let cartridge_magic_len = calldata[2];
// length has to be 22
if cartridge_magic_len != Felt::from(22) {
return Ok(());
}

// this should never fail if since our len is 22
let cartridge_magic: [Felt; 22] = calldata[3..25].try_into().unwrap();

// has to match with https://x.cartridge.gg/
if !CARTRIDGE_MAGIC.eq(&cartridge_magic) {
return Ok(());
}

// Last felt in data is the salt which is the username encoded as short string
let username_felt = event.data[event.data.len() - 1];
let username = parse_cairo_short_string(&username_felt)?;

info!(
target: LOG_TARGET,
username = %username,
address = %format!("{address:#x}"),
"Controller deployed."
);

db.add_controller(&username, &format!("{address:#x}"), block_timestamp).await?;

Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ where
block_number,
)
.await?;
debug!(target: LOG_TARGET,from = ?from, to = ?to, value = ?value, "Legacy ERC20 Transfer");
debug!(target: LOG_TARGET,from = ?from, to = ?to, value = ?value, "Legacy ERC20 Transfer.");

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/torii/indexer/src/processors/erc20_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ where
block_number,
)
.await?;
debug!(target: LOG_TARGET,from = ?from, to = ?to, value = ?value, "ERC20 Transfer");
debug!(target: LOG_TARGET,from = ?from, to = ?to, value = ?value, "ERC20 Transfer.");

Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ where
block_number,
)
.await?;
debug!(target: LOG_TARGET, from = ?from, to = ?to, token_id = ?token_id, "ERC721 Transfer");
debug!(target: LOG_TARGET, from = ?from, to = ?to, token_id = ?token_id, "ERC721 Transfer.");

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/torii/indexer/src/processors/erc721_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ where
block_number,
)
.await?;
debug!(target: LOG_TARGET, from = ?from, to = ?to, token_id = ?token_id, "ERC721 Transfer");
debug!(target: LOG_TARGET, from = ?from, to = ?to, token_id = ?token_id, "ERC721 Transfer.");

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/torii/indexer/src/processors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use torii_sqlite::Sql;

use crate::task_manager::{TaskId, TaskPriority};

pub mod controller;
pub mod erc20_legacy_transfer;
pub mod erc20_transfer;
pub mod erc721_legacy_transfer;
Expand All @@ -25,7 +26,6 @@ pub mod store_update_member;
pub mod store_update_record;
pub mod upgrade_event;
pub mod upgrade_model;

#[derive(Clone, Debug, Default)]
pub struct EventProcessorConfig {
pub historical_events: HashSet<String>,
Expand Down
9 changes: 9 additions & 0 deletions crates/torii/migrations/20250128051146_controllers.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Cartridge controllers
CREATE TABLE controllers (
id TEXT PRIMARY KEY NOT NULL, -- Username as primary key
username TEXT NOT NULL, -- Username
address TEXT NOT NULL, -- Wallet address
deployed_at TIMESTAMP NOT NULL -- Block timestamp of deployment
);

CREATE INDEX idx_controllers_address ON controllers (address);
7 changes: 7 additions & 0 deletions crates/torii/runner/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use starknet::macros::felt;
use starknet_crypto::Felt;

pub(crate) const LOG_TARGET: &str = "torii:runner";

pub(crate) const UDC_ADDRESS: Felt =
felt!("0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf");
18 changes: 12 additions & 6 deletions crates/torii/runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::sync::Arc;
use std::time::Duration;

use camino::Utf8PathBuf;
use constants::UDC_ADDRESS;
use dojo_metrics::exporters::prometheus::PrometheusRecorder;
use dojo_world::contracts::world::WorldContractReader;
use sqlx::sqlite::{
Expand All @@ -31,7 +32,6 @@ use tokio::sync::broadcast::Sender;
use tokio_stream::StreamExt;
use torii_cli::ToriiArgs;
use torii_indexer::engine::{Engine, EngineConfig, IndexingFlags, Processors};
use torii_indexer::processors::store_transaction::StoreTransactionProcessor;
use torii_indexer::processors::EventProcessorConfig;
use torii_server::proxy::Proxy;
use torii_sqlite::cache::ModelCache;
Expand All @@ -43,7 +43,9 @@ use tracing::{error, info};
use tracing_subscriber::{fmt, EnvFilter};
use url::form_urlencoded;

pub(crate) const LOG_TARGET: &str = "torii:runner";
mod constants;

use crate::constants::LOG_TARGET;

#[derive(Debug, Clone)]
pub struct Runner {
Expand All @@ -67,6 +69,13 @@ impl Runner {
.contracts
.push(Contract { address: world_address, r#type: ContractType::WORLD });

if self.args.indexing.controllers {
self.args
.indexing
.contracts
.push(Contract { address: UDC_ADDRESS, r#type: ContractType::UDC });
}

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

Expand Down Expand Up @@ -147,10 +156,7 @@ impl Runner {
)
.await?;

let processors = Processors {
transaction: vec![Box::new(StoreTransactionProcessor)],
..Processors::default()
};
let processors = Processors::default();

let (block_tx, block_rx) = tokio::sync::mpsc::channel(100);

Expand Down
1 change: 1 addition & 0 deletions crates/torii/sqlite/src/executor/erc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ impl<'c, P: Provider + Sync + Send + 'static> Executor<'c, P> {
let id = id_str.split(SQL_FELT_DELIMITER).collect::<Vec<&str>>();
match contract_type {
ContractType::WORLD => unreachable!(),
ContractType::UDC => unreachable!(),
ContractType::ERC721 => {
// account_address/contract_address:id => ERC721
assert!(id.len() == 2);
Expand Down
27 changes: 27 additions & 0 deletions crates/torii/sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,33 @@ impl Sql {
self.executor.send(rollback)?;
recv.await?
}

pub async fn add_controller(
&mut self,
username: &str,
address: &str,
block_timestamp: u64,
) -> Result<()> {
let insert_controller = "
INSERT INTO controllers (id, username, address, deployed_at)
VALUES (?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
username=EXCLUDED.username,
address=EXCLUDED.address,
deployed_at=EXCLUDED.deployed_at
RETURNING *";

let arguments = vec![
Argument::String(username.to_string()),
Argument::String(username.to_string()),
Argument::String(address.to_string()),
Argument::String(utc_dt_string_from_timestamp(block_timestamp)),
];

self.executor.send(QueryMessage::other(insert_controller.to_string(), arguments))?;

Ok(())
}
}

fn add_columns_recursive(
Expand Down
3 changes: 3 additions & 0 deletions crates/torii/sqlite/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ pub enum ContractType {
WORLD,
ERC20,
ERC721,
UDC,
}

impl std::fmt::Display for Contract {
Expand All @@ -172,6 +173,7 @@ impl FromStr for ContractType {
"world" => Ok(ContractType::WORLD),
"erc20" => Ok(ContractType::ERC20),
"erc721" => Ok(ContractType::ERC721),
"udc" => Ok(ContractType::UDC),
_ => Err(anyhow::anyhow!("Invalid ERC type: {}", input)),
}
}
Expand All @@ -183,6 +185,7 @@ impl std::fmt::Display for ContractType {
ContractType::WORLD => write!(f, "WORLD"),
ContractType::ERC20 => write!(f, "ERC20"),
ContractType::ERC721 => write!(f, "ERC721"),
ContractType::UDC => write!(f, "UDC"),
}
}
}
Expand Down

0 comments on commit a9e65a3

Please sign in to comment.