diff --git a/bin/sozo/src/commands/events.rs b/bin/sozo/src/commands/events.rs index 5471de2fde..1d0bd50891 100644 --- a/bin/sozo/src/commands/events.rs +++ b/bin/sozo/src/commands/events.rs @@ -4,12 +4,12 @@ use std::sync::Arc; use anyhow::Result; use clap::Args; use colored::Colorize; -use dojo_world::contracts::abigen::world::{self, Event as WorldEvent}; +use dojo_world::contracts::abigen::world::{ self, Event as WorldEvent }; use dojo_world::diff::WorldDiff; use scarb::core::Config; use sozo_ops::model; use sozo_scarbext::WorkspaceExt; -use starknet::core::types::{BlockId, BlockTag, EventFilter, Felt}; +use starknet::core::types::{ BlockId, BlockTag, EventFilter, Felt }; use starknet::core::utils::starknet_keccak; use starknet::providers::Provider; @@ -50,40 +50,55 @@ pub struct EventsArgs { #[command(flatten)] pub starknet: StarknetOptions, } - +const BLOCK_LIMIT: u64 = 50_000; // Maximum block range to process in one iteration impl EventsArgs { pub fn run(self, config: &Config) -> Result<()> { config.tokio_handle().block_on(async { let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; let profile_config = ws.load_profile_config()?; - let (world_diff, provider, _) = - utils::get_world_diff_and_provider(self.starknet, self.world, &ws).await?; + let (world_diff, provider, _) = utils::get_world_diff_and_provider( + self.starknet, + self.world, + &ws + ).await?; let provider = Arc::new(provider); - let from_block = if let Some(world_block) = - profile_config.env.as_ref().and_then(|e| e.world_block) - { - Some(BlockId::Number(world_block)) - } else { - self.from_block.map(BlockId::Number) - }; + let from_block = self.from_block.unwrap_or(0); + let to_block = self.to_block.unwrap_or(u64::MAX); + + if from_block >= to_block { + anyhow::bail!("Invalid block range: from_block >= to_block"); + } + + + let mut current_start = from_block; + let continuation_token = None; - let to_block = self.to_block.map(BlockId::Number); - let keys = self - .events - .map(|e| vec![e.iter().map(|event| starknet_keccak(event.as_bytes())).collect()]); + while current_start < to_block { + let current_end = (current_start + BLOCK_LIMIT).min(to_block); + let keys = self.events.clone().map(|e| + vec![ + e + .iter() + .map(|event| starknet_keccak(event.as_bytes())) + .collect() + ] + ); let event_filter = EventFilter { - from_block, - to_block, + from_block: Some(BlockId::Number(current_start)), + to_block: Some(BlockId::Number(current_end)), address: Some(world_diff.world_info.address), keys, }; - let res = - provider.get_events(event_filter, self.continuation_token, self.chunk_size).await?; + let res = provider.get_events( + event_filter, + continuation_token.clone(), + self.chunk_size + ).await?; for event in &res.events { match world::Event::try_from(event) { @@ -93,10 +108,8 @@ impl EventsArgs { &world_diff, event.block_number, event.transaction_hash, - &provider, - ) - .await - .unwrap_or_else(|e| { + &provider + ).await.unwrap_or_else(|e| { tracing::error!(?e, "Failed to process event: {:?}", ev); }); } @@ -108,11 +121,21 @@ impl EventsArgs { } } } + + + // Update continuation token or block range + let continuation_token = res.continuation_token.clone(); + if continuation_token.is_none() { + current_start = current_end + 1; + } - if let Some(continuation_token) = res.continuation_token { - println!("Continuation token: {:?}", continuation_token); + // Log continuation token if present + if let Some(token) = &continuation_token { + println!("Continuation token: {:?}", token); println!("----------------------------------------------"); } + } + Ok(()) }) @@ -125,13 +148,15 @@ async fn match_event( world_diff: &WorldDiff, block_number: Option, transaction_hash: Felt, - provider: P, + provider: P ) -> Result<()> { // Get a mapping of all the known selectors and their addresses. let contract_addresses_from_selector = world_diff.get_contracts_addresses(); // Do a reverse mapping to retrieve a contract selector from it's address. - let contract_selectors_from_address: HashMap = - contract_addresses_from_selector.into_iter().map(|(s, a)| (a, s)).collect(); + let contract_selectors_from_address: HashMap = contract_addresses_from_selector + .into_iter() + .map(|(s, a)| (a, s)) + .collect(); // Finally, cache all the known tags by creating them once for each selector. let mut tags = HashMap::new(); for (s, r) in world_diff.resources.iter() { @@ -145,47 +170,55 @@ async fn match_event( }; let (name, content) = match event { - WorldEvent::WorldSpawned(e) => ( - "World spawned".to_string(), - format!("Creator address: {:?}\nWorld class hash: {:#066x}", e.creator, e.class_hash.0), - ), + WorldEvent::WorldSpawned(e) => + ( + "World spawned".to_string(), + format!( + "Creator address: {:?}\nWorld class hash: {:#066x}", + e.creator, + e.class_hash.0 + ), + ), WorldEvent::WorldUpgraded(e) => { ("World upgraded".to_string(), format!("World class hash: {:#066x}", e.class_hash.0)) } WorldEvent::NamespaceRegistered(e) => { ("Namespace registered".to_string(), format!("Namespace: {}", e.namespace.to_string()?)) } - WorldEvent::ModelRegistered(e) => ( - "Model registered".to_string(), - format!( - "Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}", - e.namespace.to_string()?, - e.name.to_string()?, - e.class_hash.0, - e.address.0 + WorldEvent::ModelRegistered(e) => + ( + "Model registered".to_string(), + format!( + "Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}", + e.namespace.to_string()?, + e.name.to_string()?, + e.class_hash.0, + e.address.0 + ), ), - ), - WorldEvent::EventRegistered(e) => ( - "Event registered".to_string(), - format!( - "Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}", - e.namespace.to_string()?, - e.name.to_string()?, - e.class_hash.0, - e.address.0 + WorldEvent::EventRegistered(e) => + ( + "Event registered".to_string(), + format!( + "Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}", + e.namespace.to_string()?, + e.name.to_string()?, + e.class_hash.0, + e.address.0 + ), ), - ), - WorldEvent::ContractRegistered(e) => ( - "Contract registered".to_string(), - format!( - "Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}\nSalt: {:#066x}", - e.namespace.to_string()?, - e.name.to_string()?, - e.class_hash.0, - e.address.0, - e.salt + WorldEvent::ContractRegistered(e) => + ( + "Contract registered".to_string(), + format!( + "Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}\nSalt: {:#066x}", + e.namespace.to_string()?, + e.name.to_string()?, + e.class_hash.0, + e.address.0, + e.salt + ), ), - ), WorldEvent::ModelUpgraded(e) => { let tag = tags.get(&e.selector).unwrap(); ( @@ -193,7 +226,10 @@ async fn match_event( format!( "Selector: {:#066x}\nClass hash: {:#066x}\nAddress: {:#066x}\nPrev address: \ {:#066x}", - e.selector, e.class_hash.0, e.address.0, e.prev_address.0 + e.selector, + e.class_hash.0, + e.address.0, + e.prev_address.0 ), ) } @@ -204,7 +240,10 @@ async fn match_event( format!( "Selector: {:#066x}\nClass hash: {:#066x}\nAddress: {:#066x}\nPrev address: \ {:#066x}", - e.selector, e.class_hash.0, e.address.0, e.prev_address.0 + e.selector, + e.class_hash.0, + e.address.0, + e.prev_address.0 ), ) } @@ -212,7 +251,7 @@ async fn match_event( let tag = tags.get(&e.selector).unwrap(); ( format!("Contract upgraded ({})", tag), - format!("Selector: {:#066x}\nClass hash: {:#066x}", e.selector, e.class_hash.0,), + format!("Selector: {:#066x}\nClass hash: {:#066x}", e.selector, e.class_hash.0), ) } WorldEvent::ContractInitialized(e) => { @@ -232,12 +271,13 @@ async fn match_event( } WorldEvent::WriterUpdated(e) => { let tag = tags.get(&e.resource).unwrap(); - let grantee = - if let Some(selector) = contract_selectors_from_address.get(&e.contract.into()) { - tags.get(selector).unwrap().to_string() - } else { - format!("{:#066x}", e.contract.0) - }; + let grantee = if + let Some(selector) = contract_selectors_from_address.get(&e.contract.into()) + { + tags.get(selector).unwrap().to_string() + } else { + format!("{:#066x}", e.contract.0) + }; ( "Writer updated".to_string(), @@ -246,12 +286,13 @@ async fn match_event( } WorldEvent::OwnerUpdated(e) => { let tag = tags.get(&e.resource).unwrap(); - let grantee = - if let Some(selector) = contract_selectors_from_address.get(&e.contract.into()) { - tags.get(selector).unwrap().to_string() - } else { - format!("{:#066x}", e.contract.0) - }; + let grantee = if + let Some(selector) = contract_selectors_from_address.get(&e.contract.into()) + { + tags.get(selector).unwrap().to_string() + } else { + format!("{:#066x}", e.contract.0) + }; ( "Owner updated".to_string(), @@ -265,9 +306,8 @@ async fn match_event( e.keys.clone(), world_diff.world_info.address, provider, - block_id, - ) - .await?; + block_id + ).await?; ( format!("Store set record ({})", tag), @@ -302,7 +342,7 @@ async fn match_event( .iter() .map(|v| format!("{:#066x}", v)) .collect::>() - .join(", "), + .join(", ") ), ) } @@ -321,7 +361,7 @@ async fn match_event( .iter() .map(|v| format!("{:#066x}", v)) .collect::>() - .join(", "), + .join(", ") ), ) } @@ -329,13 +369,13 @@ async fn match_event( let tag = tags.get(&e.selector).unwrap(); ( format!("Store del record ({})", tag), - format!("Selector: {:#066x}\nEntity ID: {:#066x}", e.selector, e.entity_id,), + format!("Selector: {:#066x}\nEntity ID: {:#066x}", e.selector, e.entity_id), ) } WorldEvent::EventEmitted(e) => { let tag = tags.get(&e.selector).unwrap(); - let contract_tag = if let Some(selector) = - contract_selectors_from_address.get(&e.system_address.into()) + let contract_tag = if + let Some(selector) = contract_selectors_from_address.get(&e.system_address.into()) { tags.get(selector).unwrap().to_string() } else { @@ -359,7 +399,7 @@ async fn match_event( .iter() .map(|v| format!("{:#066x}", v)) .collect::>() - .join(", "), + .join(", ") ), ) }