From 9e257131620f78d7339c326c9047a7f6bfab9617 Mon Sep 17 00:00:00 2001 From: lambda-0x <0xlambda@protonmail.com> Date: Wed, 5 Jun 2024 18:31:04 +0530 Subject: [PATCH 1/5] feat(sozo): make few argument global (#2029) * make profile arguments global * make offline flag global --- bin/sozo/src/args.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/sozo/src/args.rs b/bin/sozo/src/args.rs index ba11d6a5b1..928bc8a4b1 100644 --- a/bin/sozo/src/args.rs +++ b/bin/sozo/src/args.rs @@ -33,7 +33,7 @@ pub struct SozoArgs { #[arg(long)] #[arg(env = "SOZO_OFFLINE")] - #[arg(hide_short_help = true)] + #[arg(hide_short_help = true, global = true)] #[arg(help = "Run without accessing the network.")] pub offline: bool, @@ -70,15 +70,15 @@ impl SozoArgs { #[derive(Parser, Clone, Debug)] #[group(multiple = false)] pub struct ProfileSpec { - #[arg(short = 'P', long)] + #[arg(short = 'P', long, global = true)] #[arg(help = "Specify profile to use by name.")] pub profile: Option, - #[arg(long, hide_short_help = true)] + #[arg(long, hide_short_help = true, global = true)] #[arg(help = "Use release profile.")] pub release: bool, - #[arg(long, hide_short_help = true)] + #[arg(long, hide_short_help = true, global = true)] #[arg(help = "Use dev profile.")] pub dev: bool, } From 9ea6574d45770d24197ae44947d15068f2a01e2a Mon Sep 17 00:00:00 2001 From: Larko <59736843+Larkooo@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:35:02 -0400 Subject: [PATCH 2/5] fix: metadata update use bytearray (#2031) * fix: metadataupdate use bytearray * chore: get rid of metadata update * fix: remove uri len * chore: clippy * feat: rework resource metadata to match artifacts format --------- Co-authored-by: glihm --- crates/dojo-world/src/metadata.rs | 59 +++++++++++++++++-- crates/dojo-world/src/metadata_test.rs | 13 ++-- crates/sozo/ops/src/migration/migrate.rs | 15 ++--- crates/sozo/ops/src/tests/migration.rs | 8 +-- .../core/src/processors/metadata_update.rs | 15 +---- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/crates/dojo-world/src/metadata.rs b/crates/dojo-world/src/metadata.rs index 2d50f5fa70..c432d09186 100644 --- a/crates/dojo-world/src/metadata.rs +++ b/crates/dojo-world/src/metadata.rs @@ -109,17 +109,31 @@ pub fn dojo_metadata_from_workspace(ws: &Workspace<'_>) -> DojoMetadata { if let Ok(manifest) = BaseManifest::load_from_path(&manifest_dir.join(BASE_DIR)) { for model in manifest.models { let name = model.name.to_string(); - dojo_metadata.artifacts.insert( + dojo_metadata.resources_artifacts.insert( name.clone(), - build_artifact_from_name(&sources_dir, &abis_dir.join("models"), &name), + ResourceMetadata { + name: name.clone(), + artifacts: build_artifact_from_name( + &sources_dir, + &abis_dir.join("models"), + &name, + ), + }, ); } for contract in manifest.contracts { let name = contract.name.to_string(); - dojo_metadata.artifacts.insert( + dojo_metadata.resources_artifacts.insert( name.clone(), - build_artifact_from_name(&sources_dir, &abis_dir.join("contracts"), &name), + ResourceMetadata { + name: name.clone(), + artifacts: build_artifact_from_name( + &sources_dir, + &abis_dir.join("contracts"), + &name, + ), + }, ); } } @@ -135,12 +149,19 @@ pub struct ProjectMetadata { pub env: Option, } +/// Metadata for a user defined resource (models, contracts). +#[derive(Default, Serialize, Deserialize, Debug, Clone)] +pub struct ResourceMetadata { + pub name: String, + pub artifacts: ArtifactMetadata, +} + /// Metadata collected from the project configuration and the Dojo workspace #[derive(Default, Deserialize, Debug, Clone)] pub struct DojoMetadata { pub world: WorldMetadata, pub env: Option, - pub artifacts: HashMap, + pub resources_artifacts: HashMap, } #[derive(Debug)] @@ -342,6 +363,34 @@ impl ArtifactMetadata { } } +impl ResourceMetadata { + pub async fn upload(&self) -> Result { + let mut meta = self.clone(); + let client = + IpfsClient::from_str(IPFS_CLIENT_URL)?.with_credentials(IPFS_USERNAME, IPFS_PASSWORD); + + if let Some(Uri::File(abi)) = &self.artifacts.abi { + let abi_data = std::fs::read(abi)?; + let reader = Cursor::new(abi_data); + let response = client.add(reader).await?; + meta.artifacts.abi = Some(Uri::Ipfs(format!("ipfs://{}", response.hash))) + }; + + if let Some(Uri::File(source)) = &self.artifacts.source { + let source_data = std::fs::read(source)?; + let reader = Cursor::new(source_data); + let response = client.add(reader).await?; + meta.artifacts.source = Some(Uri::Ipfs(format!("ipfs://{}", response.hash))) + }; + + let serialized = json!(meta).to_string(); + let reader = Cursor::new(serialized); + let response = client.add(reader).await?; + + Ok(response.hash) + } +} + impl DojoMetadata { pub fn env(&self) -> Option<&Environment> { self.env.as_ref() diff --git a/crates/dojo-world/src/metadata_test.rs b/crates/dojo-world/src/metadata_test.rs index 64d93723c4..412230164c 100644 --- a/crates/dojo-world/src/metadata_test.rs +++ b/crates/dojo-world/src/metadata_test.rs @@ -174,13 +174,18 @@ async fn get_full_dojo_metadata_from_workspace() { dbg!(&artifacts); for (abi_subdir, name) in artifacts { - let artifact = dojo_metadata.artifacts.get(&name); - assert!(artifact.is_some(), "bad artifact for {}", name); - let artifact = artifact.unwrap(); + let resource = dojo_metadata.resources_artifacts.get(&name); + assert!(resource.is_some(), "bad resource metadata for {}", name); + let resource = resource.unwrap(); let sanitized_name = name.replace("::", "_"); - check_artifact(artifact.clone(), sanitized_name, &abis_dir.join(abi_subdir), &sources_dir); + check_artifact( + resource.artifacts.clone(), + sanitized_name, + &abis_dir.join(abi_subdir), + &sources_dir, + ); } } diff --git a/crates/sozo/ops/src/migration/migrate.rs b/crates/sozo/ops/src/migration/migrate.rs index 044479a5d6..831fd22c48 100644 --- a/crates/sozo/ops/src/migration/migrate.rs +++ b/crates/sozo/ops/src/migration/migrate.rs @@ -9,7 +9,7 @@ use dojo_world::manifest::{ AbiFormat, BaseManifest, DeploymentManifest, DojoContract, DojoModel, Manifest, ManifestMethods, WorldContract as ManifestWorldContract, WorldMetadata, }; -use dojo_world::metadata::{dojo_metadata_from_workspace, ArtifactMetadata}; +use dojo_world::metadata::{dojo_metadata_from_workspace, ResourceMetadata}; use dojo_world::migration::class::ClassMigration; use dojo_world::migration::contract::ContractMigration; use dojo_world::migration::strategy::{generate_salt, prepare_for_migration, MigrationStrategy}; @@ -255,13 +255,12 @@ where /// on success. async fn upload_on_ipfs_and_create_resource( ui: &Ui, - element_name: String, resource_id: FieldElement, - artifact: ArtifactMetadata, + metadata: ResourceMetadata, ) -> Result { - match artifact.upload().await { + match metadata.upload().await { Ok(hash) => { - ui.print_sub(format!("{}: ipfs://{}", element_name, hash)); + ui.print_sub(format!("{}: ipfs://{}", metadata.name, hash)); create_resource_metadata(resource_id, hash) } Err(_) => Err(anyhow!("Failed to upload IPFS resource.")), @@ -330,10 +329,9 @@ where // models if !migration_output.models.is_empty() { for model_name in migration_output.models { - if let Some(m) = dojo_metadata.artifacts.get(&model_name) { + if let Some(m) = dojo_metadata.resources_artifacts.get(&model_name) { ipfs.push(upload_on_ipfs_and_create_resource( &ui, - model_name.clone(), get_selector_from_name(&model_name).expect("ASCII model name"), m.clone(), )); @@ -346,10 +344,9 @@ where if !migrated_contracts.is_empty() { for contract in migrated_contracts { - if let Some(m) = dojo_metadata.artifacts.get(&contract.name) { + if let Some(m) = dojo_metadata.resources_artifacts.get(&contract.name) { ipfs.push(upload_on_ipfs_and_create_resource( &ui, - contract.name.clone(), contract.contract_address, m.clone(), )); diff --git a/crates/sozo/ops/src/tests/migration.rs b/crates/sozo/ops/src/tests/migration.rs index 708724a4c8..1f82982bf7 100644 --- a/crates/sozo/ops/src/tests/migration.rs +++ b/crates/sozo/ops/src/tests/migration.rs @@ -484,19 +484,19 @@ async fn check_artifact_metadata( ) { let resource = world_reader.metadata(&resource_id).call().await.unwrap(); - let expected_artifact = dojo_metadata.artifacts.get(element_name); + let expected_resource = dojo_metadata.resources_artifacts.get(element_name); assert!( - expected_artifact.is_some(), + expected_resource.is_some(), "Unable to find local artifact metadata for {}", element_name ); - let expected_artifact = expected_artifact.unwrap(); + let expected_resource = expected_resource.unwrap(); check_ipfs_metadata( client, element_name, &resource.metadata_uri.to_string().unwrap(), - expected_artifact, + &expected_resource.artifacts, ) .await; } diff --git a/crates/torii/core/src/processors/metadata_update.rs b/crates/torii/core/src/processors/metadata_update.rs index 07859743b2..ed2432f712 100644 --- a/crates/torii/core/src/processors/metadata_update.rs +++ b/crates/torii/core/src/processors/metadata_update.rs @@ -4,11 +4,11 @@ use anyhow::{Error, Result}; use async_trait::async_trait; use base64::engine::general_purpose; use base64::Engine as _; +use cainome::cairo_serde::{ByteArray, CairoSerde}; use dojo_world::contracts::world::WorldContractReader; use dojo_world::metadata::{Uri, WorldMetadata}; use reqwest::Client; use starknet::core::types::{Event, MaybePendingTransactionReceipt}; -use starknet::core::utils::parse_cairo_short_string; use starknet::providers::Provider; use starknet_crypto::FieldElement; use tokio_util::bytes::Bytes; @@ -58,18 +58,7 @@ where event: &Event, ) -> Result<(), Error> { let resource = &event.data[0]; - let uri_len: u8 = event.data[1].try_into().unwrap(); - - let uri_str = if uri_len > 0 { - event.data[2..=uri_len as usize + 1] - .iter() - .map(parse_cairo_short_string) - .collect::, _>>()? - .concat() - } else { - "".to_string() - }; - + let uri_str = ByteArray::cairo_deserialize(&event.data, 1)?.to_string()?; info!( target: LOG_TARGET, resource = %format!("{:#x}", resource), From 196732baef4386262003ef0df3fc7f9cbc4ccaf2 Mon Sep 17 00:00:00 2001 From: Larko <59736843+Larkooo@users.noreply.github.com> Date: Mon, 10 Jun 2024 14:22:48 -0400 Subject: [PATCH 3/5] feat(torii-libp2p): support new layout types for SNIP-12 typed data (#2032) * feat(torii-libp2p): support new layout types * feat: handle u256 low high * feat: parse SNIP-12 compliant enum * feat: tests for new typed data * chore: move tests to tests.rs --- crates/torii/libp2p/src/server/mod.rs | 411 +++++++++++++++----------- crates/torii/libp2p/src/tests.rs | 257 +++++++++++++++- 2 files changed, 497 insertions(+), 171 deletions(-) diff --git a/crates/torii/libp2p/src/server/mod.rs b/crates/torii/libp2p/src/server/mod.rs index fa88af09cd..3562b5a46f 100644 --- a/crates/torii/libp2p/src/server/mod.rs +++ b/crates/torii/libp2p/src/server/mod.rs @@ -9,7 +9,7 @@ use std::{fs, io}; use chrono::Utc; use crypto_bigint::U256; use dojo_types::primitive::Primitive; -use dojo_types::schema::{Struct, Ty}; +use dojo_types::schema::Ty; use futures::StreamExt; use indexmap::IndexMap; use libp2p::core::multiaddr::Protocol; @@ -443,175 +443,174 @@ fn ty_keys(ty: &Ty) -> Result, Error> { } } -pub fn parse_ty_to_object(ty: &Ty) -> Result, Error> { - match ty { - Ty::Struct(struct_ty) => { - let mut object = IndexMap::new(); - for member in &struct_ty.children { - let mut member_object = IndexMap::new(); - member_object.insert("key".to_string(), PrimitiveType::Bool(member.key)); - member_object.insert( - "type".to_string(), - PrimitiveType::String(ty_to_string_type(&member.ty)), - ); - member_object.insert("value".to_string(), parse_ty_to_primitive(&member.ty)?); - object.insert(member.name.clone(), PrimitiveType::Object(member_object)); +pub fn parse_value_to_ty(value: &PrimitiveType, ty: &mut Ty) -> Result<(), Error> { + match value { + PrimitiveType::Object(object) => match ty { + Ty::Struct(struct_) => { + for (key, value) in object { + let member = + struct_.children.iter_mut().find(|member| member.name == *key).ok_or_else( + || Error::InvalidMessageError(format!("Member {} not found", key)), + )?; + + parse_value_to_ty(value, &mut member.ty)?; + } } - Ok(object) - } - _ => Err(Error::InvalidMessageError("Expected Struct type".to_string())), - } -} + // U256 is an object with two u128 fields + // low and high + Ty::Primitive(Primitive::U256(u256)) => { + let mut low = Ty::Primitive(Primitive::U128(None)); + let mut high = Ty::Primitive(Primitive::U128(None)); -pub fn ty_to_string_type(ty: &Ty) -> String { - match ty { - Ty::Primitive(primitive) => match primitive { - Primitive::U8(_) => "u8".to_string(), - Primitive::U16(_) => "u16".to_string(), - Primitive::U32(_) => "u32".to_string(), - Primitive::USize(_) => "usize".to_string(), - Primitive::U64(_) => "u64".to_string(), - Primitive::U128(_) => "u128".to_string(), - Primitive::U256(_) => "u256".to_string(), - Primitive::Felt252(_) => "felt252".to_string(), - Primitive::ClassHash(_) => "class_hash".to_string(), - Primitive::ContractAddress(_) => "contract_address".to_string(), - Primitive::Bool(_) => "bool".to_string(), - }, - Ty::Struct(_) => "struct".to_string(), - Ty::Tuple(_) => "tuple".to_string(), - Ty::Array(_) => "array".to_string(), - Ty::ByteArray(_) => "bytearray".to_string(), - Ty::Enum(_) => "enum".to_string(), - } -} + // parse the low and high fields + parse_value_to_ty(&object["low"], &mut low)?; + parse_value_to_ty(&object["high"], &mut high)?; -pub fn parse_ty_to_primitive(ty: &Ty) -> Result { - match ty { - Ty::Primitive(primitive) => match primitive { - Primitive::U8(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) - } - Primitive::U16(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) - } - Primitive::U32(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + let low = low.as_primitive().unwrap().as_u128().unwrap(); + let high = high.as_primitive().unwrap().as_u128().unwrap(); + + let mut bytes = [0u8; 32]; + bytes[..16].copy_from_slice(&high.to_be_bytes()); + bytes[16..].copy_from_slice(&low.to_be_bytes()); + + *u256 = Some(U256::from_be_slice(&bytes)); } - Primitive::USize(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + // an enum is a SNIP-12 compliant object with a single key + // where the K is the variant name + // and the value is the variant value + Ty::Enum(enum_) => { + let (option_name, value) = object.first().ok_or_else(|| { + Error::InvalidMessageError("Enum variant not found".to_string()) + })?; + + enum_.options.iter_mut().for_each(|option| { + if option.name == *option_name { + parse_value_to_ty(value, &mut option.ty).unwrap(); + } + }); + + enum_.set_option(option_name).map_err(|e| { + Error::InvalidMessageError(format!("Failed to set enum option: {}", e)) + })?; } - Primitive::U64(value) => { - Ok(PrimitiveType::Number(Number::from(value.map(|v| v).unwrap_or(0u64)))) + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid object type for {}", + ty.name() + ))); } - Primitive::U128(value) => Ok(PrimitiveType::String( - value.map(|v| v.to_string()).unwrap_or_else(|| "0".to_string()), - )), - Primitive::U256(value) => Ok(PrimitiveType::String( - value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), - )), - Primitive::Felt252(value) => Ok(PrimitiveType::String( - value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), - )), - Primitive::ClassHash(value) => Ok(PrimitiveType::String( - value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), - )), - Primitive::ContractAddress(value) => Ok(PrimitiveType::String( - value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), - )), - Primitive::Bool(value) => Ok(PrimitiveType::Bool(value.unwrap_or(false))), }, - _ => Err(Error::InvalidMessageError("Expected Primitive type".to_string())), - } -} - -pub fn parse_object_to_ty( - model: &mut Struct, - object: &IndexMap, -) -> Result<(), Error> { - for (field_name, value) in object { - let field = model.children.iter_mut().find(|m| m.name == *field_name).ok_or_else(|| { - Error::InvalidMessageError(format!("Field {} not found in model", field_name)) - })?; + PrimitiveType::Array(values) => match ty { + Ty::Array(array) => { + let inner_type = array[0].clone(); + + // clear the array, which contains the inner type + array.clear(); + + // parse each value to the inner type + for value in values { + let mut ty = inner_type.clone(); + parse_value_to_ty(value, &mut ty)?; + array.push(ty); + } + } + Ty::Tuple(tuple) => { + // our array values need to match the length of the tuple + if tuple.len() != values.len() { + return Err(Error::InvalidMessageError("Tuple length mismatch".to_string())); + } - match value { - PrimitiveType::Object(object) => { - parse_object_to_ty(model, object)?; + for (i, value) in tuple.iter_mut().enumerate() { + parse_value_to_ty(&values[i], value)?; + } } - PrimitiveType::Array(_) => { - // tuples not supported yet - unimplemented!() + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid array type for {}", + ty.name() + ))); } - PrimitiveType::Number(number) => match &mut field.ty { - Ty::Primitive(primitive) => match *primitive { - Primitive::U8(ref mut u8) => { - *u8 = Some(number.as_u64().unwrap() as u8); - } - Primitive::U16(ref mut u16) => { - *u16 = Some(number.as_u64().unwrap() as u16); - } - Primitive::U32(ref mut u32) => { - *u32 = Some(number.as_u64().unwrap() as u32); - } - Primitive::USize(ref mut usize) => { - *usize = Some(number.as_u64().unwrap() as u32); - } - Primitive::U64(ref mut u64) => { - *u64 = Some(number.as_u64().unwrap()); - } - _ => { - return Err(Error::InvalidMessageError("Invalid number type".to_string())); - } - }, - Ty::Enum(enum_) => { - enum_.option = Some(number.as_u64().unwrap() as u8); + }, + PrimitiveType::Number(number) => match ty { + Ty::Primitive(primitive) => match *primitive { + Primitive::U8(ref mut u8) => { + *u8 = Some(number.as_u64().unwrap() as u8); + } + Primitive::U16(ref mut u16) => { + *u16 = Some(number.as_u64().unwrap() as u16); + } + Primitive::U32(ref mut u32) => { + *u32 = Some(number.as_u64().unwrap() as u32); + } + Primitive::USize(ref mut usize) => { + *usize = Some(number.as_u64().unwrap() as u32); + } + Primitive::U64(ref mut u64) => { + *u64 = Some(number.as_u64().unwrap()); + } + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid number type for {}", + ty.name() + ))); } - _ => return Err(Error::InvalidMessageError("Invalid number type".to_string())), }, - PrimitiveType::Bool(boolean) => { - field.ty = Ty::Primitive(Primitive::Bool(Some(*boolean))); + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid number type for {}", + ty.name() + ))); } - PrimitiveType::String(string) => match &mut field.ty { - Ty::Primitive(primitive) => match primitive { - Primitive::U8(v) => { - *v = Some(u8::from_str(string).unwrap()); - } - Primitive::U16(v) => { - *v = Some(u16::from_str(string).unwrap()); - } - Primitive::U32(v) => { - *v = Some(u32::from_str(string).unwrap()); - } - Primitive::USize(v) => { - *v = Some(u32::from_str(string).unwrap()); - } - Primitive::U64(v) => { - *v = Some(u64::from_str(string).unwrap()); - } - Primitive::U128(v) => { - *v = Some(u128::from_str(string).unwrap()); - } - Primitive::U256(v) => { - *v = Some(U256::from_be_hex(string)); - } - Primitive::Felt252(v) => { - *v = Some(FieldElement::from_str(string).unwrap()); - } - Primitive::ClassHash(v) => { - *v = Some(FieldElement::from_str(string).unwrap()); - } - Primitive::ContractAddress(v) => { - *v = Some(FieldElement::from_str(string).unwrap()); - } - Primitive::Bool(v) => { - *v = Some(bool::from_str(string).unwrap()); - } - }, + }, + PrimitiveType::Bool(boolean) => { + *ty = Ty::Primitive(Primitive::Bool(Some(*boolean))); + } + PrimitiveType::String(string) => match ty { + Ty::Primitive(primitive) => match primitive { + Primitive::U8(v) => { + *v = Some(u8::from_str(string).unwrap()); + } + Primitive::U16(v) => { + *v = Some(u16::from_str(string).unwrap()); + } + Primitive::U32(v) => { + *v = Some(u32::from_str(string).unwrap()); + } + Primitive::USize(v) => { + *v = Some(u32::from_str(string).unwrap()); + } + Primitive::U64(v) => { + *v = Some(u64::from_str(string).unwrap()); + } + Primitive::U128(v) => { + *v = Some(u128::from_str(string).unwrap()); + } + Primitive::Felt252(v) => { + *v = Some(FieldElement::from_str(string).unwrap()); + } + Primitive::ClassHash(v) => { + *v = Some(FieldElement::from_str(string).unwrap()); + } + Primitive::ContractAddress(v) => { + *v = Some(FieldElement::from_str(string).unwrap()); + } + Primitive::Bool(v) => { + *v = Some(bool::from_str(string).unwrap()); + } _ => { - return Err(Error::InvalidMessageError("Invalid string type".to_string())); + return Err(Error::InvalidMessageError("Invalid primitive type".to_string())); } }, - } + Ty::ByteArray(s) => { + *s = string.clone(); + } + _ => { + return Err(Error::InvalidMessageError(format!( + "Invalid string type for {}", + ty.name() + ))); + } + }, } Ok(()) @@ -649,18 +648,8 @@ async fn validate_message( )) })?; - let ty_struct = if let Ty::Struct(ty_struct) = &mut ty { - ty_struct - } else { - return Err(Error::InvalidMessageError("Model is not a struct".to_string())); - }; - if let Some(object) = message.get(model_name) { - if let PrimitiveType::Object(object) = object { - parse_object_to_ty(ty_struct, object)? - } else { - return Err(Error::InvalidMessageError("Model is not a struct".to_string())); - } + parse_value_to_ty(object, &mut ty)?; } else { return Err(Error::InvalidMessageError("Model is missing".to_string())); }; @@ -703,14 +692,98 @@ fn read_or_create_certificate(path: &Path) -> anyhow::Result { Ok(cert) } +// Deprecated. These should be potentially removed. As Ty -> TypedData is now done +// on the SDKs side +pub fn parse_ty_to_object(ty: &Ty) -> Result, Error> { + match ty { + Ty::Struct(struct_ty) => { + let mut object = IndexMap::new(); + for member in &struct_ty.children { + let mut member_object = IndexMap::new(); + member_object.insert("key".to_string(), PrimitiveType::Bool(member.key)); + member_object.insert( + "type".to_string(), + PrimitiveType::String(ty_to_string_type(&member.ty)), + ); + member_object.insert("value".to_string(), parse_ty_to_primitive(&member.ty)?); + object.insert(member.name.clone(), PrimitiveType::Object(member_object)); + } + Ok(object) + } + _ => Err(Error::InvalidMessageError("Expected Struct type".to_string())), + } +} + +pub fn ty_to_string_type(ty: &Ty) -> String { + match ty { + Ty::Primitive(primitive) => match primitive { + Primitive::U8(_) => "u8".to_string(), + Primitive::U16(_) => "u16".to_string(), + Primitive::U32(_) => "u32".to_string(), + Primitive::USize(_) => "usize".to_string(), + Primitive::U64(_) => "u64".to_string(), + Primitive::U128(_) => "u128".to_string(), + Primitive::U256(_) => "u256".to_string(), + Primitive::Felt252(_) => "felt252".to_string(), + Primitive::ClassHash(_) => "class_hash".to_string(), + Primitive::ContractAddress(_) => "contract_address".to_string(), + Primitive::Bool(_) => "bool".to_string(), + }, + Ty::Struct(_) => "struct".to_string(), + Ty::Tuple(_) => "tuple".to_string(), + Ty::Array(_) => "array".to_string(), + Ty::ByteArray(_) => "bytearray".to_string(), + Ty::Enum(_) => "enum".to_string(), + } +} + +pub fn parse_ty_to_primitive(ty: &Ty) -> Result { + match ty { + Ty::Primitive(primitive) => match primitive { + Primitive::U8(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + } + Primitive::U16(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + } + Primitive::U32(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + } + Primitive::USize(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v as u64).unwrap_or(0u64)))) + } + Primitive::U64(value) => { + Ok(PrimitiveType::Number(Number::from(value.map(|v| v).unwrap_or(0u64)))) + } + Primitive::U128(value) => Ok(PrimitiveType::String( + value.map(|v| v.to_string()).unwrap_or_else(|| "0".to_string()), + )), + Primitive::U256(value) => Ok(PrimitiveType::String( + value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), + )), + Primitive::Felt252(value) => Ok(PrimitiveType::String( + value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), + )), + Primitive::ClassHash(value) => Ok(PrimitiveType::String( + value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), + )), + Primitive::ContractAddress(value) => Ok(PrimitiveType::String( + value.map(|v| format!("{:#x}", v)).unwrap_or_else(|| "0".to_string()), + )), + Primitive::Bool(value) => Ok(PrimitiveType::Bool(value.unwrap_or(false))), + }, + _ => Err(Error::InvalidMessageError("Expected Primitive type".to_string())), + } +} + #[cfg(test)] mod tests { use tempfile::tempdir; use super::*; - #[tokio::test] - async fn test_read_or_create_identity() { + #[test] + fn test_read_or_create_identity() { let dir = tempdir().unwrap(); let identity_path = dir.path().join("identity"); @@ -725,8 +798,8 @@ mod tests { dir.close().unwrap(); } - #[tokio::test] - async fn test_read_or_create_certificate() { + #[test] + fn test_read_or_create_certificate() { let dir = tempdir().unwrap(); let cert_path = dir.path().join("certificate"); diff --git a/crates/torii/libp2p/src/tests.rs b/crates/torii/libp2p/src/tests.rs index 5220333683..3d0e5581be 100644 --- a/crates/torii/libp2p/src/tests.rs +++ b/crates/torii/libp2p/src/tests.rs @@ -3,12 +3,267 @@ mod test { use std::error::Error; use crate::client::RelayClient; + use crate::server::parse_value_to_ty; + use crate::typed_data::PrimitiveType; #[cfg(target_arch = "wasm32")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + use crypto_bigint::U256; + use dojo_types::primitive::Primitive; + use dojo_types::schema::{Enum, EnumOption, Member, Struct, Ty}; + use serde_json::Number; + use starknet_crypto::FieldElement; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; + #[test] + fn test_parse_primitive_to_ty() { + // primitives + let mut ty = Ty::Primitive(Primitive::U8(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U8(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U16(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U16(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U32(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U32(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::USize(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::USize(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U64(None)); + let value = PrimitiveType::Number(Number::from(1u64)); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U64(Some(1)))); + + let mut ty = Ty::Primitive(Primitive::U128(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U128(Some(1)))); + + // test u256 with low high + let mut ty = Ty::Primitive(Primitive::U256(None)); + let value = PrimitiveType::Object( + vec![ + ("low".to_string(), PrimitiveType::String("1".to_string())), + ("high".to_string(), PrimitiveType::String("0".to_string())), + ] + .into_iter() + .collect(), + ); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::U256(Some(U256::ONE)))); + + let mut ty = Ty::Primitive(Primitive::Felt252(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::Felt252(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::ClassHash(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::ClassHash(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::ContractAddress(None)); + let value = PrimitiveType::String("1".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::ContractAddress(Some(FieldElement::ONE)))); + + let mut ty = Ty::Primitive(Primitive::Bool(None)); + let value = PrimitiveType::Bool(true); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::Primitive(Primitive::Bool(Some(true)))); + + // bytearray + let mut ty = Ty::ByteArray("".to_string()); + let value = PrimitiveType::String("mimi".to_string()); + parse_value_to_ty(&value, &mut ty).unwrap(); + assert_eq!(ty, Ty::ByteArray("mimi".to_string())); + } + + #[test] + fn test_parse_complex_to_ty() { + let mut ty = Ty::Struct(Struct { + name: "PlayerConfig".to_string(), + children: vec![ + Member { + name: "player".to_string(), + ty: Ty::Primitive(Primitive::ContractAddress(None)), + key: true, + }, + Member { name: "name".to_string(), ty: Ty::ByteArray("".to_string()), key: false }, + Member { + name: "items".to_string(), + // array of PlayerItem struct + ty: Ty::Array(vec![Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + ], + })]), + key: false, + }, + // a favorite_item field with enum type Option + Member { + name: "favorite_item".to_string(), + ty: Ty::Enum(Enum { + name: "Option".to_string(), + option: None, + options: vec![ + EnumOption { name: "None".to_string(), ty: Ty::Tuple(vec![]) }, + EnumOption { + name: "Some".to_string(), + ty: Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(None)), + key: false, + }, + ], + }), + }, + ], + }), + key: false, + }, + ], + }); + + let value = PrimitiveType::Object( + vec![ + ("player".to_string(), PrimitiveType::String("1".to_string())), + ("name".to_string(), PrimitiveType::String("mimi".to_string())), + ( + "items".to_string(), + PrimitiveType::Array(vec![PrimitiveType::Object( + vec![ + ("item_id".to_string(), PrimitiveType::String("1".to_string())), + ("quantity".to_string(), PrimitiveType::Number(Number::from(1u64))), + ] + .into_iter() + .collect(), + )]), + ), + ( + "favorite_item".to_string(), + PrimitiveType::Object( + vec![( + "Some".to_string(), + PrimitiveType::Object( + vec![ + ("item_id".to_string(), PrimitiveType::String("1".to_string())), + ( + "quantity".to_string(), + PrimitiveType::Number(Number::from(1u64)), + ), + ] + .into_iter() + .collect(), + ), + )] + .into_iter() + .collect(), + ), + ), + ] + .into_iter() + .collect(), + ); + + parse_value_to_ty(&value, &mut ty).unwrap(); + + assert_eq!( + ty, + Ty::Struct(Struct { + name: "PlayerConfig".to_string(), + children: vec![ + Member { + name: "player".to_string(), + ty: Ty::Primitive(Primitive::ContractAddress(Some(FieldElement::ONE))), + key: true, + }, + Member { + name: "name".to_string(), + ty: Ty::ByteArray("mimi".to_string()), + key: false, + }, + Member { + name: "items".to_string(), + ty: Ty::Array(vec![Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + ], + })]), + key: false, + }, + Member { + name: "favorite_item".to_string(), + ty: Ty::Enum(Enum { + name: "Option".to_string(), + option: Some(1_u8), + options: vec![ + EnumOption { name: "None".to_string(), ty: Ty::Tuple(vec![]) }, + EnumOption { + name: "Some".to_string(), + ty: Ty::Struct(Struct { + name: "PlayerItem".to_string(), + children: vec![ + Member { + name: "item_id".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + Member { + name: "quantity".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(1))), + key: false, + }, + ], + }), + }, + ] + }), + key: false, + }, + ], + }) + ); + } + // This tests subscribing to a topic and receiving a message #[cfg(not(target_arch = "wasm32"))] #[tokio::test] @@ -95,8 +350,6 @@ mod test { ), ); - println!("object ty: {:?}", parse_ty_to_object(&Ty::Struct(data)).unwrap()); - client .command_sender .publish(Message { From a636d23da43d0e9ae249cfdc4a0c8417e2a9dda1 Mon Sep 17 00:00:00 2001 From: lambda-0x <0xlambda@protonmail.com> Date: Mon, 10 Jun 2024 23:53:25 +0530 Subject: [PATCH 4/5] feat(sozo): add a subcommand to generate overlay files (#2025) * initial commit * add support for model overlays * clean up * add test for merge * separate out match statement * fix directory creation * fix: fix test typo + add comment on isolated variant test --------- Co-authored-by: glihm --- Cargo.lock | 1 + bin/sozo/src/commands/clean.rs | 2 +- bin/sozo/src/commands/dev.rs | 3 +- bin/sozo/src/commands/migrate.rs | 13 +- crates/benches/src/deployer.rs | 4 +- crates/dojo-lang/src/compiler.rs | 12 +- crates/dojo-test-utils/src/migration.rs | 3 +- crates/dojo-world/src/contracts/world_test.rs | 3 +- .../dojo-world/src/manifest/manifest_test.rs | 174 +++++++++++++++++- crates/dojo-world/src/manifest/mod.rs | 122 ++++++++++-- crates/dojo-world/src/manifest/types.rs | 3 +- crates/sozo/ops/Cargo.toml | 1 + crates/sozo/ops/src/events.rs | 6 +- crates/sozo/ops/src/migration/migrate.rs | 4 +- crates/sozo/ops/src/migration/mod.rs | 94 +++++++++- crates/sozo/ops/src/migration/utils.rs | 4 +- crates/sozo/ops/src/tests/migration.rs | 4 +- .../dojo_examples_actions_actions.toml | 6 +- .../dojo_examples_others_others.toml | 3 +- .../dev/overlays/dojo_base_base.toml | 1 + .../dev/overlays/dojo_world_world.toml | 1 + .../dojo_examples_actions_actions_moved.toml | 1 + .../dojo_examples_models_emote_message.toml | 1 + .../models/dojo_examples_models_moves.toml | 1 + .../dojo_examples_models_player_config.toml | 1 + .../models/dojo_examples_models_position.toml | 1 + ...es_others_others_contract_initialized.toml | 1 + 27 files changed, 408 insertions(+), 62 deletions(-) create mode 100644 examples/spawn-and-move/manifests/dev/overlays/dojo_base_base.toml create mode 100644 examples/spawn-and-move/manifests/dev/overlays/dojo_world_world.toml create mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_actions_actions_moved.toml create mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_emote_message.toml create mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_moves.toml create mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_player_config.toml create mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_position.toml create mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_others_others_contract_initialized.toml diff --git a/Cargo.lock b/Cargo.lock index 530db77742..d784dbbe5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12586,6 +12586,7 @@ dependencies = [ "starknet-crypto 0.6.2", "thiserror", "tokio", + "toml 0.8.13", "tracing", "tracing-log 0.1.4", "url", diff --git a/bin/sozo/src/commands/clean.rs b/bin/sozo/src/commands/clean.rs index 06fcd0a142..7d02a03834 100644 --- a/bin/sozo/src/commands/clean.rs +++ b/bin/sozo/src/commands/clean.rs @@ -3,7 +3,7 @@ use std::fs; use anyhow::Result; use camino::Utf8PathBuf; use clap::Args; -use dojo_lang::compiler::{ABIS_DIR, BASE_DIR, MANIFESTS_DIR}; +use dojo_world::manifest::{ABIS_DIR, BASE_DIR, MANIFESTS_DIR}; use scarb::core::Config; use tracing::trace; diff --git a/bin/sozo/src/commands/dev.rs b/bin/sozo/src/commands/dev.rs index ae6f7aa55b..2ff6eef64a 100644 --- a/bin/sozo/src/commands/dev.rs +++ b/bin/sozo/src/commands/dev.rs @@ -8,9 +8,8 @@ use cairo_lang_compiler::db::RootDatabase; use cairo_lang_filesystem::db::{AsFilesGroupMut, FilesGroupEx, PrivRawFileContentQuery}; use cairo_lang_filesystem::ids::FileId; use clap::Args; -use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR}; use dojo_lang::scarb_internal::build_scarb_root_database; -use dojo_world::manifest::{BaseManifest, DeploymentManifest}; +use dojo_world::manifest::{BaseManifest, DeploymentManifest, BASE_DIR, MANIFESTS_DIR}; use dojo_world::metadata::dojo_metadata_from_workspace; use dojo_world::migration::world::WorldDiff; use dojo_world::migration::TxnConfig; diff --git a/bin/sozo/src/commands/migrate.rs b/bin/sozo/src/commands/migrate.rs index 9cbd421951..3acb7f25cb 100644 --- a/bin/sozo/src/commands/migrate.rs +++ b/bin/sozo/src/commands/migrate.rs @@ -1,6 +1,6 @@ use anyhow::{anyhow, Context, Result}; use clap::{Args, Subcommand}; -use dojo_lang::compiler::MANIFESTS_DIR; +use dojo_world::manifest::MANIFESTS_DIR; use dojo_world::metadata::{dojo_metadata_from_workspace, Environment}; use dojo_world::migration::TxnConfig; use katana_rpc_api::starknet::RPC_SPEC_VERSION; @@ -50,6 +50,8 @@ pub enum MigrateCommand { #[command(flatten)] transaction: TransactionOptions, }, + #[command(about = "Generate overlays file.")] + GenerateOverlays, } impl MigrateArgs { @@ -57,6 +59,13 @@ impl MigrateArgs { trace!(args = ?self); let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; + // This variant is tested before the match on `self.command` to avoid + // having the need to spin up a Katana to generate the files. + if let MigrateCommand::GenerateOverlays = self.command { + trace!("Generating overlays."); + return migration::generate_overlays(&ws); + } + let env_metadata = if config.manifest_path().exists() { dojo_metadata_from_workspace(&ws).env().cloned() } else { @@ -81,6 +90,7 @@ impl MigrateArgs { match self.command { MigrateCommand::Plan => config.tokio_handle().block_on(async { + trace!(name, "Planning migration."); migration::migrate( &ws, world_address, @@ -99,6 +109,7 @@ impl MigrateArgs { migration::migrate(&ws, world_address, rpc_url, account, &name, false, txn_config) .await }), + _ => unreachable!("other case handled above."), } } } diff --git a/crates/benches/src/deployer.rs b/crates/benches/src/deployer.rs index abb0251c07..2a3aa0e08c 100644 --- a/crates/benches/src/deployer.rs +++ b/crates/benches/src/deployer.rs @@ -3,9 +3,9 @@ use std::path::PathBuf; use anyhow::{anyhow, bail, Context, Ok, Result}; use clap::Parser; -use dojo_lang::compiler::{DojoCompiler, DEPLOYMENTS_DIR, MANIFESTS_DIR}; +use dojo_lang::compiler::DojoCompiler; use dojo_lang::plugin::CairoPluginRepository; -use dojo_world::manifest::DeploymentManifest; +use dojo_world::manifest::{DeploymentManifest, DEPLOYMENTS_DIR, MANIFESTS_DIR}; use futures::executor::block_on; use katana_runner::KatanaRunner; use scarb::compiler::CompilerRepository; diff --git a/crates/dojo-lang/src/compiler.rs b/crates/dojo-lang/src/compiler.rs index 653aab250e..e21edfe2bb 100644 --- a/crates/dojo-lang/src/compiler.rs +++ b/crates/dojo-lang/src/compiler.rs @@ -21,7 +21,8 @@ use camino::{Utf8Path, Utf8PathBuf}; use convert_case::{Case, Casing}; use dojo_world::manifest::{ AbiFormat, Class, ComputedValueEntrypoint, DojoContract, DojoModel, Manifest, ManifestMethods, - BASE_CONTRACT_NAME, WORLD_CONTRACT_NAME, + ABIS_DIR, BASE_CONTRACT_NAME, BASE_DIR, CONTRACTS_DIR, MANIFESTS_DIR, MODELS_DIR, + WORLD_CONTRACT_NAME, }; use itertools::Itertools; use scarb::compiler::helpers::{build_compiler_config, collect_main_crate_ids}; @@ -40,15 +41,6 @@ use crate::semantics::utils::find_module_rw; const CAIRO_PATH_SEPARATOR: &str = "::"; -pub const MANIFESTS_DIR: &str = "manifests"; -pub const BASE_DIR: &str = "base"; -pub const OVERLAYS_DIR: &str = "overlays"; -pub const DEPLOYMENTS_DIR: &str = "deployments"; -pub const ABIS_DIR: &str = "abis"; - -pub const CONTRACTS_DIR: &str = "contracts"; -pub const MODELS_DIR: &str = "models"; - pub const SOURCES_DIR: &str = "src"; pub(crate) const LOG_TARGET: &str = "dojo_lang::compiler"; diff --git a/crates/dojo-test-utils/src/migration.rs b/crates/dojo-test-utils/src/migration.rs index 7ad6f25843..e8a576f341 100644 --- a/crates/dojo-test-utils/src/migration.rs +++ b/crates/dojo-test-utils/src/migration.rs @@ -1,7 +1,6 @@ use anyhow::Result; use camino::Utf8PathBuf; -use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR}; -use dojo_world::manifest::{BaseManifest, OverlayManifest}; +use dojo_world::manifest::{BaseManifest, OverlayManifest, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR}; use dojo_world::migration::strategy::{prepare_for_migration, MigrationStrategy}; use dojo_world::migration::world::WorldDiff; use katana_primitives::FieldElement; diff --git a/crates/dojo-world/src/contracts/world_test.rs b/crates/dojo-world/src/contracts/world_test.rs index 3b856c1f46..c011d573fb 100644 --- a/crates/dojo-world/src/contracts/world_test.rs +++ b/crates/dojo-world/src/contracts/world_test.rs @@ -1,14 +1,13 @@ use std::time::Duration; use camino::Utf8PathBuf; -use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR}; use dojo_test_utils::compiler; use katana_runner::KatanaRunner; use starknet::accounts::{Account, ConnectedAccount}; use starknet::core::types::{BlockId, BlockTag, FieldElement}; use super::{WorldContract, WorldContractReader}; -use crate::manifest::{BaseManifest, OverlayManifest}; +use crate::manifest::{BaseManifest, OverlayManifest, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR}; use crate::migration::strategy::prepare_for_migration; use crate::migration::world::WorldDiff; use crate::migration::{Declarable, Deployable, TxnConfig}; diff --git a/crates/dojo-world/src/manifest/manifest_test.rs b/crates/dojo-world/src/manifest/manifest_test.rs index bc92e1a4f8..a003fc0e23 100644 --- a/crates/dojo-world/src/manifest/manifest_test.rs +++ b/crates/dojo-world/src/manifest/manifest_test.rs @@ -2,7 +2,6 @@ use std::io::Write; use cainome::cairo_serde::{ByteArray, CairoSerde}; use camino::Utf8PathBuf; -use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR}; use dojo_test_utils::compiler; use dojo_test_utils::rpc::MockJsonRpcTransport; use katana_runner::KatanaRunner; @@ -14,10 +13,14 @@ use starknet::macros::{felt, selector}; use starknet::providers::jsonrpc::{JsonRpcClient, JsonRpcMethod}; use super::{ - parse_contracts_events, AbiFormat, BaseManifest, DojoContract, DojoModel, OverlayManifest, + parse_contracts_events, AbiFormat, BaseManifest, DojoContract, DojoModel, OverlayDojoContract, + OverlayManifest, }; use crate::contracts::world::test::deploy_world; -use crate::manifest::{parse_models_events, AbstractManifestError, DeploymentManifest, Manifest}; +use crate::manifest::{ + parse_models_events, AbstractManifestError, DeploymentManifest, Manifest, OverlayClass, + OverlayDojoModel, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR, +}; use crate::migration::world::WorldDiff; #[tokio::test] @@ -473,3 +476,168 @@ fn test_abi_format_load_abi_string() -> Result<(), Box> { Ok(()) } + +#[test] +fn overlay_merge_for_contract_and_model_work_as_expected() { + let other = OverlayManifest { + contracts: vec![ + OverlayDojoContract { name: "othercontract1".into(), ..Default::default() }, + OverlayDojoContract { name: "othercontract2".into(), ..Default::default() }, + OverlayDojoContract { name: "existingcontract".into(), ..Default::default() }, + ], + models: vec![ + OverlayDojoModel { name: "othermodel1".into(), ..Default::default() }, + OverlayDojoModel { name: "othermodel2".into(), ..Default::default() }, + OverlayDojoModel { name: "existingmodel".into(), ..Default::default() }, + ], + ..Default::default() + }; + + let mut current = OverlayManifest { + contracts: vec![ + OverlayDojoContract { name: "currentcontract1".into(), ..Default::default() }, + OverlayDojoContract { name: "currentcontract2".into(), ..Default::default() }, + OverlayDojoContract { name: "existingcontract".into(), ..Default::default() }, + ], + models: vec![ + OverlayDojoModel { name: "currentmodel1".into(), ..Default::default() }, + OverlayDojoModel { name: "currentmodel2".into(), ..Default::default() }, + OverlayDojoModel { name: "existingmodel".into(), ..Default::default() }, + ], + ..Default::default() + }; + + let expected = OverlayManifest { + contracts: vec![ + OverlayDojoContract { name: "currentcontract1".into(), ..Default::default() }, + OverlayDojoContract { name: "currentcontract2".into(), ..Default::default() }, + OverlayDojoContract { name: "existingcontract".into(), ..Default::default() }, + OverlayDojoContract { name: "othercontract1".into(), ..Default::default() }, + OverlayDojoContract { name: "othercontract2".into(), ..Default::default() }, + ], + models: vec![ + OverlayDojoModel { name: "currentmodel1".into(), ..Default::default() }, + OverlayDojoModel { name: "currentmodel2".into(), ..Default::default() }, + OverlayDojoModel { name: "existingmodel".into(), ..Default::default() }, + OverlayDojoModel { name: "othermodel1".into(), ..Default::default() }, + OverlayDojoModel { name: "othermodel2".into(), ..Default::default() }, + ], + ..Default::default() + }; + + current.merge(other); + + assert_eq!(current, expected); +} + +#[test] +fn overlay_merge_for_world_work_as_expected() { + // when other.world is none and current.world is some + let other = OverlayManifest { ..Default::default() }; + let mut current = OverlayManifest { + world: Some(OverlayClass { name: "world".into(), ..Default::default() }), + ..Default::default() + }; + let expected = OverlayManifest { + world: Some(OverlayClass { name: "world".into(), ..Default::default() }), + ..Default::default() + }; + current.merge(other); + + assert_eq!(current, expected); + + // when other.world is some and current.world is none + let other = OverlayManifest { + world: Some(OverlayClass { name: "world".into(), ..Default::default() }), + ..Default::default() + }; + let mut current = OverlayManifest { ..Default::default() }; + let expected = OverlayManifest { + world: Some(OverlayClass { name: "world".into(), ..Default::default() }), + ..Default::default() + }; + + current.merge(other); + assert_eq!(current, expected); + + // when other.world is some and current.world is some + let other = OverlayManifest { + world: Some(OverlayClass { name: "worldother".into(), ..Default::default() }), + ..Default::default() + }; + let mut current = OverlayManifest { + world: Some(OverlayClass { name: "worldcurrent".into(), ..Default::default() }), + ..Default::default() + }; + let expected = OverlayManifest { + world: Some(OverlayClass { name: "worldcurrent".into(), ..Default::default() }), + ..Default::default() + }; + + current.merge(other); + assert_eq!(current, expected); + + // when other.world is none and current.world is none + let other = OverlayManifest { ..Default::default() }; + let mut current = OverlayManifest { ..Default::default() }; + let expected = OverlayManifest { ..Default::default() }; + + current.merge(other); + assert_eq!(current, expected); +} + +#[test] +fn overlay_merge_for_base_work_as_expected() { + // when other.base is none and current.base is some + let other = OverlayManifest { ..Default::default() }; + let mut current = OverlayManifest { + base: Some(OverlayClass { name: "base".into(), ..Default::default() }), + ..Default::default() + }; + let expected = OverlayManifest { + base: Some(OverlayClass { name: "base".into(), ..Default::default() }), + ..Default::default() + }; + current.merge(other); + + assert_eq!(current, expected); + + // when other.base is some and current.base is none + let other = OverlayManifest { + base: Some(OverlayClass { name: "base".into(), ..Default::default() }), + ..Default::default() + }; + let mut current = OverlayManifest { ..Default::default() }; + let expected = OverlayManifest { + base: Some(OverlayClass { name: "base".into(), ..Default::default() }), + ..Default::default() + }; + + current.merge(other); + assert_eq!(current, expected); + + // when other.base is some and current.base is some + let other = OverlayManifest { + base: Some(OverlayClass { name: "baseother".into(), ..Default::default() }), + ..Default::default() + }; + let mut current = OverlayManifest { + base: Some(OverlayClass { name: "basecurrent".into(), ..Default::default() }), + ..Default::default() + }; + let expected = OverlayManifest { + base: Some(OverlayClass { name: "basecurrent".into(), ..Default::default() }), + ..Default::default() + }; + + current.merge(other); + assert_eq!(current, expected); + + // when other.base is none and current.base is none + let other = OverlayManifest { ..Default::default() }; + let mut current = OverlayManifest { ..Default::default() }; + let expected = OverlayManifest { ..Default::default() }; + + current.merge(other); + assert_eq!(current, expected); +} diff --git a/crates/dojo-world/src/manifest/mod.rs b/crates/dojo-world/src/manifest/mod.rs index d3aa32877a..5391761e49 100644 --- a/crates/dojo-world/src/manifest/mod.rs +++ b/crates/dojo-world/src/manifest/mod.rs @@ -39,6 +39,15 @@ pub const BASE_CONTRACT_NAME: &str = "dojo::base::base"; pub const RESOURCE_METADATA_CONTRACT_NAME: &str = "dojo::resource_metadata::resource_metadata"; pub const RESOURCE_METADATA_MODEL_NAME: &str = "0x5265736f757263654d65746164617461"; +pub const MANIFESTS_DIR: &str = "manifests"; +pub const BASE_DIR: &str = "base"; +pub const OVERLAYS_DIR: &str = "overlays"; +pub const DEPLOYMENTS_DIR: &str = "deployments"; +pub const ABIS_DIR: &str = "abis"; + +pub const CONTRACTS_DIR: &str = "contracts"; +pub const MODELS_DIR: &str = "models"; + #[derive(Error, Debug)] pub enum AbstractManifestError { #[error("Remote World not found.")] @@ -56,7 +65,9 @@ pub enum AbstractManifestError { #[error(transparent)] Model(#[from] ModelError), #[error(transparent)] - TOML(#[from] toml::de::Error), + TomlDe(#[from] toml::de::Error), + #[error(transparent)] + TomlSer(#[from] toml::ser::Error), #[error(transparent)] IO(#[from] io::Error), #[error("Abi couldn't be loaded from path: {0}")] @@ -93,8 +104,8 @@ impl From for DeploymentManifest { impl BaseManifest { /// Load the manifest from a file at the given path. pub fn load_from_path(path: &Utf8PathBuf) -> Result { - let contract_dir = path.join("contracts"); - let model_dir = path.join("models"); + let contract_dir = path.join(CONTRACTS_DIR); + let model_dir = path.join(MODELS_DIR); let world: Manifest = toml::from_str(&fs::read_to_string( path.join(WORLD_CONTRACT_NAME.replace("::", "_")).with_extension("toml"), @@ -136,6 +147,8 @@ impl BaseManifest { impl OverlayManifest { pub fn load_from_path(path: &Utf8PathBuf) -> Result { + fs::create_dir_all(path)?; + let mut world: Option = None; let world_path = path.join(WORLD_CONTRACT_NAME.replace("::", "_")).with_extension("toml"); @@ -151,15 +164,72 @@ impl OverlayManifest { base = Some(toml::from_str(&fs::read_to_string(base_path)?)?); } - let contract_dir = path.join("contracts"); - + let contract_dir = path.join(CONTRACTS_DIR); let contracts = if contract_dir.exists() { overlay_elements_from_path::(&contract_dir)? } else { vec![] }; - Ok(Self { world, base, contracts }) + let model_dir = path.join(MODELS_DIR); + let models = if model_dir.exists() { + overlay_elements_from_path::(&model_dir)? + } else { + vec![] + }; + + Ok(Self { world, base, contracts, models }) + } + + /// Writes `Self` to overlay manifests folder. + /// + /// - `world` and `base` manifest are written to root of the folder. + /// - `contracts` and `models` are written to their respective directories. + pub fn write_to_path_nested(&self, path: &Utf8PathBuf) -> Result<(), AbstractManifestError> { + fs::create_dir_all(path)?; + + if let Some(ref world) = self.world { + let world = toml::to_string(world)?; + let file_name = + path.join(WORLD_CONTRACT_NAME.replace("::", "_")).with_extension("toml"); + fs::write(file_name, world)?; + } + + if let Some(ref base) = self.base { + let base = toml::to_string(base)?; + let file_name = path.join(BASE_CONTRACT_NAME.replace("::", "_")).with_extension("toml"); + fs::write(file_name, base)?; + } + + overlay_dojo_contracts_to_path(&path.join(CONTRACTS_DIR), self.contracts.as_slice())?; + overlay_dojo_model_to_path(&path.join(MODELS_DIR), self.models.as_slice())?; + Ok(()) + } + + /// Add missing overlay items from `others` to `self`. + /// Note that this method don't override if certain item already exists in `self`. + pub fn merge(&mut self, other: OverlayManifest) { + if self.world.is_none() { + self.world = other.world; + } + + if self.base.is_none() { + self.base = other.base; + } + + for other_contract in other.contracts { + let found = self.contracts.iter().find(|c| c.name == other_contract.name); + if found.is_none() { + self.contracts.push(other_contract); + } + } + + for other_model in other.models { + let found = self.models.iter().find(|m| m.name == other_model.name); + if found.is_none() { + self.models.push(other_model); + } + } } } @@ -485,20 +555,6 @@ fn parse_models_events(events: Vec) -> Vec> { .collect() } -// fn elements_to_path(item_dir: &Utf8PathBuf, items: &Vec>) -> Result<()> -// where -// T: Serialize + ManifestMethods, -// { -// fs::create_dir_all(item_dir)?; -// for item in items { -// let item_toml = toml::to_string_pretty(&item)?; -// let item_name = item.name.split("::").last().unwrap(); -// fs::write(item_dir.join(item_name).with_extension("toml"), item_toml)?; -// } - -// Ok(()) -// } - fn elements_from_path(path: &Utf8PathBuf) -> Result>, AbstractManifestError> where T: DeserializeOwned + ManifestMethods, @@ -546,6 +602,32 @@ where Ok(elements) } +fn overlay_dojo_contracts_to_path( + path: &Utf8PathBuf, + elements: &[OverlayDojoContract], +) -> Result<(), AbstractManifestError> { + fs::create_dir_all(path)?; + + for element in elements { + let path = path.join(element.name.replace("::", "_")).with_extension("toml"); + fs::write(path, toml::to_string(&element)?)?; + } + Ok(()) +} + +fn overlay_dojo_model_to_path( + path: &Utf8PathBuf, + elements: &[OverlayDojoModel], +) -> Result<(), AbstractManifestError> { + fs::create_dir_all(path)?; + + for element in elements { + let path = path.join(element.name.replace("::", "_")).with_extension("toml"); + fs::write(path, toml::to_string(&element)?)?; + } + Ok(()) +} + impl ManifestMethods for DojoContract { type OverlayType = OverlayDojoContract; diff --git a/crates/dojo-world/src/manifest/types.rs b/crates/dojo-world/src/manifest/types.rs index dbbee2fd58..5eb3e67d0c 100644 --- a/crates/dojo-world/src/manifest/types.rs +++ b/crates/dojo-world/src/manifest/types.rs @@ -36,12 +36,13 @@ pub struct DeploymentManifest { pub models: Vec>, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Default, Clone, Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(PartialEq))] pub struct OverlayManifest { pub world: Option, pub base: Option, pub contracts: Vec, + pub models: Vec, } #[derive(Clone, Serialize, Deserialize, Debug)] diff --git a/crates/sozo/ops/Cargo.toml b/crates/sozo/ops/Cargo.toml index e4eded4705..f4144ae66b 100644 --- a/crates/sozo/ops/Cargo.toml +++ b/crates/sozo/ops/Cargo.toml @@ -50,6 +50,7 @@ tokio.workspace = true tracing-log = "0.1.3" tracing.workspace = true url.workspace = true +toml.workspace = true [dev-dependencies] assert_fs.workspace = true diff --git a/crates/sozo/ops/src/events.rs b/crates/sozo/ops/src/events.rs index c524fc2e96..73ae19d7fa 100644 --- a/crates/sozo/ops/src/events.rs +++ b/crates/sozo/ops/src/events.rs @@ -5,8 +5,7 @@ use anyhow::{anyhow, Result}; use cainome::parser::tokens::{CompositeInner, CompositeInnerKind, CoreBasic, Token}; use cainome::parser::AbiParser; use camino::Utf8PathBuf; -use dojo_lang::compiler::MANIFESTS_DIR; -use dojo_world::manifest::{AbiFormat, DeploymentManifest, ManifestMethods}; +use dojo_world::manifest::{AbiFormat, DeploymentManifest, ManifestMethods, MANIFESTS_DIR}; use starknet::core::types::{BlockId, EventFilter, FieldElement}; use starknet::core::utils::{parse_cairo_short_string, starknet_keccak}; use starknet::providers::jsonrpc::HttpTransport; @@ -249,8 +248,7 @@ fn process_inners( mod tests { use cainome::parser::tokens::{Array, Composite, CompositeInner, CompositeType}; use camino::Utf8Path; - use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR}; - use dojo_world::manifest::BaseManifest; + use dojo_world::manifest::{BaseManifest, BASE_DIR}; use starknet::core::types::EmittedEvent; use super::*; diff --git a/crates/sozo/ops/src/migration/migrate.rs b/crates/sozo/ops/src/migration/migrate.rs index 831fd22c48..b6c194e5a4 100644 --- a/crates/sozo/ops/src/migration/migrate.rs +++ b/crates/sozo/ops/src/migration/migrate.rs @@ -2,12 +2,12 @@ use std::path::Path; use anyhow::{anyhow, bail, Context, Result}; use camino::Utf8PathBuf; -use dojo_lang::compiler::{ABIS_DIR, BASE_DIR, DEPLOYMENTS_DIR, MANIFESTS_DIR}; use dojo_world::contracts::abi::world; use dojo_world::contracts::{cairo_utils, WorldContract}; use dojo_world::manifest::{ AbiFormat, BaseManifest, DeploymentManifest, DojoContract, DojoModel, Manifest, - ManifestMethods, WorldContract as ManifestWorldContract, WorldMetadata, + ManifestMethods, WorldContract as ManifestWorldContract, WorldMetadata, ABIS_DIR, BASE_DIR, + DEPLOYMENTS_DIR, MANIFESTS_DIR, }; use dojo_world::metadata::{dojo_metadata_from_workspace, ResourceMetadata}; use dojo_world::migration::class::ClassMigration; diff --git a/crates/sozo/ops/src/migration/mod.rs b/crates/sozo/ops/src/migration/mod.rs index 85f58dfa44..d7d45d4c49 100644 --- a/crates/sozo/ops/src/migration/mod.rs +++ b/crates/sozo/ops/src/migration/mod.rs @@ -1,8 +1,14 @@ use std::sync::Arc; +use std::{fs, io}; -use anyhow::{anyhow, Result}; -use dojo_lang::compiler::MANIFESTS_DIR; +use anyhow::{anyhow, Context, Result}; +use camino::Utf8PathBuf; use dojo_world::contracts::WorldContract; +use dojo_world::manifest::{ + DojoContract, DojoModel, Manifest, OverlayClass, OverlayDojoContract, OverlayDojoModel, + OverlayManifest, BASE_CONTRACT_NAME, BASE_DIR, CONTRACTS_DIR, MANIFESTS_DIR, MODELS_DIR, + OVERLAYS_DIR, WORLD_CONTRACT_NAME, +}; use dojo_world::migration::world::WorldDiff; use dojo_world::migration::{DeployOutput, TxnConfig, UpgradeOutput}; use scarb::core::Workspace; @@ -174,3 +180,87 @@ enum ContractDeploymentOutput { enum ContractUpgradeOutput { Output(UpgradeOutput), } + +pub fn generate_overlays(ws: &Workspace<'_>) -> Result<()> { + let profile_name = + ws.current_profile().expect("Scarb profile expected to be defined.").to_string(); + + // its path to a file so `parent` should never return `None` + let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); + let profile_dir = manifest_dir.join(MANIFESTS_DIR).join(profile_name); + + let base_manifests = profile_dir.join(BASE_DIR); + + let world = OverlayClass { name: WORLD_CONTRACT_NAME.into(), original_class_hash: None }; + let base = OverlayClass { name: BASE_CONTRACT_NAME.into(), original_class_hash: None }; + + // generate default OverlayManifest from base manifests + let contracts = overlay_dojo_contracts_from_path(&base_manifests.join(CONTRACTS_DIR)) + .with_context(|| "Failed to build default DojoContract Overlays from path.")?; + let models = overlay_model_from_path(&base_manifests.join(MODELS_DIR)) + .with_context(|| "Failed to build default DojoModel Overlays from path.")?; + + let default_overlay = + OverlayManifest { world: Some(world), base: Some(base), contracts, models }; + + let overlay_path = profile_dir.join(OVERLAYS_DIR); + + // read existing OverlayManifest from path + let mut overlay_manifest = OverlayManifest::load_from_path(&overlay_path) + .with_context(|| "Failed to load OverlayManifest from path.")?; + + // merge them to get OverlayManifest which contains all the contracts and models from base + // manifests + overlay_manifest.merge(default_overlay); + + overlay_manifest + .write_to_path_nested(&overlay_path) + .with_context(|| "Failed to write OverlayManifest to path.")?; + + Ok(()) +} + +fn overlay_dojo_contracts_from_path(path: &Utf8PathBuf) -> Result> { + let mut elements = vec![]; + + let entries = path + .read_dir()? + .map(|entry| entry.map(|e| e.path())) + .collect::, io::Error>>()?; + + for path in entries { + if path.is_file() { + let manifest: Manifest = toml::from_str(&fs::read_to_string(path)?)?; + + let overlay_manifest = + OverlayDojoContract { name: manifest.name, ..Default::default() }; + elements.push(overlay_manifest); + } else { + continue; + } + } + + Ok(elements) +} + +fn overlay_model_from_path(path: &Utf8PathBuf) -> Result> { + let mut elements = vec![]; + + let entries = path + .read_dir()? + .map(|entry| entry.map(|e| e.path())) + .collect::, io::Error>>()?; + + for path in entries { + if path.is_file() { + let manifest: Manifest = toml::from_str(&fs::read_to_string(path)?)?; + + let overlay_manifest = OverlayDojoModel { name: manifest.name, ..Default::default() }; + elements.push(overlay_manifest); + } else { + continue; + } + } + + Ok(elements) +} diff --git a/crates/sozo/ops/src/migration/utils.rs b/crates/sozo/ops/src/migration/utils.rs index d21e7c4c29..d85ffb6df8 100644 --- a/crates/sozo/ops/src/migration/utils.rs +++ b/crates/sozo/ops/src/migration/utils.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; use camino::Utf8PathBuf; -use dojo_lang::compiler::{BASE_DIR, OVERLAYS_DIR}; use dojo_world::manifest::{ - AbstractManifestError, BaseManifest, DeploymentManifest, OverlayManifest, + AbstractManifestError, BaseManifest, DeploymentManifest, OverlayManifest, BASE_DIR, + OVERLAYS_DIR, }; use scarb_ui::Ui; use starknet::accounts::{ConnectedAccount, SingleOwnerAccount}; diff --git a/crates/sozo/ops/src/tests/migration.rs b/crates/sozo/ops/src/tests/migration.rs index 1f82982bf7..006c9a731e 100644 --- a/crates/sozo/ops/src/tests/migration.rs +++ b/crates/sozo/ops/src/tests/migration.rs @@ -2,11 +2,11 @@ use std::str; use cainome::cairo_serde::ContractAddress; use camino::Utf8Path; -use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR}; use dojo_test_utils::migration::prepare_migration_with_world_and_seed; use dojo_world::contracts::{WorldContract, WorldContractReader}; use dojo_world::manifest::{ - BaseManifest, DeploymentManifest, OverlayManifest, WORLD_CONTRACT_NAME, + BaseManifest, DeploymentManifest, OverlayManifest, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR, + WORLD_CONTRACT_NAME, }; use dojo_world::metadata::{ dojo_metadata_from_workspace, ArtifactMetadata, DojoMetadata, Uri, WorldMetadata, diff --git a/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_actions_actions.toml b/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_actions_actions.toml index a8e2fd4c2d..b21fc1adec 100644 --- a/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_actions_actions.toml +++ b/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_actions_actions.toml @@ -1,6 +1,4 @@ -computed = [ ] name = "dojo_examples::actions::actions" -reads = [ ] -writes = [ "Moves", "Position" ] +reads = [] +writes = ["Moves", "Position"] init_calldata = [] - diff --git a/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_others_others.toml b/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_others_others.toml index b74df0c8fe..129f942bd8 100644 --- a/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_others_others.toml +++ b/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_others_others.toml @@ -1,5 +1,4 @@ +name = "dojo_examples::others::others" reads = [] writes = [] -computed = [] init_calldata = ["$contract_address:dojo_examples::actions::actions", "$class_hash:dojo_examples::actions::actions", "10"] -name = "dojo_examples::others::others" diff --git a/examples/spawn-and-move/manifests/dev/overlays/dojo_base_base.toml b/examples/spawn-and-move/manifests/dev/overlays/dojo_base_base.toml new file mode 100644 index 0000000000..f706470d45 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/dojo_base_base.toml @@ -0,0 +1 @@ +name = "dojo::base::base" diff --git a/examples/spawn-and-move/manifests/dev/overlays/dojo_world_world.toml b/examples/spawn-and-move/manifests/dev/overlays/dojo_world_world.toml new file mode 100644 index 0000000000..a3e686e3ef --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/dojo_world_world.toml @@ -0,0 +1 @@ +name = "dojo::world::world" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_actions_actions_moved.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_actions_actions_moved.toml new file mode 100644 index 0000000000..4958a7a15c --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_actions_actions_moved.toml @@ -0,0 +1 @@ +name = "dojo_examples::actions::actions::moved" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_emote_message.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_emote_message.toml new file mode 100644 index 0000000000..d60162cc72 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_emote_message.toml @@ -0,0 +1 @@ +name = "dojo_examples::models::emote_message" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_moves.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_moves.toml new file mode 100644 index 0000000000..dc8784e746 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_moves.toml @@ -0,0 +1 @@ +name = "dojo_examples::models::moves" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_player_config.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_player_config.toml new file mode 100644 index 0000000000..6af8240b36 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_player_config.toml @@ -0,0 +1 @@ +name = "dojo_examples::models::player_config" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_position.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_position.toml new file mode 100644 index 0000000000..df38e71c32 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_position.toml @@ -0,0 +1 @@ +name = "dojo_examples::models::position" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_others_others_contract_initialized.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_others_others_contract_initialized.toml new file mode 100644 index 0000000000..f8f3053fe5 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_others_others_contract_initialized.toml @@ -0,0 +1 @@ +name = "dojo_examples::others::others::contract_initialized" From 920500986855fdaf203471ac11900b15dcf6035f Mon Sep 17 00:00:00 2001 From: taikoon Date: Tue, 11 Jun 2024 03:47:58 +0800 Subject: [PATCH 5/5] update dead links (#2036) * update dead links * fix: correct link for dojoup info --------- Co-authored-by: glihm --- bin/sozo/README.md | 2 +- bin/torii/README.md | 2 +- crates/torii/README.md | 2 +- dojoup/README.md | 8 +++++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/bin/sozo/README.md b/bin/sozo/README.md index c88c71a7e2..bb864ed5b3 100644 --- a/bin/sozo/README.md +++ b/bin/sozo/README.md @@ -4,7 +4,7 @@ curl -L https://install.dojoengine.org | bash ``` -[Documentation](https://book.dojoengine.org/toolchain/sozo/overview) +[Documentation](https://book.dojoengine.org/toolchain/sozo) Some parts of sozo were inspired by [starkli](https://github.com/xJonathanLEI/starkli) created by Jonathan LEI. It is licensed under Apache 2.0 / MIT License. diff --git a/bin/torii/README.md b/bin/torii/README.md index 635eb16da0..21ff59ea1a 100644 --- a/bin/torii/README.md +++ b/bin/torii/README.md @@ -4,4 +4,4 @@ curl -L https://install.dojoengine.org | bash ``` -[Documentation](https://book.dojoengine.org/toolchain/torii/overview) +[Documentation](https://book.dojoengine.org/toolchain/torii) diff --git a/crates/torii/README.md b/crates/torii/README.md index 635eb16da0..21ff59ea1a 100644 --- a/crates/torii/README.md +++ b/crates/torii/README.md @@ -4,4 +4,4 @@ curl -L https://install.dojoengine.org | bash ``` -[Documentation](https://book.dojoengine.org/toolchain/torii/overview) +[Documentation](https://book.dojoengine.org/toolchain/torii) diff --git a/dojoup/README.md b/dojoup/README.md index 277e12a1a1..714d5d7c58 100644 --- a/dojoup/README.md +++ b/dojoup/README.md @@ -4,4 +4,10 @@ curl -L https://install.dojoengine.org | bash ``` -[Documentation](https://book.dojoengine.org/toolchain/dojoup) +For more details, you can then issue the following command: + +```sh +dojoup --help +``` + +[Documentation](https://book.dojoengine.org/getting-started#getting-started)