-
Notifications
You must be signed in to change notification settings - Fork 193
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): indexing cartridge controllers #2959
Changes from all commits
bc1b9bd
cecc550
4069998
18ae749
cc5c921
35a17dd
497c05c
15617e4
31833a2
ad16026
3e5fb4d
e3102f2
7788904
bf62d26
bd97e10
68f9904
a0a1cb4
c2ad549
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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(); | ||
Larkooo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 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)?; | ||
Larkooo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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 |
---|---|---|
@@ -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); |
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"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
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::{ | ||
|
@@ -31,7 +32,6 @@ | |
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; | ||
|
@@ -43,7 +43,9 @@ | |
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 { | ||
|
@@ -67,6 +69,13 @@ | |
.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 }); | ||
} | ||
Comment on lines
+72
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Ohayo! UDC address format inconsistency detected, sensei! The UDC address has two different formats in the codebase:
While both represent the same value, consider standardizing the format across all files for better maintainability.
🔗 Analysis chainVerify UDC_ADDRESS constant value, sensei! The logic for adding the UDC contract looks good, but let's ensure the UDC_ADDRESS constant is valid. Run this script to verify the UDC address format and usage: 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify UDC_ADDRESS format and usage
# Test 1: Check if UDC_ADDRESS is a valid Starknet address
ast-grep --pattern 'pub const UDC_ADDRESS: &str = "$_";'
# Test 2: Check if UDC_ADDRESS is used consistently across the codebase
rg "UDC_ADDRESS" -A 2
Length of output: 4969 |
||
|
||
let filter_layer = EnvFilter::try_from_default_env() | ||
.unwrap_or_else(|_| EnvFilter::new("info,hyper_reverse_proxy=off")); | ||
|
||
|
@@ -147,10 +156,7 @@ | |
) | ||
.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); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider tightening bounds checks in
validate
, sensei!You rely on
event.data[5..]
slicing later inprocess
. Ifevent.data
has fewer than 5 items, slicing will silently panic. It may be safer to validate the minimal number of felts here or handle it gracefully inprocess
.