diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25b08d52fd..93377d7076 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ env: jobs: test: - runs-on: ubuntu-latest-16-cores + runs-on: ubuntu-latest-32-cores container: image: ghcr.io/dojoengine/dojo-dev:v0.7.0-alpha.4 steps: @@ -20,7 +20,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: | cargo build -r --bin katana - KATANA_RUNNER_BIN=$(pwd)/target/release/katana cargo llvm-cov nextest --no-report --all-features --workspace --exclude katana --build-jobs 10 + KATANA_RUNNER_BIN=$(pwd)/target/release/katana cargo llvm-cov nextest --no-report --all-features --workspace --exclude katana --build-jobs 20 cargo llvm-cov nextest --no-report -p katana # TODO(kariy): uncomment this line when `sir` feature support Cairo 2.6.3 # cargo llvm-cov nextest --no-report -p katana --no-default-features --features sir diff --git a/Cargo.lock b/Cargo.lock index 5494bbdfe1..c06643dc96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4257,6 +4257,7 @@ dependencies = [ "thiserror", "tokio", "toml 0.8.13", + "topological-sort", "tracing", "url", ] @@ -13340,6 +13341,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + [[package]] name = "torii" version = "0.7.0-alpha.4" @@ -13430,6 +13437,7 @@ dependencies = [ "futures-channel", "futures-util", "hex", + "katana-runner", "lazy_static", "log", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 8c2d72ad0a..3fefe8f63b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,9 @@ dojo-test-utils = { path = "crates/dojo-test-utils" } dojo-types = { path = "crates/dojo-types" } dojo-world = { path = "crates/dojo-world" } +# dojo-world +topological-sort = "0.2" + # katana katana-cairo = { path = "crates/katana/cairo" } katana-codecs = { path = "crates/katana/storage/codecs" } diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index d927b24578..c14297034a 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -41,6 +41,37 @@ pub enum ModelCommand { starknet: StarknetOptions, }, + #[command(about = "Displays the model's layout into dojo storage.\n +The Dojo storage system uses the poseidon_hash function to compute +hashes, called 'hash' in the following documentation. + + How storage locations are computed ? + + model key = hash(model_keys) + + fixed layout key = parent_key + struct layout field key = hash(parent_key, field_selector) + tuple layout item key = hash(parent_key, item_index) + enum layout + variant key = parent_key + data key = hash(parent_key, variant_index) + array layout + length key = parent_key + item key = hash(parent_key, item_index) + byte array layout = parent_key + + final storage location = hash('dojo_storage', model_selector, record_key)")] + Layout { + #[arg(help = "The name of the model")] + name: String, + + #[command(flatten)] + world: WorldOptions, + + #[command(flatten)] + starknet: StarknetOptions, + }, + #[command(about = "Retrieve the schema for a model")] Schema { #[arg(help = "The name of the model")] @@ -92,6 +123,11 @@ impl ModelArgs { let provider = starknet.provider(env_metadata.as_ref()).unwrap(); model::model_contract_address(name, world_address, provider).await } + ModelCommand::Layout { name, starknet, world } => { + let world_address = world.address(env_metadata.as_ref()).unwrap(); + let provider = starknet.provider(env_metadata.as_ref()).unwrap(); + model::model_layout(name, world_address, provider).await + } ModelCommand::Schema { name, to_json, starknet, world } => { let world_address = world.address(env_metadata.as_ref()).unwrap(); let provider = starknet.provider(env_metadata.as_ref()).unwrap(); diff --git a/crates/dojo-bindgen/src/lib.rs b/crates/dojo-bindgen/src/lib.rs index ed2605f18a..3b25f4528d 100644 --- a/crates/dojo-bindgen/src/lib.rs +++ b/crates/dojo-bindgen/src/lib.rs @@ -261,7 +261,7 @@ mod tests { ) .unwrap(); - assert_eq!(data.models.len(), 5); + assert_eq!(data.models.len(), 6); assert_eq!(data.world.name, "dojo_example"); diff --git a/crates/dojo-core/src/base_test.cairo b/crates/dojo-core/src/base_test.cairo index 84ef74bf15..1e3f533b08 100644 --- a/crates/dojo-core/src/base_test.cairo +++ b/crates/dojo-core/src/base_test.cairo @@ -52,12 +52,20 @@ fn deploy_world() -> IWorldDispatcher { spawn_test_world(array![]) } +// A test contract needs to be used instead of previously used base contract since. +// contracts now require a `dojo_init` method which normal base contract doesn't have +#[dojo::contract] +mod test_contract {} + #[test] #[available_gas(6000000)] fn test_upgrade_from_world() { let world = deploy_world(); - let base_address = world.deploy_contract('salt', base::TEST_CLASS_HASH.try_into().unwrap()); + let base_address = world + .deploy_contract( + 'salt', test_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span() + ); let new_class_hash: ClassHash = contract_upgrade::TEST_CLASS_HASH.try_into().unwrap(); world.upgrade_contract(base_address, new_class_hash); @@ -74,7 +82,10 @@ fn test_upgrade_from_world() { fn test_upgrade_from_world_not_world_provider() { let world = deploy_world(); - let base_address = world.deploy_contract('salt', base::TEST_CLASS_HASH.try_into().unwrap()); + let base_address = world + .deploy_contract( + 'salt', test_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span() + ); let new_class_hash: ClassHash = contract_invalid_upgrade::TEST_CLASS_HASH.try_into().unwrap(); world.upgrade_contract(base_address, new_class_hash); @@ -86,7 +97,10 @@ fn test_upgrade_from_world_not_world_provider() { fn test_upgrade_direct() { let world = deploy_world(); - let base_address = world.deploy_contract('salt', base::TEST_CLASS_HASH.try_into().unwrap()); + let base_address = world + .deploy_contract( + 'salt', test_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span() + ); let new_class_hash: ClassHash = contract_upgrade::TEST_CLASS_HASH.try_into().unwrap(); let upgradeable_dispatcher = IUpgradeableDispatcher { contract_address: base_address }; @@ -144,8 +158,9 @@ mod invalid_model { #[abi(embed_v0)] impl InvalidModelSelector of super::IMetadataOnly { fn selector(self: @ContractState) -> felt252 { + // NOTE: Need to update this value if address changes // Pre-computed address of a contract deployed through the world. - 0x455fe9471cb954574b16581868043841391545b9225af00bf545f9acf923295 + 0x15f0ffa36184d74ead97aa501b09aed53ee7236e364997a0c21879194340ab6 } fn name(self: @ContractState) -> ByteArray { @@ -179,12 +194,13 @@ mod invalid_model_world { fn test_deploy_from_world_invalid_model() { let world = deploy_world(); - let base_address = world.deploy_contract(0, base::TEST_CLASS_HASH.try_into().unwrap()); + let contract_address = world + .deploy_contract(0, test_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span()); // This print allows to know the address of the deployed contract which must be returned // by the selector() function of invalid model, to simulate a ACL issue // (see register_model function) - base_address.print(); + contract_address.print(); world.register_model(invalid_model::TEST_CLASS_HASH.try_into().unwrap()); } diff --git a/crates/dojo-core/src/database/introspect.cairo b/crates/dojo-core/src/database/introspect.cairo index a7d2fed3a4..d3ad4e6962 100644 --- a/crates/dojo-core/src/database/introspect.cairo +++ b/crates/dojo-core/src/database/introspect.cairo @@ -16,7 +16,7 @@ enum Layout { ByteArray, // there is one layout per variant. // the `selector` field identifies the variant - // the `layout` field defines the full variant layout (variant value + optional variant data) + // the `layout` defines the variant data (could be empty for variant without data). Enum: Span, } @@ -203,18 +203,10 @@ impl Introspect_option> of Introspect> { fn layout() -> Layout { Layout::Enum( array![ - FieldLayout { - // Some - selector: 0, - layout: Layout::Tuple( - array![Layout::Fixed(array![8].span()), Introspect::::layout()].span() - ) - }, - FieldLayout { - // None - selector: 1, - layout: Layout::Tuple(array![Layout::Fixed(array![8].span())].span()) - }, + FieldLayout { // Some + selector: 0, layout: Introspect::::layout() }, + FieldLayout { // None + selector: 1, layout: Layout::Fixed(array![].span()) }, ] .span() ) diff --git a/crates/dojo-core/src/database/introspect_test.cairo b/crates/dojo-core/src/database/introspect_test.cairo index 95a993e770..c62d9561bc 100644 --- a/crates/dojo-core/src/database/introspect_test.cairo +++ b/crates/dojo-core/src/database/introspect_test.cairo @@ -35,6 +35,24 @@ struct WithNestedArrayInTuple { arr: (u8, (u16, Array, u256), u32) } +#[derive(Drop, IntrospectPacked)] +struct Vec3 { + x: u32, + y: u32, + z: u32 +} + +#[derive(IntrospectPacked)] +struct Translation { + from: Vec3, + to: Vec3 +} + +#[derive(Drop, IntrospectPacked)] +struct StructInnerNotPacked { + x: Base +} + #[derive(Drop, Introspect)] enum EnumNoData { One, @@ -43,12 +61,45 @@ enum EnumNoData { } #[derive(Drop, Introspect)] -enum EnumWithData { +enum EnumWithSameData { + One: u256, + Two: u256, + Three: u256 +} + +#[derive(Drop, Introspect)] +enum EnumWithSameTupleData { + One: (u256, u32), + Two: (u256, u32), + Three: (u256, u32) +} + +#[derive(Drop, Introspect)] +enum EnumWithVariousData { One: u32, Two: (u8, u16), Three: Array, } + +#[derive(Drop, IntrospectPacked)] +enum EnumPacked { + A: u32, + B: u32, +} + +#[derive(Drop, IntrospectPacked)] +enum EnumInnerPacked { + A: (EnumPacked, Vec3), + B: (EnumPacked, Vec3), +} + +#[derive(Drop, IntrospectPacked)] +enum EnumInnerNotPacked { + A: (EnumPacked, Base), + B: (EnumPacked, Base), +} + #[derive(Drop, Introspect)] struct StructWithOption { x: Option @@ -82,10 +133,8 @@ fn _enum(values: Array>) -> Layout { let v = *values.at(i); match v { - Option::Some(v) => { - items.append(field(i.into(), tuple(array![fixed(array![8]), v]))); - }, - Option::None => { items.append(field(i.into(), fixed(array![8]))) } + Option::Some(v) => { items.append(field(i.into(), v)); }, + Option::None => { items.append(field(i.into(), fixed(array![]))) } } i += 1; @@ -144,9 +193,25 @@ fn test_size_with_nested_array_in_tuple() { #[test] fn test_size_of_enum_without_variant_data() { let size = Introspect::::size(); - assert!(size.is_none()); + assert!(size.is_some()); + assert!(size.unwrap() == 1); +} + +#[test] +fn test_size_of_enum_with_same_variant_data() { + let size = Introspect::::size(); + assert!(size.is_some()); + assert!(size.unwrap() == 3); } +#[test] +fn test_size_of_enum_with_same_tuple_variant_data() { + let size = Introspect::::size(); + assert!(size.is_some()); + assert!(size.unwrap() == 4); +} + + #[test] fn test_size_of_struct_with_option() { let size = Introspect::::size(); @@ -155,49 +220,39 @@ fn test_size_of_struct_with_option() { #[test] fn test_size_of_enum_with_variant_data() { - let size = Introspect::::size(); + let size = Introspect::::size(); assert!(size.is_none()); } #[test] fn test_layout_of_enum_without_variant_data() { let layout = Introspect::::layout(); - let expected = Layout::Enum( - array![ - // One - field(0, tuple(array![fixed(array![8])])), - // Two - field(1, tuple(array![fixed(array![8])])), - // Three - field(2, tuple(array![fixed(array![8])])), - ] - .span() - ); + let expected = _enum(array![ // One + Option::None, // Two + Option::None, // Three + Option::None,]); assert!(layout == expected); } #[test] fn test_layout_of_enum_with_variant_data() { - let layout = Introspect::::layout(); - let expected = Layout::Enum( + let layout = Introspect::::layout(); + let expected = _enum( array![ // One - field(0, tuple(array![fixed(array![8]), fixed(array![32]),])), + Option::Some(fixed(array![32])), // Two - field( - 1, - tuple(array![fixed(array![8]), tuple(array![fixed(array![8]), fixed(array![16]),])]) - ), + Option::Some(tuple(array![fixed(array![8]), fixed(array![16])])), // Three - field(2, tuple(array![fixed(array![8]), arr(fixed(array![128])),])), + Option::Some(arr(fixed(array![128]))), ] - .span() ); assert!(layout == expected); } +#[test] fn test_layout_of_struct_with_option() { let layout = Introspect::::layout(); let expected = Layout::Struct( @@ -207,3 +262,48 @@ fn test_layout_of_struct_with_option() { assert!(layout == expected); } + +#[test] +fn test_layout_of_packed_struct() { + let layout = Introspect::::layout(); + let expected = Layout::Fixed(array![32, 32, 32].span()); + + assert!(layout == expected); +} + +#[test] +fn test_layout_of_inner_packed_struct() { + let layout = Introspect::::layout(); + let expected = Layout::Fixed(array![32, 32, 32, 32, 32, 32].span()); + + assert!(layout == expected); +} + +#[test] +#[should_panic(expected: ("A packed model layout must contain Fixed layouts only.",))] +fn test_layout_of_not_packed_inner_struct() { + let _ = Introspect::::layout(); +} + + +#[test] +fn test_layout_of_packed_enum() { + let layout = Introspect::::layout(); + let expected = Layout::Fixed(array![8, 32].span()); + + assert!(layout == expected); +} + +#[test] +fn test_layout_of_inner_packed_enum() { + let layout = Introspect::::layout(); + let expected = Layout::Fixed(array![8, 8, 32, 32, 32, 32].span()); + + assert!(layout == expected); +} + +#[test] +#[should_panic(expected: ("A packed model layout must contain Fixed layouts only.",))] +fn test_layout_of_not_packed_inner_enum() { + let _ = Introspect::::layout(); +} diff --git a/crates/dojo-core/src/world.cairo b/crates/dojo-core/src/world.cairo index 0544b1d385..b01adc0d36 100644 --- a/crates/dojo-core/src/world.cairo +++ b/crates/dojo-core/src/world.cairo @@ -9,7 +9,9 @@ trait IWorld { fn set_metadata(ref self: T, metadata: ResourceMetadata); fn model(self: @T, selector: felt252) -> (ClassHash, ContractAddress); fn register_model(ref self: T, class_hash: ClassHash); - fn deploy_contract(ref self: T, salt: felt252, class_hash: ClassHash) -> ContractAddress; + fn deploy_contract( + ref self: T, salt: felt252, class_hash: ClassHash, init_calldata: Span + ) -> ContractAddress; fn upgrade_contract(ref self: T, address: ContractAddress, class_hash: ClassHash) -> ClassHash; fn uuid(ref self: T) -> usize; fn emit(self: @T, keys: Array, values: Span); @@ -97,9 +99,11 @@ mod world { const WORLD: felt252 = 0; - // the minimum internal size of an empty ByteArray + // the minimum internal size of an empty ByteArray const MIN_BYTE_ARRAY_SIZE: u32 = 3; + const DOJO_INIT_SELECTOR: felt252 = selector!("dojo_init"); + component!(path: Config, storage: config, event: ConfigEvent); #[abi(embed_v0)] @@ -204,6 +208,7 @@ mod world { writers: LegacyMap::<(felt252, ContractAddress), bool>, #[substorage(v0)] config: Config::Storage, + initialized_contract: LegacyMap::, } #[constructor] @@ -430,12 +435,16 @@ mod world { /// /// * `salt` - The salt use for contract deployment. /// * `class_hash` - The class hash of the contract. + /// * `init_calldata` - Calldata used to initialize the contract. /// /// # Returns /// /// * `ContractAddress` - The address of the newly deployed contract. fn deploy_contract( - ref self: ContractState, salt: felt252, class_hash: ClassHash + ref self: ContractState, + salt: felt252, + class_hash: ClassHash, + init_calldata: Span, ) -> ContractAddress { let (contract_address, _) = deploy_syscall( self.contract_base.read(), salt, array![].span(), false @@ -444,6 +453,14 @@ mod world { let upgradeable_dispatcher = IUpgradeableDispatcher { contract_address }; upgradeable_dispatcher.upgrade(class_hash); + if self.initialized_contract.read(contract_address.into()) { + panic!("Contract has already been initialized"); + } else { + starknet::call_contract_syscall(contract_address, DOJO_INIT_SELECTOR, init_calldata) + .unwrap_syscall(); + self.initialized_contract.write(contract_address.into(), true); + } + self.owners.write((contract_address.into(), get_caller_address()), true); self.deployed_contracts.write(contract_address.into(), class_hash.into()); @@ -796,7 +813,7 @@ mod world { } /// Write values to the world storage. - /// + /// /// # Arguments /// * `model` - the model selector. /// * `key` - the object key. @@ -829,7 +846,7 @@ mod world { } /// Write fixed layout model record to the world storage. - /// + /// /// # Arguments /// * `model` - the model selector. /// * `key` - the model record key. @@ -844,7 +861,7 @@ mod world { } /// Write array layout model record to the world storage. - /// + /// /// # Arguments /// * `model` - the model selector. /// * `key` - the model record key. @@ -894,7 +911,7 @@ mod world { // data: Array, // pending_word: felt252, // pending_word_len: usize, - // } + // } // // That means, the length of data to write from 'values' is: // 1 + len(data) + 1 + 1 = len(data) + 3 @@ -914,7 +931,7 @@ mod world { } /// Write struct layout model record to the world storage. - /// + /// /// # Arguments /// * `model` - the model selector. /// * `key` - the model record key. @@ -944,7 +961,7 @@ mod world { } /// Write tuple layout model record to the world storage. - /// + /// /// # Arguments /// * `model` - the model selector. /// * `key` - the model record key. @@ -984,9 +1001,17 @@ mod world { let variant = *values.at(offset); assert(variant.into() < 256_u256, 'invalid variant value'); + // and write it + database::set(model, key, values, offset, array![251].span()); + offset += 1; + // find the corresponding layout and then write the full variant + let variant_data_key = Self::_field_key(key, variant); + match Self::_find_variant_layout(variant, variant_layouts) { - Option::Some(layout) => Self::_write_layout(model, key, values, ref offset, layout), + Option::Some(layout) => Self::_write_layout( + model, variant_data_key, values, ref offset, layout + ), Option::None => panic!("Unable to find the variant layout") }; } @@ -1018,7 +1043,7 @@ mod world { // data: Array, // pending_word: felt252, // pending_word_len: usize, - // } + // } // // So, just set the 3 first values to 0 (len(data), pending_world and pending_word_len) @@ -1087,16 +1112,21 @@ mod world { } fn _delete_enum_layout(model: felt252, key: felt252, variant_layouts: Span) { - // read the variant value first which is the first stored felt252 + // read the variant value let res = database::get(model, key, array![251].span()); assert(res.len() == 1, 'internal database error'); let variant = *res.at(0); assert(variant.into() < 256_u256, 'invalid variant value'); + // reset the variant value + database::delete(model, key, array![251].span()); + // find the corresponding layout and the delete the full variant + let variant_data_key = Self::_field_key(key, variant); + match Self::_find_variant_layout(variant, variant_layouts) { - Option::Some(layout) => Self::_delete_layout(model, key, layout), + Option::Some(layout) => Self::_delete_layout(model, variant_data_key, layout), Option::None => panic!("Unable to find the variant layout") }; } @@ -1185,7 +1215,7 @@ mod world { // data: Array, // pending_word: felt252, // pending_word_len: usize, - // } + // } // // So, read the length of data and compute the full size to read @@ -1260,18 +1290,22 @@ mod world { ref read_data: Array, variant_layouts: Span ) { - // read the variant value first, which is the first element of the tuple - // (because an enum is stored as a tuple). - let variant_key = Self::_field_key(key, 0); - let res = database::get(model, variant_key, array![8].span()); + // read the variant value first + let res = database::get(model, key, array![8].span()); assert(res.len() == 1, 'internal database error'); let variant = *res.at(0); assert(variant.into() < 256_u256, 'invalid variant value'); - // find the corresponding layout and the read the full variant + read_data.append(variant); + + // find the corresponding layout and the read the variant data + let variant_data_key = Self::_field_key(key, variant); + match Self::_find_variant_layout(variant, variant_layouts) { - Option::Some(layout) => Self::_read_layout(model, key, ref read_data, layout), + Option::Some(layout) => Self::_read_layout( + model, variant_data_key, ref read_data, layout + ), Option::None => panic!("Unable to find the variant layout") }; } diff --git a/crates/dojo-core/src/world_test.cairo b/crates/dojo-core/src/world_test.cairo index 7cf10a7010..5106cba9a4 100644 --- a/crates/dojo-core/src/world_test.cairo +++ b/crates/dojo-core/src/world_test.cairo @@ -819,6 +819,7 @@ fn test_program_hash_event_emit() { Option::Some(ProgramHashUpdate { program_hash: 98758347158781475198374598718743 }) ); } + #[test] #[available_gas(6000000)] fn test_facts_registry_event_emit() { @@ -833,6 +834,29 @@ fn test_facts_registry_event_emit() { Option::Some(FactsRegistryUpdate { address: contract_address_const::<0x12>() }) ); } + +#[starknet::interface] +trait IDojoInit { + fn dojo_init(self: @ContractState) -> felt252; +} + +#[dojo::contract] +mod test_contract {} + +#[test] +#[available_gas(6000000)] +#[should_panic(expected: ('Only world can init', 'ENTRYPOINT_FAILED'))] +fn test_can_call_init() { + let world = deploy_world(); + let address = world + .deploy_contract( + 'salt1', test_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span() + ); + + let dojo_init = IDojoInitDispatcher { contract_address: address }; + dojo_init.dojo_init(); +} + #[test] fn test_set_entity_with_fixed_layout() { let world = deploy_world(); diff --git a/crates/dojo-lang/src/contract.rs b/crates/dojo-lang/src/contract.rs index 570d7606cf..fac9667ba0 100644 --- a/crates/dojo-lang/src/contract.rs +++ b/crates/dojo-lang/src/contract.rs @@ -17,6 +17,7 @@ use dojo_types::system::Dependency; use crate::plugin::{DojoAuxData, SystemAuxData, DOJO_CONTRACT_ATTR}; const ALLOW_REF_SELF_ARG: &str = "allow_ref_self"; +const DOJO_INIT_FN: &str = "dojo_init"; pub struct DojoContract { diagnostics: Vec, @@ -36,6 +37,7 @@ impl DojoContract { DojoContract { diagnostics: vec![], dependencies: HashMap::new(), do_allow_ref_self }; let mut has_event = false; let mut has_storage = false; + let mut has_init = false; if let MaybeModuleBody::Some(body) = module_ast.body(db) { let mut body_nodes: Vec<_> = body @@ -60,12 +62,44 @@ impl DojoContract { if trait_path.contains("") { return system.rewrite_impl(db, impl_ast.clone()); } + } else if let ast::ModuleItem::FreeFunction(fn_ast) = el { + let fn_decl = fn_ast.declaration(db); + let fn_name = fn_decl.name(db).text(db); + + if fn_name == DOJO_INIT_FN { + has_init = true; + return system.handle_init_fn(db, fn_ast); + } } vec![RewriteNode::Copied(el.as_syntax_node())] }) .collect(); + if !has_init { + let node = RewriteNode::interpolate_patched( + " + #[starknet::interface] + trait IDojoInit { + fn $init_name$(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn $init_name$(self: @ContractState) { + assert(starknet::get_caller_address() == \ + self.world().contract_address, 'Only world can init'); + } + } + ", + &UnorderedHashMap::from([( + "init_name".to_string(), + RewriteNode::Text(DOJO_INIT_FN.to_string()), + )]), + ); + body_nodes.append(&mut vec![node]); + } + if !has_event { body_nodes.append(&mut system.create_event()) } @@ -84,8 +118,8 @@ impl DojoContract { use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; use dojo::world::IDojoResourceProvider; - - + + component!(path: dojo::components::upgradeable::upgradeable, storage: \ upgradeable, event: UpgradeableEvent); @@ -140,6 +174,55 @@ impl DojoContract { PluginResult::default() } + fn handle_init_fn( + &mut self, + db: &dyn SyntaxGroup, + fn_ast: &ast::FunctionWithBody, + ) -> Vec { + let fn_decl = fn_ast.declaration(db); + let fn_name = fn_decl.name(db).text(db); + + let (params_str, _, world_removed) = self.rewrite_parameters( + db, + fn_decl.signature(db).parameters(db), + fn_ast.stable_ptr().untyped(), + ); + + let mut world_read = ""; + if world_removed { + world_read = "let world = self.world_dispatcher.read();"; + } + + let body = fn_ast.body(db).as_syntax_node().get_text(db); + + let node = RewriteNode::interpolate_patched( + " + #[starknet::interface] + trait IDojoInit { + fn $name$($params_str$); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn $name$($params_str$) { + $world_read$ + assert(starknet::get_caller_address() == self.world().contract_address, \ + 'Only world can init'); + $body$ + } + } + ", + &UnorderedHashMap::from([ + ("name".to_string(), RewriteNode::Text(fn_name.to_string())), + ("params_str".to_string(), RewriteNode::Text(params_str)), + ("body".to_string(), RewriteNode::Text(body)), + ("world_read".to_string(), RewriteNode::Text(world_read.to_string())), + ]), + ); + + vec![node] + } + pub fn merge_event( &mut self, db: &dyn SyntaxGroup, diff --git a/crates/dojo-lang/src/introspect/layout.rs b/crates/dojo-lang/src/introspect/layout.rs index 0b7e43386d..216b00678c 100644 --- a/crates/dojo-lang/src/introspect/layout.rs +++ b/crates/dojo-lang/src/introspect/layout.rs @@ -56,7 +56,9 @@ pub fn build_variant_layouts( let selector = format!("{i}"); let variant_layout = match v.type_clause(db) { - OptionTypeClause::Empty(_) => "".to_string(), + OptionTypeClause::Empty(_) => { + "dojo::database::introspect::Layout::Fixed(array![].span())".to_string() + } OptionTypeClause::TypeClause(type_clause) => { get_layout_from_type_clause(db, diagnostics, &type_clause) } @@ -65,12 +67,7 @@ pub fn build_variant_layouts( format!( "dojo::database::introspect::FieldLayout {{ selector: {selector}, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - {variant_layout} - ].span() - ) + layout: {variant_layout} }}" ) }) @@ -183,12 +180,16 @@ pub fn build_item_layout_from_type( } } +pub fn is_custom_layout(layout: &str) -> bool { + layout.starts_with("dojo::database::introspect::Introspect::") +} + pub fn build_packed_struct_layout( db: &dyn SyntaxGroup, diagnostics: &mut Vec, struct_ast: &ItemStruct, ) -> String { - struct_ast + let layouts = struct_ast .members(db) .elements(db) .iter() @@ -199,8 +200,64 @@ pub fn build_packed_struct_layout( Some(get_packed_field_layout_from_type_clause(db, diagnostics, &m.type_clause(db))) }) + .flatten() + .collect::>(); + + if layouts.iter().any(|v| is_custom_layout(v.as_str())) { + generate_cairo_code_for_fixed_layout_with_custom_types(&layouts) + } else { + format!( + "dojo::database::introspect::Layout::Fixed( + array![ + {} + ].span() + )", + layouts.join(",") + ) + } +} + +pub fn generate_cairo_code_for_fixed_layout_with_custom_types(layouts: &[String]) -> String { + let layouts_repr = layouts + .iter() + .map(|l| { + if is_custom_layout(l) { + l.to_string() + } else { + format!("dojo::database::introspect::Layout::Fixed(array![{l}].span())") + } + }) .collect::>() - .join(",") + .join(",\n"); + + format!( + "let mut layouts = array![ + {layouts_repr} + ]; + let mut merged_layout = ArrayTrait::::new(); + + loop {{ + match ArrayTrait::pop_front(ref layouts) {{ + Option::Some(mut layout) => {{ + match layout {{ + dojo::database::introspect::Layout::Fixed(mut l) => {{ + loop {{ + match SpanTrait::pop_front(ref l) {{ + Option::Some(x) => merged_layout.append(*x), + Option::None(_) => {{ break; }} + }}; + }}; + }}, + _ => panic!(\"A packed model layout must contain Fixed layouts only.\"), + }}; + }}, + Option::None(_) => {{ break; }} + }}; + }}; + + dojo::database::introspect::Layout::Fixed(merged_layout.span()) + ", + ) } // @@ -209,23 +266,36 @@ pub fn build_packed_enum_layout( diagnostics: &mut Vec, enum_ast: &ItemEnum, ) -> String { - let variant_layouts = enum_ast - .variants(db) - .elements(db) - .iter() - .map(|v| match v.type_clause(db) { - OptionTypeClause::Empty(_) => "".to_string(), + // to be packable, all variants data must have the same size. + // as this point has already been checked before calling `build_packed_enum_layout`, + // just use the first variant to generate the fixed layout. + let elements = enum_ast.variants(db).elements(db); + let mut variant_layout = if elements.is_empty() { + vec![] + } else { + match elements.first().unwrap().type_clause(db) { + OptionTypeClause::Empty(_) => vec![], OptionTypeClause::TypeClause(type_clause) => { get_packed_field_layout_from_type_clause(db, diagnostics, &type_clause) } - }) - .collect::>(); + } + }; - if variant_layouts.is_empty() { - return "8".to_string(); - } + // don't forget the store the variant value + variant_layout.insert(0, "8".to_string()); - format!("8,{}", variant_layouts[0]) + if variant_layout.iter().any(|v| is_custom_layout(v.as_str())) { + generate_cairo_code_for_fixed_layout_with_custom_types(&variant_layout) + } else { + format!( + "dojo::database::introspect::Layout::Fixed( + array![ + {} + ].span() + )", + variant_layout.join(",") + ) + } } // @@ -233,7 +303,7 @@ pub fn get_packed_field_layout_from_type_clause( db: &dyn SyntaxGroup, diagnostics: &mut Vec, type_clause: &TypeClause, -) -> String { +) -> Vec { match type_clause.ty(db) { Expr::Path(path) => { let path_type = path.as_syntax_node().get_text(db); @@ -253,7 +323,7 @@ pub fn get_packed_field_layout_from_type_clause( message: "Unexpected expression for variant data type.".to_string(), severity: Severity::Error, }); - "ERROR".to_string() + vec!["ERROR".to_string()] } } } @@ -263,28 +333,26 @@ pub fn get_packed_item_layout_from_type( diagnostics: &mut Vec, diagnostic_item: ids::SyntaxStablePtrId, item_type: &str, -) -> String { +) -> Vec { if is_array(item_type) || is_byte_array(item_type) { diagnostics.push(PluginDiagnostic { stable_ptr: diagnostic_item, message: "Array field cannot be packed.".into(), severity: Severity::Error, }); - "ERROR".to_string() + vec!["ERROR".to_string()] } else if is_tuple(item_type) { get_packed_tuple_layout_from_type(diagnostics, diagnostic_item, item_type) } else { let primitives = primitive_type_introspection(); if let Some(p) = primitives.get(item_type) { - p.1.iter().map(|x| x.to_string()).collect::>().join(",") + vec![p.1.iter().map(|x| x.to_string()).collect::>().join(",")] } else { - diagnostics.push(PluginDiagnostic { - stable_ptr: diagnostic_item, - message: "For now, field with custom type cannot be packed".into(), - severity: Severity::Error, - }); - "ERROR".to_string() + // as we cannot verify that an enum/struct custom type is packable, + // we suppose it is and let the user verify this. + // If it's not the case, the Dojo model layout function will panic. + vec![format!("dojo::database::introspect::Introspect::<{}>::layout()", item_type)] } } } @@ -294,10 +362,9 @@ pub fn get_packed_tuple_layout_from_type( diagnostics: &mut Vec, diagnostic_item: ids::SyntaxStablePtrId, item_type: &str, -) -> String { +) -> Vec { get_tuple_item_types(item_type) .iter() - .map(|x| get_packed_item_layout_from_type(diagnostics, diagnostic_item, x)) + .flat_map(|x| get_packed_item_layout_from_type(diagnostics, diagnostic_item, x)) .collect::>() - .join(",") } diff --git a/crates/dojo-lang/src/introspect/mod.rs b/crates/dojo-lang/src/introspect/mod.rs index fb788af004..856c3477e0 100644 --- a/crates/dojo-lang/src/introspect/mod.rs +++ b/crates/dojo-lang/src/introspect/mod.rs @@ -21,18 +21,11 @@ pub fn handle_introspect_struct( packed: bool, ) -> RewriteNode { let struct_name = struct_ast.name(db).text(db).into(); - let struct_size = size::compute_struct_layout_size(db, &struct_ast); + let struct_size = size::compute_struct_layout_size(db, &struct_ast, packed); let ty = ty::build_struct_ty(db, &struct_name, &struct_ast); let layout = if packed { - format!( - "dojo::database::introspect::Layout::Fixed( - array![ - {} - ].span() - )", - layout::build_packed_struct_layout(db, diagnostics, &struct_ast) - ) + layout::build_packed_struct_layout(db, diagnostics, &struct_ast) } else { format!( "dojo::database::introspect::Layout::Struct( @@ -57,24 +50,15 @@ pub fn handle_introspect_enum( packed: bool, ) -> RewriteNode { let enum_name = enum_ast.name(db).text(db).into(); - let identical_variants = utils::are_enum_variants_identical(db, &enum_ast); - let enum_size = size::compute_enum_layout_size(db, &enum_ast, identical_variants); - let ty = ty::build_enum_ty(db, &enum_name, &enum_ast); + let variant_sizes = size::compute_enum_variant_sizes(db, &enum_ast); let layout = if packed { - if identical_variants { - format!( - "dojo::database::introspect::Layout::Fixed( - array![ - {} - ].span() - )", - layout::build_packed_enum_layout(db, diagnostics, &enum_ast) - ) + if size::is_enum_packable(&variant_sizes) { + layout::build_packed_enum_layout(db, diagnostics, &enum_ast) } else { diagnostics.push(PluginDiagnostic { stable_ptr: enum_ast.name(db).stable_ptr().0, - message: "To be packed, all variants must have exactly the same layout." + message: "To be packed, all variants must have fixed layout of same size." .to_string(), severity: Severity::Error, }); @@ -92,6 +76,8 @@ pub fn handle_introspect_enum( }; let (gen_types, gen_impls) = build_generic_types_and_impls(db, enum_ast.generic_params(db)); + let enum_size = size::compute_enum_layout_size(&variant_sizes, packed); + let ty = ty::build_enum_ty(db, &enum_name, &enum_ast); generate_introspect(&enum_name, &enum_size, &gen_types, gen_impls, &layout, &ty) } @@ -115,7 +101,6 @@ impl $name$Introspect<$generics$> of \ $size$ } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { $layout$ } diff --git a/crates/dojo-lang/src/introspect/size.rs b/crates/dojo-lang/src/introspect/size.rs index d2bd078eee..9c056897bf 100644 --- a/crates/dojo-lang/src/introspect/size.rs +++ b/crates/dojo-lang/src/introspect/size.rs @@ -7,7 +7,11 @@ use super::utils::{ get_tuple_item_types, is_array, is_byte_array, is_tuple, primitive_type_introspection, }; -pub fn compute_struct_layout_size(db: &dyn SyntaxGroup, struct_ast: &ItemStruct) -> String { +pub fn compute_struct_layout_size( + db: &dyn SyntaxGroup, + struct_ast: &ItemStruct, + is_packed: bool, +) -> String { let mut cumulated_sizes = 0; let mut is_dynamic_size = false; @@ -29,32 +33,61 @@ pub fn compute_struct_layout_size(db: &dyn SyntaxGroup, struct_ast: &ItemStruct) }) .flatten() .collect::>(); - build_size_function_body(&mut sizes, cumulated_sizes, is_dynamic_size) + build_size_function_body(&mut sizes, cumulated_sizes, is_dynamic_size, is_packed) } -pub fn compute_enum_layout_size( +pub fn compute_enum_variant_sizes( db: &dyn SyntaxGroup, enum_ast: &ItemEnum, - identical_variants: bool, +) -> Vec<(Vec, u32, bool)> { + enum_ast + .variants(db) + .elements(db) + .iter() + .map(|v| match v.type_clause(db) { + OptionTypeClause::Empty(_) => (vec![], 0, false), + OptionTypeClause::TypeClause(type_clause) => { + get_field_size_from_type_clause(db, &type_clause) + } + }) + .collect::>() +} + +pub fn is_enum_packable(variant_sizes: &[(Vec, u32, bool)]) -> bool { + if variant_sizes.is_empty() { + return true; + } + + let v0_sizes = variant_sizes[0].0.clone(); + let v0_fixed_size = variant_sizes[0].1; + + variant_sizes.iter().all(|vs| { + vs.0.len() == v0_sizes.len() + && vs.0.iter().zip(v0_sizes.iter()).all(|(a, b)| a == b) + && vs.1 == v0_fixed_size + && !vs.2 + }) +} + +pub fn compute_enum_layout_size( + variant_sizes: &[(Vec, u32, bool)], + is_packed: bool, ) -> String { + if variant_sizes.is_empty() { + return "Option::None".to_string(); + } + + let v0 = variant_sizes[0].clone(); + let identical_variants = + variant_sizes.iter().all(|vs| vs.0 == v0.0 && vs.1 == v0.1 && vs.2 == v0.2); + if identical_variants { - match enum_ast.variants(db).elements(db).first() { - Some(first_variant) => { - let (mut sizes, cumulated_sizes, is_dynamic_size) = - match first_variant.type_clause(db) { - OptionTypeClause::Empty(_) => (vec![], 0, false), - OptionTypeClause::TypeClause(type_clause) => { - get_field_size_from_type_clause(db, &type_clause) - } - }; - - // add 8 bits to store the variant identifier - sizes.insert(0, "8".to_string()); - - build_size_function_body(&mut sizes, cumulated_sizes, is_dynamic_size) - } - None => "Option::None".to_string(), - } + let (mut sizes, mut cumulated_sizes, is_dynamic_size) = v0; + + // add one felt252 to store the variant identifier + cumulated_sizes += 1; + + build_size_function_body(&mut sizes, cumulated_sizes, is_dynamic_size, is_packed) } else { "Option::None".to_string() } @@ -64,6 +97,7 @@ pub fn build_size_function_body( sizes: &mut Vec, cumulated_sizes: u32, is_dynamic_size: bool, + is_packed: bool, ) -> String { if is_dynamic_size { return "Option::None".to_string(); @@ -77,16 +111,22 @@ pub fn build_size_function_body( 0 => "Option::None".to_string(), 1 => sizes[0].clone(), _ => { + let none_check = if is_packed { + "" + } else { + "if dojo::database::utils::any_none(@sizes) { + return Option::None; + }" + }; + format!( "let sizes : Array> = array![ - {} - ]; - - if dojo::database::utils::any_none(@sizes) {{ - return Option::None; - }} - Option::Some(dojo::database::utils::sum(sizes)) - ", + {} + ]; + + {none_check} + Option::Some(dojo::database::utils::sum(sizes)) + ", sizes.join(",\n") ) } diff --git a/crates/dojo-lang/src/introspect/utils.rs b/crates/dojo-lang/src/introspect/utils.rs index 6494e9f0fe..bf7be9964c 100644 --- a/crates/dojo-lang/src/introspect/utils.rs +++ b/crates/dojo-lang/src/introspect/utils.rs @@ -1,9 +1,5 @@ use std::collections::HashMap; -use cairo_lang_syntax::node::ast::ItemEnum; -use cairo_lang_syntax::node::db::SyntaxGroup; -use cairo_lang_syntax::node::TypedSyntaxNode; - #[derive(Clone, Default)] pub struct TypeIntrospection(pub usize, pub Vec); @@ -94,16 +90,6 @@ pub fn get_tuple_item_types(ty: &str) -> Vec { items } -pub fn are_enum_variants_identical(db: &dyn SyntaxGroup, enum_ast: &ItemEnum) -> bool { - let variants = enum_ast - .variants(db) - .elements(db) - .iter() - .map(|v| v.as_syntax_node().get_text(db)) - .collect::>(); - variants.iter().all(|item| item == &variants[0]) -} - #[test] pub fn test_get_tuple_item_types() { pub fn assert_array(got: Vec, expected: Vec) { diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo_world_world.json b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo_world_world.json index d800e7d191..63207ddec9 100644 --- a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo_world_world.json +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo_world_world.json @@ -205,6 +205,10 @@ { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "init_calldata", + "type": "core::array::Span::" } ], "outputs": [ diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_24_cairo_v240.toml b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_24_cairo_v240.toml index a62805594e..d253e1bd66 100644 --- a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_24_cairo_v240.toml +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_24_cairo_v240.toml @@ -6,4 +6,5 @@ abi = "manifests/dev/abis/base/contracts/compiler_cairo_cairo_24_cairo_v240.json reads = [] writes = [] computed = [] +init_calldata = [] name = "compiler_cairo::cairo_24::cairo_v240" diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_26_cairo_v260.toml b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_26_cairo_v260.toml index e4f5304c0d..4ac01d6fdb 100644 --- a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_26_cairo_v260.toml +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_26_cairo_v260.toml @@ -6,4 +6,5 @@ abi = "manifests/dev/abis/base/contracts/compiler_cairo_cairo_26_cairo_v260.json reads = [] writes = [] computed = [] +init_calldata = [] name = "compiler_cairo::cairo_26::cairo_v260" diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_world_world.toml b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_world_world.toml index 5a021e2793..2daf97b5e5 100644 --- a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_world_world.toml +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_world_world.toml @@ -1,5 +1,5 @@ kind = "Class" -class_hash = "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0" -original_class_hash = "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0" +class_hash = "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092" +original_class_hash = "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092" abi = "manifests/dev/abis/base/dojo_world_world.json" name = "dojo::world::world" diff --git a/crates/dojo-lang/src/plugin.rs b/crates/dojo-lang/src/plugin.rs index 0be64f4c6e..fdd7d28419 100644 --- a/crates/dojo-lang/src/plugin.rs +++ b/crates/dojo-lang/src/plugin.rs @@ -514,9 +514,9 @@ impl MacroPlugin for BuiltinDojoPlugin { DOJO_INTERFACE_ATTR.to_string(), DOJO_CONTRACT_ATTR.to_string(), DOJO_EVENT_ATTR.to_string(), + DOJO_MODEL_ATTR.to_string(), "key".to_string(), "computed".to_string(), - DOJO_MODEL_ATTR.to_string(), ] } } diff --git a/crates/dojo-lang/src/plugin_test_data/introspect b/crates/dojo-lang/src/plugin_test_data/introspect index c10b97e019..3582f86aad 100644 --- a/crates/dojo-lang/src/plugin_test_data/introspect +++ b/crates/dojo-lang/src/plugin_test_data/introspect @@ -14,8 +14,8 @@ struct Vec2 { #[derive(Serde, Copy, Drop, Introspect)] enum PlainEnum { - Left: (), - Right: (), + Left, + Right, } #[derive(Serde, Copy, Drop, Introspect)] @@ -55,9 +55,9 @@ enum EnumWithComplexTuple { } #[derive(Serde, Copy, Drop, Introspect)] -enum EnumTupleOnePrimitive { - Left: (u16,), - Right: (u16,), +enum EnumWithPrimitive { + Left: u32, + Right: u32, } #[derive(Serde, Copy, Drop, Introspect)] @@ -220,7 +220,7 @@ struct StructNotPackable1 { } #[derive(IntrospectPacked)] -struct StructNotPackable2 { +struct StructPackableWithInnerPacked { x: u8, y: StructPacked1 } @@ -245,6 +245,13 @@ enum EnumPacked3 { b: u256, } + +#[derive(IntrospectPacked)] +enum EnumPackableWithInnerPacked { + a: StructPacked1, + b: StructPacked1, +} + #[derive(IntrospectPacked)] enum EnumNotPackable1 { a: u8, @@ -262,8 +269,8 @@ struct Vec2 { #[derive(Serde, Copy, Drop, Introspect)] enum PlainEnum { - Left: (), - Right: (), + Left, + Right, } #[derive(Serde, Copy, Drop, Introspect)] @@ -303,9 +310,9 @@ enum EnumWithComplexTuple { } #[derive(Serde, Copy, Drop, Introspect)] -enum EnumTupleOnePrimitive { - Left: (u16,), - Right: (u16,), +enum EnumWithPrimitive { + Left: u32, + Right: u32, } #[derive(Serde, Copy, Drop, Introspect)] @@ -468,7 +475,7 @@ struct StructNotPackable1 { } #[derive(IntrospectPacked)] -struct StructNotPackable2 { +struct StructPackableWithInnerPacked { x: u8, y: StructPacked1 } @@ -493,6 +500,13 @@ enum EnumPacked3 { b: u256, } + +#[derive(IntrospectPacked)] +enum EnumPackableWithInnerPacked { + a: StructPacked1, + b: StructPacked1, +} + #[derive(IntrospectPacked)] enum EnumNotPackable1 { a: u8, @@ -519,7 +533,6 @@ impl Vec2Introspect<> of dojo::database::introspect::Introspect> { Option::Some(2) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -583,38 +596,19 @@ impl PlainEnumDrop of core::traits::Drop::; impl PlainEnumIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + Option::Some(1) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( - array![ - - ].span() - ) - ].span() - ) + layout: dojo::database::introspect::Layout::Fixed(array![].span()) }, dojo::database::introspect::FieldLayout { selector: 1, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( - array![ - - ].span() - ) - ].span() - ) + layout: dojo::database::introspect::Layout::Fixed(array![].span()) } ].span() ) @@ -627,16 +621,8 @@ dojo::database::introspect::FieldLayout { name: 'PlainEnum', attrs: array![].span(), children: array![ - ('Left', dojo::database::introspect::Ty::Tuple( - array![ - - ].span() - )), -('Right', dojo::database::introspect::Ty::Tuple( - array![ - - ].span() - )) + ('Left', dojo::database::introspect::Ty::Tuple(array![].span())), +('Right', dojo::database::introspect::Ty::Tuple(array![].span())) ].span() } @@ -667,30 +653,19 @@ impl EnumWithPrimitiveDrop of core::traits::Drop::; impl EnumWithPrimitiveIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + Option::Some(2) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() }, dojo::database::introspect::FieldLayout { selector: 1, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() } ].span() ) @@ -735,30 +710,28 @@ impl EnumWithStructDrop of core::traits::Drop::; impl EnumWithStructIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + let sizes : Array> = array![ + dojo::database::introspect::Introspect::::size(), +Option::Some(1) + ]; + + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() }, dojo::database::introspect::FieldLayout { selector: 1, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() } ].span() ) @@ -806,27 +779,16 @@ impl EnumWithSimpleArrayIntrospect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::>::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::>::layout() }, dojo::database::introspect::FieldLayout { selector: 1, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::>::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::>::layout() } ].span() ) @@ -882,27 +844,16 @@ impl EnumWithByteArrayIntrospect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() }, dojo::database::introspect::FieldLayout { selector: 1, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() } ].span() ) @@ -947,40 +898,29 @@ impl EnumWithSimpleTupleDrop of core::traits::Drop::; impl EnumWithSimpleTupleIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + Option::Some(4) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( array![ dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout() ].span() ) - ].span() - ) }, dojo::database::introspect::FieldLayout { selector: 1, layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( array![ dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout() ].span() ) - ].span() - ) } ].span() ) @@ -1035,40 +975,38 @@ impl EnumWithComplexTupleDrop of core::traits::Drop::; impl EnumWithComplexTupleIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + let sizes : Array> = array![ + dojo::database::introspect::Introspect::::size(), +Option::Some(2) + ]; + + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( array![ dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout() ].span() ) - ].span() - ) }, dojo::database::introspect::FieldLayout { selector: 1, layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( array![ dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout() ].span() ) - ].span() - ) } ].span() ) @@ -1099,62 +1037,43 @@ dojo::database::introspect::Introspect::::ty() ) } } -impl EnumTupleOnePrimitiveSerde of core::serde::Serde:: { - fn serialize(self: @EnumTupleOnePrimitive, ref output: core::array::Array) { +impl EnumWithPrimitiveSerde of core::serde::Serde:: { + fn serialize(self: @EnumWithPrimitive, ref output: core::array::Array) { match self { - EnumTupleOnePrimitive::Left(x) => { core::serde::Serde::serialize(@0, ref output); core::serde::Serde::serialize(x, ref output); }, - EnumTupleOnePrimitive::Right(x) => { core::serde::Serde::serialize(@1, ref output); core::serde::Serde::serialize(x, ref output); }, + EnumWithPrimitive::Left(x) => { core::serde::Serde::serialize(@0, ref output); core::serde::Serde::serialize(x, ref output); }, + EnumWithPrimitive::Right(x) => { core::serde::Serde::serialize(@1, ref output); core::serde::Serde::serialize(x, ref output); }, } } - fn deserialize(ref serialized: core::array::Span) -> core::option::Option { + fn deserialize(ref serialized: core::array::Span) -> core::option::Option { let idx: felt252 = core::serde::Serde::deserialize(ref serialized)?; core::option::Option::Some( match idx { - 0 => EnumTupleOnePrimitive::Left(core::serde::Serde::deserialize(ref serialized)?), - 1 => EnumTupleOnePrimitive::Right(core::serde::Serde::deserialize(ref serialized)?), + 0 => EnumWithPrimitive::Left(core::serde::Serde::deserialize(ref serialized)?), + 1 => EnumWithPrimitive::Right(core::serde::Serde::deserialize(ref serialized)?), _ => { return core::option::Option::None; } } ) } } -impl EnumTupleOnePrimitiveCopy of core::traits::Copy::; -impl EnumTupleOnePrimitiveDrop of core::traits::Drop::; +impl EnumWithPrimitiveCopy of core::traits::Copy::; +impl EnumWithPrimitiveDrop of core::traits::Drop::; -impl EnumTupleOnePrimitiveIntrospect<> of dojo::database::introspect::Introspect> { +impl EnumWithPrimitiveIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + Option::Some(2) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Introspect::::layout() - ].span() - ) - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() }, dojo::database::introspect::FieldLayout { selector: 1, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Introspect::::layout() - ].span() - ) - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() } ].span() ) @@ -1164,19 +1083,11 @@ dojo::database::introspect::FieldLayout { fn ty() -> dojo::database::introspect::Ty { dojo::database::introspect::Ty::Enum( dojo::database::introspect::Enum { - name: 'EnumTupleOnePrimitive', + name: 'EnumWithPrimitive', attrs: array![].span(), children: array![ - ('Left', dojo::database::introspect::Ty::Tuple( - array![ - dojo::database::introspect::Introspect::::ty() - ].span() - )), -('Right', dojo::database::introspect::Ty::Tuple( - array![ - dojo::database::introspect::Introspect::::ty() - ].span() - )) + ('Left', dojo::database::introspect::Introspect::::ty()), +('Right', dojo::database::introspect::Introspect::::ty()) ].span() } @@ -1207,30 +1118,28 @@ impl EnumCustomDrop of core::traits::Drop::; impl EnumCustomIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + let sizes : Array> = array![ + dojo::database::introspect::Introspect::::size(), +Option::Some(1) + ]; + + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() }, dojo::database::introspect::FieldLayout { selector: 1, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() } ].span() ) @@ -1275,42 +1184,41 @@ impl EnumTupleMixDrop of core::traits::Drop::; impl EnumTupleMixIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + let sizes : Array> = array![ + dojo::database::introspect::Introspect::::size(), +dojo::database::introspect::Introspect::::size(), +Option::Some(2) + ]; + + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( array![ dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout() ].span() ) - ].span() - ) }, dojo::database::introspect::FieldLayout { selector: 1, layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( array![ dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout() ].span() ) - ].span() - ) } ].span() ) @@ -1372,41 +1280,25 @@ impl EnumWithDifferentVariantDataIntrospect<> of dojo::database::introspect::Int Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - - ].span() - ) + layout: dojo::database::introspect::Layout::Fixed(array![].span()) }, dojo::database::introspect::FieldLayout { selector: 1, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::::layout() }, dojo::database::introspect::FieldLayout { selector: 2, layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Layout::Tuple( array![ dojo::database::introspect::Introspect::::layout(), dojo::database::introspect::Introspect::::layout() ].span() ) - ].span() - ) } ].span() ) @@ -1442,7 +1334,6 @@ impl StructWithPrimitivesIntrospect<> of dojo::database::introspect::Introspect< Option::Some(2) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1493,18 +1384,17 @@ impl StructWithStructIntrospect<> of dojo::database::introspect::Introspect Option { let sizes : Array> = array![ - dojo::database::introspect::Introspect::::size(), + dojo::database::introspect::Introspect::::size(), Option::Some(1) - ]; + ]; - if dojo::database::utils::any_none(@sizes) { - return Option::None; - } - Option::Some(dojo::database::utils::sum(sizes)) - + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1557,7 +1447,6 @@ impl StructWithSimpleArrayIntrospect<> of dojo::database::introspect::Introspect Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1614,7 +1503,6 @@ impl StructWithByteArrayIntrospect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1667,7 +1555,6 @@ impl StructWithComplexArrayIntrospect<> of dojo::database::introspect::Introspec Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1724,7 +1611,6 @@ impl StructWithSimpleTupleIntrospect<> of dojo::database::introspect::Introspect Option::Some(4) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1785,19 +1671,18 @@ impl StructWithComplexTupleIntrospect<> of dojo::database::introspect::Introspec #[inline(always)] fn size() -> Option { let sizes : Array> = array![ - dojo::database::introspect::Introspect::::size(), + dojo::database::introspect::Introspect::::size(), dojo::database::introspect::Introspect::::size(), Option::Some(2) - ]; + ]; - if dojo::database::utils::any_none(@sizes) { - return Option::None; - } - Option::Some(dojo::database::utils::sum(sizes)) - + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1862,7 +1747,6 @@ impl StructWithNestedArraysIntrospect<> of dojo::database::introspect::Introspec Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1933,19 +1817,18 @@ impl StructWithNestedTuplesIntrospect<> of dojo::database::introspect::Introspec #[inline(always)] fn size() -> Option { let sizes : Array> = array![ - dojo::database::introspect::Introspect::::size(), + dojo::database::introspect::Introspect::::size(), dojo::database::introspect::Introspect::::size(), Option::Some(3) - ]; + ]; - if dojo::database::utils::any_none(@sizes) { - return Option::None; - } - Option::Some(dojo::database::utils::sum(sizes)) - + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2028,7 +1911,6 @@ impl StructWithNestedTuplesAndByteArrayIntrospect<> of dojo::database::introspec Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2111,7 +1993,6 @@ impl StructWithNestedEverythingIntrospect<> of dojo::database::introspect::Intro Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2244,7 +2125,6 @@ impl GenericStructIntrospect::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2293,7 +2173,6 @@ impl StructWithBadOptionIntrospect<> of dojo::database::introspect::Introspect>::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2347,29 +2226,23 @@ impl EnumWithBadOptionIntrospect<> of dojo::database::introspect::Introspect Option { let sizes : Array> = array![ - 8, -dojo::database::introspect::Introspect::>::size() - ]; + dojo::database::introspect::Introspect::>::size(), +Option::Some(1) + ]; - if dojo::database::utils::any_none(@sizes) { - return Option::None; - } - Option::Some(dojo::database::utils::sum(sizes)) - + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ dojo::database::introspect::FieldLayout { selector: 0, - layout: dojo::database::introspect::Layout::Tuple( - array![ - dojo::database::introspect::Layout::Fixed(array![8].span()), - dojo::database::introspect::Introspect::>::layout() - ].span() - ) + layout: dojo::database::introspect::Introspect::>::layout() } ].span() ) @@ -2396,7 +2269,6 @@ impl EnumIncompatibleAttrsIntrospect<> of dojo::database::introspect::Introspect Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ @@ -2426,7 +2298,6 @@ impl EnumIncompatibleAttrsIntrospect<> of dojo::database::introspect::Introspect Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Fixed( array![ @@ -2456,7 +2327,6 @@ impl StructIncompatibleAttrsIntrospect<> of dojo::database::introspect::Introspe Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2486,7 +2356,6 @@ impl StructIncompatibleAttrsIntrospect<> of dojo::database::introspect::Introspe Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Fixed( array![ @@ -2516,7 +2385,6 @@ impl StructIncompatibleAttrs2Introspect<> of dojo::database::introspect::Introsp Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2546,7 +2414,6 @@ impl StructIncompatibleAttrs2Introspect<> of dojo::database::introspect::Introsp Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Fixed( array![ @@ -2576,7 +2443,6 @@ impl EnumIncompatibleAttrs2Introspect<> of dojo::database::introspect::Introspec Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Enum( array![ @@ -2606,7 +2472,6 @@ impl EnumIncompatibleAttrs2Introspect<> of dojo::database::introspect::Introspec Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Fixed( array![ @@ -2636,7 +2501,6 @@ impl StructPacked1Introspect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Fixed( array![ @@ -2670,7 +2534,6 @@ impl StructPacked2Introspect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Fixed( array![ @@ -2709,7 +2572,6 @@ impl StructPacked3Introspect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Fixed( array![ @@ -2753,7 +2615,6 @@ impl StructNotPackable1Introspect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Fixed( array![ @@ -2790,35 +2651,54 @@ dojo::database::introspect::Member { } } -impl StructNotPackable2Introspect<> of dojo::database::introspect::Introspect> { +impl StructPackableWithInnerPackedIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { let sizes : Array> = array![ - dojo::database::introspect::Introspect::::size(), + dojo::database::introspect::Introspect::::size(), Option::Some(1) - ]; + ]; - if dojo::database::utils::any_none(@sizes) { - return Option::None; - } - Option::Some(dojo::database::utils::sum(sizes)) - + + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { - dojo::database::introspect::Layout::Fixed( - array![ - 8,ERROR - ].span() - ) + let mut layouts = array![ + dojo::database::introspect::Layout::Fixed(array![8].span()), +dojo::database::introspect::Introspect::::layout() + ]; + let mut merged_layout = ArrayTrait::::new(); + + loop { + match ArrayTrait::pop_front(ref layouts) { + Option::Some(mut layout) => { + match layout { + dojo::database::introspect::Layout::Fixed(mut l) => { + loop { + match SpanTrait::pop_front(ref l) { + Option::Some(x) => merged_layout.append(*x), + Option::None(_) => { break; } + }; + }; + }, + _ => panic!("A packed model layout must contain Fixed layouts only."), + }; + }, + Option::None(_) => { break; } + }; + }; + + dojo::database::introspect::Layout::Fixed(merged_layout.span()) + } #[inline(always)] fn ty() -> dojo::database::introspect::Ty { dojo::database::introspect::Ty::Struct( dojo::database::introspect::Struct { - name: 'StructNotPackable2', + name: 'StructPackableWithInnerPacked', attrs: array![].span(), children: array![ dojo::database::introspect::Member { @@ -2841,12 +2721,15 @@ dojo::database::introspect::Member { impl EnumPacked1Introspect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + Option::Some(1) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { - ERROR + dojo::database::introspect::Layout::Fixed( + array![ + 8 + ].span() + ) } #[inline(always)] @@ -2869,12 +2752,15 @@ impl EnumPacked1Introspect<> of dojo::database::introspect::Introspect of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + Option::Some(2) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { - ERROR + dojo::database::introspect::Layout::Fixed( + array![ + 8,8 + ].span() + ) } #[inline(always)] @@ -2897,12 +2783,15 @@ impl EnumPacked2Introspect<> of dojo::database::introspect::Introspect of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { - Option::None + Option::Some(3) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { - ERROR + dojo::database::introspect::Layout::Fixed( + array![ + 8,128,128 + ].span() + ) } #[inline(always)] @@ -2926,13 +2815,71 @@ dojo::database::introspect::Introspect::::ty() } } +impl EnumPackableWithInnerPackedIntrospect<> of dojo::database::introspect::Introspect> { + #[inline(always)] + fn size() -> Option { + let sizes : Array> = array![ + dojo::database::introspect::Introspect::::size(), +Option::Some(1) + ]; + + + Option::Some(dojo::database::utils::sum(sizes)) + + } + + fn layout() -> dojo::database::introspect::Layout { + let mut layouts = array![ + dojo::database::introspect::Layout::Fixed(array![8].span()), +dojo::database::introspect::Introspect::::layout() + ]; + let mut merged_layout = ArrayTrait::::new(); + + loop { + match ArrayTrait::pop_front(ref layouts) { + Option::Some(mut layout) => { + match layout { + dojo::database::introspect::Layout::Fixed(mut l) => { + loop { + match SpanTrait::pop_front(ref l) { + Option::Some(x) => merged_layout.append(*x), + Option::None(_) => { break; } + }; + }; + }, + _ => panic!("A packed model layout must contain Fixed layouts only."), + }; + }, + Option::None(_) => { break; } + }; + }; + + dojo::database::introspect::Layout::Fixed(merged_layout.span()) + + } + + #[inline(always)] + fn ty() -> dojo::database::introspect::Ty { + dojo::database::introspect::Ty::Enum( + dojo::database::introspect::Enum { + name: 'EnumPackableWithInnerPacked', + attrs: array![].span(), + children: array![ + ('a', dojo::database::introspect::Introspect::::ty()), +('b', dojo::database::introspect::Introspect::::ty()) + + ].span() + } + ) + } +} + impl EnumNotPackable1Introspect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { ERROR } @@ -2989,27 +2936,7 @@ error: Array field cannot be packed. y: Array ^**********^ -error: For now, field with custom type cannot be packed - --> test_src/lib.cairo:219:6 - y: StructPacked1 - ^*************^ - -error: To be packed, all variants must have exactly the same layout. - --> test_src/lib.cairo:223:6 -enum EnumPacked1 { - ^*********^ - -error: To be packed, all variants must have exactly the same layout. - --> test_src/lib.cairo:230:6 -enum EnumPacked2 { - ^*********^ - -error: To be packed, all variants must have exactly the same layout. - --> test_src/lib.cairo:237:6 -enum EnumPacked3 { - ^*********^ - -error: To be packed, all variants must have exactly the same layout. - --> test_src/lib.cairo:243:6 +error: To be packed, all variants must have fixed layout of same size. + --> test_src/lib.cairo:250:6 enum EnumNotPackable1 { ^**************^ diff --git a/crates/dojo-lang/src/plugin_test_data/model b/crates/dojo-lang/src/plugin_test_data/model index f644db0710..092bee2438 100644 --- a/crates/dojo-lang/src/plugin_test_data/model +++ b/crates/dojo-lang/src/plugin_test_data/model @@ -277,7 +277,6 @@ impl BadModelMultipleAttrIntrospect<> of dojo::database::introspect::Introspect< dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -319,7 +318,6 @@ impl BadModelMultipleVersionsIntrospect<> of dojo::database::introspect::Introsp dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -496,7 +494,6 @@ impl BadModelBadVersionTypeIntrospect<> of dojo::database::introspect::Introspec dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -673,7 +670,6 @@ impl BadModelNoVersionValueIntrospect<> of dojo::database::introspect::Introspec dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -850,7 +846,6 @@ impl BadModelUnexpectedArgWithValueIntrospect<> of dojo::database::introspect::I dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1027,7 +1022,6 @@ impl BadModelUnexpectedArgIntrospect<> of dojo::database::introspect::Introspect dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1204,7 +1198,6 @@ impl BadModelNotSupportedVersionIntrospect<> of dojo::database::introspect::Intr dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1381,7 +1374,6 @@ impl Modelv0Introspect<> of dojo::database::introspect::Introspect> { dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1558,7 +1550,6 @@ impl PositionIntrospect<> of dojo::database::introspect::Introspect> dojo::database::introspect::Introspect::::size() } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1735,7 +1726,6 @@ impl RolesIntrospect<> of dojo::database::introspect::Introspect> { Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -1912,7 +1902,6 @@ impl OnlyKeyModelIntrospect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2081,7 +2070,6 @@ impl U256KeyModelIntrospect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2250,7 +2238,6 @@ impl PlayerIntrospect<> of dojo::database::introspect::Introspect> { Option::Some(1) } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2432,7 +2419,6 @@ impl ModelWithSimpleArrayIntrospect<> of dojo::database::introspect::Introspect< Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2623,7 +2609,6 @@ impl ModelWithByteArrayIntrospect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -2810,7 +2795,6 @@ impl ModelWithComplexArrayIntrospect<> of dojo::database::introspect::Introspect Option::None } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -3001,7 +2985,6 @@ impl ModelWithTupleIntrospect<> of dojo::database::introspect::Introspect dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ @@ -3197,18 +3180,17 @@ impl ModelWithTupleNoPrimitivesIntrospect<> of dojo::database::introspect::Intro #[inline(always)] fn size() -> Option { let sizes : Array> = array![ - dojo::database::introspect::Introspect::::size(), + dojo::database::introspect::Introspect::::size(), Option::Some(3) - ]; + ]; - if dojo::database::utils::any_none(@sizes) { - return Option::None; - } - Option::Some(dojo::database::utils::sum(sizes)) - + if dojo::database::utils::any_none(@sizes) { + return Option::None; + } + Option::Some(dojo::database::utils::sum(sizes)) + } - #[inline(always)] fn layout() -> dojo::database::introspect::Layout { dojo::database::introspect::Layout::Struct( array![ diff --git a/crates/dojo-lang/src/plugin_test_data/system b/crates/dojo-lang/src/plugin_test_data/system index 7b55dc83b5..c23d1a35a0 100644 --- a/crates/dojo-lang/src/plugin_test_data/system +++ b/crates/dojo-lang/src/plugin_test_data/system @@ -202,6 +202,26 @@ mod MyNominalContract { } } +#[dojo::contract] +mod init_test { + fn dojo_init( + world: IWorldDispatcher, + actions_address: ContractAddress, + actions_class: ClassHash, + value: u8 + ) { + emit!( + world, + ContractInitialized { + contract_address: actions_address, contract_class: actions_class, value + } + ); + } +} + +#[dojo::contract] +mod no_init_test {} + //! > generated_cairo_code #[starknet::contract] mod spawn { @@ -407,6 +427,16 @@ error: Unsupported attribute. #[dojo::contract] ^***************^ +error: Unsupported attribute. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + error: Unsupported attribute. --> test_src/lib.cairo:44:5 #[storage] @@ -452,6 +482,11 @@ error: Unsupported attribute. #[dojo::contract] ^***************^ +error: Unsupported attribute. + --> test_src/lib.cairo:1:1 +#[dojo::contract] +^***************^ + error: Unknown inline item macro: 'component'. --> test_src/lib.cairo:11:1 #[dojo::contract] @@ -487,6 +522,11 @@ error: Unsupported attribute. #[dojo::contract] ^***************^ +error: Unsupported attribute. + --> test_src/lib.cairo:11:1 +#[dojo::contract] +^***************^ + error: Unknown inline item macro: 'component'. --> test_src/lib.cairo:18:1 #[dojo::contract] @@ -522,6 +562,11 @@ error: Unsupported attribute. #[dojo::contract] ^***************^ +error: Unsupported attribute. + --> test_src/lib.cairo:18:1 +#[dojo::contract] +^***************^ + error: Unknown inline item macro: 'component'. --> test_src/lib.cairo:28:1 #[dojo::contract] @@ -557,6 +602,11 @@ error: Unsupported attribute. #[dojo::contract] ^***************^ +error: Unsupported attribute. + --> test_src/lib.cairo:28:1 +#[dojo::contract] +^***************^ + error: Unknown inline item macro: 'component'. --> test_src/lib.cairo:54:1 #[dojo::contract] @@ -617,6 +667,11 @@ error: Unsupported attribute. #[dojo::contract] ^***************^ +error: Unsupported attribute. + --> test_src/lib.cairo:54:1 +#[dojo::contract] +^***************^ + error: Unknown inline item macro: 'component'. --> test_src/lib.cairo:93:1 #[dojo::contract(allow_ref_self)] @@ -657,6 +712,11 @@ error: Unsupported attribute. #[dojo::contract(allow_ref_self)] ^*******************************^ +error: Unsupported attribute. + --> test_src/lib.cairo:93:1 +#[dojo::contract(allow_ref_self)] +^*******************************^ + error: Unknown inline item macro: 'component'. --> test_src/lib.cairo:126:1 #[dojo::contract] @@ -697,6 +757,11 @@ error: Unsupported attribute. #[dojo::contract] ^***************^ +error: Unsupported attribute. + --> test_src/lib.cairo:126:1 +#[dojo::contract] +^***************^ + error: Unknown inline item macro: 'component'. --> test_src/lib.cairo:162:1 #[dojo::contract] @@ -737,6 +802,91 @@ error: Unsupported attribute. #[dojo::contract] ^***************^ +error: Unsupported attribute. + --> test_src/lib.cairo:162:1 +#[dojo::contract] +^***************^ + +error: Unknown inline item macro: 'component'. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:199:1 +#[dojo::contract] +^***************^ + +error: Unknown inline item macro: 'component'. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:216:1 +#[dojo::contract] +^***************^ + //! > expanded_cairo_code #[starknet::component] @@ -788,6 +938,18 @@ trait IAllowedRefSelf { return (); } + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } + #[event] #[derive(Drop, starknet::Event)] enum Event { @@ -833,6 +995,18 @@ impl EventDrop of core::traits::Drop::; value } + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } + #[event] #[derive(Drop, starknet::Event)] enum Event { @@ -881,6 +1055,18 @@ impl EventDrop of core::traits::Drop::; return (); } + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } + #[event] #[derive(Drop, starknet::Event)] enum Event { @@ -935,6 +1121,18 @@ impl EventDrop of core::traits::Drop::; address: ContractAddress, } + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } + #[storage] struct Storage { world_dispatcher: IWorldDispatcher, @@ -990,8 +1188,20 @@ impl TestEventDrop of core::traits::Drop::; testcomponent1_event: testcomponent1::Event, testcomponent2_event: testcomponent2::Event } -impl EventDrop of core::traits::Drop::; + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } +impl EventDrop of core::traits::Drop::; + } #[starknet::interface] @@ -1037,6 +1247,18 @@ impl EventDrop of core::traits::Drop::; fn spawn(ref self: ContractState) {} } + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } + #[event] #[derive(Drop, starknet::Event)] enum Event { @@ -1138,6 +1360,18 @@ self: @ContractState, world: IWorldDispatcher, vec: Vec2, another_wo } } + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } + #[event] #[derive(Drop, starknet::Event)] enum Event { @@ -1215,6 +1449,18 @@ let world = self.world_dispatcher.read(); } } + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } + #[event] #[derive(Drop, starknet::Event)] enum Event { @@ -1234,3 +1480,123 @@ impl ActionDrop of core::traits::Drop::; impl EventDrop of core::traits::Drop::; } + + #[starknet::contract] + mod init_test { + use dojo::world; + use dojo::world::IWorldDispatcher; + use dojo::world::IWorldDispatcherTrait; + use dojo::world::IWorldProvider; + use dojo::world::IDojoResourceProvider; + + #[abi(embed_v0)] + impl DojoResourceProviderImpl of IDojoResourceProvider { + fn dojo_resource(self: @ContractState) -> felt252 { + 'init_test' + } + } + + #[abi(embed_v0)] + impl WorldProviderImpl of IWorldProvider { + fn world(self: @ContractState) -> IWorldDispatcher { + self.world_dispatcher.read() + } + } + + #[abi(embed_v0)] + impl UpgradableImpl = dojo::components::upgradeable::upgradeable::UpgradableImpl; + + + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState, actions_address: ContractAddress, actions_class: ClassHash, value: u8 +); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState, actions_address: ContractAddress, actions_class: ClassHash, value: u8 +) { + let world = self.world_dispatcher.read(); + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + { + emit!( + world, + ContractInitialized { + contract_address: actions_address, contract_class: actions_class, value + } + ); + } + + } + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + UpgradeableEvent: dojo::components::upgradeable::upgradeable::Event, + } + + #[storage] + struct Storage { + world_dispatcher: IWorldDispatcher, + #[substorage(v0)] + upgradeable: dojo::components::upgradeable::upgradeable::Storage, + } +impl EventDrop of core::traits::Drop::; + + } + + #[starknet::contract] + mod no_init_test { + use dojo::world; + use dojo::world::IWorldDispatcher; + use dojo::world::IWorldDispatcherTrait; + use dojo::world::IWorldProvider; + use dojo::world::IDojoResourceProvider; + + #[abi(embed_v0)] + impl DojoResourceProviderImpl of IDojoResourceProvider { + fn dojo_resource(self: @ContractState) -> felt252 { + 'no_init_test' + } + } + + #[abi(embed_v0)] + impl WorldProviderImpl of IWorldProvider { + fn world(self: @ContractState) -> IWorldDispatcher { + self.world_dispatcher.read() + } + } + + #[abi(embed_v0)] + impl UpgradableImpl = dojo::components::upgradeable::upgradeable::UpgradableImpl; + + + #[starknet::interface] + trait IDojoInit { + fn dojo_init(self: @ContractState); + } + + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + fn dojo_init(self: @ContractState) { + assert(starknet::get_caller_address() == self.world().contract_address, 'Only world can init'); + } + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + UpgradeableEvent: dojo::components::upgradeable::upgradeable::Event, + } + + #[storage] + struct Storage { + world_dispatcher: IWorldDispatcher, + #[substorage(v0)] + upgradeable: dojo::components::upgradeable::upgradeable::Storage, + } +impl EventDrop of core::traits::Drop::; + + } diff --git a/crates/dojo-test-utils/src/compiler.rs b/crates/dojo-test-utils/src/compiler.rs index c5c3e5a7f1..b8d30c5775 100644 --- a/crates/dojo-test-utils/src/compiler.rs +++ b/crates/dojo-test-utils/src/compiler.rs @@ -58,7 +58,8 @@ pub fn copy_build_project_temp( let temp_project_path = temp_project_dir.join("Scarb").with_extension("toml").to_string(); let dojo_core_path = Utf8PathBuf::from(dojo_core_path); - let ignore_dirs = ["manifests", "target"]; + // we don't ignore `manifests` because `overylays` are required for successful migration + let ignore_dirs = ["target"]; copy_project_temp(&source_project_dir, &temp_project_dir, &dojo_core_path, &ignore_dirs) .unwrap(); diff --git a/crates/dojo-test-utils/src/migration.rs b/crates/dojo-test-utils/src/migration.rs index fbf52d5e1c..7ad6f25843 100644 --- a/crates/dojo-test-utils/src/migration.rs +++ b/crates/dojo-test-utils/src/migration.rs @@ -15,12 +15,20 @@ pub fn prepare_migration( // In testing, profile name is always dev. let profile_name = "dev"; - let manifest = BaseManifest::load_from_path( + let mut manifest = BaseManifest::load_from_path( &manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(BASE_DIR), ) .unwrap(); - let world = WorldDiff::compute(manifest, None); + let overlay_manifest = OverlayManifest::load_from_path( + &manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(OVERLAYS_DIR), + ) + .unwrap(); + + manifest.merge(overlay_manifest); + + let mut world = WorldDiff::compute(manifest, None); + world.update_order().unwrap(); prepare_for_migration(None, felt!("0x12345"), &target_dir, world) } @@ -46,7 +54,8 @@ pub fn prepare_migration_with_world_and_seed( manifest.merge(overlay_manifest); - let world = WorldDiff::compute(manifest, None); + let mut world = WorldDiff::compute(manifest, None); + world.update_order().unwrap(); let seed = cairo_short_string_to_felt(seed).unwrap(); prepare_for_migration(world_address, seed, &target_dir, world) diff --git a/crates/dojo-types/src/schema.rs b/crates/dojo-types/src/schema.rs index e461dd8dfc..5b7a2615f3 100644 --- a/crates/dojo-types/src/schema.rs +++ b/crates/dojo-types/src/schema.rs @@ -250,7 +250,6 @@ impl std::fmt::Display for Ty { let str = self .iter() .filter_map(|ty| match ty { - Ty::Primitive(_) => None, Ty::Struct(s) => { let mut struct_str = format!("struct {} {{\n", s.name); for member in &s.children { @@ -272,6 +271,7 @@ impl std::fmt::Display for Ty { } Ty::Array(items_ty) => Some(format!("Array<{}>", items_ty[0].name())), Ty::ByteArray(_) => Some("ByteArray".to_string()), + _ => None, }) .collect::>() .join("\n\n"); diff --git a/crates/dojo-world/Cargo.toml b/crates/dojo-world/Cargo.toml index afc946495a..8504077caa 100644 --- a/crates/dojo-world/Cargo.toml +++ b/crates/dojo-world/Cargo.toml @@ -23,6 +23,7 @@ smol_str.workspace = true starknet-crypto.workspace = true starknet.workspace = true thiserror.workspace = true +topological-sort.workspace = true tracing.workspace = true cainome.workspace = true diff --git a/crates/dojo-world/src/contracts/abi/world.rs b/crates/dojo-world/src/contracts/abi/world.rs index 7e23eee81f..e2958635f1 100644 --- a/crates/dojo-world/src/contracts/abi/world.rs +++ b/crates/dojo-world/src/contracts/abi/world.rs @@ -211,6 +211,10 @@ abigen!( { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "init_calldata", + "type": "core::array::Span::" } ], "outputs": [ diff --git a/crates/dojo-world/src/contracts/model_test.rs b/crates/dojo-world/src/contracts/model_test.rs index 1b581b0e5e..f32a7b5943 100644 --- a/crates/dojo-world/src/contracts/model_test.rs +++ b/crates/dojo-world/src/contracts/model_test.rs @@ -65,7 +65,7 @@ async fn test_model() { assert_eq!( position.class_hash(), - felt!("0x03c3632f38ab3ba550bd3c596e2af55002d43bc76b7b660a3a57b49795307c58") + felt!("0x03ab26e88be7885877f93964880ccb63a8acd8e58f941c48bef52f191fa79868") ); let moves = world.model_reader("Moves").await.unwrap(); diff --git a/crates/dojo-world/src/contracts/world_test.rs b/crates/dojo-world/src/contracts/world_test.rs index 3c5afe8886..b96f25476b 100644 --- a/crates/dojo-world/src/contracts/world_test.rs +++ b/crates/dojo-world/src/contracts/world_test.rs @@ -1,14 +1,14 @@ use std::time::Duration; use camino::Utf8PathBuf; -use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR}; +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::FieldElement; use super::{WorldContract, WorldContractReader}; -use crate::manifest::BaseManifest; +use crate::manifest::{BaseManifest, OverlayManifest}; use crate::migration::strategy::prepare_for_migration; use crate::migration::world::WorldDiff; use crate::migration::{Declarable, Deployable, TxnConfig}; @@ -40,20 +40,31 @@ pub async fn deploy_world( // Dev profile is used by default for testing: let profile_name = "dev"; - let manifest = BaseManifest::load_from_path( + let mut manifest = BaseManifest::load_from_path( &manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(BASE_DIR), ) .unwrap(); - let world = WorldDiff::compute(manifest.clone(), None); + + let overlay_manifest = OverlayManifest::load_from_path( + &manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(OVERLAYS_DIR), + ) + .unwrap(); + + manifest.merge(overlay_manifest); + + let mut world = WorldDiff::compute(manifest.clone(), None); + world.update_order().unwrap(); + let account = sequencer.account(0); - let strategy = prepare_for_migration( + let mut strategy = prepare_for_migration( None, FieldElement::from_hex_be("0x12345").unwrap(), target_dir, world, ) .unwrap(); + strategy.resolve_variable(strategy.world_address().unwrap()).unwrap(); let base_class_hash = strategy.base.unwrap().declare(&account, &TxnConfig::default()).await.unwrap().class_hash; @@ -104,6 +115,7 @@ pub async fn deploy_world( base_class_hash, &account, &TxnConfig::default(), + &contract.diff.init_calldata, ) .await .unwrap(); diff --git a/crates/dojo-world/src/manifest/manifest_test.rs b/crates/dojo-world/src/manifest/manifest_test.rs index 33f09297b6..bc92e1a4f8 100644 --- a/crates/dojo-world/src/manifest/manifest_test.rs +++ b/crates/dojo-world/src/manifest/manifest_test.rs @@ -2,7 +2,7 @@ use std::io::Write; use cainome::cairo_serde::{ByteArray, CairoSerde}; use camino::Utf8PathBuf; -use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR}; +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; @@ -13,7 +13,9 @@ use starknet::core::types::{EmittedEvent, FieldElement}; use starknet::macros::{felt, selector}; use starknet::providers::jsonrpc::{JsonRpcClient, JsonRpcMethod}; -use super::{parse_contracts_events, AbiFormat, BaseManifest, DojoContract, DojoModel}; +use super::{ + parse_contracts_events, AbiFormat, BaseManifest, DojoContract, DojoModel, OverlayManifest, +}; use crate::contracts::world::test::deploy_world; use crate::manifest::{parse_models_events, AbstractManifestError, DeploymentManifest, Manifest}; use crate::migration::world::WorldDiff; @@ -382,20 +384,27 @@ fn fetch_remote_manifest() { .tokio_handle() .block_on(async { deploy_world(&runner, &temp_project_dir, &artifacts_path).await }); - let local_manifest = BaseManifest::load_from_path( + let mut local_manifest = BaseManifest::load_from_path( &temp_project_dir.join(MANIFESTS_DIR).join(profile_name).join(BASE_DIR), ) .unwrap(); + let overlay_manifest = OverlayManifest::load_from_path( + &temp_project_dir.join(MANIFESTS_DIR).join(profile_name).join(OVERLAYS_DIR), + ) + .unwrap(); + + local_manifest.merge(overlay_manifest); + let remote_manifest = config.tokio_handle().block_on(async { DeploymentManifest::load_from_remote(provider, world_address).await.unwrap() }); - assert_eq!(local_manifest.models.len(), 5); - assert_eq!(local_manifest.contracts.len(), 1); + assert_eq!(local_manifest.models.len(), 6); + assert_eq!(local_manifest.contracts.len(), 2); - assert_eq!(remote_manifest.models.len(), 5); - assert_eq!(remote_manifest.contracts.len(), 1); + assert_eq!(remote_manifest.models.len(), 6); + assert_eq!(remote_manifest.contracts.len(), 2); // compute diff from local and remote manifest diff --git a/crates/dojo-world/src/manifest/mod.rs b/crates/dojo-world/src/manifest/mod.rs index 04fc8624aa..d3aa32877a 100644 --- a/crates/dojo-world/src/manifest/mod.rs +++ b/crates/dojo-world/src/manifest/mod.rs @@ -579,6 +579,9 @@ impl ManifestMethods for DojoContract { if let Some(writes) = old.writes { self.writes = writes; } + if let Some(init_calldata) = old.init_calldata { + self.init_calldata = init_calldata; + } } } diff --git a/crates/dojo-world/src/manifest/types.rs b/crates/dojo-world/src/manifest/types.rs index afc6f13406..dbbee2fd58 100644 --- a/crates/dojo-world/src/manifest/types.rs +++ b/crates/dojo-world/src/manifest/types.rs @@ -94,9 +94,14 @@ pub struct DojoContract { #[serde_as(as = "UfeHex")] pub base_class_hash: FieldElement, pub abi: Option, + #[serde(default)] pub reads: Vec, + #[serde(default)] pub writes: Vec, + #[serde(default)] pub computed: Vec, + #[serde(default)] + pub init_calldata: Vec, } /// Represents a declaration of a model. @@ -152,6 +157,7 @@ pub struct OverlayDojoContract { pub original_class_hash: Option, pub reads: Option>, pub writes: Option>, + pub init_calldata: Option>, } #[serde_as] diff --git a/crates/dojo-world/src/metadata_test.rs b/crates/dojo-world/src/metadata_test.rs index 1245313aa9..8da0b1af36 100644 --- a/crates/dojo-world/src/metadata_test.rs +++ b/crates/dojo-world/src/metadata_test.rs @@ -145,10 +145,9 @@ async fn get_full_dojo_metadata_from_workspace() { ); assert!(env.world_address.is_some()); - assert!( - env.world_address - .unwrap() - .eq("0x1c958955aedbc7b8e2f051767d3369168e88bc5074b0f39e5f8cd2539138281") + assert_eq!( + env.world_address.unwrap(), + "0x26a8d9f2ac0348182bea206d913908ef77e439416713592ebc85941a69048d6" ); assert!(env.keystore_path.is_none()); @@ -175,6 +174,7 @@ async fn get_full_dojo_metadata_from_workspace() { let artifacts = get_artifacts_from_manifest(&manifest_dir); + dbg!(&artifacts); for (abi_subdir, name) in artifacts { let artifact = dojo_metadata.artifacts.get(&name); assert!(artifact.is_some(), "bad artifact for {}", name); @@ -224,6 +224,9 @@ fn get_artifacts_from_manifest(manifest_dir: &Utf8PathBuf) -> Vec<(String, Strin // Some models are inside actions, we need a better way to gather those. let name = name.replace("_actions_", "::actions::"); let name = name.replace("::actions_", "::actions::"); + + let name = name.replace("_others_", "::others::"); + let name = name.replace("::others_", "::others::"); artifacts.push(("models".to_string(), name)); } @@ -231,6 +234,7 @@ fn get_artifacts_from_manifest(manifest_dir: &Utf8PathBuf) -> Vec<(String, Strin for entry in fs::read_dir(contracts_dir).unwrap().flatten() { let name = entry.path().file_stem().unwrap().to_string_lossy().to_string(); let name = name.replace("_actions_", "::actions::"); + let name = name.replace("_others_", "::others::"); artifacts.push(("contracts".to_string(), name)); } diff --git a/crates/dojo-world/src/migration/class.rs b/crates/dojo-world/src/migration/class.rs index 7f2376694f..e5b29f16d9 100644 --- a/crates/dojo-world/src/migration/class.rs +++ b/crates/dojo-world/src/migration/class.rs @@ -38,7 +38,7 @@ impl Display for ClassDiff { } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct ClassMigration { pub diff: ClassDiff, pub artifact_path: PathBuf, diff --git a/crates/dojo-world/src/migration/contract.rs b/crates/dojo-world/src/migration/contract.rs index 166fbfbed8..59e6c929f9 100644 --- a/crates/dojo-world/src/migration/contract.rs +++ b/crates/dojo-world/src/migration/contract.rs @@ -16,6 +16,7 @@ pub struct ContractDiff { pub original_class_hash: FieldElement, pub base_class_hash: FieldElement, pub remote_class_hash: Option, + pub init_calldata: Vec, } impl StateDiff for ContractDiff { @@ -44,7 +45,7 @@ impl Display for ContractDiff { } // Represents a contract that needs to be migrated to the remote state -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct ContractMigration { pub salt: FieldElement, pub diff: ContractDiff, diff --git a/crates/dojo-world/src/migration/mod.rs b/crates/dojo-world/src/migration/mod.rs index eb83aa4b62..4f6b306bad 100644 --- a/crates/dojo-world/src/migration/mod.rs +++ b/crates/dojo-world/src/migration/mod.rs @@ -1,5 +1,6 @@ use std::fs::File; use std::path::PathBuf; +use std::str::FromStr; use std::sync::Arc; use anyhow::{anyhow, Result}; @@ -72,6 +73,8 @@ pub enum MigrationError { WaitingError(#[from] TransactionWaitingError), #[error(transparent)] ArtifactError(#[from] anyhow::Error), + #[error("Bad init calldata.")] + BadInitCalldata, } /// Represents the type of migration that should be performed. @@ -172,6 +175,7 @@ pub trait Deployable: Declarable + Sync { base_class_hash: FieldElement, account: &SingleOwnerAccount, txn_config: &TxnConfig, + calldata: &[String], ) -> Result as Account>::SignError>> where P: Provider + Sync + Send, @@ -203,11 +207,18 @@ pub trait Deployable: Declarable + Sync { } } - Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => Call { - calldata: vec![self.salt(), class_hash], - selector: selector!("deploy_contract"), - to: world_address, - }, + Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => { + let init_calldata: Vec = calldata + .iter() + .map(|s| FieldElement::from_str(s)) + .collect::, _>>() + .map_err(|_| MigrationError::BadInitCalldata)?; + + let mut calldata = + vec![self.salt(), class_hash, FieldElement::from(calldata.len())]; + calldata.extend(init_calldata); + Call { calldata, selector: selector!("deploy_contract"), to: world_address } + } Ok(_) => { return Err(MigrationError::ContractAlreadyDeployed(contract_address)); diff --git a/crates/dojo-world/src/migration/strategy.rs b/crates/dojo-world/src/migration/strategy.rs index 8dddb832b0..2a1ce96155 100644 --- a/crates/dojo-world/src/migration/strategy.rs +++ b/crates/dojo-world/src/migration/strategy.rs @@ -13,7 +13,7 @@ use super::contract::{ContractDiff, ContractMigration}; use super::world::WorldDiff; use super::MigrationType; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MigrationStrategy { pub world_address: Option, pub world: Option, @@ -59,6 +59,31 @@ impl MigrationStrategy { MigrationItemsInfo { new, update } } + + pub fn resolve_variable(&mut self, world_address: FieldElement) -> Result<()> { + let contracts_clone = self.contracts.clone(); + for contract in self.contracts.iter_mut() { + for field in contract.diff.init_calldata.iter_mut() { + if let Some(dependency) = field.strip_prefix("$contract_address:") { + let dependency_contract = + contracts_clone.iter().find(|c| c.diff.name == dependency).unwrap(); + let contract_address = get_contract_address( + generate_salt(&dependency_contract.diff.name), + dependency_contract.diff.base_class_hash, + &[], + world_address, + ); + *field = contract_address.to_string(); + } else if let Some(dependency) = field.strip_prefix("$class_hash:") { + let dependency_contract = + contracts_clone.iter().find(|c| c.diff.name == dependency).unwrap(); + *field = dependency_contract.diff.local_class_hash.to_string(); + } + } + } + + Ok(()) + } } /// construct migration strategy diff --git a/crates/dojo-world/src/migration/world.rs b/crates/dojo-world/src/migration/world.rs index 7fdad6e43d..27adf6c69a 100644 --- a/crates/dojo-world/src/migration/world.rs +++ b/crates/dojo-world/src/migration/world.rs @@ -1,7 +1,11 @@ use std::fmt::Display; +use std::mem; +use std::str::FromStr; +use anyhow::{bail, Result}; use convert_case::{Case, Casing}; use starknet_crypto::FieldElement; +use topological_sort::TopologicalSort; use super::class::ClassDiff; use super::contract::ContractDiff; @@ -74,6 +78,7 @@ impl WorldDiff { .find(|r| r.inner.class_hash() == contract.inner.class_hash()) .map(|r| *r.inner.class_hash()) }), + init_calldata: contract.inner.init_calldata.clone(), } }) .collect::>(); @@ -91,6 +96,7 @@ impl WorldDiff { original_class_hash: *local.world.inner.original_class_hash(), base_class_hash: *local.base.inner.class_hash(), remote_class_hash: remote.map(|m| *m.world.inner.class_hash()), + init_calldata: vec![], }; WorldDiff { world, base, contracts, models } @@ -107,6 +113,63 @@ impl WorldDiff { count += self.contracts.iter().filter(|s| !s.is_same()).count(); count } + + pub fn update_order(&mut self) -> Result<()> { + let mut ts = TopologicalSort::<&str>::new(); + + // make the dependency graph by reading the constructor_calldata + for contract in self.contracts.iter() { + let curr_name: &str = &contract.name; + ts.insert(curr_name); + + for field in &contract.init_calldata { + if let Some(dependency) = field.strip_prefix("$contract_address:") { + ts.add_dependency(dependency, curr_name); + } else if let Some(dependency) = field.strip_prefix("$class_hash:") { + ts.add_dependency(dependency, curr_name); + } else { + // verify its a field element + match FieldElement::from_str(field) { + Ok(_) => continue, + Err(e) => bail!(format!( + "Expected init_calldata element to be a special variable (i.e. \ + starting with $contract_address or $class_hash) or be a \ + FieldElement. Failed with error: {e:?}" + )), + } + } + } + } + + let mut calculated_order = vec![]; + + while !ts.is_empty() { + let mut values = ts.pop_all(); + // if `ts` is not empty and `pop_all` returns an empty vector it means there is a cyclic + // dependency see: https://docs.rs/topological-sort/latest/topological_sort/struct.TopologicalSort.html#method.pop_all + if values.is_empty() { + bail!("Cyclic dependency detected in `init_calldata`"); + } + + values.sort(); + calculated_order.extend(values); + } + + let mut new_contracts = vec![]; + + for c_name in calculated_order { + let contract = match self.contracts.iter().find(|c| c.name == c_name) { + Some(c) => c, + None => bail!("Unidentified contract found in `init_calldata`"), + }; + + new_contracts.push(contract.clone()); + } + + mem::swap(&mut self.contracts, &mut new_contracts); + + Ok(()) + } } impl Display for WorldDiff { diff --git a/crates/dojo-world/src/migration/world_test.rs b/crates/dojo-world/src/migration/world_test.rs index be50a4044c..e84e1b2cec 100644 --- a/crates/dojo-world/src/migration/world_test.rs +++ b/crates/dojo-world/src/migration/world_test.rs @@ -105,3 +105,70 @@ fn diff_when_local_and_remote_are_different() { assert!(diff.models.iter().any(|m| m.name == "dojo_mock::models::model_2")); assert!(diff.contracts.iter().any(|c| c.name == "dojo_mock::contracts::my_contract")); } + +#[test] +fn updating_order_as_expected() { + let init_calldata = vec![ + ("c4", vec!["$contract_address:c1", "0x0"]), + ("c3", vec!["0x0"]), + ("c5", vec!["$contract_address:c4", "0x0"]), + ("c7", vec!["$contract_address:c4", "0x0"]), + ("c2", vec!["0x0"]), + ("c6", vec!["$contract_address:c4", "$contract_address:c3", "0x0"]), + ("c1", vec!["0x0"]), + ]; + + let mut contracts = vec![]; + for calldata in init_calldata { + contracts.push(ContractDiff { + init_calldata: calldata.1.iter().map(|c| c.to_string()).collect(), + name: calldata.0.to_string(), + ..Default::default() + }); + } + + let mut diff = WorldDiff { + world: ContractDiff::default(), + base: ClassDiff::default(), + contracts, + models: vec![], + }; + + diff.update_order().unwrap(); + + let expected_order = ["c1", "c2", "c3", "c4", "c5", "c6", "c7"]; + for (i, contract) in diff.contracts.iter().enumerate() { + assert_eq!(contract.name, expected_order[i]); + } +} + +#[test] +fn updating_order_when_cyclic_dependency_fail() { + let init_calldata = vec![ + ("c4", vec!["$contract_address:c1", "$contract_address:c6", "0x0"]), + ("c3", vec!["0x0"]), + ("c5", vec!["$contract_address:c4", "0x0"]), + ("c7", vec!["$contract_address:c4", "0x0"]), + ("c2", vec!["0x0"]), + ("c6", vec!["$contract_address:c4", "$contract_address:c3", "0x0"]), + ("c1", vec!["0x0"]), + ]; + + let mut contracts = vec![]; + for calldata in init_calldata { + contracts.push(ContractDiff { + init_calldata: calldata.1.iter().map(|c| c.to_string()).collect(), + name: calldata.0.to_string(), + ..Default::default() + }); + } + + let mut diff = WorldDiff { + world: ContractDiff::default(), + base: ClassDiff::default(), + contracts, + models: vec![], + }; + + assert!(diff.update_order().is_err_and(|e| e.to_string().contains("Cyclic"))); +} diff --git a/crates/sozo/ops/src/migration/migrate.rs b/crates/sozo/ops/src/migration/migrate.rs index 70a9eb1c74..044479a5d6 100644 --- a/crates/sozo/ops/src/migration/migrate.rs +++ b/crates/sozo/ops/src/migration/migrate.rs @@ -494,6 +494,7 @@ where contract.diff.base_class_hash, migrator, txn_config, + &contract.diff.init_calldata, ) .await { @@ -541,7 +542,10 @@ where } Err(e) => { ui.verbose(format!("{e:?}")); - return Err(anyhow!("Failed to migrate {name}: {e}")); + return Err(anyhow!( + "Failed to migrate {name}: {e}. Please also verify init calldata is valid, if \ + any." + )); } } } diff --git a/crates/sozo/ops/src/migration/mod.rs b/crates/sozo/ops/src/migration/mod.rs index e998b0fa36..85f58dfa44 100644 --- a/crates/sozo/ops/src/migration/mod.rs +++ b/crates/sozo/ops/src/migration/mod.rs @@ -83,7 +83,9 @@ where // Calculate diff between local and remote World manifests. ui.print_step(2, "🧰", "Evaluating Worlds diff..."); - let diff = WorldDiff::compute(local_manifest.clone(), remote_manifest.clone()); + let mut diff = WorldDiff::compute(local_manifest.clone(), remote_manifest.clone()); + diff.update_order()?; + let total_diffs = diff.count_diffs(); ui.print_sub(format!("Total diffs found: {total_diffs}")); @@ -94,6 +96,7 @@ where let mut strategy = prepare_migration(&target_dir, diff, name, world_address, &ui)?; let world_address = strategy.world_address().expect("world address must exist"); + strategy.resolve_variable(world_address)?; if dry_run { print_strategy(&ui, account.provider(), &strategy, world_address).await; diff --git a/crates/sozo/ops/src/model.rs b/crates/sozo/ops/src/model.rs index 21d206ab19..fd11cec2dd 100644 --- a/crates/sozo/ops/src/model.rs +++ b/crates/sozo/ops/src/model.rs @@ -1,10 +1,15 @@ use anyhow::Result; +use cainome::cairo_serde::{ByteArray, CairoSerde}; +use dojo_types::schema::Ty; use dojo_world::contracts::model::ModelReader; use dojo_world::contracts::world::WorldContractReader; use starknet::core::types::{BlockId, BlockTag, FieldElement}; +use starknet::core::utils::get_selector_from_name; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; +const INDENT: &str = " "; + pub async fn model_class_hash( name: String, world_address: FieldElement, @@ -35,6 +40,29 @@ pub async fn model_contract_address( Ok(()) } +pub async fn model_layout( + name: String, + world_address: FieldElement, + provider: JsonRpcClient, +) -> Result<()> { + let mut world_reader = WorldContractReader::new(world_address, &provider); + world_reader.set_block(BlockId::Tag(BlockTag::Pending)); + + let model = world_reader.model_reader(&name).await?; + let layout = match model.layout().await { + Ok(x) => x, + Err(_) => anyhow::bail!( + "[Incorrect layout]\nThe model is packed but contains at least one custom type field \ + which is not packed.\nPlease check your model to fix this." + ), + }; + let schema = model.schema().await?; + + deep_print_layout(&name, &layout, &schema); + + Ok(()) +} + pub async fn model_schema( name: String, world_address: FieldElement, @@ -50,7 +78,7 @@ pub async fn model_schema( if to_json { println!("{}", serde_json::to_string_pretty(&schema)?) } else { - println!("{schema}"); + deep_print_ty(schema); } Ok(()) @@ -66,9 +94,560 @@ pub async fn model_get( world_reader.set_block(BlockId::Tag(BlockTag::Pending)); let model = world_reader.model_reader(&name).await?; - let entity = model.entity(&keys).await?; + let schema = model.schema().await?; + let values = model.entity_storage(&keys).await?; - println!("{entity}"); + deep_print_record(&schema, &keys, &values); Ok(()) } + +#[derive(Clone, Debug)] +struct LayoutInfo { + layout_type: LayoutInfoType, + name: String, + fields: Vec, +} + +#[derive(Clone, Debug)] +enum LayoutInfoType { + Struct, + Enum, + Tuple, + Array, +} + +#[derive(Clone, Debug)] +struct FieldLayoutInfo { + selector: String, + name: String, + layout: String, +} + +fn format_fixed(layout: &[u8]) -> String { + format!("[{}]", layout.iter().map(|x| x.to_string()).collect::>().join(", ")) +} + +fn format_layout_ref(type_name: &str) -> String { + format!("layout({type_name})") +} + +fn format_selector(selector: String) -> String { + if selector.starts_with("0x") { format!("[{}]", selector) } else { selector } +} + +fn format_name(name: String) -> String { + if !name.is_empty() { format!(" {} ", name) } else { name } +} + +fn format_field(selector: String, name: String, layout: String) -> String { + let layout = if layout.eq("[]") { "".to_string() } else { format!(": {layout}") }; + + format!("{INDENT}{:<20}{:<18}{}", format_selector(selector), format_name(name), layout) +} + +fn format_field_layout( + layout: &dojo_world::contracts::model::abigen::model::Layout, + schema: &dojo_types::schema::Ty, +) -> String { + match layout { + dojo_world::contracts::model::abigen::model::Layout::Fixed(x) => format_fixed(x), + dojo_world::contracts::model::abigen::model::Layout::ByteArray => { + "layout(ByteArray)".to_string() + } + _ => format_layout_ref(&get_name_from_schema(schema)), + } +} + +fn is_layout_in_list(list: &[LayoutInfo], name: &String) -> bool { + list.iter().any(|x| x.name.eq(name)) +} + +fn get_name_from_schema(schema: &dojo_types::schema::Ty) -> String { + match schema { + dojo_types::schema::Ty::Struct(s) => s.name.clone(), + dojo_types::schema::Ty::Enum(e) => e.name.clone(), + dojo_types::schema::Ty::Primitive(p) => match p { + dojo_types::primitive::Primitive::U8(_) => "u8".to_string(), + dojo_types::primitive::Primitive::U16(_) => "u16".to_string(), + dojo_types::primitive::Primitive::U32(_) => "u32".to_string(), + dojo_types::primitive::Primitive::U64(_) => "u64".to_string(), + dojo_types::primitive::Primitive::U128(_) => "u128".to_string(), + dojo_types::primitive::Primitive::U256(_) => "u256".to_string(), + dojo_types::primitive::Primitive::USize(_) => "usize".to_string(), + dojo_types::primitive::Primitive::Bool(_) => "bool".to_string(), + dojo_types::primitive::Primitive::Felt252(_) => "felt252".to_string(), + dojo_types::primitive::Primitive::ClassHash(_) => "ClassHash".to_string(), + dojo_types::primitive::Primitive::ContractAddress(_) => "ContractAddress".to_string(), + }, + dojo_types::schema::Ty::Tuple(t) => { + format!("({})", t.iter().map(get_name_from_schema).collect::>().join(", ")) + } + dojo_types::schema::Ty::Array(a) => format!("Array<{}>", get_name_from_schema(&a[0])), + _ => "".to_string(), + } +} + +fn get_printable_layout_list_from_struct( + field_layouts: &[dojo_world::contracts::model::abigen::model::FieldLayout], + schema: &dojo_types::schema::Ty, + layout_list: &mut Vec, +) { + if let dojo_types::schema::Ty::Struct(ss) = schema { + let name = get_name_from_schema(schema); + + // process main struct + if !is_layout_in_list(layout_list, &name) { + layout_list.push(LayoutInfo { + layout_type: LayoutInfoType::Struct, + name, + fields: field_layouts + .iter() + .zip(ss.children.iter().filter(|x| !x.key)) + .map(|(l, m)| FieldLayoutInfo { + selector: format!("{:#x}", l.selector), + name: m.name.clone(), + layout: format_field_layout(&l.layout, &m.ty), + }) + .collect::>(), + }); + } + + // process members + for (member_layout, member) in + field_layouts.iter().zip(ss.children.iter().filter(|x| !x.key)) + { + get_printable_layout_list(&member_layout.layout, &member.ty, layout_list); + } + }; +} + +fn get_printable_layout_list_from_enum( + field_layouts: &[dojo_world::contracts::model::abigen::model::FieldLayout], + schema: &dojo_types::schema::Ty, + layout_list: &mut Vec, +) { + if let dojo_types::schema::Ty::Enum(se) = schema { + let name = get_name_from_schema(schema); + + // proces main enum + if !is_layout_in_list(layout_list, &name) { + layout_list.push(LayoutInfo { + layout_type: LayoutInfoType::Enum, + name, + fields: field_layouts + .iter() + .zip(se.options.iter()) + .map(|(l, o)| FieldLayoutInfo { + selector: format!("{:#x}", l.selector), + name: o.name.to_string(), + layout: format_field_layout(&l.layout, &o.ty), + }) + .collect::>(), + }); + } + + // process variants + for (variant_layout, variant) in field_layouts.iter().zip(se.options.iter()) { + get_printable_layout_list(&variant_layout.layout, &variant.ty, layout_list); + } + } +} + +fn get_printable_layout_list_from_tuple( + item_layouts: &[dojo_world::contracts::model::abigen::model::Layout], + schema: &dojo_types::schema::Ty, + layout_list: &mut Vec, +) { + if let dojo_types::schema::Ty::Tuple(st) = schema { + let name = get_name_from_schema(schema); + + // process tuple + if !is_layout_in_list(layout_list, &name) { + layout_list.push(LayoutInfo { + layout_type: LayoutInfoType::Tuple, + name, + fields: item_layouts + .iter() + .enumerate() + .zip(st.iter()) + .map(|((i, l), s)| FieldLayoutInfo { + selector: format!("{:#x}", i), + name: "".to_string(), + layout: format_field_layout(l, s), + }) + .collect::>(), + }); + } + + // process tuple items + for (item_layout, item_schema) in item_layouts.iter().zip(st.iter()) { + get_printable_layout_list(item_layout, item_schema, layout_list); + } + } +} + +fn get_printable_layout_list_from_array( + item_layout: &dojo_world::contracts::model::abigen::model::Layout, + schema: &dojo_types::schema::Ty, + layout_list: &mut Vec, +) { + if let dojo_types::schema::Ty::Array(sa) = schema { + let name = get_name_from_schema(schema); + + // process array + if !is_layout_in_list(layout_list, &name) { + layout_list.push(LayoutInfo { + layout_type: LayoutInfoType::Array, + name, + fields: vec![FieldLayoutInfo { + selector: "[ItemIndex]".to_string(), + name: "".to_string(), + layout: format_field_layout(item_layout, &sa[0]), + }], + }); + } + + // process array item + get_printable_layout_list(item_layout, &sa[0], layout_list); + } +} + +fn get_printable_layout_list( + root_layout: &dojo_world::contracts::model::abigen::model::Layout, + schema: &dojo_types::schema::Ty, + layout_list: &mut Vec, +) { + match root_layout { + dojo_world::contracts::model::abigen::model::Layout::Struct(ls) => { + get_printable_layout_list_from_struct(ls, schema, layout_list); + } + dojo_world::contracts::model::abigen::model::Layout::Enum(le) => { + get_printable_layout_list_from_enum(le, schema, layout_list); + } + dojo_world::contracts::model::abigen::model::Layout::Tuple(lt) => { + get_printable_layout_list_from_tuple(lt, schema, layout_list); + } + dojo_world::contracts::model::abigen::model::Layout::Array(la) => { + get_printable_layout_list_from_array(&la[0], schema, layout_list); + } + _ => {} + }; +} + +fn print_layout_info(layout_info: LayoutInfo) { + let fields = layout_info + .fields + .into_iter() + .map(|f| format_field(f.selector, f.name, f.layout)) + .collect::>(); + let layout_title = match layout_info.layout_type { + LayoutInfoType::Struct => format!("Struct {} {{", layout_info.name), + + LayoutInfoType::Enum => { + format!("{:<42}: [251] (variant id)", format!("Enum {} {{", layout_info.name)) + } + LayoutInfoType::Tuple => format!("{} (", layout_info.name), + LayoutInfoType::Array => { + format!("{:<42}: [32] (length)", format!("{} (", layout_info.name)) + } + }; + let end_token = match layout_info.layout_type { + LayoutInfoType::Struct => '}', + LayoutInfoType::Enum => '}', + LayoutInfoType::Tuple => ')', + LayoutInfoType::Array => ')', + }; + + println!( + "{layout_title} +{} +{end_token}\n", + fields.join("\n") + ); +} + +// print the full Layout tree +fn deep_print_layout( + name: &String, + layout: &dojo_world::contracts::model::abigen::model::Layout, + schema: &dojo_types::schema::Ty, +) { + if let dojo_world::contracts::model::abigen::model::Layout::Fixed(lf) = layout { + println!("\n{} (packed)", name); + println!(" selector : {:#x}", get_selector_from_name(name).unwrap()); + println!(" layout : {}", format_fixed(lf)); + } else { + let mut layout_list = vec![]; + get_printable_layout_list(layout, schema, &mut layout_list); + + println!("\n{} selector: {:#x}\n", name, get_selector_from_name(name).unwrap()); + + for l in layout_list { + print_layout_info(l); + } + } +} + +fn _start_indent(level: usize, start_indent: bool) -> String { + if start_indent { INDENT.repeat(level) } else { "".to_string() } +} + +fn format_primitive( + p: &dojo_types::primitive::Primitive, + values: &mut Vec, + level: usize, + start_indent: bool, +) -> String { + let mut _p = *p; + let _ = _p.deserialize(values); + + format!("{}{}", _start_indent(level, start_indent), _p.to_sql_value().unwrap()) +} + +fn format_byte_array(values: &mut Vec, level: usize, start_indent: bool) -> String { + let bytearray = ByteArray::cairo_deserialize(values, 0).unwrap(); + values.drain(0..ByteArray::cairo_serialized_size(&bytearray)); + + format!("{}{}", _start_indent(level, start_indent), ByteArray::to_string(&bytearray).unwrap()) +} + +fn format_field_value( + member: &dojo_types::schema::Member, + values: &mut Vec, + level: usize, +) -> String { + let field_repr = format_record_value(&member.ty, values, level, false); + format!("{}{:<16}: {field_repr}", INDENT.repeat(level), member.name) +} + +fn format_array( + item: &dojo_types::schema::Ty, + values: &mut Vec, + level: usize, + start_indent: bool, +) -> String { + let length: u32 = values.remove(0).try_into().unwrap(); + let mut items = vec![]; + + for _ in 0..length { + items.push(format_record_value(item, values, level + 1, true)); + } + + format!( + "{}[\n{}\n{}]", + _start_indent(level, start_indent), + items.join(",\n"), + INDENT.repeat(level) + ) +} + +fn format_tuple( + items: &[dojo_types::schema::Ty], + values: &mut Vec, + level: usize, + start_indent: bool, +) -> String { + if items.is_empty() { + return "".to_string(); + } + + let items_repr = items + .iter() + .map(|x| format_record_value(x, values, level + 1, true)) + .collect::>() + .join(",\n"); + + format!("{}(\n{}\n{})", _start_indent(level, start_indent), items_repr, INDENT.repeat(level)) +} + +fn format_struct( + schema: &dojo_types::schema::Struct, + values: &mut Vec, + level: usize, + start_indent: bool, +) -> String { + let fields = schema + .children + .iter() + .map(|m| format_field_value(m, values, level + 1)) + .collect::>(); + + format!( + "{}{{\n{}\n{}}}", + _start_indent(level, start_indent), + fields.join(",\n"), + INDENT.repeat(level) + ) +} + +fn format_enum( + schema: &dojo_types::schema::Enum, + values: &mut Vec, + level: usize, + start_indent: bool, +) -> String { + let variant_index: u8 = values.remove(0).try_into().unwrap(); + let variant_index: usize = variant_index.into(); + let variant_name = format!("{}::{}", schema.name, schema.options[variant_index].name); + let variant_data = + format_record_value(&schema.options[variant_index].ty, values, level + 1, true); + + if variant_data.is_empty() { + format!("{}{variant_name}", _start_indent(level, start_indent),) + } else { + format!( + "{}{variant_name}(\n{}\n{})", + _start_indent(level, start_indent), + variant_data, + INDENT.repeat(level) + ) + } +} + +fn format_record_value( + schema: &dojo_types::schema::Ty, + values: &mut Vec, + level: usize, + start_indent: bool, +) -> String { + match schema { + dojo_types::schema::Ty::Primitive(p) => format_primitive(p, values, level, start_indent), + dojo_types::schema::Ty::ByteArray(_) => format_byte_array(values, level, start_indent), + dojo_types::schema::Ty::Struct(s) => format_struct(s, values, level, start_indent), + dojo_types::schema::Ty::Enum(e) => format_enum(e, values, level, start_indent), + dojo_types::schema::Ty::Array(a) => format_array(&a[0], values, level, start_indent), + dojo_types::schema::Ty::Tuple(t) => format_tuple(t, values, level, start_indent), + } +} + +// print the structured record values +fn deep_print_record( + schema: &dojo_types::schema::Ty, + keys: &[FieldElement], + values: &[FieldElement], +) { + let mut model_values = vec![]; + model_values.extend(keys); + model_values.extend(values); + + println!("{}", format_record_value(schema, &mut model_values, 0, true)); +} + +fn get_ty_repr(ty: &Ty) -> String { + match ty { + Ty::Primitive(p) => p.to_string(), + Ty::Struct(s) => s.name.clone(), + Ty::Enum(e) => e.name.clone(), + Ty::Tuple(items) => { + if items.is_empty() { + "".to_string() + } else { + format!("({},)", items.iter().map(get_ty_repr).collect::>().join(", ")) + } + } + Ty::Array(items) => format!("Array<{}>", get_ty_repr(&items[0])), + Ty::ByteArray(_) => "ByteArray".to_string(), + } +} + +// to verify if a Ty has already been processed (i.e is in the list), +// just compare their type representation. +fn is_ty_already_in_list(ty_list: &[Ty], ty: &Ty) -> bool { + let ty_repr = get_ty_repr(ty); + ty_list.iter().any(|t| get_ty_repr(t).eq(&ty_repr)) +} + +// parse the Ty tree from its root and extract Ty to print. +// (basically, structs and enums) +fn get_printable_ty_list(root_ty: &Ty, ty_list: &mut Vec) { + match root_ty { + Ty::Primitive(_) => {} + Ty::ByteArray(_) => {} + Ty::Struct(s) => { + if !is_ty_already_in_list(ty_list, root_ty) { + ty_list.push(root_ty.clone()); + } + + for member in &s.children { + if !is_ty_already_in_list(ty_list, &member.ty) { + get_printable_ty_list(&member.ty, ty_list); + } + } + } + Ty::Enum(e) => { + if !ty_list.contains(root_ty) { + ty_list.push(root_ty.clone()); + } + + for child in &e.options { + if !is_ty_already_in_list(ty_list, &child.ty) { + get_printable_ty_list(&child.ty, ty_list); + } + } + } + Ty::Tuple(tuple) => { + for item_ty in tuple { + if !is_ty_already_in_list(ty_list, item_ty) { + get_printable_ty_list(item_ty, ty_list); + } + } + } + Ty::Array(items_ty) => { + if !is_ty_already_in_list(ty_list, &items_ty[0]) { + get_printable_ty_list(&items_ty[0], ty_list) + } + } + }; +} + +pub fn format_ty_field(name: &String, ty: &Ty, is_key: bool) -> String { + let ty_repr = get_ty_repr(ty); + let ty_repr = if ty_repr.is_empty() { "".to_string() } else { format!(": {ty_repr}") }; + let key_repr = if is_key { " #[key]\n".to_string() } else { "".to_string() }; + + format! {"{key_repr} {name}{ty_repr}"} +} + +// print Ty representation if required. +// For example, there is no need to print any information about arrays or tuples +// as they are members of struct and their items will be printed. +pub fn print_ty(ty: &Ty) { + let ty_repr = match ty { + Ty::Struct(s) => { + let mut struct_str = format!("struct {} {{\n", s.name); + for member in &s.children { + struct_str.push_str(&format!( + "{},\n", + format_ty_field(&member.name, &member.ty, member.key) + )); + } + struct_str.push('}'); + Some(struct_str) + } + Ty::Enum(e) => { + let mut enum_str = format!("enum {} {{\n", e.name); + for child in &e.options { + enum_str + .push_str(&format!("{},\n", format_ty_field(&child.name, &child.ty, false))); + } + enum_str.push('}'); + Some(enum_str) + } + _ => None, + }; + + if let Some(ty_repr) = ty_repr { + println!("{}\n\n", ty_repr); + } +} + +// print the full Ty tree +pub fn deep_print_ty(root: Ty) { + let mut ty_list = vec![]; + get_printable_ty_list(&root, &mut ty_list); + + for ty in ty_list { + print_ty(&ty); + } +} diff --git a/crates/sozo/ops/src/tests/migration.rs b/crates/sozo/ops/src/tests/migration.rs index 45047fe345..708724a4c8 100644 --- a/crates/sozo/ops/src/tests/migration.rs +++ b/crates/sozo/ops/src/tests/migration.rs @@ -217,7 +217,8 @@ async fn migrate_with_auto_authorize() { let config = setup::load_config(); let ws = setup::setup_ws(&config); - let migration = setup::setup_migration(&config).unwrap(); + let mut migration = setup::setup_migration(&config).unwrap(); + migration.resolve_variable(migration.world_address().unwrap()).unwrap(); let manifest_base = config.manifest_path().parent().unwrap(); let mut manifest = diff --git a/crates/torii/core/Cargo.toml b/crates/torii/core/Cargo.toml index 6f99f9e213..cf1cb8cdd3 100644 --- a/crates/torii/core/Cargo.toml +++ b/crates/torii/core/Cargo.toml @@ -43,3 +43,4 @@ camino.workspace = true dojo-test-utils = { path = "../../dojo-test-utils" } scarb.workspace = true sozo = { path = "../../../bin/sozo" } +katana-runner.workspace = true diff --git a/crates/torii/core/src/sql_test.rs b/crates/torii/core/src/sql_test.rs index 1b9d0ea9f6..360e0a988c 100644 --- a/crates/torii/core/src/sql_test.rs +++ b/crates/torii/core/src/sql_test.rs @@ -3,12 +3,10 @@ use std::str::FromStr; use camino::Utf8PathBuf; use dojo_test_utils::compiler; use dojo_test_utils::migration::prepare_migration; -use dojo_test_utils::sequencer::{ - get_default_test_starknet_config, SequencerConfig, TestSequencer, -}; use dojo_world::contracts::world::WorldContractReader; use dojo_world::migration::TxnConfig; use dojo_world::utils::TransactionWaiter; +use katana_runner::KatanaRunner; use scarb::ops; use sozo_ops::migration::execute_strategy; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; @@ -68,16 +66,16 @@ async fn test_load_from_remote() { let base_dir = manifest_path.parent().unwrap(); let target_dir = format!("{}/target/dev", base_dir); - let migration = prepare_migration(base_dir.into(), target_dir.into()).unwrap(); + let mut migration = prepare_migration(base_dir.into(), target_dir.into()).unwrap(); + migration.resolve_variable(migration.world_address().unwrap()).unwrap(); - let sequencer = - TestSequencer::start(SequencerConfig::default(), get_default_test_starknet_config()).await; + let sequencer = KatanaRunner::new().expect("Failed to start runner."); let provider = JsonRpcClient::new(HttpTransport::new(sequencer.url())); let world = WorldContractReader::new(migration.world_address().unwrap(), &provider); - let mut account = sequencer.account(); + let mut account = sequencer.account(0); account.set_block_id(BlockId::Tag(BlockTag::Pending)); let ws = ops::read_workspace(config.manifest_path(), &config) @@ -114,7 +112,7 @@ async fn test_load_from_remote() { let _block_timestamp = 1710754478_u64; let models = sqlx::query("SELECT * FROM models").fetch_all(&pool).await.unwrap(); - assert_eq!(models.len(), 5); + assert_eq!(models.len(), 6); let (id, name, packed_size, unpacked_size): (String, String, u8, u8) = sqlx::query_as( "SELECT id, name, packed_size, unpacked_size FROM models WHERE name = 'Position'", @@ -125,7 +123,7 @@ async fn test_load_from_remote() { assert_eq!(id, format!("{:#x}", get_selector_from_name("Position").unwrap())); assert_eq!(name, "Position"); - assert_eq!(packed_size, 0); + assert_eq!(packed_size, 1); assert_eq!(unpacked_size, 2); let (id, name, packed_size, unpacked_size): (String, String, u8, u8) = sqlx::query_as( @@ -138,7 +136,7 @@ async fn test_load_from_remote() { assert_eq!(id, format!("{:#x}", get_selector_from_name("Moves").unwrap())); assert_eq!(name, "Moves"); assert_eq!(packed_size, 0); - assert_eq!(unpacked_size, 0); + assert_eq!(unpacked_size, 2); let (id, name, packed_size, unpacked_size): (String, String, u8, u8) = sqlx::query_as( "SELECT id, name, packed_size, unpacked_size FROM models WHERE name = 'PlayerConfig'", diff --git a/crates/torii/grpc/src/server/mod.rs b/crates/torii/grpc/src/server/mod.rs index b4fec0933e..3d0f8cbd81 100644 --- a/crates/torii/grpc/src/server/mod.rs +++ b/crates/torii/grpc/src/server/mod.rs @@ -132,7 +132,7 @@ impl DojoWorld { contract_address: model.3, packed_size: model.4, unpacked_size: model.5, - layout: hex::decode(&model.6).unwrap(), + layout: model.6.as_bytes().to_vec(), schema: serde_json::to_vec(&schema).unwrap(), }); } @@ -474,7 +474,7 @@ impl DojoWorld { .await?; let schema = self.model_cache.schema(&model).await?; - let layout = hex::decode(&layout).unwrap(); + let layout = layout.as_bytes().to_vec(); Ok(proto::types::ModelMetadata { name, diff --git a/crates/torii/grpc/src/server/tests/entities_test.rs b/crates/torii/grpc/src/server/tests/entities_test.rs index 9c9a871280..4776659558 100644 --- a/crates/torii/grpc/src/server/tests/entities_test.rs +++ b/crates/torii/grpc/src/server/tests/entities_test.rs @@ -36,7 +36,12 @@ async fn test_entities_queries() { sqlx::migrate!("../migrations").run(&pool).await.unwrap(); let base_path = "../../../examples/spawn-and-move"; let target_path = format!("{}/target/dev", base_path); - let migration = prepare_migration(base_path.into(), target_path.into()).unwrap(); + + let mut migration = prepare_migration(base_path.into(), target_path.into()).unwrap(); + migration.resolve_variable(migration.world_address().unwrap()).unwrap(); + + dbg!(&migration); + let sequencer = TestSequencer::start(SequencerConfig::default(), get_default_test_starknet_config()).await; let provider = Arc::new(JsonRpcClient::new(HttpTransport::new(sequencer.url()))); diff --git a/crates/torii/types-test/manifests/dev/base/dojo_world_world.toml b/crates/torii/types-test/manifests/dev/base/dojo_world_world.toml index 5a021e2793..2daf97b5e5 100644 --- a/crates/torii/types-test/manifests/dev/base/dojo_world_world.toml +++ b/crates/torii/types-test/manifests/dev/base/dojo_world_world.toml @@ -1,5 +1,5 @@ kind = "Class" -class_hash = "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0" -original_class_hash = "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0" +class_hash = "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092" +original_class_hash = "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092" abi = "manifests/dev/abis/base/dojo_world_world.json" name = "dojo::world::world" diff --git a/dojoup/dojoup b/dojoup/dojoup index 494a288576..bf9c0d999f 100755 --- a/dojoup/dojoup +++ b/dojoup/dojoup @@ -254,7 +254,7 @@ EOF } usage() { - cat 1>&2 <&2 <<'EOF' The installer for Dojo. Update or revert to a specific Dojo version with ease. diff --git a/examples/spawn-and-move/Scarb.toml b/examples/spawn-and-move/Scarb.toml index 87e2185eb9..99366ce45b 100644 --- a/examples/spawn-and-move/Scarb.toml +++ b/examples/spawn-and-move/Scarb.toml @@ -25,4 +25,4 @@ rpc_url = "http://localhost:5050/" # Default account for katana with seed = 0 account_address = "0x6162896d1d7ab204c7ccac6dd5f8e9e7c25ecd5ae4fcb4ad32e57786bb46e03" private_key = "0x1800000000300000180000000000030000000000003006001800006600" -world_address = "0x1c958955aedbc7b8e2f051767d3369168e88bc5074b0f39e5f8cd2539138281" +world_address = "0x26a8d9f2ac0348182bea206d913908ef77e439416713592ebc85941a69048d6" diff --git a/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_actions_actions.json b/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_actions_actions.json index 1e068262bb..21aed968a7 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_actions_actions.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_actions_actions.json @@ -210,6 +210,24 @@ } ] }, + { + "type": "impl", + "name": "IDojoInitImpl", + "interface_name": "dojo_examples::actions::actions::IDojoInit" + }, + { + "type": "interface", + "name": "dojo_examples::actions::actions::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [], + "outputs": [], + "state_mutability": "view" + } + ] + }, { "type": "impl", "name": "UpgradableImpl", diff --git a/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_others_others.json b/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_others_others.json new file mode 100644 index 0000000000..36d8c3ef78 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_others_others.json @@ -0,0 +1,146 @@ +[ + { + "type": "impl", + "name": "DojoResourceProviderImpl", + "interface_name": "dojo::world::IDojoResourceProvider" + }, + { + "type": "interface", + "name": "dojo::world::IDojoResourceProvider", + "items": [ + { + "type": "function", + "name": "dojo_resource", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "WorldProviderImpl", + "interface_name": "dojo::world::IWorldProvider" + }, + { + "type": "struct", + "name": "dojo::world::IWorldDispatcher", + "members": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "type": "interface", + "name": "dojo::world::IWorldProvider", + "items": [ + { + "type": "function", + "name": "world", + "inputs": [], + "outputs": [ + { + "type": "dojo::world::IWorldDispatcher" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "IDojoInitImpl", + "interface_name": "dojo_examples::others::others::IDojoInit" + }, + { + "type": "interface", + "name": "dojo_examples::others::others::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [ + { + "name": "actions_address", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "actions_class", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "value", + "type": "core::integer::u8" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "UpgradableImpl", + "interface_name": "dojo::components::upgradeable::IUpgradeable" + }, + { + "type": "interface", + "name": "dojo::components::upgradeable::IUpgradeable", + "items": [ + { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "new_class_hash", + "type": "core::starknet::class_hash::ClassHash" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "type": "event", + "name": "dojo::components::upgradeable::upgradeable::Upgraded", + "kind": "struct", + "members": [ + { + "name": "class_hash", + "type": "core::starknet::class_hash::ClassHash", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "dojo::components::upgradeable::upgradeable::Event", + "kind": "enum", + "variants": [ + { + "name": "Upgraded", + "type": "dojo::components::upgradeable::upgradeable::Upgraded", + "kind": "nested" + } + ] + }, + { + "type": "event", + "name": "dojo_examples::others::others::Event", + "kind": "enum", + "variants": [ + { + "name": "UpgradeableEvent", + "type": "dojo::components::upgradeable::upgradeable::Event", + "kind": "nested" + } + ] + } +] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/dev/abis/base/dojo_world_world.json b/examples/spawn-and-move/manifests/dev/abis/base/dojo_world_world.json index d800e7d191..63207ddec9 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/dojo_world_world.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/dojo_world_world.json @@ -205,6 +205,10 @@ { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "init_calldata", + "type": "core::array::Span::" } ], "outputs": [ diff --git a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_others_others_contract_initialized.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_others_others_contract_initialized.json new file mode 100644 index 0000000000..1dbbd313d8 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_others_others_contract_initialized.json @@ -0,0 +1,367 @@ +[ + { + "type": "impl", + "name": "DojoModelImpl", + "interface_name": "dojo::model::IModel" + }, + { + "type": "struct", + "name": "core::byte_array::ByteArray", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "type": "enum", + "name": "core::option::Option::", + "variants": [ + { + "name": "Some", + "type": "core::integer::u32" + }, + { + "name": "None", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::FieldLayout", + "members": [ + { + "name": "selector", + "type": "core::felt252" + }, + { + "name": "layout", + "type": "dojo::database::introspect::Layout" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::database::introspect::Layout", + "variants": [ + { + "name": "Fixed", + "type": "core::array::Span::" + }, + { + "name": "Struct", + "type": "core::array::Span::" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + }, + { + "name": "Enum", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Member", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "ty", + "type": "dojo::database::introspect::Ty" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Struct", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::<(core::felt252, dojo::database::introspect::Ty)>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::<(core::felt252, dojo::database::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Enum", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::<(core::felt252, dojo::database::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::database::introspect::Ty", + "variants": [ + { + "name": "Primitive", + "type": "core::felt252" + }, + { + "name": "Struct", + "type": "dojo::database::introspect::Struct" + }, + { + "name": "Enum", + "type": "dojo::database::introspect::Enum" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + } + ] + }, + { + "type": "interface", + "name": "dojo::model::IModel", + "items": [ + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "version", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u8" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "layout", + "inputs": [], + "outputs": [ + { + "type": "dojo::database::introspect::Layout" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "schema", + "inputs": [], + "outputs": [ + { + "type": "dojo::database::introspect::Ty" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "contract_initializedImpl", + "interface_name": "dojo_examples::others::others::Icontract_initialized" + }, + { + "type": "struct", + "name": "dojo_examples::others::others::ContractInitialized", + "members": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "contract_class", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "value", + "type": "core::integer::u8" + } + ] + }, + { + "type": "interface", + "name": "dojo_examples::others::others::Icontract_initialized", + "items": [ + { + "type": "function", + "name": "ensure_abi", + "inputs": [ + { + "name": "model", + "type": "dojo_examples::others::others::ContractInitialized" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "dojo_examples::others::others::contract_initialized::Event", + "kind": "enum", + "variants": [] + } +] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_actions_actions.json b/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_actions_actions.json index 1e068262bb..21aed968a7 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_actions_actions.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_actions_actions.json @@ -210,6 +210,24 @@ } ] }, + { + "type": "impl", + "name": "IDojoInitImpl", + "interface_name": "dojo_examples::actions::actions::IDojoInit" + }, + { + "type": "interface", + "name": "dojo_examples::actions::actions::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [], + "outputs": [], + "state_mutability": "view" + } + ] + }, { "type": "impl", "name": "UpgradableImpl", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_others_others.json b/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_others_others.json new file mode 100644 index 0000000000..36d8c3ef78 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_others_others.json @@ -0,0 +1,146 @@ +[ + { + "type": "impl", + "name": "DojoResourceProviderImpl", + "interface_name": "dojo::world::IDojoResourceProvider" + }, + { + "type": "interface", + "name": "dojo::world::IDojoResourceProvider", + "items": [ + { + "type": "function", + "name": "dojo_resource", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "WorldProviderImpl", + "interface_name": "dojo::world::IWorldProvider" + }, + { + "type": "struct", + "name": "dojo::world::IWorldDispatcher", + "members": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "type": "interface", + "name": "dojo::world::IWorldProvider", + "items": [ + { + "type": "function", + "name": "world", + "inputs": [], + "outputs": [ + { + "type": "dojo::world::IWorldDispatcher" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "IDojoInitImpl", + "interface_name": "dojo_examples::others::others::IDojoInit" + }, + { + "type": "interface", + "name": "dojo_examples::others::others::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [ + { + "name": "actions_address", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "actions_class", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "value", + "type": "core::integer::u8" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "UpgradableImpl", + "interface_name": "dojo::components::upgradeable::IUpgradeable" + }, + { + "type": "interface", + "name": "dojo::components::upgradeable::IUpgradeable", + "items": [ + { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "new_class_hash", + "type": "core::starknet::class_hash::ClassHash" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "type": "event", + "name": "dojo::components::upgradeable::upgradeable::Upgraded", + "kind": "struct", + "members": [ + { + "name": "class_hash", + "type": "core::starknet::class_hash::ClassHash", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "dojo::components::upgradeable::upgradeable::Event", + "kind": "enum", + "variants": [ + { + "name": "Upgraded", + "type": "dojo::components::upgradeable::upgradeable::Upgraded", + "kind": "nested" + } + ] + }, + { + "type": "event", + "name": "dojo_examples::others::others::Event", + "kind": "enum", + "variants": [ + { + "name": "UpgradeableEvent", + "type": "dojo::components::upgradeable::upgradeable::Event", + "kind": "nested" + } + ] + } +] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/dojo_world_world.json b/examples/spawn-and-move/manifests/dev/abis/deployments/dojo_world_world.json index d800e7d191..63207ddec9 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/dojo_world_world.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/dojo_world_world.json @@ -205,6 +205,10 @@ { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "init_calldata", + "type": "core::array::Span::" } ], "outputs": [ diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_others_others_contract_initialized.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_others_others_contract_initialized.json new file mode 100644 index 0000000000..1dbbd313d8 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_others_others_contract_initialized.json @@ -0,0 +1,367 @@ +[ + { + "type": "impl", + "name": "DojoModelImpl", + "interface_name": "dojo::model::IModel" + }, + { + "type": "struct", + "name": "core::byte_array::ByteArray", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "type": "enum", + "name": "core::option::Option::", + "variants": [ + { + "name": "Some", + "type": "core::integer::u32" + }, + { + "name": "None", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::FieldLayout", + "members": [ + { + "name": "selector", + "type": "core::felt252" + }, + { + "name": "layout", + "type": "dojo::database::introspect::Layout" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::database::introspect::Layout", + "variants": [ + { + "name": "Fixed", + "type": "core::array::Span::" + }, + { + "name": "Struct", + "type": "core::array::Span::" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + }, + { + "name": "Enum", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Member", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "ty", + "type": "dojo::database::introspect::Ty" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Struct", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::<(core::felt252, dojo::database::introspect::Ty)>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::<(core::felt252, dojo::database::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Enum", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::<(core::felt252, dojo::database::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::database::introspect::Ty", + "variants": [ + { + "name": "Primitive", + "type": "core::felt252" + }, + { + "name": "Struct", + "type": "dojo::database::introspect::Struct" + }, + { + "name": "Enum", + "type": "dojo::database::introspect::Enum" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + } + ] + }, + { + "type": "interface", + "name": "dojo::model::IModel", + "items": [ + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "version", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u8" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "layout", + "inputs": [], + "outputs": [ + { + "type": "dojo::database::introspect::Layout" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "schema", + "inputs": [], + "outputs": [ + { + "type": "dojo::database::introspect::Ty" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "contract_initializedImpl", + "interface_name": "dojo_examples::others::others::Icontract_initialized" + }, + { + "type": "struct", + "name": "dojo_examples::others::others::ContractInitialized", + "members": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "contract_class", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "value", + "type": "core::integer::u8" + } + ] + }, + { + "type": "interface", + "name": "dojo_examples::others::others::Icontract_initialized", + "items": [ + { + "type": "function", + "name": "ensure_abi", + "inputs": [ + { + "name": "model", + "type": "dojo_examples::others::others::ContractInitialized" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "dojo_examples::others::others::contract_initialized::Event", + "kind": "enum", + "variants": [] + } +] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_actions_actions.toml b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_actions_actions.toml index 1d97364950..5f84fa6760 100644 --- a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_actions_actions.toml +++ b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_actions_actions.toml @@ -1,9 +1,10 @@ kind = "DojoContract" -class_hash = "0x5b617d120767e91d40621dd939b092f48975a8fa1c5236ac68f97a4ffaf45b" -original_class_hash = "0x5b617d120767e91d40621dd939b092f48975a8fa1c5236ac68f97a4ffaf45b" +class_hash = "0xfe09a42928890d712af285559b4452ebbc448428dfa91179bfee66715a2add" +original_class_hash = "0xfe09a42928890d712af285559b4452ebbc448428dfa91179bfee66715a2add" base_class_hash = "0x0" abi = "manifests/dev/abis/base/contracts/dojo_examples_actions_actions.json" reads = [] writes = [] computed = [] +init_calldata = [] name = "dojo_examples::actions::actions" diff --git a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_others_others.toml b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_others_others.toml new file mode 100644 index 0000000000..0526bc5473 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_others_others.toml @@ -0,0 +1,10 @@ +kind = "DojoContract" +class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" +original_class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" +base_class_hash = "0x0" +abi = "manifests/dev/abis/base/contracts/dojo_examples_others_others.json" +reads = [] +writes = [] +computed = [] +init_calldata = [] +name = "dojo_examples::others::others" diff --git a/examples/spawn-and-move/manifests/dev/base/dojo_world_world.toml b/examples/spawn-and-move/manifests/dev/base/dojo_world_world.toml index 5a021e2793..2daf97b5e5 100644 --- a/examples/spawn-and-move/manifests/dev/base/dojo_world_world.toml +++ b/examples/spawn-and-move/manifests/dev/base/dojo_world_world.toml @@ -1,5 +1,5 @@ kind = "Class" -class_hash = "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0" -original_class_hash = "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0" +class_hash = "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092" +original_class_hash = "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092" abi = "manifests/dev/abis/base/dojo_world_world.json" name = "dojo::world::world" diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_actions_actions_moved.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_actions_actions_moved.toml index 1c55c2930f..a9e0f20f78 100644 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_actions_actions_moved.toml +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_actions_actions_moved.toml @@ -1,6 +1,6 @@ kind = "DojoModel" -class_hash = "0x5508ab47983d4842a780fe483cb9ba5d24ad4b8d0196f767cd5983398b9f4c4" -original_class_hash = "0x5508ab47983d4842a780fe483cb9ba5d24ad4b8d0196f767cd5983398b9f4c4" +class_hash = "0x6f85952ceeb7783fb265a4b2d235db48e7a36357bbce7a56997ca5798c95187" +original_class_hash = "0x6f85952ceeb7783fb265a4b2d235db48e7a36357bbce7a56997ca5798c95187" abi = "manifests/dev/abis/base/models/dojo_examples_actions_actions_moved.json" name = "dojo_examples::actions::actions::moved" diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_emote_message.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_emote_message.toml index c408805a96..668a594127 100644 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_emote_message.toml +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_emote_message.toml @@ -1,6 +1,6 @@ kind = "DojoModel" -class_hash = "0x3c690e6a69960642e2e276299c04ee4eb57f8dabb0f59dc96b09faf39c82a9" -original_class_hash = "0x3c690e6a69960642e2e276299c04ee4eb57f8dabb0f59dc96b09faf39c82a9" +class_hash = "0x3e8f99f02409b7bc3dfffba58ee3807e3fb3513a2dcac1b0bd1f1118ea79ecc" +original_class_hash = "0x3e8f99f02409b7bc3dfffba58ee3807e3fb3513a2dcac1b0bd1f1118ea79ecc" abi = "manifests/dev/abis/base/models/dojo_examples_models_emote_message.json" name = "dojo_examples::models::emote_message" diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_moves.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_moves.toml index 4b5c015a39..db94e2ff11 100644 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_moves.toml +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_moves.toml @@ -1,6 +1,6 @@ kind = "DojoModel" -class_hash = "0x6eeffc6c72945b6ef419d3c67ed377408437782fdc41fa7a52339cd30d6c563" -original_class_hash = "0x6eeffc6c72945b6ef419d3c67ed377408437782fdc41fa7a52339cd30d6c563" +class_hash = "0x402d1ee5171aac681d16fa8c248a0498678082152e0f4c416776a71fc270684" +original_class_hash = "0x402d1ee5171aac681d16fa8c248a0498678082152e0f4c416776a71fc270684" abi = "manifests/dev/abis/base/models/dojo_examples_models_moves.json" name = "dojo_examples::models::moves" diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_player_config.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_player_config.toml index a285a51830..bbaa577a1c 100644 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_player_config.toml +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_player_config.toml @@ -1,6 +1,6 @@ kind = "DojoModel" -class_hash = "0x74e835af876c9f95977537b91d60d656f6ff2a4a8b2bb8d47448f345980f612" -original_class_hash = "0x74e835af876c9f95977537b91d60d656f6ff2a4a8b2bb8d47448f345980f612" +class_hash = "0x596f2ea7f78e3828f6daa798a271ef0a98741967afa40e680c2d80879be7d09" +original_class_hash = "0x596f2ea7f78e3828f6daa798a271ef0a98741967afa40e680c2d80879be7d09" abi = "manifests/dev/abis/base/models/dojo_examples_models_player_config.json" name = "dojo_examples::models::player_config" diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_position.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_position.toml index 88609b7a59..5bcac0ef22 100644 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_position.toml +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_position.toml @@ -1,6 +1,6 @@ kind = "DojoModel" -class_hash = "0x3c3632f38ab3ba550bd3c596e2af55002d43bc76b7b660a3a57b49795307c58" -original_class_hash = "0x3c3632f38ab3ba550bd3c596e2af55002d43bc76b7b660a3a57b49795307c58" +class_hash = "0x3ab26e88be7885877f93964880ccb63a8acd8e58f941c48bef52f191fa79868" +original_class_hash = "0x3ab26e88be7885877f93964880ccb63a8acd8e58f941c48bef52f191fa79868" abi = "manifests/dev/abis/base/models/dojo_examples_models_position.json" name = "dojo_examples::models::position" diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_others_others_contract_initialized.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_others_others_contract_initialized.toml new file mode 100644 index 0000000000..f267d02139 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_others_others_contract_initialized.toml @@ -0,0 +1,20 @@ +kind = "DojoModel" +class_hash = "0x6b54d690f4f43e3011fe05fabf3e5f1807b3a026430eb175534336e9a90d5a5" +original_class_hash = "0x6b54d690f4f43e3011fe05fabf3e5f1807b3a026430eb175534336e9a90d5a5" +abi = "manifests/dev/abis/base/models/dojo_examples_others_others_contract_initialized.json" +name = "dojo_examples::others::others::contract_initialized" + +[[members]] +name = "contract_address" +type = "ContractAddress" +key = true + +[[members]] +name = "contract_class" +type = "ClassHash" +key = false + +[[members]] +name = "value" +type = "u8" +key = false diff --git a/examples/spawn-and-move/manifests/dev/manifest.json b/examples/spawn-and-move/manifests/dev/manifest.json index 727658b2bc..00db8977b0 100644 --- a/examples/spawn-and-move/manifests/dev/manifest.json +++ b/examples/spawn-and-move/manifests/dev/manifest.json @@ -1,8 +1,8 @@ { "world": { "kind": "WorldContract", - "class_hash": "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0", - "original_class_hash": "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0", + "class_hash": "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092", + "original_class_hash": "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092", "abi": [ { "type": "impl", @@ -210,6 +210,10 @@ { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "init_calldata", + "type": "core::array::Span::" } ], "outputs": [ @@ -951,8 +955,8 @@ ] } ], - "address": "0x1c958955aedbc7b8e2f051767d3369168e88bc5074b0f39e5f8cd2539138281", - "transaction_hash": "0x703e38b6957635cccc0f9ddddd43356025f260de7f3593523157838e4443281", + "address": "0x26a8d9f2ac0348182bea206d913908ef77e439416713592ebc85941a69048d6", + "transaction_hash": "0x4fb663d83b07373bf2844ef51acfb10d1e957a2fd6d573b7360ad64710df6cc", "block_number": 3, "seed": "dojo_examples", "metadata": { @@ -971,9 +975,9 @@ "contracts": [ { "kind": "DojoContract", - "address": "0x21d87b58131a6879752e3b658d658fe3a80a42d85228ba8aec5220c4a5c364c", - "class_hash": "0x5b617d120767e91d40621dd939b092f48975a8fa1c5236ac68f97a4ffaf45b", - "original_class_hash": "0x5b617d120767e91d40621dd939b092f48975a8fa1c5236ac68f97a4ffaf45b", + "address": "0x43ec1c3a2195bc6adaa4134b7183cec4bae46f1388cb41c0fc808aba7e04b0", + "class_hash": "0xfe09a42928890d712af285559b4452ebbc448428dfa91179bfee66715a2add", + "original_class_hash": "0xfe09a42928890d712af285559b4452ebbc448428dfa91179bfee66715a2add", "base_class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", "abi": [ { @@ -1187,6 +1191,24 @@ } ] }, + { + "type": "impl", + "name": "IDojoInitImpl", + "interface_name": "dojo_examples::actions::actions::IDojoInit" + }, + { + "type": "interface", + "name": "dojo_examples::actions::actions::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [], + "outputs": [], + "state_mutability": "view" + } + ] + }, { "type": "impl", "name": "UpgradableImpl", @@ -1253,7 +1275,170 @@ "Position" ], "computed": [], + "init_calldata": [], "name": "dojo_examples::actions::actions" + }, + { + "kind": "DojoContract", + "address": "0x55d1b3b80ddfd912e33a6cb331665dfc0b8f1738fcbe8d4c4191e27cd12f9c4", + "class_hash": "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a", + "original_class_hash": "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a", + "base_class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", + "abi": [ + { + "type": "impl", + "name": "DojoResourceProviderImpl", + "interface_name": "dojo::world::IDojoResourceProvider" + }, + { + "type": "interface", + "name": "dojo::world::IDojoResourceProvider", + "items": [ + { + "type": "function", + "name": "dojo_resource", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "WorldProviderImpl", + "interface_name": "dojo::world::IWorldProvider" + }, + { + "type": "struct", + "name": "dojo::world::IWorldDispatcher", + "members": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "type": "interface", + "name": "dojo::world::IWorldProvider", + "items": [ + { + "type": "function", + "name": "world", + "inputs": [], + "outputs": [ + { + "type": "dojo::world::IWorldDispatcher" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "IDojoInitImpl", + "interface_name": "dojo_examples::others::others::IDojoInit" + }, + { + "type": "interface", + "name": "dojo_examples::others::others::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [ + { + "name": "actions_address", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "actions_class", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "value", + "type": "core::integer::u8" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "UpgradableImpl", + "interface_name": "dojo::components::upgradeable::IUpgradeable" + }, + { + "type": "interface", + "name": "dojo::components::upgradeable::IUpgradeable", + "items": [ + { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "new_class_hash", + "type": "core::starknet::class_hash::ClassHash" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "type": "event", + "name": "dojo::components::upgradeable::upgradeable::Upgraded", + "kind": "struct", + "members": [ + { + "name": "class_hash", + "type": "core::starknet::class_hash::ClassHash", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "dojo::components::upgradeable::upgradeable::Event", + "kind": "enum", + "variants": [ + { + "name": "Upgraded", + "type": "dojo::components::upgradeable::upgradeable::Upgraded", + "kind": "nested" + } + ] + }, + { + "type": "event", + "name": "dojo_examples::others::others::Event", + "kind": "enum", + "variants": [ + { + "name": "UpgradeableEvent", + "type": "dojo::components::upgradeable::upgradeable::Event", + "kind": "nested" + } + ] + } + ], + "reads": [], + "writes": [], + "computed": [], + "init_calldata": [ + "$contract_address:dojo_examples::actions::actions", + "$class_hash:dojo_examples::actions::actions", + "10" + ], + "name": "dojo_examples::others::others" } ], "models": [ @@ -1271,8 +1456,8 @@ "key": false } ], - "class_hash": "0x5508ab47983d4842a780fe483cb9ba5d24ad4b8d0196f767cd5983398b9f4c4", - "original_class_hash": "0x5508ab47983d4842a780fe483cb9ba5d24ad4b8d0196f767cd5983398b9f4c4", + "class_hash": "0x6f85952ceeb7783fb265a4b2d235db48e7a36357bbce7a56997ca5798c95187", + "original_class_hash": "0x6f85952ceeb7783fb265a4b2d235db48e7a36357bbce7a56997ca5798c95187", "abi": [ { "type": "impl", @@ -1678,8 +1863,8 @@ "key": false } ], - "class_hash": "0x3c690e6a69960642e2e276299c04ee4eb57f8dabb0f59dc96b09faf39c82a9", - "original_class_hash": "0x3c690e6a69960642e2e276299c04ee4eb57f8dabb0f59dc96b09faf39c82a9", + "class_hash": "0x3e8f99f02409b7bc3dfffba58ee3807e3fb3513a2dcac1b0bd1f1118ea79ecc", + "original_class_hash": "0x3e8f99f02409b7bc3dfffba58ee3807e3fb3513a2dcac1b0bd1f1118ea79ecc", "abi": [ { "type": "impl", @@ -2090,8 +2275,8 @@ "key": false } ], - "class_hash": "0x6eeffc6c72945b6ef419d3c67ed377408437782fdc41fa7a52339cd30d6c563", - "original_class_hash": "0x6eeffc6c72945b6ef419d3c67ed377408437782fdc41fa7a52339cd30d6c563", + "class_hash": "0x402d1ee5171aac681d16fa8c248a0498678082152e0f4c416776a71fc270684", + "original_class_hash": "0x402d1ee5171aac681d16fa8c248a0498678082152e0f4c416776a71fc270684", "abi": [ { "type": "impl", @@ -2511,8 +2696,8 @@ "key": false } ], - "class_hash": "0x74e835af876c9f95977537b91d60d656f6ff2a4a8b2bb8d47448f345980f612", - "original_class_hash": "0x74e835af876c9f95977537b91d60d656f6ff2a4a8b2bb8d47448f345980f612", + "class_hash": "0x596f2ea7f78e3828f6daa798a271ef0a98741967afa40e680c2d80879be7d09", + "original_class_hash": "0x596f2ea7f78e3828f6daa798a271ef0a98741967afa40e680c2d80879be7d09", "abi": [ { "type": "impl", @@ -2914,8 +3099,8 @@ "key": false } ], - "class_hash": "0x3c3632f38ab3ba550bd3c596e2af55002d43bc76b7b660a3a57b49795307c58", - "original_class_hash": "0x3c3632f38ab3ba550bd3c596e2af55002d43bc76b7b660a3a57b49795307c58", + "class_hash": "0x3ab26e88be7885877f93964880ccb63a8acd8e58f941c48bef52f191fa79868", + "original_class_hash": "0x3ab26e88be7885877f93964880ccb63a8acd8e58f941c48bef52f191fa79868", "abi": [ { "type": "impl", @@ -3294,6 +3479,396 @@ } ], "name": "dojo_examples::models::position" + }, + { + "kind": "DojoModel", + "members": [ + { + "name": "contract_address", + "type": "ContractAddress", + "key": true + }, + { + "name": "contract_class", + "type": "ClassHash", + "key": false + }, + { + "name": "value", + "type": "u8", + "key": false + } + ], + "class_hash": "0x6b54d690f4f43e3011fe05fabf3e5f1807b3a026430eb175534336e9a90d5a5", + "original_class_hash": "0x6b54d690f4f43e3011fe05fabf3e5f1807b3a026430eb175534336e9a90d5a5", + "abi": [ + { + "type": "impl", + "name": "DojoModelImpl", + "interface_name": "dojo::model::IModel" + }, + { + "type": "struct", + "name": "core::byte_array::ByteArray", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "type": "enum", + "name": "core::option::Option::", + "variants": [ + { + "name": "Some", + "type": "core::integer::u32" + }, + { + "name": "None", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::FieldLayout", + "members": [ + { + "name": "selector", + "type": "core::felt252" + }, + { + "name": "layout", + "type": "dojo::database::introspect::Layout" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::database::introspect::Layout", + "variants": [ + { + "name": "Fixed", + "type": "core::array::Span::" + }, + { + "name": "Struct", + "type": "core::array::Span::" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + }, + { + "name": "Enum", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Member", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "ty", + "type": "dojo::database::introspect::Ty" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Struct", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::<(core::felt252, dojo::database::introspect::Ty)>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::<(core::felt252, dojo::database::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Enum", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::<(core::felt252, dojo::database::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::database::introspect::Ty", + "variants": [ + { + "name": "Primitive", + "type": "core::felt252" + }, + { + "name": "Struct", + "type": "dojo::database::introspect::Struct" + }, + { + "name": "Enum", + "type": "dojo::database::introspect::Enum" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + } + ] + }, + { + "type": "interface", + "name": "dojo::model::IModel", + "items": [ + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "version", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u8" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "layout", + "inputs": [], + "outputs": [ + { + "type": "dojo::database::introspect::Layout" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "schema", + "inputs": [], + "outputs": [ + { + "type": "dojo::database::introspect::Ty" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "contract_initializedImpl", + "interface_name": "dojo_examples::others::others::Icontract_initialized" + }, + { + "type": "struct", + "name": "dojo_examples::others::others::ContractInitialized", + "members": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "contract_class", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "value", + "type": "core::integer::u8" + } + ] + }, + { + "type": "interface", + "name": "dojo_examples::others::others::Icontract_initialized", + "items": [ + { + "type": "function", + "name": "ensure_abi", + "inputs": [ + { + "name": "model", + "type": "dojo_examples::others::others::ContractInitialized" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "dojo_examples::others::others::contract_initialized::Event", + "kind": "enum", + "variants": [] + } + ], + "name": "dojo_examples::others::others::contract_initialized" } ] } \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/dev/manifest.toml b/examples/spawn-and-move/manifests/dev/manifest.toml index e3324ca12e..66680bf397 100644 --- a/examples/spawn-and-move/manifests/dev/manifest.toml +++ b/examples/spawn-and-move/manifests/dev/manifest.toml @@ -1,10 +1,10 @@ [world] kind = "WorldContract" -class_hash = "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0" -original_class_hash = "0x64728e0c0713811c751930f8d3292d683c23f107c89b0a101425d9e80adb1c0" +class_hash = "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092" +original_class_hash = "0x1fafbe78a4676c8998d2de2053bc2fba24aebbd4fb86f03ff6af87ad3314092" abi = "manifests/dev/abis/deployments/dojo_world_world.json" -address = "0x1c958955aedbc7b8e2f051767d3369168e88bc5074b0f39e5f8cd2539138281" -transaction_hash = "0x703e38b6957635cccc0f9ddddd43356025f260de7f3593523157838e4443281" +address = "0x26a8d9f2ac0348182bea206d913908ef77e439416713592ebc85941a69048d6" +transaction_hash = "0x4fb663d83b07373bf2844ef51acfb10d1e957a2fd6d573b7360ad64710df6cc" block_number = 3 seed = "dojo_examples" name = "dojo::world::world" @@ -21,9 +21,9 @@ name = "dojo::base::base" [[contracts]] kind = "DojoContract" -address = "0x21d87b58131a6879752e3b658d658fe3a80a42d85228ba8aec5220c4a5c364c" -class_hash = "0x5b617d120767e91d40621dd939b092f48975a8fa1c5236ac68f97a4ffaf45b" -original_class_hash = "0x5b617d120767e91d40621dd939b092f48975a8fa1c5236ac68f97a4ffaf45b" +address = "0x43ec1c3a2195bc6adaa4134b7183cec4bae46f1388cb41c0fc808aba7e04b0" +class_hash = "0xfe09a42928890d712af285559b4452ebbc448428dfa91179bfee66715a2add" +original_class_hash = "0xfe09a42928890d712af285559b4452ebbc448428dfa91179bfee66715a2add" base_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" abi = "manifests/dev/abis/deployments/contracts/dojo_examples_actions_actions.json" reads = [] @@ -32,12 +32,30 @@ writes = [ "Position", ] computed = [] +init_calldata = [] name = "dojo_examples::actions::actions" +[[contracts]] +kind = "DojoContract" +address = "0x55d1b3b80ddfd912e33a6cb331665dfc0b8f1738fcbe8d4c4191e27cd12f9c4" +class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" +original_class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" +base_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" +abi = "manifests/dev/abis/deployments/contracts/dojo_examples_others_others.json" +reads = [] +writes = [] +computed = [] +init_calldata = [ + "$contract_address:dojo_examples::actions::actions", + "$class_hash:dojo_examples::actions::actions", + "10", +] +name = "dojo_examples::others::others" + [[models]] kind = "DojoModel" -class_hash = "0x5508ab47983d4842a780fe483cb9ba5d24ad4b8d0196f767cd5983398b9f4c4" -original_class_hash = "0x5508ab47983d4842a780fe483cb9ba5d24ad4b8d0196f767cd5983398b9f4c4" +class_hash = "0x6f85952ceeb7783fb265a4b2d235db48e7a36357bbce7a56997ca5798c95187" +original_class_hash = "0x6f85952ceeb7783fb265a4b2d235db48e7a36357bbce7a56997ca5798c95187" abi = "manifests/dev/abis/deployments/models/dojo_examples_actions_actions_moved.json" name = "dojo_examples::actions::actions::moved" @@ -53,8 +71,8 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x3c690e6a69960642e2e276299c04ee4eb57f8dabb0f59dc96b09faf39c82a9" -original_class_hash = "0x3c690e6a69960642e2e276299c04ee4eb57f8dabb0f59dc96b09faf39c82a9" +class_hash = "0x3e8f99f02409b7bc3dfffba58ee3807e3fb3513a2dcac1b0bd1f1118ea79ecc" +original_class_hash = "0x3e8f99f02409b7bc3dfffba58ee3807e3fb3513a2dcac1b0bd1f1118ea79ecc" abi = "manifests/dev/abis/deployments/models/dojo_examples_models_emote_message.json" name = "dojo_examples::models::emote_message" @@ -70,8 +88,8 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x6eeffc6c72945b6ef419d3c67ed377408437782fdc41fa7a52339cd30d6c563" -original_class_hash = "0x6eeffc6c72945b6ef419d3c67ed377408437782fdc41fa7a52339cd30d6c563" +class_hash = "0x402d1ee5171aac681d16fa8c248a0498678082152e0f4c416776a71fc270684" +original_class_hash = "0x402d1ee5171aac681d16fa8c248a0498678082152e0f4c416776a71fc270684" abi = "manifests/dev/abis/deployments/models/dojo_examples_models_moves.json" name = "dojo_examples::models::moves" @@ -92,8 +110,8 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x74e835af876c9f95977537b91d60d656f6ff2a4a8b2bb8d47448f345980f612" -original_class_hash = "0x74e835af876c9f95977537b91d60d656f6ff2a4a8b2bb8d47448f345980f612" +class_hash = "0x596f2ea7f78e3828f6daa798a271ef0a98741967afa40e680c2d80879be7d09" +original_class_hash = "0x596f2ea7f78e3828f6daa798a271ef0a98741967afa40e680c2d80879be7d09" abi = "manifests/dev/abis/deployments/models/dojo_examples_models_player_config.json" name = "dojo_examples::models::player_config" @@ -119,8 +137,8 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x3c3632f38ab3ba550bd3c596e2af55002d43bc76b7b660a3a57b49795307c58" -original_class_hash = "0x3c3632f38ab3ba550bd3c596e2af55002d43bc76b7b660a3a57b49795307c58" +class_hash = "0x3ab26e88be7885877f93964880ccb63a8acd8e58f941c48bef52f191fa79868" +original_class_hash = "0x3ab26e88be7885877f93964880ccb63a8acd8e58f941c48bef52f191fa79868" abi = "manifests/dev/abis/deployments/models/dojo_examples_models_position.json" name = "dojo_examples::models::position" @@ -133,3 +151,25 @@ key = true name = "vec" type = "Vec2" key = false + +[[models]] +kind = "DojoModel" +class_hash = "0x6b54d690f4f43e3011fe05fabf3e5f1807b3a026430eb175534336e9a90d5a5" +original_class_hash = "0x6b54d690f4f43e3011fe05fabf3e5f1807b3a026430eb175534336e9a90d5a5" +abi = "manifests/dev/abis/deployments/models/dojo_examples_others_others_contract_initialized.json" +name = "dojo_examples::others::others::contract_initialized" + +[[models.members]] +name = "contract_address" +type = "ContractAddress" +key = true + +[[models.members]] +name = "contract_class" +type = "ClassHash" +key = false + +[[models.members]] +name = "value" +type = "u8" +key = false 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 b18cfcaedb..a8e2fd4c2d 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 @@ -2,3 +2,5 @@ computed = [ ] name = "dojo_examples::actions::actions" 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 new file mode 100644 index 0000000000..b74df0c8fe --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_others_others.toml @@ -0,0 +1,5 @@ +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/src/actions.cairo b/examples/spawn-and-move/src/actions.cairo index 6b9156343b..65dc16c55c 100644 --- a/examples/spawn-and-move/src/actions.cairo +++ b/examples/spawn-and-move/src/actions.cairo @@ -123,7 +123,7 @@ mod tests { // deploy systems contract let contract_address = world - .deploy_contract('salt', actions::TEST_CLASS_HASH.try_into().unwrap()); + .deploy_contract('salt', actions::TEST_CLASS_HASH.try_into().unwrap(), array![].span()); let actions_system = IActionsDispatcher { contract_address }; // System calls diff --git a/examples/spawn-and-move/src/lib.cairo b/examples/spawn-and-move/src/lib.cairo index e3149f1627..44309e5fca 100644 --- a/examples/spawn-and-move/src/lib.cairo +++ b/examples/spawn-and-move/src/lib.cairo @@ -1,3 +1,4 @@ mod actions; mod models; mod utils; +mod others; diff --git a/examples/spawn-and-move/src/models.cairo b/examples/spawn-and-move/src/models.cairo index 635b9c312f..e04859bdf4 100644 --- a/examples/spawn-and-move/src/models.cairo +++ b/examples/spawn-and-move/src/models.cairo @@ -47,13 +47,17 @@ struct Moves { last_direction: Direction } -#[derive(Copy, Drop, Serde, Introspect)] +#[derive(Copy, Drop, Serde, IntrospectPacked)] struct Vec2 { x: u32, y: u32 } -#[derive(Copy, Drop, Serde)] +// If `Vec2` wasn't packed, the `Position` would be invalid, +// and a runtime error would be thrown. +// Any field that is a custom type into a `IntrospectPacked` type +// must be packed. +#[derive(Copy, Drop, Serde, IntrospectPacked)] #[dojo::model] struct Position { #[key] @@ -61,6 +65,8 @@ struct Position { vec: Vec2, } +// Every field inside a model must derive `Introspect` or `IntrospectPacked`. +// `IntrospectPacked` can also be used into models that are only using `Introspect`. #[derive(Copy, Drop, Serde, Introspect)] struct PlayerItem { item_id: u32, diff --git a/examples/spawn-and-move/src/others.cairo b/examples/spawn-and-move/src/others.cairo new file mode 100644 index 0000000000..0f27d036a8 --- /dev/null +++ b/examples/spawn-and-move/src/others.cairo @@ -0,0 +1,31 @@ +#[dojo::contract] +mod others { + use starknet::{ContractAddress, ClassHash, get_caller_address}; + use dojo_examples::models::{Position, Moves, Direction, Vec2}; + use dojo_examples::utils::next_position; + + #[derive(Copy, Drop, Serde)] + #[dojo::event] + #[dojo::model] + struct ContractInitialized { + #[key] + contract_address: ContractAddress, + contract_class: ClassHash, + value: u8, + } + + + fn dojo_init( + world: IWorldDispatcher, + actions_address: ContractAddress, + actions_class: ClassHash, + value: u8 + ) { + emit!( + world, + ContractInitialized { + contract_address: actions_address, contract_class: actions_class, value + } + ); + } +}