From b6cca2d019dd639c1d7fd8dbd0ba16a8596b3a0d Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 2 Nov 2024 22:51:36 -0600 Subject: [PATCH] feat(sozo): add events back --- bin/sozo/src/commands/events.rs | 48 ++++------ bin/sozo/src/commands/mod.rs | 6 ++ crates/dojo/world/src/diff/resource.rs | 9 ++ crates/dojo/world/src/local/resource.rs | 12 ++- crates/sozo/ops/src/events.rs | 115 +++++++----------------- crates/sozo/ops/src/lib.rs | 1 + 6 files changed, 74 insertions(+), 117 deletions(-) diff --git a/bin/sozo/src/commands/events.rs b/bin/sozo/src/commands/events.rs index 5b7eee895d..b8e4212858 100644 --- a/bin/sozo/src/commands/events.rs +++ b/bin/sozo/src/commands/events.rs @@ -24,6 +24,7 @@ pub struct EventsArgs { #[arg(short, long)] #[arg(help = "Number of events to return per page")] + #[arg(default_value_t = 100)] pub chunk_size: u64, #[arg(long)] @@ -43,45 +44,30 @@ pub struct EventsArgs { impl EventsArgs { pub fn run(self, config: &Config) -> Result<()> { - let env_metadata = utils::load_metadata_from_config(config)?; - trace!(?env_metadata, "Metadata loaded from config."); - - let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; - trace!(ws_members_count = ws.members().count(), "Read workspace."); - - let project_dir = ws.manifest_path().parent().unwrap().to_path_buf(); - trace!(?project_dir, "Project directory defined from workspace."); + config.tokio_handle().block_on(async { + let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; - let provider = self.starknet.provider(env_metadata.as_ref())?; - trace!(?provider, "Starknet RPC client provider."); + let (world_diff, provider, _) = utils::get_world_diff_and_provider( + self.starknet, + self.world, + &ws, + ) + .await?; - let world_address = self.world.address(env_metadata.as_ref())?; - let event_filter = events::get_event_filter( - self.from_block, - self.to_block, - self.events, - Some(world_address), - ); - trace!( - from_block = self.from_block, - to_block = self.to_block, - chunk_size = self.chunk_size, - "Created event filter." - ); - let profile_name = - ws.current_profile().expect("Scarb profile expected at this point.").to_string(); - trace!(profile_name, "Current profile."); + let event_filter = events::get_event_filter( + self.from_block, + self.to_block, + self.events, + Some(world_diff.world_info.address), + ); - config.tokio_handle().block_on(async { trace!("Starting async event parsing."); events::parse( + &world_diff, + &provider, self.chunk_size, - provider, self.continuation_token, event_filter, - self.json, - &project_dir, - &profile_name, ) .await }) diff --git a/bin/sozo/src/commands/mod.rs b/bin/sozo/src/commands/mod.rs index db3f634390..316c476bac 100644 --- a/bin/sozo/src/commands/mod.rs +++ b/bin/sozo/src/commands/mod.rs @@ -2,6 +2,7 @@ use core::fmt; use anyhow::Result; use clap::Subcommand; +use events::EventsArgs; use scarb::core::{Config, Package, Workspace}; use tracing::info_span; @@ -16,6 +17,7 @@ pub(crate) mod inspect; pub(crate) mod migrate; pub(crate) mod options; pub(crate) mod test; +pub(crate) mod events; use build::BuildArgs; use call::CallArgs; @@ -48,6 +50,8 @@ pub enum Commands { Hash(Box), #[command(about = "Initialize a new dojo project")] Init(Box), + #[command(about = "Inspect events emitted by the world")] + Events(Box), } impl fmt::Display for Commands { @@ -62,6 +66,7 @@ impl fmt::Display for Commands { Commands::Test(_) => write!(f, "Test"), Commands::Hash(_) => write!(f, "Hash"), Commands::Init(_) => write!(f, "Init"), + Commands::Events(_) => write!(f, "Event"), } } } @@ -84,6 +89,7 @@ pub fn run(command: Commands, config: &Config) -> Result<()> { Commands::Test(args) => args.run(config), Commands::Hash(args) => args.run().map(|_| ()), Commands::Init(args) => args.run(config), + Commands::Events(args) => args.run(config), } } diff --git a/crates/dojo/world/src/diff/resource.rs b/crates/dojo/world/src/diff/resource.rs index fc16320b54..b2402399c9 100644 --- a/crates/dojo/world/src/diff/resource.rs +++ b/crates/dojo/world/src/diff/resource.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use starknet::core::types::contract::AbiEntry; use starknet_crypto::Felt; use crate::local::ResourceLocal; @@ -107,4 +108,12 @@ impl ResourceDiff { ResourceDiff::Synced(_, remote) => remote.current_class_hash(), } } + + pub fn abi(&self) -> Vec { + match self { + ResourceDiff::Created(local) => local.abi(), + ResourceDiff::Updated(local, _) => local.abi(), + ResourceDiff::Synced(local, _) => local.abi(), + } + } } diff --git a/crates/dojo/world/src/local/resource.rs b/crates/dojo/world/src/local/resource.rs index f40b1fd54f..9822da1731 100644 --- a/crates/dojo/world/src/local/resource.rs +++ b/crates/dojo/world/src/local/resource.rs @@ -1,5 +1,5 @@ use dojo_types::naming; -use starknet::core::types::contract::SierraClass; +use starknet::core::types::contract::{AbiEntry, SierraClass}; use starknet::core::types::Felt; use crate::{DojoSelector, ResourceType}; @@ -100,6 +100,16 @@ impl ResourceLocal { } } + /// Returns the ABI of the resource. + pub fn abi(&self) -> Vec { + match self { + ResourceLocal::Contract(c) => c.common.class.abi.clone(), + ResourceLocal::Model(m) => m.common.class.abi.clone(), + ResourceLocal::Event(e) => e.common.class.abi.clone(), + _ => Vec::new(), + } + } + /// Returns the dojo selector of the resource. pub fn dojo_selector(&self) -> DojoSelector { match self { diff --git a/crates/sozo/ops/src/events.rs b/crates/sozo/ops/src/events.rs index 1bf378b101..4ca951a7c3 100644 --- a/crates/sozo/ops/src/events.rs +++ b/crates/sozo/ops/src/events.rs @@ -1,21 +1,17 @@ use std::collections::{HashMap, VecDeque}; -use std::fs; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Result}; use cainome::cairo_serde::{ByteArray, CairoSerde}; use cainome::parser::tokens::{CompositeInner, CompositeInnerKind, CoreBasic, Token}; use cainome::parser::AbiParser; -use camino::Utf8PathBuf; -use dojo_world::contracts::naming::get_filename_from_tag; -use dojo_world::manifest::{ - AbiFormat, DeploymentManifest, ManifestMethods, BASE_CONTRACT_TAG, DEPLOYMENT_DIR, - MANIFESTS_DIR, TARGET_DIR, WORLD_CONTRACT_TAG, -}; +use dojo_world::diff::WorldDiff; +use starknet::core::types::contract::AbiEntry; use starknet::core::types::{BlockId, EventFilter, Felt}; use starknet::core::utils::{parse_cairo_short_string, starknet_keccak}; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::{JsonRpcClient, Provider}; +/// Returns an event filter for the world with the given parameters. pub fn get_event_filter( from_block: Option, to_block: Option, @@ -32,41 +28,19 @@ pub fn get_event_filter( EventFilter { from_block, to_block, address: world_address, keys } } +/// Parses and prints events of the world. pub async fn parse( + world_diff: &WorldDiff, + provider: &JsonRpcClient, chunk_size: u64, - provider: JsonRpcClient, continuation_token: Option, event_filter: EventFilter, - json: bool, - project_dir: &Utf8PathBuf, - profile_name: &str, ) -> Result<()> { - let events_map = if !json { - let manifest_dir = project_dir.join(MANIFESTS_DIR).join(profile_name); - let target_dir = project_dir.join(TARGET_DIR).join(profile_name); - let deployed_manifest = - manifest_dir.join(DEPLOYMENT_DIR).join("manifest").with_extension("toml"); - - if !deployed_manifest.exists() { - return Err(anyhow!("Run scarb migrate before running this command")); - } - - Some(extract_events( - &DeploymentManifest::load_from_path(&deployed_manifest)?, - project_dir, - &target_dir, - )?) - } else { - None - }; - + let events_map = extract_events(world_diff)?; let res = provider.get_events(event_filter, continuation_token, chunk_size).await?; - if let Some(events_map) = events_map { - parse_and_print_events(res, events_map)?; - } else { - println!("{}", serde_json::to_string_pretty(&res).unwrap()); - } + parse_and_print_events(res, events_map)?; + Ok(()) } @@ -77,20 +51,9 @@ fn is_event(token: &Token) -> bool { } } -fn extract_events( - manifest: &DeploymentManifest, - project_dir: &Utf8PathBuf, - target_dir: &Utf8PathBuf, -) -> Result>> { - fn process_abi( - events: &mut HashMap>, - full_abi_path: &Utf8PathBuf, - ) -> Result<()> { - let abi_str = fs::read_to_string(full_abi_path) - .with_context(|| format!("Failed to read ABI file at path: {}", full_abi_path))?; - - // TODO: add support for events emitted by world once its present in ABI - match AbiParser::tokens_from_abi_string(&abi_str, &HashMap::new()) { +fn extract_events(world_diff: &WorldDiff) -> Result>> { + fn process_abi(events: &mut HashMap>, abi: &Vec) -> Result<()> { + match AbiParser::collect_tokens(abi, &HashMap::new()) { Ok(tokens) => { for token in tokens.structs { if is_event(&token) { @@ -108,30 +71,12 @@ fn extract_events( let mut events_map = HashMap::new(); - for contract in &manifest.contracts { - if let Some(AbiFormat::Path(abi_path)) = contract.inner.abi() { - let full_abi_path = project_dir.join(abi_path); - process_abi(&mut events_map, &full_abi_path)?; - } - } + process_abi(&mut events_map, &world_diff.world_info.class.abi)?; - for model in &manifest.models { - if let Some(AbiFormat::Path(abi_path)) = model.inner.abi() { - let full_abi_path = project_dir.join(abi_path); - process_abi(&mut events_map, &full_abi_path)?; - } + for r in world_diff.resources.values() { + process_abi(&mut events_map, &r.abi())?; } - // Read the world and base ABI from scarb artifacts as the - // manifest does not include them (at least base is not included). - let world_abi_path = - target_dir.join(format!("{}.json", get_filename_from_tag(WORLD_CONTRACT_TAG))); - process_abi(&mut events_map, &world_abi_path)?; - - let base_abi_path = - target_dir.join(format!("{}.json", get_filename_from_tag(BASE_CONTRACT_TAG))); - process_abi(&mut events_map, &base_abi_path)?; - Ok(events_map) } @@ -269,26 +214,26 @@ fn process_inners( #[cfg(test)] mod tests { use cainome::parser::tokens::{Array, Composite, CompositeInner, CompositeType}; - use camino::Utf8Path; - use dojo_world::manifest::WORLD_QUALIFIED_PATH; + use dojo_test_utils::compiler::CompilerTestSetup; + use scarb::compiler::Profile; + use sozo_scarbext::WorkspaceExt; use starknet::core::types::EmittedEvent; use super::*; + const WORLD_QUALIFIED_PATH: &str = "dojo::world::world_contract::world"; + #[test] fn extract_events_work_as_expected() { - let profile_name = "dev"; - let project_dir = Utf8Path::new("../../../examples/spawn-and-move").to_path_buf(); - let manifest_dir = project_dir.join(MANIFESTS_DIR).join(profile_name); - println!("manifest_dir {:?}", manifest_dir); - let target_dir = project_dir.join(TARGET_DIR).join(profile_name); - println!("target dir {:?}", target_dir); - let manifest = DeploymentManifest::load_from_path( - &manifest_dir.join(DEPLOYMENT_DIR).join("manifest").with_extension("toml"), - ) - .unwrap(); - - let result = extract_events(&manifest, &project_dir, &target_dir).unwrap(); + let setup = CompilerTestSetup::from_examples("../../dojo/core", "../../../examples/"); + let config = setup.build_test_config("spawn-and-move", Profile::DEV); + + let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); + + let world_local = ws.load_world_local().unwrap(); + let world_diff = WorldDiff::from_local(world_local).unwrap(); + + let result = extract_events(&world_diff).unwrap(); // we are just collecting all events from manifest file so just verifying count should work assert_eq!(result.len(), 20); diff --git a/crates/sozo/ops/src/lib.rs b/crates/sozo/ops/src/lib.rs index 2c04a0f9b3..c488b20649 100644 --- a/crates/sozo/ops/src/lib.rs +++ b/crates/sozo/ops/src/lib.rs @@ -1,6 +1,7 @@ // #![cfg_attr(not(test), warn(unused_crate_dependencies))] pub mod account; +pub mod events; pub mod migrate; pub mod migration_ui;