From 836a48cadd449f62080ec89ae3ee509f59ce557c Mon Sep 17 00:00:00 2001 From: Tarrence van As Date: Wed, 27 Sep 2023 14:25:40 -0400 Subject: [PATCH] Update sozo model --- Cargo.lock | 2 + .../dojo-lang/src/manifest_test_data/manifest | 30 +++++- crates/dojo-lang/src/model.rs | 10 +- .../dojo-lang/src/plugin_test_data/introspect | 10 +- crates/dojo-lang/src/plugin_test_data/model | 30 +++++- crates/dojo-types/Cargo.toml | 2 + crates/dojo-types/src/component.rs | 12 +-- crates/dojo-types/src/core.rs | 97 +++++++++++++++++++ crates/dojo-types/src/lib.rs | 1 + crates/sozo/src/args.rs | 4 +- crates/sozo/src/commands/mod.rs | 4 +- .../src/commands/{component.rs => model.rs} | 26 ++--- crates/sozo/src/ops/mod.rs | 2 +- .../sozo/src/ops/{component.rs => model.rs} | 16 +-- crates/torii/client/src/contract/mod.rs | 2 +- .../src/contract/{component.rs => model.rs} | 90 +++++++++-------- .../{component_test.rs => model_test.rs} | 26 ++--- crates/torii/client/src/contract/world.rs | 17 ++-- crates/torii/client/src/provider/jsonrpc.rs | 8 +- crates/torii/client/wasm/Cargo.lock | 2 + .../core/src/processors/register_model.rs | 2 +- crates/torii/core/src/sql.rs | 47 ++------- crates/torii/core/src/sql_test.rs | 2 +- 23 files changed, 297 insertions(+), 145 deletions(-) create mode 100644 crates/dojo-types/src/core.rs rename crates/sozo/src/commands/{component.rs => model.rs} (73%) rename crates/sozo/src/ops/{component.rs => model.rs} (69%) rename crates/torii/client/src/contract/{component.rs => model.rs} (74%) rename crates/torii/client/src/contract/{component_test.rs => model_test.rs} (78%) diff --git a/Cargo.lock b/Cargo.lock index 8e4008272a..766167ad1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2164,8 +2164,10 @@ dependencies = [ name = "dojo-types" version = "0.2.1" dependencies = [ + "hex", "serde", "starknet", + "thiserror", ] [[package]] diff --git a/crates/dojo-lang/src/manifest_test_data/manifest b/crates/dojo-lang/src/manifest_test_data/manifest index f8b3c1ff2b..d07c4fa928 100644 --- a/crates/dojo-lang/src/manifest_test_data/manifest +++ b/crates/dojo-lang/src/manifest_test_data/manifest @@ -804,7 +804,7 @@ test_manifest_file "key": false } ], - "class_hash": "0x3682ef3ef44d8db8c8bfe72533e2e3b17e7b80efd6550b365deed7b4b3f8597", + "class_hash": "0x3dedf3d747db10aee693c0d034fcde1064ec66c84d285e60630f860dc9df566", "abi": [ { "type": "function", @@ -819,7 +819,18 @@ test_manifest_file }, { "type": "function", - "name": "size", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u32" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", "inputs": [], "outputs": [ { @@ -956,7 +967,7 @@ test_manifest_file "key": false } ], - "class_hash": "0x69889772f44397619cd8965660e1c8e80ba5f0c917ba40df29b2ffa5b440745", + "class_hash": "0x1b5f19668b9299cea232978c59856b16da649e6aa4dfc3f2a42aa8435e06bc7", "abi": [ { "type": "function", @@ -971,7 +982,18 @@ test_manifest_file }, { "type": "function", - "name": "size", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u32" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", "inputs": [], "outputs": [ { diff --git a/crates/dojo-lang/src/model.rs b/crates/dojo-lang/src/model.rs index 7ce01e3b6b..fc62785b54 100644 --- a/crates/dojo-lang/src/model.rs +++ b/crates/dojo-lang/src/model.rs @@ -127,10 +127,18 @@ pub fn handle_model_struct( } #[external(v0)] - fn size(self: @ContractState) -> usize { + fn unpacked_size(self: @ContractState) -> usize { dojo::database::schema::SchemaIntrospection::<$type_name$>::size() } + #[external(v0)] + fn packed_size(self: @ContractState) -> usize { + let mut layout = ArrayTrait::new(); + dojo::database::schema::SchemaIntrospection::<$type_name$>::layout(ref layout); + let mut layout_span = layout.span(); + dojo::packing::calculate_packed_size(ref layout_span) + } + #[external(v0)] fn layout(self: @ContractState) -> Span { let mut layout = ArrayTrait::new(); diff --git a/crates/dojo-lang/src/plugin_test_data/introspect b/crates/dojo-lang/src/plugin_test_data/introspect index a8b6c7343c..aeed661cdf 100644 --- a/crates/dojo-lang/src/plugin_test_data/introspect +++ b/crates/dojo-lang/src/plugin_test_data/introspect @@ -180,10 +180,18 @@ mod position { } #[external(v0)] - fn size(self: @ContractState) -> usize { + fn unpacked_size(self: @ContractState) -> usize { dojo::database::schema::SchemaIntrospection::::size() } + #[external(v0)] + fn packed_size(self: @ContractState) -> usize { + let mut layout = ArrayTrait::new(); + dojo::database::schema::SchemaIntrospection::::layout(ref layout); + let mut layout_span = layout.span(); + dojo::packing::calculate_packed_size(ref layout_span) + } + #[external(v0)] fn layout(self: @ContractState) -> Span { let mut layout = ArrayTrait::new(); diff --git a/crates/dojo-lang/src/plugin_test_data/model b/crates/dojo-lang/src/plugin_test_data/model index 8d8c1bc826..a5c09cb890 100644 --- a/crates/dojo-lang/src/plugin_test_data/model +++ b/crates/dojo-lang/src/plugin_test_data/model @@ -164,10 +164,18 @@ mod position { } #[external(v0)] - fn size(self: @ContractState) -> usize { + fn unpacked_size(self: @ContractState) -> usize { dojo::database::schema::SchemaIntrospection::::size() } + #[external(v0)] + fn packed_size(self: @ContractState) -> usize { + let mut layout = ArrayTrait::new(); + dojo::database::schema::SchemaIntrospection::::layout(ref layout); + let mut layout_span = layout.span(); + dojo::packing::calculate_packed_size(ref layout_span) + } + #[external(v0)] fn layout(self: @ContractState) -> Span { let mut layout = ArrayTrait::new(); @@ -294,10 +302,18 @@ mod roles { } #[external(v0)] - fn size(self: @ContractState) -> usize { + fn unpacked_size(self: @ContractState) -> usize { dojo::database::schema::SchemaIntrospection::::size() } + #[external(v0)] + fn packed_size(self: @ContractState) -> usize { + let mut layout = ArrayTrait::new(); + dojo::database::schema::SchemaIntrospection::::layout(ref layout); + let mut layout_span = layout.span(); + dojo::packing::calculate_packed_size(ref layout_span) + } + #[external(v0)] fn layout(self: @ContractState) -> Span { let mut layout = ArrayTrait::new(); @@ -426,10 +442,18 @@ mod player { } #[external(v0)] - fn size(self: @ContractState) -> usize { + fn unpacked_size(self: @ContractState) -> usize { dojo::database::schema::SchemaIntrospection::::size() } + #[external(v0)] + fn packed_size(self: @ContractState) -> usize { + let mut layout = ArrayTrait::new(); + dojo::database::schema::SchemaIntrospection::::layout(ref layout); + let mut layout_span = layout.span(); + dojo::packing::calculate_packed_size(ref layout_span) + } + #[external(v0)] fn layout(self: @ContractState) -> Span { let mut layout = ArrayTrait::new(); diff --git a/crates/dojo-types/Cargo.toml b/crates/dojo-types/Cargo.toml index 55983f0290..0606c9d2e4 100644 --- a/crates/dojo-types/Cargo.toml +++ b/crates/dojo-types/Cargo.toml @@ -6,5 +6,7 @@ version = "0.2.1" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +hex = "0.4.3" serde.workspace = true starknet.workspace = true +thiserror.workspace = true diff --git a/crates/dojo-types/src/component.rs b/crates/dojo-types/src/component.rs index a1ed74fcbe..d454639700 100644 --- a/crates/dojo-types/src/component.rs +++ b/crates/dojo-types/src/component.rs @@ -10,7 +10,7 @@ pub struct Member { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum Ty { - Name(String), + Terminal(String), Struct(Struct), Enum(Enum), } @@ -18,7 +18,7 @@ pub enum Ty { impl Ty { pub fn name(&self) -> String { match self { - Ty::Name(s) => s.clone(), + Ty::Terminal(s) => s.clone(), Ty::Struct(s) => s.name.clone(), Ty::Enum(e) => e.name.clone(), } @@ -33,7 +33,7 @@ impl Ty { fn flatten_ty(ty: Ty) -> Vec { let mut items = vec![]; match ty { - Ty::Name(_) => { + Ty::Terminal(_) => { items.push(ty.clone()); } Ty::Struct(mut s) => { @@ -48,7 +48,7 @@ impl Ty { _ => {} } - s.children[i].ty = Ty::Name(member.ty.name()); + s.children[i].ty = Ty::Terminal(member.ty.name()); } items.push(Ty::Struct(s)) @@ -65,7 +65,7 @@ impl Ty { _ => {} } - e.values[i] = Ty::Name(ty.name()); + e.values[i] = Ty::Terminal(ty.name()); } items.push(Ty::Enum(e)) @@ -83,7 +83,7 @@ impl std::fmt::Display for Ty { let str = items .iter() .map(|ty| match ty { - Ty::Name(s) => s.to_string(), + Ty::Terminal(s) => s.to_string(), Ty::Struct(s) => { let mut struct_str = format!("struct {} {{\n", s.name); for member in &s.children { diff --git a/crates/dojo-types/src/core.rs b/crates/dojo-types/src/core.rs new file mode 100644 index 0000000000..d234cbc246 --- /dev/null +++ b/crates/dojo-types/src/core.rs @@ -0,0 +1,97 @@ +use std::str::FromStr; + +use starknet::core::types::FieldElement; + +pub enum CairoType { + U8, + U16, + U32, + U64, + U128, + U256, + USize, + Bool, + ContractAddress, + ClassHash, + Felt252, +} + +#[derive(Debug, thiserror::Error)] +pub enum CairoTypeError { + #[error("Value must have at least one FieldElement")] + MissingFieldElement, + #[error("Not enough FieldElements for U256")] + NotEnoughFieldElements, + #[error("Unsupported CairoType for SQL formatting")] + UnsupportedType, +} + +impl FromStr for CairoType { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "u8" => Ok(CairoType::U8), + "u16" => Ok(CairoType::U16), + "u32" => Ok(CairoType::U32), + "u64" => Ok(CairoType::U64), + "u128" => Ok(CairoType::U128), + "u256" => Ok(CairoType::U256), + "usize" => Ok(CairoType::USize), + "bool" => Ok(CairoType::Bool), + "ContractAddress" => Ok(CairoType::ContractAddress), + "ClassHash" => Ok(CairoType::ClassHash), + "felt252" => Ok(CairoType::Felt252), + _ => Err(()), + } + } +} + +impl CairoType { + pub fn to_sql_type(&self) -> String { + match self { + CairoType::U8 + | CairoType::U16 + | CairoType::U32 + | CairoType::U64 + | CairoType::USize + | CairoType::Bool => "INTEGER".to_string(), + CairoType::U128 + | CairoType::U256 + | CairoType::ContractAddress + | CairoType::ClassHash + | CairoType::Felt252 => "TEXT".to_string(), + } + } + + pub fn format_for_sql(&self, value: Vec<&FieldElement>) -> Result { + if value.is_empty() { + return Err(CairoTypeError::MissingFieldElement); + } + + match self { + CairoType::U8 + | CairoType::U16 + | CairoType::U32 + | CairoType::U64 + | CairoType::USize + | CairoType::Bool => Ok(format!(", '{}'", value[0])), + CairoType::U128 + | CairoType::ContractAddress + | CairoType::ClassHash + | CairoType::Felt252 => Ok(format!(", '{:0>64x}'", value[0])), + CairoType::U256 => { + if value.len() < 2 { + Err(CairoTypeError::NotEnoughFieldElements) + } else { + let mut buffer = [0u8; 32]; + let value0_bytes = value[0].to_bytes_be(); + let value1_bytes = value[1].to_bytes_be(); + buffer[..16].copy_from_slice(&value0_bytes); + buffer[16..].copy_from_slice(&value1_bytes); + Ok(format!(", '{}'", hex::encode(buffer))) + } + } + } + } +} diff --git a/crates/dojo-types/src/lib.rs b/crates/dojo-types/src/lib.rs index f0ff6617a9..a52c4b61ee 100644 --- a/crates/dojo-types/src/lib.rs +++ b/crates/dojo-types/src/lib.rs @@ -1,4 +1,5 @@ pub mod component; +pub mod core; pub mod event; pub mod storage; pub mod system; diff --git a/crates/sozo/src/args.rs b/crates/sozo/src/args.rs index f5b81ab298..e52fd7b907 100644 --- a/crates/sozo/src/args.rs +++ b/crates/sozo/src/args.rs @@ -10,12 +10,12 @@ use tracing_log::AsTrace; use crate::commands::auth::AuthArgs; use crate::commands::build::BuildArgs; use crate::commands::completions::CompletionsArgs; -use crate::commands::component::ComponentArgs; use crate::commands::dev::DevArgs; use crate::commands::events::EventsArgs; use crate::commands::execute::ExecuteArgs; use crate::commands::init::InitArgs; use crate::commands::migrate::MigrateArgs; +use crate::commands::model::ModelArgs; use crate::commands::register::RegisterArgs; use crate::commands::test::TestArgs; @@ -64,7 +64,7 @@ pub enum Commands { #[command(about = "Execute a world's system")] Execute(ExecuteArgs), #[command(about = "Interact with a worlds components")] - Component(ComponentArgs), + Model(ModelArgs), #[command(about = "Register new systems and components")] Register(RegisterArgs), #[command(about = "Queries world events")] diff --git a/crates/sozo/src/commands/mod.rs b/crates/sozo/src/commands/mod.rs index 7ce24a257e..e093ab8ab8 100644 --- a/crates/sozo/src/commands/mod.rs +++ b/crates/sozo/src/commands/mod.rs @@ -6,12 +6,12 @@ use crate::args::Commands; pub(crate) mod auth; pub(crate) mod build; pub(crate) mod completions; -pub(crate) mod component; pub(crate) mod dev; pub(crate) mod events; pub(crate) mod execute; pub(crate) mod init; pub(crate) mod migrate; +pub(crate) mod model; pub(crate) mod options; pub(crate) mod register; pub(crate) mod test; @@ -28,7 +28,7 @@ pub fn run(command: Commands, config: &Config) -> Result<()> { Commands::Dev(args) => args.run(config), Commands::Auth(args) => args.run(config), Commands::Execute(args) => args.run(config), - Commands::Component(args) => args.run(config), + Commands::Model(args) => args.run(config), Commands::Register(args) => args.run(config), Commands::Events(args) => args.run(config), Commands::Completions(args) => args.run(), diff --git a/crates/sozo/src/commands/component.rs b/crates/sozo/src/commands/model.rs similarity index 73% rename from crates/sozo/src/commands/component.rs rename to crates/sozo/src/commands/model.rs index 67046a817d..a460b00679 100644 --- a/crates/sozo/src/commands/component.rs +++ b/crates/sozo/src/commands/model.rs @@ -6,19 +6,19 @@ use starknet::core::types::FieldElement; use super::options::starknet::StarknetOptions; use super::options::world::WorldOptions; -use crate::ops::component; +use crate::ops::model; #[derive(Debug, Args)] -pub struct ComponentArgs { +pub struct ModelArgs { #[command(subcommand)] - command: ComponentCommands, + command: ModelCommands, } #[derive(Debug, Subcommand)] -pub enum ComponentCommands { - #[command(about = "Get the class hash of a component")] - Get { - #[arg(help = "The name of the component")] +pub enum ModelCommands { + #[command(about = "Retrieve the class hash of a model")] + ClassHash { + #[arg(help = "The name of the model")] name: String, #[command(flatten)] @@ -28,9 +28,9 @@ pub enum ComponentCommands { starknet: StarknetOptions, }, - #[command(about = "Retrieve the schema for a component")] + #[command(about = "Retrieve the schema for a model")] Schema { - #[arg(help = "The name of the component")] + #[arg(help = "The name of the model")] name: String, #[command(flatten)] @@ -44,9 +44,9 @@ pub enum ComponentCommands { to_json: bool, }, - #[command(about = "Get the component value for an entity")] + #[command(about = "Retrieve the model value for an entity")] Entity { - #[arg(help = "The name of the component")] + #[arg(help = "The name of the model")] name: String, #[arg(value_name = "KEYS")] @@ -62,7 +62,7 @@ pub enum ComponentCommands { }, } -impl ComponentArgs { +impl ModelArgs { pub fn run(self, config: &Config) -> Result<()> { let env_metadata = if config.manifest_path().exists() { let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; @@ -73,6 +73,6 @@ impl ComponentArgs { None }; - config.tokio_handle().block_on(component::execute(self.command, env_metadata)) + config.tokio_handle().block_on(model::execute(self.command, env_metadata)) } } diff --git a/crates/sozo/src/ops/mod.rs b/crates/sozo/src/ops/mod.rs index 02302ca2f2..abf4eb9c5c 100644 --- a/crates/sozo/src/ops/mod.rs +++ b/crates/sozo/src/ops/mod.rs @@ -1,6 +1,6 @@ pub mod auth; -pub mod component; pub mod events; pub mod execute; pub mod migration; +pub mod model; pub mod register; diff --git a/crates/sozo/src/ops/component.rs b/crates/sozo/src/ops/model.rs similarity index 69% rename from crates/sozo/src/ops/component.rs rename to crates/sozo/src/ops/model.rs index 830fc89031..b2eb532341 100644 --- a/crates/sozo/src/ops/component.rs +++ b/crates/sozo/src/ops/model.rs @@ -3,26 +3,26 @@ use dojo_world::metadata::Environment; use starknet::core::types::{BlockId, BlockTag}; use torii_client::contract::world::WorldContractReader; -use crate::commands::component::ComponentCommands; +use crate::commands::model::ModelCommands; -pub async fn execute(command: ComponentCommands, env_metadata: Option) -> Result<()> { +pub async fn execute(command: ModelCommands, env_metadata: Option) -> Result<()> { match command { - ComponentCommands::Get { name, world, starknet } => { + ModelCommands::ClassHash { name, world, starknet } => { let world_address = world.address(env_metadata.as_ref())?; let provider = starknet.provider(env_metadata.as_ref())?; let world = WorldContractReader::new(world_address, &provider); - let component = world.component(&name, BlockId::Tag(BlockTag::Pending)).await?; + let component = world.model(&name, BlockId::Tag(BlockTag::Pending)).await?; println!("{:#x}", component.class_hash()); } - ComponentCommands::Schema { name, world, starknet, to_json } => { + ModelCommands::Schema { name, world, starknet, to_json } => { let world_address = world.address(env_metadata.as_ref())?; let provider = starknet.provider(env_metadata.as_ref())?; let world = WorldContractReader::new(world_address, &provider); - let component = world.component(&name, BlockId::Tag(BlockTag::Pending)).await?; + let component = world.model(&name, BlockId::Tag(BlockTag::Pending)).await?; let schema = component.schema(BlockId::Tag(BlockTag::Pending)).await?; @@ -33,12 +33,12 @@ pub async fn execute(command: ComponentCommands, env_metadata: Option { + ModelCommands::Entity { name, keys, starknet, world, .. } => { let world_address = world.address(env_metadata.as_ref())?; let provider = starknet.provider(env_metadata.as_ref())?; let world = WorldContractReader::new(world_address, &provider); - let component = world.component(&name, BlockId::Tag(BlockTag::Pending)).await?; + let component = world.model(&name, BlockId::Tag(BlockTag::Pending)).await?; let entity = component.entity(keys, BlockId::Tag(BlockTag::Pending)).await?; diff --git a/crates/torii/client/src/contract/mod.rs b/crates/torii/client/src/contract/mod.rs index 94b8f34a34..a994f340ec 100644 --- a/crates/torii/client/src/contract/mod.rs +++ b/crates/torii/client/src/contract/mod.rs @@ -1,2 +1,2 @@ -pub mod component; +pub mod model; pub mod world; diff --git a/crates/torii/client/src/contract/component.rs b/crates/torii/client/src/contract/model.rs similarity index 74% rename from crates/torii/client/src/contract/component.rs rename to crates/torii/client/src/contract/model.rs index 967a0fde23..48700ed8ee 100644 --- a/crates/torii/client/src/contract/component.rs +++ b/crates/torii/client/src/contract/model.rs @@ -14,11 +14,11 @@ use starknet_crypto::poseidon_hash_many; use crate::contract::world::{ContractReaderError, WorldContractReader}; #[cfg(test)] -#[path = "component_test.rs"] -mod test; +#[path = "model_test.rs"] +mod model_test; #[derive(Debug, thiserror::Error)] -pub enum ComponentError

{ +pub enum ModelError

{ #[error(transparent)] ProviderError(ProviderError

), #[error("Invalid schema")] @@ -35,20 +35,20 @@ pub enum ComponentError

{ ContractReaderError(ContractReaderError

), } -pub struct ComponentReader<'a, P: Provider + Sync> { +pub struct ModelReader<'a, P: Provider + Sync> { world: &'a WorldContractReader<'a, P>, class_hash: FieldElement, name: FieldElement, } -impl<'a, P: Provider + Sync> ComponentReader<'a, P> { +impl<'a, P: Provider + Sync> ModelReader<'a, P> { pub async fn new( world: &'a WorldContractReader<'a, P>, name: String, block_id: BlockId, - ) -> Result, ComponentError> { - let name = cairo_short_string_to_felt(&name) - .map_err(ComponentError::CairoShortStringToFeltError)?; + ) -> Result, ModelError> { + let name = + cairo_short_string_to_felt(&name).map_err(ModelError::CairoShortStringToFeltError)?; let res = world .provider .call( @@ -60,7 +60,7 @@ impl<'a, P: Provider + Sync> ComponentReader<'a, P> { block_id, ) .await - .map_err(ComponentError::ProviderError)?; + .map_err(ModelError::ProviderError)?; Ok(Self { world, class_hash: res[0], name }) } @@ -69,26 +69,41 @@ impl<'a, P: Provider + Sync> ComponentReader<'a, P> { self.class_hash } - pub async fn schema(&self, block_id: BlockId) -> Result> { + pub async fn schema(&self, block_id: BlockId) -> Result> { let entrypoint = get_selector_from_name("schema").unwrap(); let res = self .world .executor_call(self.class_hash, vec![entrypoint, FieldElement::ZERO], block_id) .await - .map_err(ComponentError::ContractReaderError)?; + .map_err(ModelError::ContractReaderError)?; parse_ty::

(&res[1..]) } - pub async fn size(&self, block_id: BlockId) -> Result> { + pub async fn packed_size( + &self, + block_id: BlockId, + ) -> Result> { + let entrypoint = get_selector_from_name("packed_size").unwrap(); + + let res = self + .world + .executor_call(self.class_hash, vec![entrypoint, FieldElement::ZERO], block_id) + .await + .map_err(ModelError::ContractReaderError)?; + + Ok(res[1]) + } + + pub async fn size(&self, block_id: BlockId) -> Result> { let entrypoint = get_selector_from_name("size").unwrap(); let res = self .world .executor_call(self.class_hash, vec![entrypoint, FieldElement::ZERO], block_id) .await - .map_err(ComponentError::ContractReaderError)?; + .map_err(ModelError::ContractReaderError)?; Ok(res[1]) } @@ -96,42 +111,42 @@ impl<'a, P: Provider + Sync> ComponentReader<'a, P> { pub async fn layout( &self, block_id: BlockId, - ) -> Result, ComponentError> { + ) -> Result, ModelError> { let entrypoint = get_selector_from_name("layout").unwrap(); let res = self .world .executor_call(self.class_hash, vec![entrypoint, FieldElement::ZERO], block_id) .await - .map_err(ComponentError::ContractReaderError)?; + .map_err(ModelError::ContractReaderError)?; - Ok(res[3..].into()) + Ok(res[2..].into()) } pub async fn entity( &self, keys: Vec, block_id: BlockId, - ) -> Result, ComponentError> { - let size: u8 = self.size(block_id).await?.try_into().unwrap(); + ) -> Result, ModelError> { + let packed_size: u8 = self.packed_size(block_id).await?.try_into().unwrap(); let layout = self.layout(block_id).await?; let key = poseidon_hash_many(&keys); let key = poseidon_hash_many(&[short_string!("dojo_storage"), self.name, key]); let mut packed = vec![]; - for slot in 0..size { + for slot in 0..packed_size { let value = self .world .provider .get_storage_at(self.world.address, key + slot.into(), block_id) .await - .map_err(ComponentError::ProviderError)?; + .map_err(ModelError::ProviderError)?; packed.push(value); } - let unpacked = unpack::

(packed, layout)?; + let unpacked = unpack::

(packed, layout.clone())?; Ok(unpacked) } @@ -151,22 +166,22 @@ impl<'a, P: Provider + Sync> ComponentReader<'a, P> { pub fn unpack( mut packed: Vec, layout: Vec, -) -> Result, ComponentError> { +) -> Result, ModelError> { packed.reverse(); let mut unpacked = vec![]; - let mut unpacking: U256 = packed.pop().ok_or(ComponentError::UnpackingEntity)?.as_ref().into(); + let mut unpacking: U256 = packed.pop().ok_or(ModelError::UnpackingEntity)?.as_ref().into(); let mut offset = 0; // Iterate over the layout. for size in layout { - let size: u8 = size.try_into().map_err(|_| ComponentError::ConvertingFelt)?; + let size: u8 = size.try_into().map_err(|_| ModelError::ConvertingFelt)?; let size: usize = size.into(); let remaining_bits = 251 - offset; // If there are less remaining bits than the size, move to the next felt for unpacking. if remaining_bits < size { - unpacking = packed.pop().ok_or(ComponentError::UnpackingEntity)?.as_ref().into(); + unpacking = packed.pop().ok_or(ModelError::UnpackingEntity)?.as_ref().into(); offset = 0; } @@ -177,7 +192,7 @@ pub fn unpack( let result = mask & (unpacking >> offset); let result_fe = FieldElement::from_hex_be(&result.to_string()) - .map_err(|_| ComponentError::ConvertingFelt)?; + .map_err(|_| ModelError::ConvertingFelt)?; unpacked.push(result_fe); // Update unpacking to be the shifted value after extracting the result. @@ -187,25 +202,24 @@ pub fn unpack( Ok(unpacked) } -fn parse_ty(data: &[FieldElement]) -> Result> { +fn parse_ty(data: &[FieldElement]) -> Result> { let member_type: u8 = data[0].try_into().unwrap(); match member_type { 0 => parse_simple::

(&data[1..]), 1 => parse_struct::

(&data[1..]), 2 => parse_enum::

(&data[1..]), - _ => Err(ComponentError::InvalidSchema), + _ => Err(ModelError::InvalidSchema), } } -fn parse_simple(data: &[FieldElement]) -> Result> { - let ty = - parse_cairo_short_string(&data[0]).map_err(ComponentError::ParseCairoShortStringError)?; - Ok(Ty::Name(ty)) +fn parse_simple(data: &[FieldElement]) -> Result> { + let ty = parse_cairo_short_string(&data[0]).map_err(ModelError::ParseCairoShortStringError)?; + Ok(Ty::Terminal(ty)) } -fn parse_struct(data: &[FieldElement]) -> Result> { +fn parse_struct(data: &[FieldElement]) -> Result> { let name = - parse_cairo_short_string(&data[0]).map_err(ComponentError::ParseCairoShortStringError)?; + parse_cairo_short_string(&data[0]).map_err(ModelError::ParseCairoShortStringError)?; let attrs_len: u32 = data[1].try_into().unwrap(); let attrs_slice_start = 2; @@ -230,9 +244,9 @@ fn parse_struct(data: &[FieldElement]) -> Result(data: &[FieldElement]) -> Result> { +fn parse_member(data: &[FieldElement]) -> Result> { let name = - parse_cairo_short_string(&data[0]).map_err(ComponentError::ParseCairoShortStringError)?; + parse_cairo_short_string(&data[0]).map_err(ModelError::ParseCairoShortStringError)?; let attributes_len: u32 = data[1].try_into().unwrap(); let slice_start = 2; @@ -246,9 +260,9 @@ fn parse_member(data: &[FieldElement]) -> Result(data: &[FieldElement]) -> Result> { +fn parse_enum(data: &[FieldElement]) -> Result> { let name = - parse_cairo_short_string(&data[0]).map_err(ComponentError::ParseCairoShortStringError)?; + parse_cairo_short_string(&data[0]).map_err(ModelError::ParseCairoShortStringError)?; let attrs_len: u32 = data[1].try_into().unwrap(); let attrs_slice_start = 2; diff --git a/crates/torii/client/src/contract/component_test.rs b/crates/torii/client/src/contract/model_test.rs similarity index 78% rename from crates/torii/client/src/contract/component_test.rs rename to crates/torii/client/src/contract/model_test.rs index 6e6b8ace6e..73c43e6434 100644 --- a/crates/torii/client/src/contract/component_test.rs +++ b/crates/torii/client/src/contract/model_test.rs @@ -10,7 +10,7 @@ use crate::contract::world::test::deploy_world; use crate::contract::world::WorldContractReader; #[tokio::test(flavor = "multi_thread")] -async fn test_component() { +async fn test_model() { let sequencer = TestSequencer::start(SequencerConfig::default(), get_default_test_starknet_config()).await; let account = sequencer.account(); @@ -23,7 +23,7 @@ async fn test_component() { let block_id = BlockId::Tag(BlockTag::Latest); let world = WorldContractReader::new(world_address, provider); - let position = world.component("Position", block_id).await.unwrap(); + let position = world.model("Position", block_id).await.unwrap(); let schema = position.schema(block_id).await.unwrap(); assert_eq!( @@ -33,7 +33,7 @@ async fn test_component() { children: vec![ Member { name: "player".to_string(), - ty: Ty::Name("ContractAddress".to_string()), + ty: Ty::Terminal("ContractAddress".to_string()), key: true }, Member { @@ -43,12 +43,12 @@ async fn test_component() { children: vec![ Member { name: "x".to_string(), - ty: Ty::Name("u32".to_string()), + ty: Ty::Terminal("u32".to_string()), key: false }, Member { name: "y".to_string(), - ty: Ty::Name("u32".to_string()), + ty: Ty::Terminal("u32".to_string()), key: false } ] @@ -67,7 +67,7 @@ async fn test_component() { .unwrap() ); - let moves = world.component("Moves", block_id).await.unwrap(); + let moves = world.model("Moves", block_id).await.unwrap(); let schema = moves.schema(block_id).await.unwrap(); assert_eq!( @@ -77,12 +77,12 @@ async fn test_component() { children: vec![ Member { name: "player".to_string(), - ty: Ty::Name("ContractAddress".to_string()), + ty: Ty::Terminal("ContractAddress".to_string()), key: true }, Member { name: "remaining".to_string(), - ty: Ty::Name("u8".to_string()), + ty: Ty::Terminal("u8".to_string()), key: false }, Member { @@ -90,11 +90,11 @@ async fn test_component() { ty: Ty::Enum(Enum { name: "Direction".to_string(), values: vec![ - Ty::Name("None".to_string()), - Ty::Name("Left".to_string()), - Ty::Name("Right".to_string()), - Ty::Name("Up".to_string()), - Ty::Name("Down".to_string()) + Ty::Terminal("None".to_string()), + Ty::Terminal("Left".to_string()), + Ty::Terminal("Right".to_string()), + Ty::Terminal("Up".to_string()), + Ty::Terminal("Down".to_string()) ] }), key: false diff --git a/crates/torii/client/src/contract/world.rs b/crates/torii/client/src/contract/world.rs index a1a68ee1f5..2bcf7d21cb 100644 --- a/crates/torii/client/src/contract/world.rs +++ b/crates/torii/client/src/contract/world.rs @@ -7,7 +7,7 @@ use starknet::core::utils::{ }; use starknet::providers::{Provider, ProviderError}; -use crate::contract::component::{ComponentError, ComponentReader}; +use crate::contract::model::{ModelError, ModelReader}; #[cfg(test)] #[path = "world_test.rs"] @@ -54,13 +54,13 @@ impl<'a, A: ConnectedAccount + Sync> WorldContract<'a, A> { pub async fn grant_writer( &self, - component: &str, + model: &str, system: &str, ) -> Result< InvokeTransactionResult, WorldContractError::Error>, > { - let component = cairo_short_string_to_felt(component) + let component = cairo_short_string_to_felt(model) .map_err(WorldContractError::CairoShortStringToFeltError)?; let system = cairo_short_string_to_felt(system) .map_err(WorldContractError::CairoShortStringToFeltError)?; @@ -119,9 +119,8 @@ impl<'a, A: ConnectedAccount + Sync> WorldContract<'a, A> { &'a self, name: &str, block_id: BlockId, - ) -> Result, ComponentError<::Error>> - { - self.reader.component(name, block_id).await + ) -> Result, ModelError<::Error>> { + self.reader.model(name, block_id).await } } @@ -259,11 +258,11 @@ impl<'a, P: Provider + Sync> WorldContractReader<'a, P> { .map_err(ContractReaderError::ProviderError) } - pub async fn component( + pub async fn model( &'a self, name: &str, block_id: BlockId, - ) -> Result, ComponentError> { - ComponentReader::new(self, name.to_string(), block_id).await + ) -> Result, ModelError> { + ModelReader::new(self, name.to_string(), block_id).await } } diff --git a/crates/torii/client/src/provider/jsonrpc.rs b/crates/torii/client/src/provider/jsonrpc.rs index e71290d633..6235eccc5e 100644 --- a/crates/torii/client/src/provider/jsonrpc.rs +++ b/crates/torii/client/src/provider/jsonrpc.rs @@ -5,13 +5,13 @@ use starknet::providers::JsonRpcClient; use starknet_crypto::FieldElement; use super::Provider; -use crate::contract::component::ComponentError; +use crate::contract::model::ModelError; use crate::contract::world::WorldContractReader; #[derive(Debug, thiserror::Error)] pub enum JsonRpcProviderError

{ #[error(transparent)] - ComponetReader(ComponentError

), + ComponetReader(ModelError

), } /// An implementation of [Provider] which uses a Starknet [JsonRpcClient] to query the World. @@ -48,7 +48,7 @@ where async fn component(&self, name: &str) -> Result { let world = self.world(); let class_hash = world - .component(name, self.block_id) + .model(name, self.block_id) .await .map_err(JsonRpcProviderError::ComponetReader)? .class_hash(); @@ -62,7 +62,7 @@ where ) -> Result, Self::Error> { let world = self.world(); let component = world - .component(component, self.block_id) + .model(component, self.block_id) .await .map_err(JsonRpcProviderError::ComponetReader)?; component.entity(keys, self.block_id).await.map_err(JsonRpcProviderError::ComponetReader) diff --git a/crates/torii/client/wasm/Cargo.lock b/crates/torii/client/wasm/Cargo.lock index b43a52a9ff..367d14c307 100644 --- a/crates/torii/client/wasm/Cargo.lock +++ b/crates/torii/client/wasm/Cargo.lock @@ -469,8 +469,10 @@ dependencies = [ name = "dojo-types" version = "0.2.1" dependencies = [ + "hex", "serde", "starknet", + "thiserror", ] [[package]] diff --git a/crates/torii/core/src/processors/register_model.rs b/crates/torii/core/src/processors/register_model.rs index 288996db59..e9e754e39c 100644 --- a/crates/torii/core/src/processors/register_model.rs +++ b/crates/torii/core/src/processors/register_model.rs @@ -28,7 +28,7 @@ impl EventProcessor

for RegisterModelProcessor event: &Event, ) -> Result<(), Error> { let name = parse_cairo_short_string(&event.data[0])?; - let model = world.component(&name, BlockId::Tag(BlockTag::Latest)).await?; + let model = world.model(&name, BlockId::Tag(BlockTag::Latest)).await?; let schema = model.schema(BlockId::Tag(BlockTag::Latest)).await?; let layout = model.layout(BlockId::Tag(BlockTag::Latest)).await?; info!("Registered model: {}", name); diff --git a/crates/torii/core/src/sql.rs b/crates/torii/core/src/sql.rs index 59572fbfaa..7d9d100c87 100644 --- a/crates/torii/core/src/sql.rs +++ b/crates/torii/core/src/sql.rs @@ -1,11 +1,11 @@ -use std::collections::HashMap; +use std::str::FromStr; use anyhow::Result; use async_trait::async_trait; use chrono::{DateTime, Utc}; use dojo_types::component::Ty; +use dojo_types::core::CairoType; use dojo_world::manifest::{Manifest, System}; -use lazy_static::lazy_static; use sqlx::pool::PoolConnection; use sqlx::sqlite::SqliteRow; use sqlx::{Executor, Pool, Row, Sqlite}; @@ -21,26 +21,6 @@ use crate::types::{Entity, Model as ModelType}; #[path = "sql_test.rs"] mod test; -lazy_static! { - static ref CAIRO_TO_SQL_TYPE: HashMap = { - let mut m = HashMap::new(); - m.insert("u8".to_string(), "INTEGER".to_string()); - m.insert("u16".to_string(), "INTEGER".to_string()); - m.insert("u32".to_string(), "INTEGER".to_string()); - m.insert("u64".to_string(), "INTEGER".to_string()); - m.insert("u128".to_string(), "TEXT".to_string()); - m.insert("u256".to_string(), "TEXT".to_string()); - m.insert("usize".to_string(), "INTEGER".to_string()); - m.insert("bool".to_string(), "INTEGER".to_string()); - // m.insert("Cursor".to_string(), "TEXT".to_string()); - m.insert("ContractAddress".to_string(), "TEXT".to_string()); - m.insert("ClassHash".to_string(), "TEXT".to_string()); - // m.insert("DateTime".to_string(), "TEXT".to_string()); - m.insert("felt252".to_string(), "TEXT".to_string()); - m - }; -} - #[async_trait] pub trait Executable { async fn execute(&self) -> Result<()>; @@ -236,7 +216,7 @@ impl Sql { .await?; let (primitive_members, _): (Vec<_>, Vec<_>) = - members.into_iter().partition(|member| CAIRO_TO_SQL_TYPE.contains_key(&member.2)); + members.into_iter().partition(|member| CairoType::from_str(&member.2).is_ok()); // keys are part of model members, so combine keys and model values array let mut member_values: Vec = Vec::new(); @@ -250,7 +230,7 @@ impl Sql { format!( "INSERT OR REPLACE INTO [{id}] (entity_id, external_{name}) VALUES \ ('{entity_id}' {})", - format_value(&ty, &value).unwrap() + CairoType::from_str(&ty).unwrap().format_for_sql(vec![&value]).unwrap() ) }) .collect(); @@ -378,17 +358,6 @@ fn model_names_sql_string(entity_result: Option, new_model: &str) -> Ok(model_names) } -fn format_value(ty: &str, value: &FieldElement) -> Result { - match CAIRO_TO_SQL_TYPE.get(ty) { - Some(sql_type) => match sql_type.as_str() { - "INTEGER" => Ok(format!(", '{}'", value)), - "TEXT" => Ok(format!(", '{:#x}'", value)), - _ => Err(anyhow::anyhow!("Format not supported for type: {}", ty)), - }, - _ => Err(anyhow::anyhow!("Format not supported for type: {}", ty)), - } -} - fn felts_sql_string(felts: &[FieldElement]) -> String { felts.iter().map(|k| format!("{:#x}", k)).collect::>().join("/") + "/" } @@ -408,8 +377,12 @@ fn build_model_query(model: &Ty, model_idx: usize, parent_id: Option) -> match model { Ty::Struct(s) => { for (member_idx, member) in s.children.iter().enumerate() { - if let Some(sql_type) = CAIRO_TO_SQL_TYPE.get(&member.ty.name()) { - query.push_str(&format!("external_{} {}, ", member.name, sql_type)); + if let Ok(cairo_type) = CairoType::from_str(&member.ty.name()) { + query.push_str(&format!( + "external_{} {}, ", + member.name, + cairo_type.to_sql_type() + )); }; queries.push(format!( diff --git a/crates/torii/core/src/sql_test.rs b/crates/torii/core/src/sql_test.rs index 72111069f1..5a1c9d36e3 100644 --- a/crates/torii/core/src/sql_test.rs +++ b/crates/torii/core/src/sql_test.rs @@ -49,7 +49,7 @@ async fn test_load_from_manifest(pool: SqlitePool) { name: "Position".into(), children: vec![Member { name: "test".into(), - ty: Ty::Name("u32".to_string()), + ty: Ty::Terminal("u32".to_string()), key: false, }], }),