From ed9f0cedef1db52ff596beb88ab7fa82b055ea9e Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 6 Jul 2024 18:12:23 -0600 Subject: [PATCH] feat: namespaces (#2148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Namespace feature * Handle namespace for models and contracts (#2023) * fix(migrate): when `init_calldata` depends on contract that was already deployed (#2058) * fix(migrate): when `init_calldata` depends on contract that was already deployed * add tests * fix(ci): ensure katana runs in debian:bookworm-slim (#2073) * Ensure katana runs in debian:bookworm-slim * chore: switch to cartridge fork and adjust CI to 4 core to build Katana * chore: add old prover-sdk version to check CI fail * fix katana bin permissions * chore: add ensure-docker as requirement for big jobs * chore: point to http-prover using rustls-tls * dbg * wip * chore: bump to artifacts v4 * fix: typo * wip * fix: don't use path explicitely * wip * fix: fix artifacts managment * add ls * fix: use other path with checkout * fix: remove prints --------- Co-authored-by: glihm * [dojo-core]: update gas logs (#1877) dojo-core: update gas logs Co-authored-by: glihm * Prepare release: v0.7.1 (#2076) Co-authored-by: glihm * fix: allow the use of self and world in an exclusive manner (#2063) * fix: allow the use of self and world in an exclusive manner * fix: fmt * fix: cairo fmt * fix: ensure a warning is emitted with generate trait and world * fix: cairo fmt * Update devcontainer image: v0.7.1 (#2077) Co-authored-by: glihm * Namespace feature --------- Co-authored-by: lambda-0x <0xlambda@protonmail.com> Co-authored-by: Tarrence van As Co-authored-by: glihm Co-authored-by: notV4l <122404722+notV4l@users.noreply.github.com> Co-authored-by: glihm * fix: update manifests * fix: update manifests * fix: update world address in Scarb.toml * fix world_test * Namespace feature improvements (#2108) * standardize model/system interface for name/namespace/selectors * wip * Overlays (#2127) * load custom overlay manifests * update metadata management + some fixes * update sozo commands with namespace * fix: change tag separator to `-` (#2132) * wip * fix tests * update sozo auth command + auto-auth feature (#2134) * update sozo auth command + auto-auth feature * fix: fix some tests --------- Co-authored-by: glihm * replace {WORLD,BASE}_CONTRACT_NAME by {WORLD,BASE}_CONTRACT_TAG (#2136) * replace {WORLD,BASE}_CONTRACT_NAME by {WORLD,BASE}_CONTRACT_TAG * fix: ensure model selector correctly computed in tests * fix: wip on tests and use Katana Runner --------- Co-authored-by: glihm * fix(sozo): migration with seed and world address both provided (#2135) * initial commit * add seed to manifest and remove from cli * add overlays for dojo_init * make tests compile * fix some tests * fix lints * fix: use same name for seed tests --------- Co-authored-by: glihm * feat(torii-core): namespace in processor & db migration (#2130) * feat(torii-core): namespace in processor & db migration * refactor: selector logic and model readers for new namespace * feat: compute correct selector on model register * fix: contract address in event data * refactor: update proto for new namespace * feat: update torii client for new namespaces * refactor: cache and schema types * feat: rebase from main update to new types * feat: update grpc service to support namespaces * feat: add namespace to broker * feat: fix libp2p and update grpc subs for pattern on models * feat: finish up refactor on grpc * chore: revert changes on proto to use onlky model field * feat: first graphql support * fix: composite clauses & wrap up * fix: storage and tests * refactor: storage to use selector * fix: storage subscriptions * escape composite tbales * Overlays (#2127) * load custom overlay manifests * update metadata management + some fixes * update sozo commands with namespace * fix: change tag separator to `-` (#2132) * wip * fix tests * merge and remove print * fix: remove print * fix: fmt and clippy wip * fix: graphql tests * model tests * query test * fmt * fix: query * chore: graphql subscription tests * wip: fix testing * wip: wip on testing * fix: fix typo * fix: use correct model name * wip: testing * feat: add namespace to model object * fix: fix subscription tests * fix: fix tests * fix: fmt * fix: clippy * wip: latest torii test * fix: fmt * fix(torii-libp2p): validate namespace message * fix(torii-libp2p): escape table name * fmt --------- Co-authored-by: Rémy Baranx Co-authored-by: glihm * fix: fix compiler diags by rewriting the function with rewrite node (#2141) * fix: fix compiler diags by rewriting the function with rewrite node * fix: ensure dojo init also shows correct diagnostics * fix: fmt + clippy * fix: ensure impl is correctly impl with RewriteNode * fix: add missing mapping to interfaces * fix: ensure wasm compatibility * fix: test fixes for wasm32 changes (#2144) * fix: test fixes for wasm32 changes * fix: fix cairo tests * fix: clean subcommand (#2122) * fix: clean should only remove target/manifest file of particular profile by defaul * fix lints * fix formatting * fix: ensure every target is considered independently --------- Co-authored-by: glihm * fix: uncomment tests * fix: add more tests * fix: fix metadata test * fix: add tests and fix metadata * fix: attempt to fix flaky test with 1s delay --------- Co-authored-by: remy.baranx@gmail.com Co-authored-by: lambda-0x <0xlambda@protonmail.com> Co-authored-by: Tarrence van As Co-authored-by: notV4l <122404722+notV4l@users.noreply.github.com> Co-authored-by: glihm Co-authored-by: notV4l Co-authored-by: Larko <59736843+Larkooo@users.noreply.github.com> --- .vscode/launch.json | 15 + Cargo.lock | 15 + bin/sozo/src/commands/auth.rs | 230 +- bin/sozo/src/commands/build.rs | 5 +- bin/sozo/src/commands/call.rs | 17 +- bin/sozo/src/commands/clean.rs | 235 +- bin/sozo/src/commands/dev.rs | 17 +- bin/sozo/src/commands/events.rs | 6 +- bin/sozo/src/commands/execute.rs | 21 +- bin/sozo/src/commands/migrate.rs | 25 +- bin/sozo/src/commands/model.rs | 54 +- .../commands/options/account/controller.rs | 13 +- bin/sozo/src/commands/print_env.rs | 11 +- bin/sozo/src/utils.rs | 14 +- bin/sozo/tests/register_test.rs | 19 +- bin/sozo/tests/test_build.rs | 5 +- bin/sozo/tests/test_migrate.rs | 47 + .../contracts/src/tests/test_world.cairo | 2 +- crates/dojo-bindgen/src/lib.rs | 90 +- .../src/plugins/typescript/mod.rs | 23 +- .../src/plugins/typescript_v2/mod.rs | 60 +- crates/dojo-bindgen/src/plugins/unity/mod.rs | 18 +- crates/dojo-core/Scarb.lock | 4 +- crates/dojo-core/src/base_test.cairo | 38 +- crates/dojo-core/src/contract.cairo | 15 + crates/dojo-core/src/lib.cairo | 4 + crates/dojo-core/src/model.cairo | 25 +- crates/dojo-core/src/packing_test.cairo | 3 - crates/dojo-core/src/resource_metadata.cairo | 30 +- crates/dojo-core/src/test_utils.cairo | 5 +- crates/dojo-core/src/utils.cairo | 6 + crates/dojo-core/src/utils_test.cairo | 17 + crates/dojo-core/src/world.cairo | 500 ++- crates/dojo-core/src/world_test.cairo | 442 +- crates/dojo-lang/Cargo.toml | 5 +- crates/dojo-lang/src/compiler.rs | 313 +- crates/dojo-lang/src/compiler_test.rs | 5 +- crates/dojo-lang/src/contract.rs | 411 +- crates/dojo-lang/src/interface.rs | 53 +- crates/dojo-lang/src/lib.rs | 1 + .../compiler_cairo/Scarb.toml | 2 + ...v240.json => ccf-cairo_v240-8d921297.json} | 0 ...v260.json => ccf-cairo_v260-465ec7fe.json} | 0 .../manifests/dev/abis/base/dojo-base.json | 49 +- ...{dojo_world_world.json => dojo-world.json} | 117 +- ...v240.toml => ccf-cairo_v240-8d921297.toml} | 5 +- ...v260.toml => ccf-cairo_v260-465ec7fe.toml} | 5 +- .../manifests/dev/base/dojo-base.toml | 4 +- .../manifests/dev/base/dojo-world.toml | 6 + .../manifests/dev/base/dojo_world_world.toml | 5 - crates/dojo-lang/src/model.rs | 112 +- crates/dojo-lang/src/plugin.rs | 54 +- crates/dojo-lang/src/plugin_test_data/model | 1520 ++++++- crates/dojo-lang/src/plugin_test_data/system | 698 +-- crates/dojo-lang/src/utils.rs | 47 + crates/dojo-test-utils/build.rs | 16 +- crates/dojo-test-utils/src/compiler.rs | 21 +- crates/dojo-test-utils/src/migration.rs | 29 +- crates/dojo-types/src/lib.rs | 6 +- crates/dojo-types/src/schema.rs | 1 + crates/dojo-world/Cargo.toml | 5 +- crates/dojo-world/src/contracts/abi/world.rs | 117 +- crates/dojo-world/src/contracts/mod.rs | 1 + crates/dojo-world/src/contracts/model.rs | 39 +- crates/dojo-world/src/contracts/model_test.rs | 21 +- crates/dojo-world/src/contracts/naming.rs | 146 + crates/dojo-world/src/contracts/world.rs | 18 +- crates/dojo-world/src/contracts/world_test.rs | 55 +- .../dojo-world/src/manifest/manifest_test.rs | 336 +- crates/dojo-world/src/manifest/mod.rs | 314 +- crates/dojo-world/src/manifest/types.rs | 21 +- crates/dojo-world/src/metadata.rs | 144 +- crates/dojo-world/src/metadata_test.rs | 105 +- crates/dojo-world/src/migration/class.rs | 5 +- crates/dojo-world/src/migration/contract.rs | 5 +- crates/dojo-world/src/migration/mod.rs | 8 +- crates/dojo-world/src/migration/strategy.rs | 82 +- crates/dojo-world/src/migration/world.rs | 52 +- crates/dojo-world/src/migration/world_test.rs | 106 +- crates/dojo-world/src/utils.rs | 1 + crates/katana/core/tests/sequencer.rs | 9 +- crates/sozo/ops/Cargo.toml | 2 + crates/sozo/ops/src/auth.rs | 242 +- crates/sozo/ops/src/call.rs | 4 +- crates/sozo/ops/src/events.rs | 100 +- crates/sozo/ops/src/execute.rs | 4 +- crates/sozo/ops/src/migration/auto_auth.rs | 29 +- crates/sozo/ops/src/migration/migrate.rs | 158 +- crates/sozo/ops/src/migration/mod.rs | 205 +- crates/sozo/ops/src/migration/utils.rs | 17 +- crates/sozo/ops/src/model.rs | 22 +- crates/sozo/ops/src/register.rs | 12 +- crates/sozo/ops/src/tests/auth.rs | 54 +- crates/sozo/ops/src/tests/call.rs | 41 +- crates/sozo/ops/src/tests/migration.rs | 252 +- crates/sozo/ops/src/tests/setup.rs | 20 +- crates/sozo/ops/src/tests/utils.rs | 6 +- crates/sozo/ops/src/utils.rs | 23 +- crates/torii/client/src/client/error.rs | 5 +- crates/torii/client/src/client/mod.rs | 32 +- crates/torii/client/src/client/storage.rs | 35 +- .../torii/client/src/client/subscription.rs | 43 +- crates/torii/core/src/cache.rs | 44 +- crates/torii/core/src/error.rs | 2 + crates/torii/core/src/model.rs | 163 +- .../core/src/processors/event_message.rs | 2 +- .../core/src/processors/register_model.rs | 14 +- .../core/src/processors/store_del_record.rs | 2 +- .../core/src/processors/store_set_record.rs | 2 +- crates/torii/core/src/sql.rs | 83 +- crates/torii/core/src/sql_test.rs | 98 +- crates/torii/core/src/types.rs | 1 + crates/torii/graphql/Cargo.toml | 1 + crates/torii/graphql/src/object/entity.rs | 19 +- crates/torii/graphql/src/object/model.rs | 1 + crates/torii/graphql/src/object/model_data.rs | 17 +- crates/torii/graphql/src/query/data.rs | 6 +- crates/torii/graphql/src/query/mod.rs | 7 +- crates/torii/graphql/src/schema.rs | 21 +- .../torii/graphql/src/tests/entities_test.rs | 12 +- .../torii/graphql/src/tests/metadata_test.rs | 4 + crates/torii/graphql/src/tests/mod.rs | 58 +- crates/torii/graphql/src/tests/models_test.rs | 26 +- .../graphql/src/tests/subscription_test.rs | 34 +- crates/torii/graphql/src/utils.rs | 13 + crates/torii/grpc/Cargo.toml | 3 +- crates/torii/grpc/proto/types.proto | 46 +- crates/torii/grpc/src/server/mod.rs | 183 +- .../grpc/src/server/subscriptions/entity.rs | 29 +- .../src/server/subscriptions/event_message.rs | 29 +- .../src/server/subscriptions/model_diff.rs | 4 +- .../grpc/src/server/tests/entities_test.rs | 43 +- crates/torii/grpc/src/types/mod.rs | 14 +- crates/torii/grpc/src/types/schema.rs | 119 +- crates/torii/libp2p/Cargo.toml | 1 + crates/torii/libp2p/src/server/mod.rs | 31 +- crates/torii/libp2p/src/tests.rs | 30 +- .../20240702140704_model_namespace.sql | 3 + crates/torii/types-test/Scarb.toml | 10 +- .../manifests/dev/base/dojo_world_world.toml | 5 - examples/spawn-and-move/Scarb.toml | 6 +- ...on => dojo_examples-actions-40b6994c.json} | 88 +- .../dojo_examples-mock_token-31599eb2.json} | 91 +- .../dojo_examples-others-61de2c18.json} | 70 +- .../abis/base/dojo-base.json} | 49 +- ...{dojo_world_world.json => dojo-world.json} | 117 +- ...xamples-ContractInitialized-376b7bd6.json} | 33 + .../dojo_examples-Message-1bb1d226.json} | 33 + .../dojo_examples-MockToken-38903c7c.json} | 33 + .../models/dojo_examples-Moved-318ae40d.json} | 33 + ...json => dojo_examples-Moves-2e2accba.json} | 33 + .../dojo_examples-PlayerConfig-3adad785.json} | 33 + .../dojo_examples-Position-1e145e26.json} | 33 + ...dojo_examples-ServerProfile-4caad1e6.json} | 33 + ...on => dojo_examples-actions-40b6994c.json} | 88 +- .../dojo_examples-mock_token-31599eb2.json | 195 + .../dojo_examples-others-61de2c18.json} | 70 +- ...{dojo_world_world.json => dojo-world.json} | 117 +- ...xamples-ContractInitialized-376b7bd6.json} | 33 + ...on => dojo_examples-Message-1bb1d226.json} | 33 + .../dojo_examples-MockToken-38903c7c.json} | 33 + .../models/dojo_examples-Moved-318ae40d.json} | 33 + .../models/dojo_examples-Moves-2e2accba.json} | 33 + .../dojo_examples-PlayerConfig-3adad785.json} | 33 + ...n => dojo_examples-Position-1e145e26.json} | 33 + ...dojo_examples-ServerProfile-4caad1e6.json} | 33 + .../models/dojo_examples_models_moves.json | 393 -- ...es_others_others_contract_initialized.json | 367 -- .../dojo_examples-actions-40b6994c.toml | 11 + .../dojo_examples-mock_token-31599eb2.toml | 11 + .../dojo_examples-others-61de2c18.toml | 11 + .../dojo_examples_actions_actions.toml | 10 - .../dojo_examples_mock_token_mock_token.toml | 10 - .../dojo_examples_others_others.toml | 10 - .../{dojo_base_base.toml => dojo-base.toml} | 4 +- .../manifests/dev/base/dojo-world.toml | 6 + .../manifests/dev/base/dojo_world_world.toml | 5 - ...examples-ContractInitialized-376b7bd6.toml | 21 + .../dojo_examples-Message-1bb1d226.toml | 26 + .../dojo_examples-MockToken-38903c7c.toml | 16 + .../models/dojo_examples-Moved-318ae40d.toml | 16 + .../models/dojo_examples-Moves-2e2accba.toml | 21 + .../dojo_examples-PlayerConfig-3adad785.toml | 26 + .../dojo_examples-Position-1e145e26.toml | 16 + .../dojo_examples-ServerProfile-4caad1e6.toml | 21 + .../dojo_examples_actions_actions_moved.toml | 15 - .../models/dojo_examples_models_message.toml | 25 - .../dojo_examples_models_mock_token.toml | 15 - .../models/dojo_examples_models_moves.toml | 20 - .../dojo_examples_models_player_config.toml | 25 - .../models/dojo_examples_models_position.toml | 15 - .../dojo_examples_models_server_profile.toml | 20 - ...es_others_others_contract_initialized.toml | 20 - .../manifests/dev/manifest.json | 981 ++++- .../manifests/dev/manifest.toml | 171 +- .../dojo_examples_actions_actions.toml | 4 - .../dojo_examples_others_others.toml | 4 - .../dev/overlays/dojo_base_base.toml | 1 - .../dev/overlays/dojo_world_world.toml | 1 - .../dojo_examples_actions_actions_moved.toml | 1 - .../dojo_examples_models_emote_message.toml | 1 - .../models/dojo_examples_models_moves.toml | 1 - .../dojo_examples_models_player_config.toml | 1 - .../models/dojo_examples_models_position.toml | 1 - ...es_others_others_contract_initialized.toml | 1 - ...on => dojo_examples-actions-40b6994c.json} | 126 +- .../dojo_examples-mock_token-31599eb2.json | 195 + .../dojo_examples-others-61de2c18.json} | 70 +- .../abis/base/dojo-base.json} | 49 +- ...{dojo_world_world.json => dojo-world.json} | 117 +- ...xamples-ContractInitialized-376b7bd6.json} | 33 + .../dojo_examples-Message-1bb1d226.json} | 33 + .../dojo_examples-MockToken-38903c7c.json} | 33 + .../models/dojo_examples-Moved-318ae40d.json} | 33 + .../models/dojo_examples-Moves-2e2accba.json} | 33 + .../dojo_examples-PlayerConfig-3adad785.json} | 33 + .../dojo_examples-Position-1e145e26.json} | 33 + ...dojo_examples-ServerProfile-4caad1e6.json} | 69 +- .../dojo_examples_actions_actions.json | 290 -- .../abis/deployments/dojo_world_world.json | 996 ----- .../dojo_examples_actions_actions_moved.json | 389 -- .../dojo_examples_models_emote_message.json | 389 -- .../models/dojo_examples_models_message.json | 371 -- .../models/dojo_examples_models_position.json | 377 -- .../dojo_examples-actions-40b6994c.toml | 11 + .../dojo_examples-mock_token-31599eb2.toml | 11 + .../dojo_examples-others-61de2c18.toml | 11 + .../dojo_examples_actions_actions.toml | 10 - .../dojo_examples_mock_token_mock_token.toml | 10 - .../dojo_examples_others_others.toml | 10 - .../manifests/release/base/dojo-base.toml | 4 +- .../manifests/release/base/dojo-world.toml | 6 + .../release/base/dojo_world_world.toml | 5 - ...examples-ContractInitialized-376b7bd6.toml | 21 + .../dojo_examples-Message-1bb1d226.toml | 26 + .../dojo_examples-MockToken-38903c7c.toml | 16 + .../models/dojo_examples-Moved-318ae40d.toml | 16 + .../models/dojo_examples-Moves-2e2accba.toml | 21 + .../dojo_examples-PlayerConfig-3adad785.toml | 26 + .../dojo_examples-Position-1e145e26.toml | 16 + .../dojo_examples-ServerProfile-4caad1e6.toml | 21 + .../dojo_examples_actions_actions_moved.toml | 15 - .../models/dojo_examples_models_message.toml | 25 - .../dojo_examples_models_mock_token.toml | 15 - .../models/dojo_examples_models_moves.toml | 20 - .../dojo_examples_models_player_config.toml | 25 - .../models/dojo_examples_models_position.toml | 15 - ...es_others_others_contract_initialized.toml | 20 - .../manifests/release/manifest.json | 3910 ----------------- .../manifests/release/manifest.toml | 185 - .../dojo_examples_actions_actions.toml | 4 - .../dojo_examples_others_others.toml | 8 - .../release/overlays/dojo_base_base.toml | 1 - .../release/overlays/dojo_world_world.toml | 1 - .../dojo_examples_actions_actions_moved.toml | 1 - .../models/dojo_examples_models_message.toml | 1 - .../dojo_examples_models_mock_token.toml | 1 - .../models/dojo_examples_models_moves.toml | 1 - .../dojo_examples_models_player_config.toml | 1 - .../models/dojo_examples_models_position.toml | 1 - ...es_others_others_contract_initialized.toml | 1 - .../spawn-and-move/overlays/dev/actions.toml | 2 + .../spawn-and-move/overlays/dev/others.toml | 2 + examples/spawn-and-move/src/actions.cairo | 2 +- scripts/rebuild_test_artifacts.sh | 10 + 265 files changed, 9908 insertions(+), 11604 deletions(-) create mode 100644 bin/sozo/tests/test_migrate.rs create mode 100644 crates/dojo-core/src/contract.cairo create mode 100644 crates/dojo-core/src/utils.cairo create mode 100644 crates/dojo-core/src/utils_test.cairo rename crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/{compiler_cairo_cairo_24_cairo_v240.json => ccf-cairo_v240-8d921297.json} (100%) rename crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/{compiler_cairo_cairo_26_cairo_v260.json => ccf-cairo_v260-465ec7fe.json} (100%) rename examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_mock_token_mock_token.json => crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo-base.json (67%) rename crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/{dojo_world_world.json => dojo-world.json} (89%) rename crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/{compiler_cairo_cairo_24_cairo_v240.toml => ccf-cairo_v240-8d921297.toml} (66%) rename crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/{compiler_cairo_cairo_26_cairo_v260.toml => ccf-cairo_v260-465ec7fe.toml} (66%) rename examples/spawn-and-move/manifests/release/base/dojo_base_base.toml => crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo-base.toml (66%) create mode 100644 crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo-world.toml delete mode 100644 crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_world_world.toml create mode 100644 crates/dojo-lang/src/utils.rs create mode 100644 crates/dojo-world/src/contracts/naming.rs create mode 100644 crates/torii/migrations/20240702140704_model_namespace.sql delete mode 100644 crates/torii/types-test/manifests/dev/base/dojo_world_world.toml rename examples/spawn-and-move/manifests/dev/abis/base/contracts/{dojo_examples_actions_actions.json => dojo_examples-actions-40b6994c.json} (85%) rename examples/spawn-and-move/manifests/dev/abis/{deployments/contracts/dojo_examples_others_others.json => base/contracts/dojo_examples-mock_token-31599eb2.json} (61%) rename examples/spawn-and-move/manifests/{release/abis/deployments/contracts/dojo_examples_others_others.json => dev/abis/base/contracts/dojo_examples-others-61de2c18.json} (67%) rename examples/spawn-and-move/manifests/{release/abis/base/contracts/dojo_examples_mock_token_mock_token.json => dev/abis/base/dojo-base.json} (67%) rename examples/spawn-and-move/manifests/dev/abis/base/{dojo_world_world.json => dojo-world.json} (89%) rename examples/spawn-and-move/manifests/{release/abis/base/models/dojo_examples_others_others_contract_initialized.json => dev/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json} (92%) rename examples/spawn-and-move/manifests/{release/abis/base/models/dojo_examples_models_message.json => dev/abis/base/models/dojo_examples-Message-1bb1d226.json} (91%) rename examples/spawn-and-move/manifests/dev/abis/{deployments/models/dojo_examples_models_mock_token.json => base/models/dojo_examples-MockToken-38903c7c.json} (91%) rename examples/spawn-and-move/manifests/{release/abis/base/models/dojo_examples_actions_actions_moved.json => dev/abis/base/models/dojo_examples-Moved-318ae40d.json} (92%) rename examples/spawn-and-move/manifests/dev/abis/base/models/{dojo_examples_models_moves.json => dojo_examples-Moves-2e2accba.json} (92%) rename examples/spawn-and-move/manifests/dev/abis/{deployments/models/dojo_examples_models_player_config.json => base/models/dojo_examples-PlayerConfig-3adad785.json} (92%) rename examples/spawn-and-move/manifests/{release/abis/base/models/dojo_examples_models_position.json => dev/abis/base/models/dojo_examples-Position-1e145e26.json} (92%) rename examples/spawn-and-move/manifests/dev/abis/{deployments/models/dojo_examples_models_server_profile.json => base/models/dojo_examples-ServerProfile-4caad1e6.json} (91%) rename examples/spawn-and-move/manifests/dev/abis/deployments/contracts/{dojo_examples_actions_actions.json => dojo_examples-actions-40b6994c.json} (85%) create mode 100644 examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples-mock_token-31599eb2.json rename examples/spawn-and-move/manifests/{release/abis/base/contracts/dojo_examples_others_others.json => dev/abis/deployments/contracts/dojo_examples-others-61de2c18.json} (67%) rename examples/spawn-and-move/manifests/dev/abis/deployments/{dojo_world_world.json => dojo-world.json} (89%) rename examples/spawn-and-move/manifests/{release/abis/deployments/models/dojo_examples_others_others_contract_initialized.json => dev/abis/deployments/models/dojo_examples-ContractInitialized-376b7bd6.json} (92%) rename examples/spawn-and-move/manifests/dev/abis/deployments/models/{dojo_examples_models_message.json => dojo_examples-Message-1bb1d226.json} (91%) rename examples/spawn-and-move/manifests/{release/abis/base/models/dojo_examples_models_mock_token.json => dev/abis/deployments/models/dojo_examples-MockToken-38903c7c.json} (91%) rename examples/spawn-and-move/manifests/dev/abis/{base/models/dojo_examples_actions_actions_moved.json => deployments/models/dojo_examples-Moved-318ae40d.json} (92%) rename examples/spawn-and-move/manifests/{release/abis/base/models/dojo_examples_models_moves.json => dev/abis/deployments/models/dojo_examples-Moves-2e2accba.json} (92%) rename examples/spawn-and-move/manifests/{release/abis/base/models/dojo_examples_models_player_config.json => dev/abis/deployments/models/dojo_examples-PlayerConfig-3adad785.json} (92%) rename examples/spawn-and-move/manifests/dev/abis/deployments/models/{dojo_examples_models_position.json => dojo_examples-Position-1e145e26.json} (92%) rename examples/spawn-and-move/manifests/dev/abis/{base/models/dojo_examples_models_server_profile.json => deployments/models/dojo_examples-ServerProfile-4caad1e6.json} (91%) delete mode 100644 examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_moves.json delete mode 100644 examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_others_others_contract_initialized.json create mode 100644 examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-actions-40b6994c.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-mock_token-31599eb2.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-others-61de2c18.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_actions_actions.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_mock_token_mock_token.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_others_others.toml rename examples/spawn-and-move/manifests/dev/base/{dojo_base_base.toml => dojo-base.toml} (66%) create mode 100644 examples/spawn-and-move/manifests/dev/base/dojo-world.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/dojo_world_world.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples-ContractInitialized-376b7bd6.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Message-1bb1d226.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples-MockToken-38903c7c.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Moved-318ae40d.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Moves-2e2accba.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples-PlayerConfig-3adad785.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Position-1e145e26.toml create mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples-ServerProfile-4caad1e6.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples_actions_actions_moved.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_message.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_mock_token.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_moves.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_player_config.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_position.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_server_profile.toml delete mode 100644 examples/spawn-and-move/manifests/dev/base/models/dojo_examples_others_others_contract_initialized.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_actions_actions.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_others_others.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/dojo_base_base.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/dojo_world_world.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_actions_actions_moved.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_emote_message.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_moves.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_player_config.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_position.toml delete mode 100644 examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_others_others_contract_initialized.toml rename examples/spawn-and-move/manifests/release/abis/base/contracts/{dojo_examples_actions_actions.json => dojo_examples-actions-40b6994c.json} (75%) create mode 100644 examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-mock_token-31599eb2.json rename examples/spawn-and-move/manifests/{dev/abis/base/contracts/dojo_examples_others_others.json => release/abis/base/contracts/dojo_examples-others-61de2c18.json} (67%) rename examples/spawn-and-move/manifests/{dev/abis/deployments/contracts/dojo_examples_mock_token_mock_token.json => release/abis/base/dojo-base.json} (67%) rename examples/spawn-and-move/manifests/release/abis/base/{dojo_world_world.json => dojo-world.json} (89%) rename examples/spawn-and-move/manifests/{dev/abis/base/models/dojo_examples_others_others_contract_initialized.json => release/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json} (92%) rename examples/spawn-and-move/manifests/{dev/abis/base/models/dojo_examples_models_message.json => release/abis/base/models/dojo_examples-Message-1bb1d226.json} (91%) rename examples/spawn-and-move/manifests/{dev/abis/base/models/dojo_examples_models_mock_token.json => release/abis/base/models/dojo_examples-MockToken-38903c7c.json} (91%) rename examples/spawn-and-move/manifests/{dev/abis/deployments/models/dojo_examples_actions_actions_moved.json => release/abis/base/models/dojo_examples-Moved-318ae40d.json} (92%) rename examples/spawn-and-move/manifests/release/abis/{deployments/models/dojo_examples_models_moves.json => base/models/dojo_examples-Moves-2e2accba.json} (92%) rename examples/spawn-and-move/manifests/release/abis/{deployments/models/dojo_examples_models_player_config.json => base/models/dojo_examples-PlayerConfig-3adad785.json} (92%) rename examples/spawn-and-move/manifests/{dev/abis/base/models/dojo_examples_models_position.json => release/abis/base/models/dojo_examples-Position-1e145e26.json} (92%) rename examples/spawn-and-move/manifests/{dev/abis/base/models/dojo_examples_models_player_config.json => release/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json} (88%) delete mode 100644 examples/spawn-and-move/manifests/release/abis/deployments/contracts/dojo_examples_actions_actions.json delete mode 100644 examples/spawn-and-move/manifests/release/abis/deployments/dojo_world_world.json delete mode 100644 examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_actions_actions_moved.json delete mode 100644 examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_emote_message.json delete mode 100644 examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_message.json delete mode 100644 examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_position.json create mode 100644 examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-actions-40b6994c.toml create mode 100644 examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-mock_token-31599eb2.toml create mode 100644 examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-others-61de2c18.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_actions_actions.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_mock_token_mock_token.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_others_others.toml rename crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_base_base.toml => examples/spawn-and-move/manifests/release/base/dojo-base.toml (65%) create mode 100644 examples/spawn-and-move/manifests/release/base/dojo-world.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/dojo_world_world.toml create mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples-ContractInitialized-376b7bd6.toml create mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples-Message-1bb1d226.toml create mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples-MockToken-38903c7c.toml create mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples-Moved-318ae40d.toml create mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples-Moves-2e2accba.toml create mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples-PlayerConfig-3adad785.toml create mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples-Position-1e145e26.toml create mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples-ServerProfile-4caad1e6.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples_actions_actions_moved.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_message.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_mock_token.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_moves.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_player_config.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_position.toml delete mode 100644 examples/spawn-and-move/manifests/release/base/models/dojo_examples_others_others_contract_initialized.toml delete mode 100644 examples/spawn-and-move/manifests/release/manifest.json delete mode 100644 examples/spawn-and-move/manifests/release/manifest.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/contracts/dojo_examples_actions_actions.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/contracts/dojo_examples_others_others.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/dojo_base_base.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/dojo_world_world.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_actions_actions_moved.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_message.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_mock_token.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_moves.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_player_config.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_position.toml delete mode 100644 examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_others_others_contract_initialized.toml create mode 100644 examples/spawn-and-move/overlays/dev/actions.toml create mode 100644 examples/spawn-and-move/overlays/dev/others.toml diff --git a/.vscode/launch.json b/.vscode/launch.json index 89693441c5..f141a5a8c7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -23,6 +23,21 @@ "apply" ] }, + { + "name": "Sozo", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/target/debug/sozo", + "args": [ + "--manifest-path", + "examples/spawn-and-move/Scarb.toml", + "events", + "--world", + "0x04c972a756d796d716f665a8079dbf9aff05bbba41a2b8194553073f31d7d393", + "--chunk-size", + "100" + ] + }, { "type": "lldb", "request": "launch", diff --git a/Cargo.lock b/Cargo.lock index 7bd95ca5c5..8e116f949c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5097,6 +5097,7 @@ name = "dojo-lang" version = "0.7.3" dependencies = [ "anyhow", + "cainome 0.2.3 (git+https://github.com/cartridge-gg/cainome?tag=v0.3.0)", "cairo-lang-compiler 2.6.3 (git+https://github.com/starkware-libs/cairo?rev=d9984ef58e2f704909e271f2f01327f520ded632)", "cairo-lang-debug 2.6.3 (git+https://github.com/starkware-libs/cairo?rev=d9984ef58e2f704909e271f2f01327f520ded632)", "cairo-lang-defs 2.6.3 (git+https://github.com/starkware-libs/cairo?rev=d9984ef58e2f704909e271f2f01327f520ded632)", @@ -5128,6 +5129,7 @@ dependencies = [ "num-traits 0.2.19", "once_cell", "pretty_assertions", + "regex", "salsa", "scarb", "semver 1.0.23", @@ -5136,6 +5138,7 @@ dependencies = [ "serde_with 2.3.3", "smol_str", "starknet 0.11.0", + "starknet-crypto 0.7.1", "test-log", "thiserror", "toml 0.8.14", @@ -5260,6 +5263,7 @@ dependencies = [ "topological-sort", "tracing", "url", + "walkdir", ] [[package]] @@ -13431,6 +13435,7 @@ dependencies = [ "dojo-world", "futures", "ipfs-api-backend-hyper", + "itertools 0.12.1", "katana-runner", "num-bigint", "num-traits 0.2.19", @@ -13444,6 +13449,7 @@ dependencies = [ "smol_str", "starknet 0.11.0", "starknet-crypto 0.7.1", + "tee", "thiserror", "tokio", "toml 0.8.14", @@ -14422,6 +14428,12 @@ dependencies = [ "xattr", ] +[[package]] +name = "tee" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c12559dba7383625faaff75be24becf35bfc885044375bcab931111799a3da" + [[package]] name = "tempfile" version = "3.10.1" @@ -15050,6 +15062,7 @@ dependencies = [ "dojo-test-utils", "dojo-types", "dojo-world", + "katana-runner", "lazy_static", "regex", "scarb", @@ -15088,6 +15101,7 @@ dependencies = [ "futures-util", "hex", "hyper 0.14.29", + "katana-runner", "num-traits 0.2.19", "parking_lot 0.12.3", "prost 0.11.9", @@ -15133,6 +15147,7 @@ dependencies = [ "dojo-world", "futures", "indexmap 2.2.6", + "katana-runner", "libp2p", "libp2p-webrtc", "libp2p-webrtc-websys", diff --git a/bin/sozo/src/commands/auth.rs b/bin/sozo/src/commands/auth.rs index 554f7fe945..550ece1124 100644 --- a/bin/sozo/src/commands/auth.rs +++ b/bin/sozo/src/commands/auth.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use dojo_world::metadata::Environment; +use dojo_world::metadata::{get_default_namespace_from_ws, Environment}; use scarb::core::Config; use scarb_ui::Ui; use sozo_ops::auth; @@ -63,6 +63,9 @@ impl AuthArgs { let env_metadata = utils::load_metadata_from_config(config)?; trace!(metadata=?env_metadata, "Loaded environment."); + let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; + let default_namespace = get_default_namespace_from_ws(&ws); + match self.command { AuthCommand::Grant { kind, world, starknet, account, transaction } => { config.tokio_handle().block_on(grant( @@ -74,6 +77,7 @@ impl AuthArgs { kind, transaction, config, + &default_namespace, )) } AuthCommand::Revoke { kind, world, starknet, account, transaction } => { @@ -86,6 +90,7 @@ impl AuthArgs { kind, transaction, config, + &default_namespace, )) } } @@ -94,26 +99,38 @@ impl AuthArgs { #[derive(Debug, Subcommand)] pub enum AuthKind { - #[command(about = "Grant a contract permission to write to a model.")] + #[command(about = "Grant a contract permission to write to a resource (contract, model or \ + namespace).")] Writer { #[arg(num_args = 1..)] #[arg(required = true)] - #[arg(value_name = "model,contract_address")] - #[arg(help = "A list of models and contract address to grant write access to. Comma \ - separated values to indicate model name and contract address e.g. \ - model_name,path::to::contract model_name,contract_address ")] - models_contracts: Vec, + #[arg(value_name = "resource,contract_tag_or_address")] + #[arg(help = "A list of resource/contract couples to grant write access to. +Comma separated values to indicate resource identifier and contract tag or address. +A resource identifier must use the following format: \ + :.\n +Some examples: + model:dojo_examples-Moves,0x1234 + m:Moves,0x1234 + ns:dojo_examples,actions +")] + models_contracts: Vec, }, - #[command(about = "Grant ownership of a resource.")] + #[command(about = "Grant ownership of a resource (contract, model or namespace).")] Owner { #[arg(num_args = 1..)] #[arg(required = true)] #[arg(value_name = "resource,owner_address")] - #[arg(help = "A list of owners and resources to grant ownership to. Comma separated \ - values to indicate owner address and resouce e.g. \ - contract:path::to::contract,0x1234 contract:contract_address,0x1111, \ - model:model_name,0xbeef")] - owners_resources: Vec, + #[arg(help = "A list of resources and owners to grant ownership to. +Comma separated values to indicate resource identifier and owner address. +A resource identifier must use the following format: \ + :.\n +Some examples: + model:dojo_examples-Moves,0x1234 + m:Moves,0x1234 + ns:dojo_examples,0xbeef +")] + owners_resources: Vec, }, } @@ -127,6 +144,7 @@ pub async fn grant( kind: AuthKind, transaction: TransactionOptions, config: &Config, + default_namespace: &str, ) -> Result<()> { trace!(?kind, ?world, ?starknet, ?account, ?transaction, "Executing Grant command."); let world = @@ -138,14 +156,16 @@ pub async fn grant( contracts=?models_contracts, "Granting Writer permissions." ); - auth::grant_writer(ui, &world, models_contracts, transaction.into()).await + auth::grant_writer(ui, &world, models_contracts, transaction.into(), default_namespace) + .await } AuthKind::Owner { owners_resources } => { trace!( resources=?owners_resources, "Granting Owner permissions." ); - auth::grant_owner(ui, &world, owners_resources, transaction.into()).await + auth::grant_owner(ui, &world, owners_resources, transaction.into(), default_namespace) + .await } } } @@ -160,6 +180,7 @@ pub async fn revoke( kind: AuthKind, transaction: TransactionOptions, config: &Config, + default_namespace: &str, ) -> Result<()> { trace!(?kind, ?world, ?starknet, ?account, ?transaction, "Executing Revoke command."); let world = @@ -171,14 +192,16 @@ pub async fn revoke( contracts=?models_contracts, "Revoking Writer permissions." ); - auth::revoke_writer(ui, &world, models_contracts, transaction.into()).await + auth::revoke_writer(ui, &world, models_contracts, transaction.into(), default_namespace) + .await } AuthKind::Owner { owners_resources } => { trace!( resources=?owners_resources, "Revoking Owner permissions." ); - auth::revoke_owner(ui, &world, owners_resources, transaction.into()).await + auth::revoke_owner(ui, &world, owners_resources, transaction.into(), default_namespace) + .await } } } @@ -187,50 +210,149 @@ pub async fn revoke( mod tests { use std::str::FromStr; - use dojo_world::contracts::cairo_utils; use starknet::core::types::Felt; use super::*; #[test] - fn test_owner_resource_from_str() { - // Test valid input - let input = "contract:path::to::contract,0x1234"; - let expected_owner = Felt::from_hex("0x1234").unwrap(); - let expected_resource = auth::ResourceType::Contract("path::to::contract".to_string()); - let expected = auth::OwnerResource { owner: expected_owner, resource: expected_resource }; - let result = auth::OwnerResource::from_str(input).unwrap(); - assert_eq!(result, expected); - - // Test valid input with model - let input = "model:model_name,0x1234"; - let expected_owner = Felt::from_hex("0x1234").unwrap(); - let expected_model = cairo_utils::str_to_felt("model_name").unwrap(); - let expected_resource = auth::ResourceType::Model(expected_model); - let expected = auth::OwnerResource { owner: expected_owner, resource: expected_resource }; - let result = auth::OwnerResource::from_str(input).unwrap(); - assert_eq!(result, expected); - - // Test invalid input - let input = "invalid_input"; - let result = auth::OwnerResource::from_str(input); - assert!(result.is_err()); + fn test_resource_type_from_str() { + let inputs = [ + ( + "contract:name:contract_name", + auth::ResourceType::Contract("name:contract_name".to_string()), + ), + ("c:0x1234", auth::ResourceType::Contract("0x1234".to_string())), + ("model:name:model_name", auth::ResourceType::Model("name:model_name".to_string())), + ("m:name:model_name", auth::ResourceType::Model("name:model_name".to_string())), + ( + "namespace:namespace_name", + auth::ResourceType::Namespace("namespace_name".to_string()), + ), + ("ns:namespace_name", auth::ResourceType::Namespace("namespace_name".to_string())), + ]; + + for (input, expected) in inputs { + let res = auth::ResourceType::from_str(input); + assert!(res.is_ok(), "Unable to parse input '{input}'"); + + let resource = res.unwrap(); + assert!( + resource == expected, + "Wrong resource type: expected: {:#?} got: {:#?}", + expected, + resource + ); + } + } + + #[test] + fn test_resource_type_from_str_bad_resource_identifier() { + let input = "other:model_name"; + let res = auth::ResourceType::from_str(input); + assert!(res.is_err(), "Bad identifier: This resource should not be accepted: '{input}'"); + } + + #[test] + fn test_resource_type_from_str_bad_resource_format() { + let input = "model_name"; + let res = auth::ResourceType::from_str(input); + assert!(res.is_err(), "Bad format: This resource should not be accepted: '{input}'"); + } + + #[test] + fn test_resource_writer_from_str() { + let inputs = [ + ( + "model:name:model_name,name:contract_name", + auth::ResourceWriter { + resource: auth::ResourceType::Model("name:model_name".to_string()), + tag_or_address: "name:contract_name".to_string(), + }, + ), + ( + "ns:namespace_name,0x1234", + auth::ResourceWriter { + resource: auth::ResourceType::Namespace("namespace_name".to_string()), + tag_or_address: "0x1234".to_string(), + }, + ), + ]; + + for (input, expected) in inputs { + let res = auth::ResourceWriter::from_str(input); + assert!(res.is_ok(), "Unable to parse input '{input}'"); + + let writer = res.unwrap(); + assert!( + writer == expected, + "Wrong resource writer: expected: {:#?} got: {:#?}", + expected, + writer + ); + } + } + + #[test] + fn test_resource_writer_from_str_bad_format() { + let input = "model_name"; + let res = auth::ResourceWriter::from_str(input); + assert!(res.is_err(), "Bad format: This resource writer should not be accepted: '{input}'"); + } + + #[test] + fn test_resource_writer_from_str_bad_owner_address() { + let input = "model:model_name:bad_address"; + let res = auth::ResourceWriter::from_str(input); + assert!( + res.is_err(), + "Bad address: This resource writer should not be accepted: '{input}'" + ); + } + + #[test] + fn test_resource_owner_from_str() { + let inputs = [ + ( + "model:name:model_name,0x1234", + auth::ResourceOwner { + resource: auth::ResourceType::Model("name:model_name".to_string()), + owner: Felt::from_hex("0x1234").unwrap(), + }, + ), + ( + "ns:namespace_name,0x1111", + auth::ResourceOwner { + resource: auth::ResourceType::Namespace("namespace_name".to_string()), + owner: Felt::from_hex("0x1111").unwrap(), + }, + ), + ]; + + for (input, expected) in inputs { + let res = auth::ResourceOwner::from_str(input); + assert!(res.is_ok(), "Unable to parse input '{input}'"); + + let owner = res.unwrap(); + assert!( + owner == expected, + "Wrong resource owner: expected: {:#?} got: {:#?}", + expected, + owner + ); + } + } + + #[test] + fn test_resource_owner_from_str_bad_format() { + let input = "model_name"; + let res = auth::ResourceOwner::from_str(input); + assert!(res.is_err(), "Bad format: This resource owner should not be accepted: '{input}'"); } #[test] - fn test_model_contract_from_str() { - // Test valid input - let input = "model_name,0x1234"; - let expected_model = cairo_utils::str_to_felt("model_name").unwrap(); - let expected_contract = "0x1234"; - let expected = - auth::ModelContract { model: expected_model, contract: expected_contract.to_string() }; - let result = auth::ModelContract::from_str(input).unwrap(); - assert_eq!(result, expected); - - // Test invalid input - let input = "invalid_input"; - let result = auth::ModelContract::from_str(input); - assert!(result.is_err()); + fn test_resource_owner_from_str_bad_owner_address() { + let input = "model:model_name:bad_address"; + let res = auth::ResourceOwner::from_str(input); + assert!(res.is_err(), "Bad address: This resource owner should not be accepted: '{input}'"); } } diff --git a/bin/sozo/src/commands/build.rs b/bin/sozo/src/commands/build.rs index f7384266f0..da94807c98 100644 --- a/bin/sozo/src/commands/build.rs +++ b/bin/sozo/src/commands/build.rs @@ -127,7 +127,7 @@ impl BuildArgs { trace!(pluginManager=?bindgen, "Generating bindings."); // Only generate bindgen if a current package is defined with dojo metadata. - if let Some(dojo_metadata) = dojo_metadata_from_workspace(&ws) { + if let Ok(dojo_metadata) = dojo_metadata_from_workspace(&ws) { tokio::runtime::Runtime::new() .unwrap() .block_on(bindgen.generate(dojo_metadata.skip_migration)) @@ -184,6 +184,7 @@ mod tests { use dojo_test_utils::compiler; use prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE; use prettytable::{format, Cell, Row, Table}; + use scarb::compiler::Profile; use sozo_ops::statistics::ContractStatistics; use super::{create_stats_table, BuildArgs, *}; @@ -195,7 +196,7 @@ mod tests { let source_project_dir = Utf8PathBuf::from("../../examples/spawn-and-move/"); let dojo_core_path = Utf8PathBuf::from("../../crates/dojo-core"); - let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path); + let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path, Profile::DEV); let build_args = BuildArgs { bindings_output: "generated".to_string(), diff --git a/bin/sozo/src/commands/call.rs b/bin/sozo/src/commands/call.rs index e4caf0b554..fcac0ac293 100644 --- a/bin/sozo/src/commands/call.rs +++ b/bin/sozo/src/commands/call.rs @@ -1,5 +1,7 @@ use anyhow::Result; use clap::Args; +use dojo_world::contracts::naming::ensure_namespace; +use dojo_world::metadata::get_default_namespace_from_ws; use scarb::core::Config; use starknet::core::types::Felt; use tracing::trace; @@ -11,8 +13,8 @@ use crate::utils; #[derive(Debug, Args)] #[command(about = "Call a system with the given calldata.")] pub struct CallArgs { - #[arg(help = "The address or the fully qualified name of the contract to call.")] - pub contract: String, + #[arg(help = "The tag or address of the contract to call.")] + pub tag_or_address: String, #[arg(help = "The name of the entrypoint to call.")] pub entrypoint: String, @@ -40,6 +42,15 @@ impl CallArgs { let env_metadata = utils::load_metadata_from_config(config)?; trace!(?env_metadata, "Loaded metadata from config."); + + let tag_or_address = if utils::is_address(&self.tag_or_address) { + self.tag_or_address + } else { + let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; + let default_namespace = get_default_namespace_from_ws(&ws); + ensure_namespace(&self.tag_or_address, &default_namespace) + }; + config.tokio_handle().block_on(async { let world_reader = utils::world_reader_from_env_metadata(self.world, self.starknet, &env_metadata) @@ -48,7 +59,7 @@ impl CallArgs { sozo_ops::call::call( world_reader, - self.contract, + tag_or_address, self.entrypoint, self.calldata, self.block_id, diff --git a/bin/sozo/src/commands/clean.rs b/bin/sozo/src/commands/clean.rs index bf44226bb8..54650fcd96 100644 --- a/bin/sozo/src/commands/clean.rs +++ b/bin/sozo/src/commands/clean.rs @@ -1,18 +1,23 @@ use std::fs; -use anyhow::Result; +use anyhow::{Context, Result}; use camino::Utf8PathBuf; use clap::Args; use dojo_world::manifest::{ABIS_DIR, BASE_DIR, MANIFESTS_DIR}; use scarb::core::Config; +use scarb::ops; use tracing::trace; #[derive(Debug, Args)] pub struct CleanArgs { - #[arg(short, long)] + #[arg(long)] #[arg(help = "Removes all the generated files, including scarb artifacts and ALL the \ manifests files.")] - pub all: bool, + pub full: bool, + + #[arg(long)] + #[arg(help = "Clean all profiles.")] + pub all_profiles: bool, } impl CleanArgs { @@ -39,109 +44,211 @@ impl CleanArgs { let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; trace!(ws=?ws, "Workspace read successfully."); - let profile_name = - ws.current_profile().expect("Scarb profile is expected at this point.").to_string(); - - // Manifest path is always a file, we can unwrap safely to get the - // parent folder. - let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); + let profile_names = if self.all_profiles { + let mut profiles = ws.profile_names().expect("given method never returns an error"); + // currently scarb can return duplicate profiles and since the vector is sorted we can + // remove duplicates by calling dedup + profiles.dedup(); + profiles + } else { + vec![ + ws.current_profile().expect("Scarb profile is expected at this point.").to_string(), + ] + }; + + for profile_name in profile_names { + // Manifest path is always a file, we can unwrap safely to get the + // parent folder. + let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); + + // By default, this command cleans the build manifests and scarb artifacts. + trace!("Cleaning Scarb artifacts and build manifests."); + + { + // copied from scarb::ops::clean since scarb cleans build file of all the profiles + // we only want to clean build files for specified profile + // + // cleaning build files for all profiles would create inconsistency with the + // manifest files in `manifests` directory + let ws = ops::read_workspace(config.manifest_path(), config)?; + let path = ws.target_dir().path_unchecked().join(&profile_name); + if path.exists() { + fs::remove_dir_all(path).context("failed to clean generated artifacts")?; + } + } - let profile_dir = manifest_dir.join(MANIFESTS_DIR).join(profile_name); + let profile_dir = manifest_dir.join(MANIFESTS_DIR).join(&profile_name); - // By default, this command cleans the build manifests and scarb artifacts. - trace!("Cleaning Scarb artifacts and build manifests."); - scarb::ops::clean(config)?; - Self::clean_manifests(&profile_dir)?; + Self::clean_manifests(&profile_dir)?; - if self.all && profile_dir.exists() { - trace!(?profile_dir, "Removing entire profile directory."); - fs::remove_dir_all(profile_dir)?; + if self.full && profile_dir.exists() { + trace!(?profile_dir, "Removing entire profile directory."); + fs::remove_dir_all(profile_dir)?; + } } Ok(()) } } +// these tests assume `example/spawn-and-move` is build for `dev` and `release` profile, +// which are normally built by the `build.rs` of `dojo-test-utils`. #[cfg(test)] mod tests { use dojo_test_utils::compiler; - use dojo_world::metadata::dojo_metadata_from_workspace; - use dojo_world::migration::TxnConfig; - use katana_runner::KatanaRunner; - use sozo_ops::migration; + use scarb::compiler::Profile; use super::*; #[test] - fn test_clean() { - let source_project = "../../examples/spawn-and-move/Scarb.toml"; + fn default_clean_works() { + let source_project = "../../examples/spawn-and-move"; let dojo_core_path = "../../crates/dojo-core"; - // Build a completely new project in it's own directory. - let (temp_project_dir, config, _) = - compiler::copy_build_project_temp(source_project, dojo_core_path, true); + let config = compiler::copy_tmp_config( + &Utf8PathBuf::from(source_project), + &Utf8PathBuf::from(dojo_core_path), + Profile::DEV, + ); + + let temp_project_dir = config.manifest_path().parent().unwrap().to_path_buf(); + + let clean_cmd = CleanArgs { full: false, all_profiles: false }; + clean_cmd.run(&config).unwrap(); + + let dev_profile_name = "dev"; + let release_profile_name = "release"; + + let target_dev_dir = temp_project_dir.join("target").join(dev_profile_name); + let target_release_dir = temp_project_dir.join("target").join(release_profile_name); + + let dev_manifests_dir = temp_project_dir.join("manifests").join(dev_profile_name); + let release_manifests_dir = temp_project_dir.join("manifests").join(release_profile_name); + + let dev_manifests_base_dir = dev_manifests_dir.join("base"); + let dev_manifests_abis_base_dir = dev_manifests_dir.join("abis").join("base"); + let release_manifests_base_dir = release_manifests_dir.join("base"); + let release_manifests_abis_base_dir = release_manifests_dir.join("abis").join("base"); + + let dev_manifests_abis_depl_dir = dev_manifests_dir.join("abis").join("deployments"); + + let dev_manifest_toml = dev_manifests_dir.join("manifest").with_extension("toml"); + let dev_manifest_json = dev_manifests_dir.join("manifest").with_extension("json"); + + assert!(fs::read_dir(target_dev_dir).is_err(), "Expected 'target/dev' to be empty"); + assert!( + fs::read_dir(target_release_dir).is_ok(), + "Expected 'target/release' to be present" + ); + + assert!( + fs::read_dir(dev_manifests_base_dir).is_err(), + "Expected 'manifests/dev/base' to be empty" + ); + assert!( + fs::read_dir(dev_manifests_abis_base_dir).is_err(), + "Expected 'manifests/dev/abis/base' to be empty" + ); + assert!( + fs::read_dir(&dev_manifests_abis_depl_dir).is_ok(), + "Expected 'manifests/dev/abis/deployments' to not be empty" + ); + + // we expect release profile to be not affected + assert!( + fs::read_dir(release_manifests_base_dir).is_ok(), + "Expected 'manifests/release/base' to be non empty" + ); + assert!( + fs::read_dir(release_manifests_abis_base_dir).is_ok(), + "Expected 'manifests/release/abis/base' to be non empty" + ); - let runner = KatanaRunner::new().expect("Fail to set runner"); + assert!(dev_manifest_toml.exists(), "Expected 'manifest.toml' to exist"); + assert!(dev_manifest_json.exists(), "Expected 'manifest.json' to exist"); - let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); + let clean_cmd = CleanArgs { full: true, all_profiles: false }; + clean_cmd.run(&config).unwrap(); - let dojo_metadata = dojo_metadata_from_workspace(&ws).expect( - "No current package with dojo metadata found, clean is not yet support for workspaces.", + assert!( + fs::read_dir(&dev_manifests_abis_depl_dir).is_err(), + "Expected 'manifests/dev/abis/deployments' to be empty" ); + assert!(!dev_manifest_toml.exists(), "Expected 'manifest.toml' to not exist"); + assert!(!dev_manifest_json.exists(), "Expected 'manifest.json' to not exist"); + } - // Plan the migration to generate some manifests other than base. - config.tokio_handle().block_on(async { - migration::migrate( - &ws, - None, - runner.endpoint(), - runner.account(0), - "dojo_examples", - true, - TxnConfig::default(), - dojo_metadata.skip_migration, - ) - .await - .unwrap() - }); - - let clean_cmd = CleanArgs { all: false }; + #[test] + fn all_profile_clean_works() { + let source_project = "../../examples/spawn-and-move"; + let dojo_core_path = "../../crates/dojo-core"; + + let config = compiler::copy_tmp_config( + &Utf8PathBuf::from(source_project), + &Utf8PathBuf::from(dojo_core_path), + Profile::DEV, + ); + + let temp_project_dir = config.manifest_path().parent().unwrap().to_path_buf(); + + let clean_cmd = CleanArgs { full: false, all_profiles: true }; clean_cmd.run(&config).unwrap(); - let profile_name = config.profile().to_string(); + let dev_profile_name = "dev"; + let release_profile_name = "release"; + + let target_dev_dir = temp_project_dir.join("target").join(dev_profile_name); + let target_release_dir = temp_project_dir.join("target").join(release_profile_name); + + let dev_manifests_dir = temp_project_dir.join("manifests").join(dev_profile_name); + let release_manifests_dir = temp_project_dir.join("manifests").join(release_profile_name); - let target_dev_dir = temp_project_dir.join("target").join(&profile_name); - let profile_manifests_dir = temp_project_dir.join("manifests").join(&profile_name); - let manifests_dev_base_dir = profile_manifests_dir.join("base"); - let manifests_dev_abis_base_dir = profile_manifests_dir.join("abis").join("base"); - let manifests_dev_abis_depl_dir = profile_manifests_dir.join("abis").join("deployments"); - let manifest_toml = profile_manifests_dir.join("manifest").with_extension("toml"); - let manifest_json = profile_manifests_dir.join("manifest").with_extension("json"); + let dev_manifests_base_dir = dev_manifests_dir.join("base"); + let dev_manifests_abis_base_dir = dev_manifests_dir.join("abis").join("base"); + let release_manifests_base_dir = release_manifests_dir.join("base"); + let release_manifests_abis_base_dir = release_manifests_dir.join("abis").join("base"); + + let dev_manifests_abis_depl_dir = dev_manifests_dir.join("abis").join("deployments"); + + let dev_manifest_toml = dev_manifests_dir.join("manifest").with_extension("toml"); + let dev_manifest_json = dev_manifests_dir.join("manifest").with_extension("json"); assert!(fs::read_dir(target_dev_dir).is_err(), "Expected 'target/dev' to be empty"); + assert!(fs::read_dir(target_release_dir).is_err(), "Expected 'target/release' to be empty"); + assert!( - fs::read_dir(manifests_dev_base_dir).is_err(), + fs::read_dir(dev_manifests_base_dir).is_err(), "Expected 'manifests/dev/base' to be empty" ); assert!( - fs::read_dir(manifests_dev_abis_base_dir).is_err(), + fs::read_dir(dev_manifests_abis_base_dir).is_err(), "Expected 'manifests/dev/abis/base' to be empty" ); assert!( - fs::read_dir(&manifests_dev_abis_depl_dir).is_ok(), + fs::read_dir(&dev_manifests_abis_depl_dir).is_ok(), "Expected 'manifests/dev/abis/deployments' to not be empty" ); - assert!(manifest_toml.exists(), "Expected 'manifest.toml' to exist"); - assert!(manifest_json.exists(), "Expected 'manifest.json' to exist"); - let clean_cmd = CleanArgs { all: true }; + assert!( + fs::read_dir(release_manifests_base_dir).is_err(), + "Expected 'manifests/release/base' to be empty" + ); + assert!( + fs::read_dir(release_manifests_abis_base_dir).is_err(), + "Expected 'manifests/release/abis/base' to be empty" + ); + + assert!(dev_manifest_toml.exists(), "Expected 'manifest.toml' to exist"); + assert!(dev_manifest_json.exists(), "Expected 'manifest.json' to exist"); + + let clean_cmd = CleanArgs { full: true, all_profiles: true }; clean_cmd.run(&config).unwrap(); assert!( - fs::read_dir(&manifests_dev_abis_depl_dir).is_err(), + fs::read_dir(&dev_manifests_abis_depl_dir).is_err(), "Expected 'manifests/dev/abis/deployments' to be empty" ); - assert!(!manifest_toml.exists(), "Expected 'manifest.toml' to not exist"); - assert!(!manifest_json.exists(), "Expected 'manifest.json' to not exist"); + assert!(!dev_manifest_toml.exists(), "Expected 'manifest.toml' to not exist"); + assert!(!dev_manifest_json.exists(), "Expected 'manifest.json' to not exist"); } } diff --git a/bin/sozo/src/commands/dev.rs b/bin/sozo/src/commands/dev.rs index 53e7687ae8..72b8687cac 100644 --- a/bin/sozo/src/commands/dev.rs +++ b/bin/sozo/src/commands/dev.rs @@ -16,12 +16,6 @@ use super::options::world::WorldOptions; #[derive(Debug, Args)] pub struct DevArgs { - #[arg(long)] - #[arg(help = "Name of the World.")] - #[arg(long_help = "Name of the World. It's hash will be used as a salt when deploying the \ - contract to avoid address conflicts.")] - pub name: Option, - #[command(flatten)] pub world: WorldOptions, @@ -53,13 +47,9 @@ impl DevArgs { BuildArgs::default().run(config)?; info!("Initial build completed."); - let _ = MigrateArgs::new_apply( - self.name.clone(), - self.world.clone(), - self.starknet.clone(), - self.account.clone(), - ) - .run(config); + let _ = + MigrateArgs::new_apply(self.world.clone(), self.starknet.clone(), self.account.clone()) + .run(config); info!( directory = watched_directory.to_string(), @@ -89,7 +79,6 @@ impl DevArgs { let _ = BuildArgs::default().run(config); let _ = MigrateArgs::new_apply( - self.name.clone(), self.world.clone(), self.starknet.clone(), self.account.clone(), diff --git a/bin/sozo/src/commands/events.rs b/bin/sozo/src/commands/events.rs index c0766d8566..0028ca2558 100644 --- a/bin/sozo/src/commands/events.rs +++ b/bin/sozo/src/commands/events.rs @@ -49,8 +49,8 @@ impl EventsArgs { let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; trace!(ws_members_count = ws.members().count(), "Read workspace."); - let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); - trace!(?manifest_dir, "Manifest directory defined from workspace."); + let project_dir = ws.manifest_path().parent().unwrap().to_path_buf(); + trace!(?project_dir, "Project directory defined from workspace."); let provider = self.starknet.provider(env_metadata.as_ref())?; trace!(?provider, "Starknet RPC client provider."); @@ -79,7 +79,7 @@ impl EventsArgs { self.continuation_token, event_filter, self.json, - &manifest_dir, + &project_dir, &profile_name, ) .await diff --git a/bin/sozo/src/commands/execute.rs b/bin/sozo/src/commands/execute.rs index 243c1595a8..a100285df0 100644 --- a/bin/sozo/src/commands/execute.rs +++ b/bin/sozo/src/commands/execute.rs @@ -1,5 +1,7 @@ use anyhow::Result; use clap::Args; +use dojo_world::contracts::naming::ensure_namespace; +use dojo_world::metadata::get_default_namespace_from_ws; use scarb::core::Config; use sozo_ops::execute; use tracing::trace; @@ -14,9 +16,10 @@ use crate::utils; #[derive(Debug, Args)] #[command(about = "Execute a system with the given calldata.")] pub struct ExecuteArgs { - #[arg(help = "The address of the contract to be executed. Or fully qualified contract name \ - (ex: dojo_example::actions::actions")] - pub contract: String, + #[arg( + help = "The address or the tag (ex: dojo_examples:actions) of the contract to be executed." + )] + pub tag_or_address: String, #[arg(help = "The name of the entrypoint to be executed.")] pub entrypoint: String, @@ -49,6 +52,14 @@ impl ExecuteArgs { trace!(args = ?self); let env_metadata = utils::load_metadata_from_config(config)?; + let tag_or_address = if utils::is_address(&self.tag_or_address) { + self.tag_or_address + } else { + let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; + let default_namespace = get_default_namespace_from_ws(&ws); + ensure_namespace(&self.tag_or_address, &default_namespace) + }; + config.tokio_handle().block_on(async { let world = utils::world_from_env_metadata( self.world, @@ -62,7 +73,7 @@ impl ExecuteArgs { let tx_config = self.transaction.into(); trace!( - contract=?self.contract, + contract=?tag_or_address, entrypoint=self.entrypoint, calldata=?self.calldata, "Executing Execute command." @@ -76,7 +87,7 @@ impl ExecuteArgs { execute::execute( &config.ui(), - self.contract, + tag_or_address, self.entrypoint, calldata, &world, diff --git a/bin/sozo/src/commands/migrate.rs b/bin/sozo/src/commands/migrate.rs index c4dfc43fb5..c697a15e7c 100644 --- a/bin/sozo/src/commands/migrate.rs +++ b/bin/sozo/src/commands/migrate.rs @@ -24,13 +24,6 @@ pub struct MigrateArgs { #[command(subcommand)] pub command: MigrateCommand, - #[arg(long, global = true)] - #[arg(help = "Name of the World.")] - #[arg(long_help = "Name of the World. It's hash will be used as a salt when deploying the \ - contract to avoid address conflicts. If not provided root package's name \ - will be used.")] - name: Option, - #[command(flatten)] world: WorldOptions, @@ -57,14 +50,12 @@ pub enum MigrateCommand { impl MigrateArgs { /// Creates a new `MigrateArgs` with the `Apply` command. pub fn new_apply( - name: Option, world: WorldOptions, starknet: StarknetOptions, account: AccountOptions, ) -> Self { Self { command: MigrateCommand::Apply { transaction: TransactionOptions::init_wait() }, - name, world, starknet, account, @@ -74,15 +65,7 @@ impl MigrateArgs { pub fn run(self, config: &Config) -> Result<()> { trace!(args = ?self); let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; - - let dojo_metadata = if let Some(metadata) = dojo_metadata_from_workspace(&ws) { - metadata - } else { - return Err(anyhow!( - "No current package with dojo metadata found, migrate is not yet support for \ - workspaces." - )); - }; + let dojo_metadata = dojo_metadata_from_workspace(&ws)?; // This variant is tested before the match on `self.command` to avoid // having the need to spin up a Katana to generate the files. @@ -103,11 +86,9 @@ impl MigrateArgs { return Err(anyhow!("Build project using `sozo build` first")); } - let MigrateArgs { name, world, starknet, account, .. } = self; + let MigrateArgs { world, starknet, account, .. } = self; - let name = name.unwrap_or_else(|| { - ws.current_package().expect("Root package to be present").id.name.to_string() - }); + let name = dojo_metadata.world.seed; let (world_address, account, rpc_url) = config.tokio_handle().block_on(async { setup_env(&ws, account, starknet, world, &name, env_metadata.as_ref()).await diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index d004cf7f13..c0e1997582 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -1,5 +1,7 @@ use anyhow::Result; use clap::{Args, Subcommand}; +use dojo_world::contracts::naming::ensure_namespace; +use dojo_world::metadata::get_default_namespace_from_ws; use scarb::core::Config; use sozo_ops::model; use starknet::core::types::Felt; @@ -19,8 +21,8 @@ pub struct ModelArgs { pub enum ModelCommand { #[command(about = "Retrieve the class hash of a model")] ClassHash { - #[arg(help = "The name of the model")] - name: String, + #[arg(help = "The tag or name of the model")] + tag_or_name: String, #[command(flatten)] world: WorldOptions, @@ -31,8 +33,8 @@ pub enum ModelCommand { #[command(about = "Retrieve the contract address of a model")] ContractAddress { - #[arg(help = "The name of the model")] - name: String, + #[arg(help = "The tag or name of the model")] + tag_or_name: String, #[command(flatten)] world: WorldOptions, @@ -62,8 +64,8 @@ hashes, called 'hash' in the following documentation. final storage location = hash('dojo_storage', model_selector, record_key)")] Layout { - #[arg(help = "The name of the model")] - name: String, + #[arg(help = "The tag or name of the model")] + tag_or_name: String, #[command(flatten)] world: WorldOptions, @@ -74,8 +76,8 @@ hashes, called 'hash' in the following documentation. #[command(about = "Retrieve the schema for a model")] Schema { - #[arg(help = "The name of the model")] - name: String, + #[arg(help = "The tag or name of the model")] + tag_or_name: String, #[command(flatten)] world: WorldOptions, @@ -90,8 +92,8 @@ hashes, called 'hash' in the following documentation. #[command(about = "Get a models value for the provided key")] Get { - #[arg(help = "The name of the model")] - name: String, + #[arg(help = "The tag or name of the model")] + tag_or_name: String, #[arg(value_name = "KEYS")] #[arg(value_delimiter = ',')] @@ -109,34 +111,46 @@ hashes, called 'hash' in the following documentation. impl ModelArgs { pub fn run(self, config: &Config) -> Result<()> { trace!(args = ?self); + let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; let env_metadata = utils::load_metadata_from_config(config)?; + let default_namespace = get_default_namespace_from_ws(&ws); config.tokio_handle().block_on(async { match self.command { - ModelCommand::ClassHash { name, starknet, world } => { + ModelCommand::ClassHash { tag_or_name, starknet, world } => { + let tag = ensure_namespace(&tag_or_name, &default_namespace); + let world_address = world.address(env_metadata.as_ref()).unwrap(); let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_class_hash(name, world_address, provider).await + model::model_class_hash(tag, world_address, provider).await } - ModelCommand::ContractAddress { name, starknet, world } => { + ModelCommand::ContractAddress { tag_or_name, starknet, world } => { + let tag = ensure_namespace(&tag_or_name, &default_namespace); + let world_address = world.address(env_metadata.as_ref()).unwrap(); let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_contract_address(name, world_address, provider).await + model::model_contract_address(tag, world_address, provider).await } - ModelCommand::Layout { name, starknet, world } => { + ModelCommand::Layout { tag_or_name, starknet, world } => { + let tag = ensure_namespace(&tag_or_name, &default_namespace); + 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 + model::model_layout(tag, world_address, provider).await } - ModelCommand::Schema { name, to_json, starknet, world } => { + ModelCommand::Schema { tag_or_name, to_json, starknet, world } => { + let tag = ensure_namespace(&tag_or_name, &default_namespace); + let world_address = world.address(env_metadata.as_ref()).unwrap(); let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_schema(name, world_address, provider, to_json).await + model::model_schema(tag, world_address, provider, to_json).await } - ModelCommand::Get { name, keys, starknet, world } => { + ModelCommand::Get { tag_or_name, keys, starknet, world } => { + let tag = ensure_namespace(&tag_or_name, &default_namespace); + let world_address = world.address(env_metadata.as_ref()).unwrap(); let provider = starknet.provider(env_metadata.as_ref()).unwrap(); - model::model_get(name, keys, world_address, provider).await + model::model_get(tag, keys, world_address, provider).await } } }) diff --git a/bin/sozo/src/commands/options/account/controller.rs b/bin/sozo/src/commands/options/account/controller.rs index 3cdd75fad2..cc2b385ba4 100644 --- a/bin/sozo/src/commands/options/account/controller.rs +++ b/bin/sozo/src/commands/options/account/controller.rs @@ -4,6 +4,7 @@ use account_sdk::deploy_contract::UDC_ADDRESS; use account_sdk::signers::HashSigner; use anyhow::{Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; +use dojo_world::contracts::naming::get_name_from_tag; use dojo_world::manifest::{BaseManifest, DojoContract, Manifest}; use dojo_world::migration::strategy::generate_salt; use scarb::core::Config; @@ -155,13 +156,13 @@ fn collect_policies_from_base_manifest( let contract_address = get_dojo_contract_address(world_address, &contract); let abis = contract.inner.abi.unwrap().load_abi_string(&base_path)?; let abis = serde_json::from_str::>(&abis)?; - policies_from_abis(&mut policies, &contract.name, contract_address, &abis); + policies_from_abis(&mut policies, &contract.inner.tag, contract_address, &abis); } // get method from world contract let abis = manifest.world.inner.abi.unwrap().load_abi_string(&base_path)?; let abis = serde_json::from_str::>(&abis)?; - policies_from_abis(&mut policies, &manifest.world.name, world_address, &abis); + policies_from_abis(&mut policies, "world", world_address, &abis); // special policy for sending declare tx // corresponds to [account_sdk::account::DECLARATION_SELECTOR] @@ -181,7 +182,7 @@ fn collect_policies_from_base_manifest( /// ABIs in the project. fn policies_from_abis( policies: &mut Vec, - contract_name: &str, + contract_tag: &str, contract_address: Felt, entries: &[AbiEntry], ) { @@ -191,13 +192,13 @@ fn policies_from_abis( // we only create policies for non-view functions if let StateMutability::External = f.state_mutability { let policy = Policy { target: contract_address, method: f.name.to_string() }; - trace!(name = contract_name, target = format!("{:#x}", policy.target), method = %policy.method, "Adding policy"); + trace!(tag = contract_tag, target = format!("{:#x}", policy.target), method = %policy.method, "Adding policy"); policies.push(policy); } } AbiEntry::Interface(i) => { - policies_from_abis(policies, contract_name, contract_address, &i.items) + policies_from_abis(policies, contract_tag, contract_address, &i.items) } _ => {} @@ -209,7 +210,7 @@ fn get_dojo_contract_address(world_address: Felt, manifest: &Manifest Result, Error> { let env_metadata = if config.manifest_path().exists() { let ws = scarb::ops::read_workspace(config.manifest_path(), config)?; - let dojo_metadata = if let Some(metadata) = dojo_metadata_from_workspace(&ws) { - metadata - } else { - return Err(anyhow!( - "No current package with dojo metadata found, workspaces are not suppored yet." - )); - }; + let dojo_metadata = dojo_metadata_from_workspace(&ws)?; dojo_metadata.env().cloned() } else { @@ -135,3 +129,7 @@ pub fn generate_version() -> String { ); version_string } + +pub fn is_address(tag_or_address: &str) -> bool { + tag_or_address.starts_with("0x") +} diff --git a/bin/sozo/tests/register_test.rs b/bin/sozo/tests/register_test.rs index 31607e3624..f9dd35f063 100644 --- a/bin/sozo/tests/register_test.rs +++ b/bin/sozo/tests/register_test.rs @@ -3,9 +3,10 @@ mod utils; use camino::Utf8PathBuf; use dojo_test_utils::compiler; use dojo_test_utils::migration::prepare_migration; -use dojo_world::metadata::dojo_metadata_from_workspace; +use dojo_world::metadata::{dojo_metadata_from_workspace, get_default_namespace_from_ws}; use dojo_world::migration::TxnConfig; use katana_runner::KatanaRunner; +use scarb::compiler::Profile; use scarb::ops; use sozo_ops::migration::execute_strategy; use starknet::accounts::Account; @@ -17,7 +18,7 @@ async fn reregister_models() { let source_project_dir = Utf8PathBuf::from("../../examples/spawn-and-move/"); let dojo_core_path = Utf8PathBuf::from("../../crates/dojo-core"); - let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path); + let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path, Profile::DEV); let ws = ops::read_workspace(config.manifest_path(), &config) .unwrap_or_else(|op| panic!("Error building workspace: {op:?}")); @@ -27,9 +28,15 @@ async fn reregister_models() { let target_path = ws.target_dir().path_existent().unwrap().join(ws.config().profile().to_string()); - let migration = - prepare_migration(source_project_dir.clone(), target_path, dojo_metadata.skip_migration) - .unwrap(); + let default_namespace = get_default_namespace_from_ws(&ws); + + let migration = prepare_migration( + source_project_dir.clone(), + target_path, + dojo_metadata.skip_migration, + &default_namespace, + ) + .unwrap(); let sequencer = KatanaRunner::new().expect("Failed to start runner."); @@ -44,7 +51,7 @@ async fn reregister_models() { let rpc_url = &sequencer.url().to_string(); let moves_model = - migration.models.iter().find(|m| m.diff.name == "dojo_examples::models::moves").unwrap(); + migration.models.iter().find(|m| m.diff.tag == "dojo_examples-Moves").unwrap(); let moves_model_class_hash = &format!("0x{:x}", moves_model.diff.local_class_hash); let args_vec = [ "register", diff --git a/bin/sozo/tests/test_build.rs b/bin/sozo/tests/test_build.rs index 552f62f1fc..722d231036 100644 --- a/bin/sozo/tests/test_build.rs +++ b/bin/sozo/tests/test_build.rs @@ -3,11 +3,12 @@ mod utils; use std::fs; use utils::snapbox::get_snapbox; -use utils::stdout::expected_stdout; #[test] fn test_invalid_cairo_version() { let path = fs::canonicalize("./tests/test_data/invalid_cairo_version"); let assert = get_snapbox().arg("build").current_dir(path.unwrap()).assert().failure(); - assert.stdout_eq(expected_stdout("wrong-cairo-version")); + assert.stdout_matches( + "[..]Specified cairo version not supported by dojo. Please verify and update dojo[..]", + ); } diff --git a/bin/sozo/tests/test_migrate.rs b/bin/sozo/tests/test_migrate.rs new file mode 100644 index 0000000000..35d4a57fa7 --- /dev/null +++ b/bin/sozo/tests/test_migrate.rs @@ -0,0 +1,47 @@ +mod utils; + +use camino::Utf8PathBuf; +use dojo_test_utils::compiler; +use katana_runner::KatanaRunner; +use scarb::compiler::Profile; +use starknet::accounts::Account; +use starknet::core::types::{BlockId, BlockTag}; +use utils::snapbox::get_snapbox; + +#[tokio::test(flavor = "multi_thread")] +async fn migrate_dry_run() { + let source_project_dir = Utf8PathBuf::from("../../examples/spawn-and-move/"); + let dojo_core_path = Utf8PathBuf::from("../../crates/dojo-core"); + + let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path, Profile::DEV); + + let sequencer = KatanaRunner::new().expect("Failed to start runner."); + + let mut account = sequencer.account(0); + account.set_block_id(BlockId::Tag(BlockTag::Pending)); + + let account_address = &format!("0x{:x}", account.address()); + let private_key = + &format!("0x{:x}", sequencer.account_data(0).private_key.as_ref().unwrap().secret_scalar()); + let rpc_url = &sequencer.url().to_string(); + + let args_vec = [ + "migrate", + "plan", + "--account-address", + account_address, + "--rpc-url", + rpc_url, + "--private-key", + private_key, + "--manifest-path", + config.manifest_path().as_ref(), + ]; + + let assert = get_snapbox().args(args_vec.iter()).assert().success(); + assert!(format!("{:?}", assert.get_output()).contains("Migration Strategy")); + assert!(format!("{:?}", assert.get_output()).contains("# Base Contract")); + assert!(format!("{:?}", assert.get_output()).contains("# Models (8)")); + assert!(format!("{:?}", assert.get_output()).contains("# World")); + assert!(format!("{:?}", assert.get_output()).contains("# Contracts (3)")); +} diff --git a/crates/benches/contracts/src/tests/test_world.cairo b/crates/benches/contracts/src/tests/test_world.cairo index b7c8309913..74dfd8023e 100644 --- a/crates/benches/contracts/src/tests/test_world.cairo +++ b/crates/benches/contracts/src/tests/test_world.cairo @@ -25,7 +25,7 @@ mod tests { let mut models = array![position::TEST_CLASS_HASH, moves::TEST_CLASS_HASH]; // deploy world with models - let world = spawn_test_world(models); + let world = spawn_test_world("benches", models); // deploy systems contract let contract_address = world diff --git a/crates/dojo-bindgen/src/lib.rs b/crates/dojo-bindgen/src/lib.rs index 97e8074868..06b2225ada 100644 --- a/crates/dojo-bindgen/src/lib.rs +++ b/crates/dojo-bindgen/src/lib.rs @@ -5,10 +5,9 @@ use std::path::PathBuf; use cainome::parser::tokens::Token; use cainome::parser::{AbiParser, TokenizedAbi}; use camino::Utf8PathBuf; -use convert_case::{Case, Casing}; use dojo_world::manifest::BaseManifest; pub mod error; -use error::{BindgenResult, Error}; +use error::BindgenResult; mod plugins; use plugins::typescript::TypescriptPlugin; @@ -19,10 +18,8 @@ pub use plugins::BuiltinPlugins; #[derive(Debug, PartialEq)] pub struct DojoModel { - /// PascalCase name of the model. - pub name: String, - /// Fully qualified path of the model type in cairo code. - pub qualified_path: String, + /// model tag. + pub tag: String, /// List of tokens found in the model contract ABI. /// Only structs and enums are currently used. pub tokens: TokenizedAbi, @@ -30,8 +27,8 @@ pub struct DojoModel { #[derive(Debug, PartialEq)] pub struct DojoContract { - /// Contract's fully qualified name. - pub qualified_path: String, + /// Contract tag. + pub tag: String, /// Full ABI of the contract in case the plugin wants to make extra checks, /// or generated other functions than the systems. pub tokens: TokenizedAbi, @@ -122,7 +119,7 @@ fn gather_dojo_data( let mut base_manifest = BaseManifest::load_from_path(&base_manifest_dir)?; if let Some(skip_manifests) = skip_migration { - base_manifest.remove_items(skip_manifests); + base_manifest.remove_tags(skip_manifests); } let mut models = HashMap::new(); @@ -138,6 +135,7 @@ fn gather_dojo_data( .load_abi_string(&root_dir)?; let tokens = AbiParser::tokens_from_abi_string(&abi, &HashMap::new())?; + let tag = contract_manifest.inner.tag.clone(); // Identify the systems -> for now only take the functions from the // interfaces. @@ -151,12 +149,7 @@ fn gather_dojo_data( } } - let contract_name = contract_manifest.name.to_string(); - - contracts.insert( - contract_name.clone(), - DojoContract { qualified_path: contract_name, tokens, systems }, - ); + contracts.insert(tag.clone(), DojoContract { tag, tokens, systems }); } for model_manifest in &base_manifest.models { @@ -169,27 +162,11 @@ fn gather_dojo_data( .load_abi_string(&root_dir)?; let tokens = AbiParser::tokens_from_abi_string(&abi, &HashMap::new())?; + let tag = model_manifest.inner.tag.clone(); - let name = model_manifest.name.to_string(); + let model = DojoModel { tag: tag.clone(), tokens: filter_model_tokens(&tokens) }; - if let Some(model_name) = model_name_from_fully_qualified_path(&name) { - let model_pascal_case = model_name.from_case(Case::Snake).to_case(Case::Pascal); - - let model = DojoModel { - name: model_pascal_case.clone(), - qualified_path: name - .replace(&model_name, &model_pascal_case) - .trim_end_matches(".json") - .to_string(), - tokens: filter_model_tokens(&tokens), - }; - - models.insert(model_pascal_case, model); - } else { - return Err(Error::Format(format!( - "Could not extract model name from file name `{name}`" - ))); - } + models.insert(tag.clone(), model); } let world = DojoWorld { name: root_package_name.to_string() }; @@ -234,36 +211,14 @@ fn filter_model_tokens(tokens: &TokenizedAbi) -> TokenizedAbi { TokenizedAbi { structs, enums, ..Default::default() } } -/// Extracts a model name from the fully qualified path of the model. -/// -/// # Example -/// -/// The fully qualified name "dojo_examples::models::position" should return "position". -/// -/// # Arguments -/// -/// * `file_name` - Fully qualified model name. -fn model_name_from_fully_qualified_path(file_name: &str) -> Option { - let parts: Vec<&str> = file_name.split("::").collect(); - - // TODO: we may want to have inside the manifest the name of the model struct - // instead of extracting it from the file's name. - parts.last().map(|last_part| last_part.to_string()) -} - #[cfg(test)] mod tests { use dojo_test_utils::compiler; use dojo_world::metadata::dojo_metadata_from_workspace; + use scarb::compiler::Profile; use super::*; - #[test] - fn model_name_from_fully_qualified_path_ok() { - let file_name = "dojo_examples::models::position"; - assert_eq!(model_name_from_fully_qualified_path(file_name), Some("position".to_string())); - } - #[test] fn gather_data_ok() { let manifest_path = Utf8PathBuf::from("src/test_data/spawn-and-move/Scarb.toml"); @@ -271,6 +226,7 @@ mod tests { let config = compiler::copy_tmp_config( &Utf8PathBuf::from("../../examples/spawn-and-move"), &Utf8PathBuf::from("../dojo-core"), + Profile::DEV, ); let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); @@ -287,20 +243,16 @@ mod tests { assert_eq!(data.world.name, "dojo_example"); - let pos = data.models.get("Position").unwrap(); - assert_eq!(pos.name, "Position"); - assert_eq!(pos.qualified_path, "dojo_examples::models::Position"); + let pos = data.models.get("dojo_examples-Position").unwrap(); + assert_eq!(pos.tag, "dojo_examples-Position"); - let moves = data.models.get("Moves").unwrap(); - assert_eq!(moves.name, "Moves"); - assert_eq!(moves.qualified_path, "dojo_examples::models::Moves"); + let moves = data.models.get("dojo_examples-Moves").unwrap(); + assert_eq!(moves.tag, "dojo_examples-Moves"); - let moved = data.models.get("Message").unwrap(); - assert_eq!(moved.name, "Message"); - assert_eq!(moved.qualified_path, "dojo_examples::models::Message"); + let moved = data.models.get("dojo_examples-Message").unwrap(); + assert_eq!(moved.tag, "dojo_examples-Message"); - let player_config = data.models.get("PlayerConfig").unwrap(); - assert_eq!(player_config.name, "PlayerConfig"); - assert_eq!(player_config.qualified_path, "dojo_examples::models::PlayerConfig"); + let player_config = data.models.get("dojo_examples-PlayerConfig").unwrap(); + assert_eq!(player_config.tag, "dojo_examples-PlayerConfig"); } } diff --git a/crates/dojo-bindgen/src/plugins/typescript/mod.rs b/crates/dojo-bindgen/src/plugins/typescript/mod.rs index 9b00739fe8..d44496b9b7 100644 --- a/crates/dojo-bindgen/src/plugins/typescript/mod.rs +++ b/crates/dojo-bindgen/src/plugins/typescript/mod.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use async_trait::async_trait; use cainome::parser::tokens::{Composite, CompositeType, Function, Token}; +use dojo_world::contracts::naming; use crate::error::BindgenResult; use crate::plugins::BuiltinPlugin; @@ -264,7 +265,7 @@ type {} = ", } // first index is our model struct - if token.type_name() == model.name { + if token.type_name() == naming::get_name_from_tag(&model.tag) { models_structs.push(token.to_composite().unwrap().clone()); } @@ -378,12 +379,10 @@ export function defineContractComponents(world: World) { ) } - // Formats a contract file path into a pretty contract name - // eg. dojo_examples::actions::actions.json -> Actions - fn formatted_contract_name(contract_file_name: &str) -> String { - let contract_name = - contract_file_name.split("::").last().unwrap().trim_end_matches(".json"); - contract_name.to_string() + // Formats a contract tag into a pretty contract name + // eg. dojo_examples-actions -> Actions + fn formatted_contract_name(tag: &str) -> String { + naming::capitalize(&naming::get_name_from_tag(tag)) } // Handles a contract definition and its underlying systems @@ -430,10 +429,10 @@ export function defineContractComponents(world: World) { }}; }} ", - contract.qualified_path, + contract.tag, // capitalize contract name - TypescriptPlugin::formatted_contract_name(&contract.qualified_path), - TypescriptPlugin::formatted_contract_name(&contract.qualified_path), + TypescriptPlugin::formatted_contract_name(&contract.tag), + TypescriptPlugin::formatted_contract_name(&contract.tag), systems, contract .systems @@ -453,8 +452,8 @@ export function defineContractComponents(world: World) { .map(|c| { format!( "{}: {}()", - TypescriptPlugin::formatted_contract_name(&c.qualified_path), - TypescriptPlugin::formatted_contract_name(&c.qualified_path) + TypescriptPlugin::formatted_contract_name(&c.tag), + TypescriptPlugin::formatted_contract_name(&c.tag) ) }) .collect::>() diff --git a/crates/dojo-bindgen/src/plugins/typescript_v2/mod.rs b/crates/dojo-bindgen/src/plugins/typescript_v2/mod.rs index d7846ceef0..a4b8c1076e 100644 --- a/crates/dojo-bindgen/src/plugins/typescript_v2/mod.rs +++ b/crates/dojo-bindgen/src/plugins/typescript_v2/mod.rs @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf}; use async_trait::async_trait; use cainome::parser::tokens::{Composite, CompositeType, Function, Token}; use convert_case::Casing; +use dojo_world::contracts::naming; use crate::error::BindgenResult; use crate::plugins::BuiltinPlugin; @@ -112,10 +113,15 @@ import { let mut result_mapping = Vec::new(); for model in models { - query_fields - .push(format!("{model_name}: ModelClause<{model_name}>;", model_name = model.name)); - - result_mapping.push(format!("{model_name}: {model_name};", model_name = model.name)); + query_fields.push(format!( + "{model_name}: ModelClause<{model_name}>;", + model_name = naming::get_name_from_tag(&model.tag) + )); + + result_mapping.push(format!( + "{model_name}: {model_name};", + model_name = naming::get_name_from_tag(&model.tag) + )); } format!( @@ -241,7 +247,7 @@ function convertQueryToToriiClause(query: Query): Clause | undefined {{ {} }} ", - TypeScriptV2Plugin::formatted_contract_name(&contract.qualified_path) + TypeScriptV2Plugin::formatted_contract_name(&contract.tag) .to_case(convert_case::Case::Pascal), systems, ); @@ -256,7 +262,7 @@ function convertQueryToToriiClause(query: Query): Clause | undefined {{ .map(|contract| { format!( "{}Address: string;", - TypeScriptV2Plugin::formatted_contract_name(&contract.qualified_path) + TypeScriptV2Plugin::formatted_contract_name(&contract.tag) .to_case(convert_case::Case::Camel) ) }) @@ -299,12 +305,10 @@ function convertQueryToToriiClause(query: Query): Clause | undefined {{ format!( "{camel_case_name}: {pascal_case_name}Calls; {camel_case_name}Address: string;", - camel_case_name = - TypeScriptV2Plugin::formatted_contract_name(&contract.qualified_path) - .to_case(convert_case::Case::Camel), - pascal_case_name = - TypeScriptV2Plugin::formatted_contract_name(&contract.qualified_path) - .to_case(convert_case::Case::Pascal) + camel_case_name = TypeScriptV2Plugin::formatted_contract_name(&contract.tag) + .to_case(convert_case::Case::Camel), + pascal_case_name = TypeScriptV2Plugin::formatted_contract_name(&contract.tag) + .to_case(convert_case::Case::Pascal) ) }) .collect::>() @@ -324,9 +328,8 @@ function convertQueryToToriiClause(query: Query): Clause | undefined {{ }} this.{contract_name}Address = {contract_name}Address;", - contract_name = - TypeScriptV2Plugin::formatted_contract_name(&contract.qualified_path) - .to_case(convert_case::Case::Camel) + contract_name = TypeScriptV2Plugin::formatted_contract_name(&contract.tag) + .to_case(convert_case::Case::Camel) ) }) .collect::>() @@ -337,9 +340,8 @@ function convertQueryToToriiClause(query: Query): Clause | undefined {{ .map(|contract| { format!( "this.{camel_case_name}Address = params.{camel_case_name}Address;", - camel_case_name = - TypeScriptV2Plugin::formatted_contract_name(&contract.qualified_path) - .to_case(convert_case::Case::Camel), + camel_case_name = TypeScriptV2Plugin::formatted_contract_name(&contract.tag) + .to_case(convert_case::Case::Camel), ) }) .collect::>() @@ -351,12 +353,10 @@ function convertQueryToToriiClause(query: Query): Clause | undefined {{ format!( "this.{camel_case_name} = new \ {pascal_case_name}Calls(this.{camel_case_name}Address, this._account);", - camel_case_name = - TypeScriptV2Plugin::formatted_contract_name(&contract.qualified_path) - .to_case(convert_case::Case::Camel), - pascal_case_name = - TypeScriptV2Plugin::formatted_contract_name(&contract.qualified_path) - .to_case(convert_case::Case::Pascal) + camel_case_name = TypeScriptV2Plugin::formatted_contract_name(&contract.tag) + .to_case(convert_case::Case::Camel), + pascal_case_name = TypeScriptV2Plugin::formatted_contract_name(&contract.tag) + .to_case(convert_case::Case::Pascal) ) }) .collect::>() @@ -576,12 +576,10 @@ type {} = ", ) } - // Formats a contract file path into a pretty contract name - // eg. dojo_examples::actions::actions.json -> Actions - fn formatted_contract_name(contract_file_name: &str) -> String { - let contract_name = - contract_file_name.split("::").last().unwrap().trim_end_matches(".json"); - contract_name.to_string() + // Formats a contract tag into a pretty contract name + // eg. dojo_examples-actions -> Actions + fn formatted_contract_name(tag: &str) -> String { + naming::capitalize(&naming::get_name_from_tag(tag)) } fn generate_code_content(data: &DojoData) -> String { @@ -632,6 +630,7 @@ mod tests { use camino::Utf8PathBuf; use dojo_test_utils::compiler; use dojo_world::metadata::dojo_metadata_from_workspace; + use scarb::compiler::Profile; use super::*; use crate::gather_dojo_data; @@ -652,6 +651,7 @@ mod tests { let config = compiler::copy_tmp_config( &Utf8PathBuf::from("../../examples/spawn-and-move"), &Utf8PathBuf::from("../dojo-core"), + Profile::DEV, ); let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); diff --git a/crates/dojo-bindgen/src/plugins/unity/mod.rs b/crates/dojo-bindgen/src/plugins/unity/mod.rs index 8fe748a940..dadd24dd20 100644 --- a/crates/dojo-bindgen/src/plugins/unity/mod.rs +++ b/crates/dojo-bindgen/src/plugins/unity/mod.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use async_trait::async_trait; use cainome::parser::tokens::{Composite, CompositeType, Function, FunctionOutputKind, Token}; +use dojo_world::contracts::naming; use crate::error::BindgenResult; use crate::plugins::BuiltinPlugin; @@ -235,7 +236,7 @@ public class {} : ModelInstance {{ handled_tokens.insert(token.type_path(), token.to_composite().unwrap().to_owned()); // first index is our model struct - if token.type_name() == model.name { + if token.type_name() == naming::get_name_from_tag(&model.tag) { model_struct = Some(token.to_composite().unwrap()); continue; } @@ -474,13 +475,10 @@ public class {} : ModelInstance {{ ) } - // Formats a contract file path into a pretty contract name - // eg. dojo_examples::actions::actions.json -> Actions - fn formatted_contract_name(contract_file_name: &str) -> String { - let contract_name = - contract_file_name.split("::").last().unwrap().trim_end_matches(".json"); - // capitalize contract name - contract_name.chars().next().unwrap().to_uppercase().to_string() + &contract_name[1..] + // Formats a contract tag into a pretty contract name + // eg. dojo_examples-actions.json -> Actions + fn formatted_contract_name(tag: &str) -> String { + naming::capitalize(&naming::get_name_from_tag(tag)) } // Handles a contract definition and its underlying systems @@ -515,9 +513,9 @@ public class {} : MonoBehaviour {{ {} }} ", - contract.qualified_path, + contract.tag, // capitalize contract name - UnityPlugin::formatted_contract_name(&contract.qualified_path), + UnityPlugin::formatted_contract_name(&contract.tag), systems ); diff --git a/crates/dojo-core/Scarb.lock b/crates/dojo-core/Scarb.lock index 137f6cc0e0..7ce0b7bfe8 100644 --- a/crates/dojo-core/Scarb.lock +++ b/crates/dojo-core/Scarb.lock @@ -10,5 +10,5 @@ dependencies = [ [[package]] name = "dojo_plugin" -version = "0.3.11" -source = "git+https://github.com/dojoengine/dojo?tag=v0.3.11#1e651b5d4d3b79b14a7d8aa29a92062fcb9e6659" +version = "0.4.1" +source = "git+https://github.com/dojoengine/dojo?tag=v0.7.2#3da5cad9fdd39b81551e0668015d88262e6c5fc4" diff --git a/crates/dojo-core/src/base_test.cairo b/crates/dojo-core/src/base_test.cairo index a90d40b071..0532c1fa1b 100644 --- a/crates/dojo-core/src/base_test.cairo +++ b/crates/dojo-core/src/base_test.cairo @@ -49,7 +49,7 @@ use contract_upgrade::{IQuantumLeapDispatcher, IQuantumLeapDispatcherTrait}; // Utils fn deploy_world() -> IWorldDispatcher { - spawn_test_world(array![]) + spawn_test_world("dojo", array![]) } // A test contract needs to be used instead of previously used base contract since. @@ -111,6 +111,8 @@ fn test_upgrade_direct() { trait IMetadataOnly { fn selector(self: @T) -> felt252; fn name(self: @T) -> ByteArray; + fn namespace(self: @T) -> ByteArray; + fn namespace_selector(self: @T) -> felt252; } #[starknet::contract] @@ -125,6 +127,14 @@ mod invalid_legacy_model { 0x1b1edb46931b1a98d8c6ecf2703e8483ec1d85fb75b3e9c061eab383fc8f8f1 } + fn namespace(self: @ContractState) -> ByteArray { + "dojo" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::utils::hash(@Self::namespace(self)) + } + fn name(self: @ContractState) -> ByteArray { "invalid_legacy_model" } @@ -144,6 +154,14 @@ mod invalid_legacy_model_world { 0 } + fn namespace(self: @ContractState) -> ByteArray { + "dojo" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::utils::hash(@Self::namespace(self)) + } + fn name(self: @ContractState) -> ByteArray { "invalid_legacy_model" } @@ -160,7 +178,15 @@ mod invalid_model { fn selector(self: @ContractState) -> felt252 { // NOTE: Need to update this value if address changes // Pre-computed address of a contract deployed through the world. - 0x1b1edb46931b1a98d8c6ecf2703e8483ec1d85fb75b3e9c061eab383fc8f8f1 + 0x3f692e9669a95a2ace68e1eec4fdc26594d4b1413d78a62262249d9108c4194 + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::utils::hash(@Self::namespace(self)) } fn name(self: @ContractState) -> ByteArray { @@ -182,6 +208,14 @@ mod invalid_model_world { 0 } + fn namespace(self: @ContractState) -> ByteArray { + "dojo" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::utils::hash(@Self::namespace(self)) + } + fn name(self: @ContractState) -> ByteArray { "invalid_model_world" } diff --git a/crates/dojo-core/src/contract.cairo b/crates/dojo-core/src/contract.cairo new file mode 100644 index 0000000000..c791225c3e --- /dev/null +++ b/crates/dojo-core/src/contract.cairo @@ -0,0 +1,15 @@ +#[starknet::interface] +trait IContract { + fn contract_name(self: @T) -> ByteArray; + fn selector(self: @T) -> felt252; + + /// Returns the namespace of a contract. + fn namespace(self: @T) -> ByteArray; + + // Returns the namespace selector built from its name. + /// namespace_selector = hash(namespace_name) + fn namespace_selector(self: @T) -> felt252; + + // Returns the contract tag + fn tag(self: @T) -> ByteArray; +} diff --git a/crates/dojo-core/src/lib.cairo b/crates/dojo-core/src/lib.cairo index adbe199c73..2aed7f15fe 100644 --- a/crates/dojo-core/src/lib.cairo +++ b/crates/dojo-core/src/lib.cairo @@ -7,12 +7,16 @@ mod database; mod database_test; mod interfaces; mod model; +mod contract; mod packing; #[cfg(test)] mod packing_test; mod world; #[cfg(test)] mod world_test; +mod utils; +#[cfg(test)] +mod utils_test; // Since Scarb 2.6.0 there's an optimization that does not // build tests for dependencies and it's not configurable. diff --git a/crates/dojo-core/src/model.cairo b/crates/dojo-core/src/model.cairo index 934e3619c7..f577b41d58 100644 --- a/crates/dojo-core/src/model.cairo +++ b/crates/dojo-core/src/model.cairo @@ -5,10 +5,26 @@ trait Model { fn entity( world: IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout ) -> T; + + /// Returns the name of the model as it was written in Cairo code. fn name() -> ByteArray; fn version() -> u8; + + /// Returns the model selector built from its name and its namespace. + /// model selector = hash(hash(namespace_name), hash(model_name)) fn selector() -> felt252; fn instance_selector(self: @T) -> felt252; + + /// Returns the namespace of the model as it was written in the `dojo::model` attribute. + fn namespace() -> ByteArray; + + /// Returns the model namespace selector built from its namespace. + /// namespace_selector = hash(namespace_name) + fn namespace_selector() -> felt252; + + // Returns the model tag + fn tag() -> ByteArray; + fn keys(self: @T) -> Span; fn values(self: @T) -> Span; fn layout() -> dojo::database::introspect::Layout; @@ -21,6 +37,9 @@ trait IModel { fn selector(self: @T) -> felt252; fn name(self: @T) -> ByteArray; fn version(self: @T) -> u8; + fn namespace(self: @T) -> ByteArray; + fn namespace_selector(self: @T) -> felt252; + fn tag(self: @T) -> ByteArray; fn unpacked_size(self: @T) -> Option; fn packed_size(self: @T) -> Option; fn layout(self: @T) -> dojo::database::introspect::Layout; @@ -36,12 +55,14 @@ trait IModel { /// * `class_hash` - Class Hash of the model. fn deploy_and_get_metadata( salt: felt252, class_hash: starknet::ClassHash -) -> SyscallResult<(starknet::ContractAddress, ByteArray, felt252)> { +) -> SyscallResult<(starknet::ContractAddress, ByteArray, felt252, ByteArray, felt252)> { let (contract_address, _) = starknet::deploy_syscall( class_hash, salt, array![].span(), false, )?; let model = IModelDispatcher { contract_address }; let name = model.name(); let selector = model.selector(); - Result::Ok((contract_address, name, selector)) + let namespace = model.namespace(); + let namespace_selector = model.namespace_selector(); + Result::Ok((contract_address, name, selector, namespace, namespace_selector)) } diff --git a/crates/dojo-core/src/packing_test.cairo b/crates/dojo-core/src/packing_test.cairo index 58d340aff6..5a26c19e3b 100644 --- a/crates/dojo-core/src/packing_test.cairo +++ b/crates/dojo-core/src/packing_test.cairo @@ -349,9 +349,6 @@ fn test_pack_with_offset() { assert!(packed.len() == 2, "bad packed length"); - println!("first item: {}", *packed.at(0)); - println!("second item: {}", *packed.at(1)); - assert!(*packed.at(0) == 0x70006, "bad packed first item"); assert!(*packed.at(1) == 0x0900000000000000000000000000000008, "bad packed second item"); } diff --git a/crates/dojo-core/src/resource_metadata.cairo b/crates/dojo-core/src/resource_metadata.cairo index 16935479b8..270742a521 100644 --- a/crates/dojo-core/src/resource_metadata.cairo +++ b/crates/dojo-core/src/resource_metadata.cairo @@ -3,12 +3,9 @@ //! Manually expand to ensure that dojo-core //! does not depend on dojo plugin to be built. //! -use dojo::world::IWorldDispatcherTrait; - +use dojo::world::{IWorldDispatcherTrait}; use dojo::model::Model; -const RESOURCE_METADATA_SELECTOR: felt252 = selector!("ResourceMetadata"); - fn initial_address() -> starknet::ContractAddress { starknet::contract_address_const::<0>() } @@ -19,7 +16,7 @@ fn initial_class_hash() -> starknet::ClassHash { >() } -#[derive(Drop, Serde, PartialEq, Clone)] +#[derive(Drop, Serde, PartialEq, Clone, Debug)] struct ResourceMetadata { // #[key] resource_id: felt252, @@ -32,7 +29,7 @@ impl ResourceMetadataModel of dojo::model::Model { keys: Span, layout: dojo::database::introspect::Layout ) -> ResourceMetadata { - let values = world.entity(RESOURCE_METADATA_SELECTOR, keys, layout); + let values = world.entity(Self::selector(), keys, layout); let mut serialized = core::array::ArrayTrait::new(); core::array::serialize_array_helper(keys, ref serialized); core::array::serialize_array_helper(values, ref serialized); @@ -60,7 +57,9 @@ impl ResourceMetadataModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - RESOURCE_METADATA_SELECTOR + poseidon::poseidon_hash_span( + array![Self::namespace_selector(), dojo::utils::hash(@Self::name())].span() + ) } #[inline(always)] @@ -68,6 +67,18 @@ impl ResourceMetadataModel of dojo::model::Model { Self::selector() } + fn namespace() -> ByteArray { + "__DOJO__" + } + + fn namespace_selector() -> felt252 { + dojo::utils::hash(@Self::namespace()) + } + + fn tag() -> ByteArray { + "__DOJO__:ResourceMetadata" + } + #[inline(always)] fn keys(self: @ResourceMetadata) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -145,7 +156,6 @@ impl ResourceMetadataIntrospect<> of dojo::database::introspect::Introspect ByteArray { + ResourceMetadataModel::namespace() + } + #[external(v0)] fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() diff --git a/crates/dojo-core/src/test_utils.cairo b/crates/dojo-core/src/test_utils.cairo index 1bb6c91286..8efd9e2ccf 100644 --- a/crates/dojo-core/src/test_utils.cairo +++ b/crates/dojo-core/src/test_utils.cairo @@ -40,7 +40,7 @@ fn deploy_with_world_address(class_hash: felt252, world: IWorldDispatcher) -> Co deploy_contract(class_hash, array![world.contract_address.into()].span()) } -fn spawn_test_world(models: Array) -> IWorldDispatcher { +fn spawn_test_world(namespace: ByteArray, models: Array) -> IWorldDispatcher { let salt = testing::get_available_gas(); // deploy world @@ -54,6 +54,9 @@ fn spawn_test_world(models: Array) -> IWorldDispatcher { let world = IWorldDispatcher { contract_address: world_address }; + // register namespace + world.register_namespace(namespace); + // register models let mut index = 0; loop { diff --git a/crates/dojo-core/src/utils.cairo b/crates/dojo-core/src/utils.cairo new file mode 100644 index 0000000000..92654ce538 --- /dev/null +++ b/crates/dojo-core/src/utils.cairo @@ -0,0 +1,6 @@ +/// Compute the poseidon hash of a serialized ByteArray +fn hash(data: @ByteArray) -> felt252 { + let mut serialized = ArrayTrait::new(); + Serde::serialize(data, ref serialized); + poseidon::poseidon_hash_span(serialized.span()) +} diff --git a/crates/dojo-core/src/utils_test.cairo b/crates/dojo-core/src/utils_test.cairo new file mode 100644 index 0000000000..2a28ed4546 --- /dev/null +++ b/crates/dojo-core/src/utils_test.cairo @@ -0,0 +1,17 @@ +#[derive(Drop, Copy, Serde)] +#[dojo::model(namespace: "my_namespace")] +struct MyModel { + #[key] + x: u8, + y: u8 +} + +#[test] +fn test_hash_computation() { + // Be sure that the namespace hash computed in `dojo-lang` in Rust is equal + // to the one computed in Cairo by dojo::utils:hash + let namespace = dojo::model::Model::::namespace(); + let namespace_selector = dojo::model::Model::::namespace_selector(); + + assert(dojo::utils::hash(@namespace) == namespace_selector, 'invalid computed hash'); +} diff --git a/crates/dojo-core/src/world.cairo b/crates/dojo-core/src/world.cairo index e58610bdec..bfe194ff02 100644 --- a/crates/dojo-core/src/world.cairo +++ b/crates/dojo-core/src/world.cairo @@ -9,6 +9,7 @@ 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 register_namespace(ref self: T, namespace: ByteArray); fn deploy_contract( ref self: T, salt: felt252, class_hash: ClassHash, init_calldata: Span ) -> ContractAddress; @@ -29,13 +30,21 @@ trait IWorld { ref self: T, model: felt252, keys: Span, layout: dojo::database::introspect::Layout ); fn base(self: @T) -> ClassHash; + + /// In Dojo, there are 2 levels of authorization: `owner` and `writer`. + /// Only accounts can own a resource while any contract can write to a resource, + /// as soon as it has granted the write access from an owner of the resource. fn is_owner(self: @T, address: ContractAddress, resource: felt252) -> bool; fn grant_owner(ref self: T, address: ContractAddress, resource: felt252); fn revoke_owner(ref self: T, address: ContractAddress, resource: felt252); - fn is_writer(self: @T, model: felt252, contract: ContractAddress) -> bool; - fn grant_writer(ref self: T, model: felt252, contract: ContractAddress); - fn revoke_writer(ref self: T, model: felt252, contract: ContractAddress); + fn is_writer(self: @T, resource: felt252, contract: ContractAddress) -> bool; + fn grant_writer(ref self: T, resource: felt252, contract: ContractAddress); + fn revoke_writer(ref self: T, resource: felt252, contract: ContractAddress); + + fn can_write_resource(self: @T, resource_id: felt252, contract: ContractAddress) -> bool; + fn can_write_model(self: @T, model_id: felt252, contract: ContractAddress) -> bool; + fn can_write_namespace(self: @T, namespace_id: felt252, contract: ContractAddress) -> bool; } #[starknet::interface] @@ -48,18 +57,22 @@ trait IWorldProvider { fn world(self: @T) -> IWorldDispatcher; } -#[starknet::interface] -trait IDojoResourceProvider { - fn dojo_resource(self: @T) -> felt252; -} - mod Errors { const METADATA_DESER: felt252 = 'metadata deser error'; const NOT_OWNER: felt252 = 'not owner'; const NOT_OWNER_WRITER: felt252 = 'not owner or writer'; + const NO_WRITE_ACCESS: felt252 = 'no write access'; + const NO_MODEL_WRITE_ACCESS: felt252 = 'no model write access'; + const NO_NAMESPACE_WRITE_ACCESS: felt252 = 'no namespace write access'; + const NAMESPACE_NOT_REGISTERED: felt252 = 'namespace not registered'; + const NOT_REGISTERED: felt252 = 'resource not registered'; const INVALID_MODEL_NAME: felt252 = 'invalid model name'; + const INVALID_NAMESPACE_NAME: felt252 = 'invalid namespace name'; + const INVALID_RESOURCE_SELECTOR: felt252 = 'invalid resource selector'; const OWNER_ONLY_UPGRADE: felt252 = 'only owner can upgrade'; const OWNER_ONLY_UPDATE: felt252 = 'only owner can update'; + const NAMESPACE_ALREADY_REGISTERED: felt252 = 'namespace already registered'; + const UNEXPECTED_ERROR: felt252 = 'unexpected error'; } #[starknet::contract] @@ -85,15 +98,16 @@ mod world { use dojo::database; use dojo::database::introspect::{Introspect, Layout, FieldLayout}; use dojo::components::upgradeable::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait}; + use dojo::contract::{IContractDispatcher, IContractDispatcherTrait}; use dojo::config::component::Config; - use dojo::model::Model; + use dojo::model::{Model, IModelDispatcher, IModelDispatcherImpl}; use dojo::interfaces::{ IUpgradeableState, IFactRegistryDispatcher, IFactRegistryDispatcherImpl, StorageUpdate, ProgramOutput }; use dojo::world::{IWorldDispatcher, IWorld, IUpgradeableWorld}; use dojo::resource_metadata; - use dojo::resource_metadata::{ResourceMetadata, RESOURCE_METADATA_SELECTOR}; + use dojo::resource_metadata::ResourceMetadata; use super::Errors; @@ -112,12 +126,13 @@ mod world { #[event] #[derive(Drop, starknet::Event)] - enum Event { + pub enum Event { WorldSpawned: WorldSpawned, ContractDeployed: ContractDeployed, ContractUpgraded: ContractUpgraded, WorldUpgraded: WorldUpgraded, MetadataUpdate: MetadataUpdate, + NamespaceRegistered: NamespaceRegistered, ModelRegistered: ModelRegistered, StoreSetRecord: StoreSetRecord, StoreDelRecord: StoreDelRecord, @@ -148,6 +163,8 @@ mod world { salt: felt252, class_hash: ClassHash, address: ContractAddress, + namespace: ByteArray, + name: ByteArray } #[derive(Drop, starknet::Event)] @@ -162,9 +179,16 @@ mod world { uri: ByteArray } + #[derive(Drop, starknet::Event, Debug, PartialEq)] + pub struct NamespaceRegistered { + namespace: ByteArray, + hash: felt252 + } + #[derive(Drop, starknet::Event)] struct ModelRegistered { name: ByteArray, + namespace: ByteArray, class_hash: ClassHash, prev_class_hash: ClassHash, address: ContractAddress, @@ -186,7 +210,7 @@ mod world { #[derive(Drop, starknet::Event)] struct WriterUpdated { - model: felt252, + resource: felt252, contract: ContractAddress, value: bool } @@ -203,8 +227,7 @@ mod world { contract_base: ClassHash, nonce: usize, models_count: usize, - models: LegacyMap::, - deployed_contracts: LegacyMap::, + resources: LegacyMap::, owners: LegacyMap::<(felt252, ContractAddress), bool>, writers: LegacyMap::<(felt252, ContractAddress), bool>, #[substorage(v0)] @@ -212,17 +235,45 @@ mod world { initialized_contract: LegacyMap::, } + #[derive(Drop, starknet::Store, Default, Debug)] + enum ResourceData { + Model: (ClassHash, ContractAddress), + Contract, + Namespace, + #[default] + None, + } + + #[generate_trait] + impl ResourceDataIsNoneImpl of ResourceDataIsNoneTrait { + fn is_none(self: @ResourceData) -> bool { + match self { + ResourceData::None => true, + _ => false + } + } + } + #[constructor] fn constructor(ref self: ContractState, contract_base: ClassHash) { let creator = starknet::get_tx_info().unbox().account_contract_address; self.contract_base.write(contract_base); - self.owners.write((WORLD, creator), true); + + self.resources.write(WORLD, ResourceData::Contract); self - .models + .resources .write( - RESOURCE_METADATA_SELECTOR, - (resource_metadata::initial_class_hash(), resource_metadata::initial_address()) + dojo::model::Model::::selector(), + ResourceData::Model( + (resource_metadata::initial_class_hash(), resource_metadata::initial_address()) + ) ); + self.owners.write((WORLD, creator), true); + + let dojo_namespace_hash = dojo::utils::hash(@"__DOJO__"); + + self.resources.write(dojo_namespace_hash, ResourceData::Namespace); + self.owners.write((dojo_namespace_hash, creator), true); self.config.initializer(creator); @@ -239,7 +290,7 @@ mod world { fn metadata(self: @ContractState, resource_id: felt252) -> ResourceMetadata { let mut data = self ._read_model_data( - RESOURCE_METADATA_SELECTOR, + dojo::model::Model::::selector(), array![resource_id].span(), Model::::layout() ); @@ -258,7 +309,10 @@ mod world { /// /// `metadata` - The metadata content for the resource. fn set_metadata(ref self: ContractState, metadata: ResourceMetadata) { - assert_can_write(@self, metadata.resource_id, get_caller_address()); + assert( + self.can_write_resource(metadata.resource_id, get_caller_address()), + Errors::NO_WRITE_ACCESS + ); let model = Model::::selector(); let keys = Model::::keys(@metadata); @@ -289,16 +343,17 @@ mod world { /// Grants ownership of the resource to the address. /// Can only be called by an existing owner or the world admin. - /// + /// + /// Note that this resource must have been registered to the world first. + /// /// # Arguments /// /// * `address` - The contract address. /// * `resource` - The resource. fn grant_owner(ref self: ContractState, address: ContractAddress, resource: felt252) { - let caller = get_caller_address(); - assert( - self.is_owner(caller, resource) || self.is_owner(caller, WORLD), Errors::NOT_OWNER - ); + assert(!self.resources.read(resource).is_none(), Errors::NOT_REGISTERED); + assert(self.is_account_owner(resource), Errors::NOT_OWNER); + self.owners.write((resource, address), true); EventEmitter::emit(ref self, OwnerUpdated { address, resource, value: true }); @@ -307,72 +362,158 @@ mod world { /// Revokes owner permission to the contract for the model. /// Can only be called by an existing owner or the world admin. /// + /// Note that this resource must have been registered to the world first. + /// /// # Arguments /// /// * `address` - The contract address. /// * `resource` - The resource. fn revoke_owner(ref self: ContractState, address: ContractAddress, resource: felt252) { - let caller = get_caller_address(); - assert( - self.is_owner(caller, resource) || self.is_owner(caller, WORLD), Errors::NOT_OWNER - ); + assert(!self.resources.read(resource).is_none(), Errors::NOT_REGISTERED); + assert(self.is_account_owner(resource), Errors::NOT_OWNER); + self.owners.write((resource, address), false); EventEmitter::emit(ref self, OwnerUpdated { address, resource, value: false }); } - /// Checks if the provided contract is a writer of the model. + /// Checks if the provided contract is a writer of the resource. + /// + /// Note: that this function just indicates if a contract has the `writer` role for the resource, + /// without applying any specific rule. For example, for a model, the write access right + /// to the model namespace is not checked. + /// It does not even check if the contract is an owner of the resource. + /// Please use more high-level functions such `can_write_model` for that. /// /// # Arguments /// - /// * `model` - The name of the model. + /// * `resource` - The hash of the resource name. /// * `contract` - The name of the contract. /// /// # Returns /// - /// * `bool` - True if the contract is a writer of the model, false otherwise - fn is_writer(self: @ContractState, model: felt252, contract: ContractAddress) -> bool { - self.writers.read((model, contract)) + /// * `bool` - True if the contract is a writer of the resource, false otherwise + fn is_writer(self: @ContractState, resource: felt252, contract: ContractAddress) -> bool { + self.writers.read((resource, contract)) } - /// Grants writer permission to the contract for the model. - /// Can only be called by an existing model owner or the world admin. + /// Grants writer permission to the contract for the resource. + /// Can only be called by an existing resource owner or the world admin. + /// + /// Note that this resource must have been registered to the world first. /// /// # Arguments /// - /// * `model` - The name of the model. + /// * `resource` - The hash of the resource name. /// * `contract` - The name of the contract. - fn grant_writer(ref self: ContractState, model: felt252, contract: ContractAddress) { - let caller = get_caller_address(); + fn grant_writer(ref self: ContractState, resource: felt252, contract: ContractAddress) { + assert(!self.resources.read(resource).is_none(), Errors::NOT_REGISTERED); + assert(self.is_account_owner(resource), Errors::NOT_OWNER); - assert( - self.is_owner(caller, model) || self.is_owner(caller, WORLD), - Errors::NOT_OWNER_WRITER - ); - self.writers.write((model, contract), true); + self.writers.write((resource, contract), true); - EventEmitter::emit(ref self, WriterUpdated { model, contract, value: true }); + EventEmitter::emit(ref self, WriterUpdated { resource, contract, value: true }); } /// Revokes writer permission to the contract for the model. /// Can only be called by an existing model writer, owner or the world admin. /// + /// Note that this resource must have been registered to the world first. + /// /// # Arguments /// /// * `model` - The name of the model. /// * `contract` - The name of the contract. - fn revoke_writer(ref self: ContractState, model: felt252, contract: ContractAddress) { - let caller = get_caller_address(); + fn revoke_writer(ref self: ContractState, resource: felt252, contract: ContractAddress) { + assert(!self.resources.read(resource).is_none(), Errors::NOT_REGISTERED); + let caller = get_caller_address(); assert( - self.is_writer(model, caller) - || self.is_owner(caller, model) - || self.is_owner(caller, WORLD), + self.is_writer(resource, caller) || self.is_account_owner(resource), Errors::NOT_OWNER_WRITER ); - self.writers.write((model, contract), false); + self.writers.write((resource, contract), false); - EventEmitter::emit(ref self, WriterUpdated { model, contract, value: false }); + EventEmitter::emit(ref self, WriterUpdated { resource, contract, value: false }); + } + + /// Checks if the provided contract can write to the resource. + /// + /// Note: Contrary to `is_writer`, this function checks resource specific rules. + /// For example, for a model, it checks if the contract is a write/owner of the resource, + /// OR a write/owner of the namespace. + /// + /// # Arguments + /// + /// * `resource_id` - The resource IUpgradeableDispatcher. + /// * `contract` - The name of the contract. + /// + /// # Returns + /// + /// * `bool` - True if the contract can write to the resource, false otherwise + fn can_write_resource( + self: @ContractState, resource_id: felt252, contract: ContractAddress + ) -> bool { + // TODO: use match self.resources... directly when https://github.com/starkware-libs/cairo/pull/5743 fixed + let resource: ResourceData = self.resources.read(resource_id); + match resource { + ResourceData::Model((_, model_address)) => self + ._check_model_write_access(resource_id, model_address, contract), + ResourceData::Namespace | + ResourceData::Contract => self._check_basic_write_access(resource_id, contract), + ResourceData::None => panic_with_felt252(Errors::INVALID_RESOURCE_SELECTOR) + } + } + + /// Checks if the provided contract can write to the model. + /// It panics if the resource selector is not a model. + /// + /// Note: Contrary to `is_writer`, this function checks if the contract is a write/owner of the model, + /// OR a write/owner of the namespace. + /// + /// # Arguments + /// + /// * `model_id` - The model selector. + /// * `contract` - The name of the contract. + /// + /// # Returns + /// + /// * `bool` - True if the contract can write to the model, false otherwise + fn can_write_model( + self: @ContractState, model_id: felt252, contract: ContractAddress + ) -> bool { + // TODO: use match self.resources... directly when https://github.com/starkware-libs/cairo/pull/5743 fixed + let resource: ResourceData = self.resources.read(model_id); + match resource { + ResourceData::Model((_, model_address)) => self + ._check_model_write_access(model_id, model_address, contract), + _ => panic_with_felt252(Errors::INVALID_RESOURCE_SELECTOR) + } + } + + /// Checks if the provided contract can write to the namespace. + /// It panics if the resource selector is not a namespace. + /// + /// Note: Contrary to `is_writer`, this function also checks if the caller account is + /// the owner of the namespace. + /// + /// # Arguments + /// + /// * `namespace_id` - The namespace selector. + /// * `contract` - The name of the contract. + /// + /// # Returns + /// + /// * `bool` - True if the contract can write to the namespace, false otherwise + fn can_write_namespace( + self: @ContractState, namespace_id: felt252, contract: ContractAddress + ) -> bool { + // TODO: use match self.resources... directly when https://github.com/starkware-libs/cairo/pull/5743 fixed + let resource: ResourceData = self.resources.read(namespace_id); + match resource { + ResourceData::Namespace => self._check_basic_write_access(namespace_id, contract), + _ => panic_with_felt252(Errors::INVALID_RESOURCE_SELECTOR) + } } /// Registers a model in the world. If the model is already registered, @@ -385,7 +526,8 @@ mod world { let caller = get_caller_address(); let salt = self.models_count.read(); - let (address, name, selector) = dojo::model::deploy_and_get_metadata( + let (address, name, selector, namespace, namespace_selector) = + dojo::model::deploy_and_get_metadata( salt.into(), class_hash ) .unwrap_syscall(); @@ -396,31 +538,75 @@ mod world { starknet::contract_address::ContractAddressZeroable::zero(), ); - // Avoids a model name to conflict with already deployed contract, - // which can cause ACL issue with current ACL implementation. - if selector.is_zero() || self.deployed_contracts.read(selector).is_non_zero() { + assert( + self._is_namespace_registered(namespace_selector), Errors::NAMESPACE_NOT_REGISTERED + ); + assert( + self.can_write_namespace(namespace_selector, get_caller_address()), + Errors::NO_NAMESPACE_WRITE_ACCESS + ); + + if selector.is_zero() { panic_with_felt252(Errors::INVALID_MODEL_NAME); } - // If model is already registered, validate permission to update. - let model_data: (ClassHash, ContractAddress) = self.models.read(selector); - let (current_class_hash, current_address) = model_data; - - if current_class_hash.is_non_zero() { - assert(self.is_owner(caller, selector), Errors::OWNER_ONLY_UPDATE); - prev_class_hash = current_class_hash; - prev_address = current_address; - } else { - self.owners.write((selector, caller), true); + // TODO: use match self.resources... directly when https://github.com/starkware-libs/cairo/pull/5743 fixed + let resource: ResourceData = self.resources.read(selector); + + match resource { + // If model is already registered, validate permission to update. + ResourceData::Model(( + model_hash, model_address + )) => { + assert(self.is_account_owner(selector), Errors::OWNER_ONLY_UPDATE); + prev_class_hash = model_hash; + prev_address = model_address; + }, + // new model + ResourceData::None => { self.owners.write((selector, caller), true); }, + // Avoids a model name to conflict with already registered resource, + // which can cause ACL issue with current ACL implementation. + _ => panic_with_felt252(Errors::INVALID_MODEL_NAME) }; - self.models.write(selector, (class_hash, address)); + self.resources.write(selector, ResourceData::Model((class_hash, address))); EventEmitter::emit( ref self, - ModelRegistered { name, prev_address, address, class_hash, prev_class_hash } + ModelRegistered { + name, namespace, prev_address, address, class_hash, prev_class_hash + } ); } + /// Registers a namespace in the world. + /// + /// # Arguments + /// + /// * `namespace` - The name of the namespace to be registered. + fn register_namespace(ref self: ContractState, namespace: ByteArray) { + let caller_account = self._get_account_address(); + + let hash = dojo::utils::hash(@namespace); + + // TODO: use match self.resources... directly when https://github.com/starkware-libs/cairo/pull/5743 fixed + let resource: ResourceData = self.resources.read(hash); + match resource { + ResourceData::Namespace => { + if !self.is_account_owner(hash) { + panic_with_felt252(Errors::NAMESPACE_ALREADY_REGISTERED); + } + }, + ResourceData::None => { + self.resources.write(hash, ResourceData::Namespace); + self.owners.write((hash, caller_account), true); + + EventEmitter::emit(ref self, NamespaceRegistered { namespace, hash }); + }, + _ => { panic_with_felt252(Errors::INVALID_NAMESPACE_NAME); } + }; + } + + /// Gets the class hash of a registered model. /// /// # Arguments @@ -431,7 +617,12 @@ mod world { /// /// * (`ClassHash`, `ContractAddress`) - The class hash and the contract address of the model. fn model(self: @ContractState, selector: felt252) -> (ClassHash, ContractAddress) { - self.models.read(selector) + // TODO: use match self.resources... directly when https://github.com/starkware-libs/cairo/pull/5743 fixed + let resource: ResourceData = self.resources.read(selector); + match resource { + ResourceData::Model(m) => m, + _ => panic_with_felt252(Errors::INVALID_RESOURCE_SELECTOR) + } } /// Deploys a contract associated with the world. @@ -458,6 +649,19 @@ mod world { let upgradeable_dispatcher = IUpgradeableDispatcher { contract_address }; upgradeable_dispatcher.upgrade(class_hash); + // namespace checking + let dispatcher = IContractDispatcher { contract_address }; + let namespace = dispatcher.namespace(); + let name = dispatcher.contract_name(); + let namespace_selector = dispatcher.namespace_selector(); + assert( + self._is_namespace_registered(namespace_selector), Errors::NAMESPACE_NOT_REGISTERED + ); + assert( + self.can_write_namespace(namespace_selector, get_caller_address()), + Errors::NO_NAMESPACE_WRITE_ACCESS + ); + if self.initialized_contract.read(contract_address.into()) { panic!("Contract has already been initialized"); } else { @@ -468,10 +672,11 @@ mod world { self.owners.write((contract_address.into(), get_caller_address()), true); - self.deployed_contracts.write(contract_address.into(), class_hash.into()); + self.resources.write(contract_address.into(), ResourceData::Contract); EventEmitter::emit( - ref self, ContractDeployed { salt, class_hash, address: contract_address } + ref self, + ContractDeployed { salt, class_hash, address: contract_address, namespace, name } ); contract_address @@ -490,7 +695,7 @@ mod world { fn upgrade_contract( ref self: ContractState, address: ContractAddress, class_hash: ClassHash ) -> ClassHash { - assert(is_account_owner(@self, address.into()), Errors::NOT_OWNER); + assert(self.is_account_owner(address.into()), Errors::NOT_OWNER); IUpgradeableDispatcher { contract_address: address }.upgrade(class_hash); EventEmitter::emit(ref self, ContractUpgraded { class_hash, address }); class_hash @@ -536,7 +741,9 @@ mod world { values: Span, layout: dojo::database::introspect::Layout ) { - assert_can_write(@self, model, get_caller_address()); + assert( + self.can_write_model(model, get_caller_address()), Errors::NO_MODEL_WRITE_ACCESS + ); self._write_model_data(model, keys, values, layout); EventEmitter::emit(ref self, StoreSetRecord { table: model, keys, values }); @@ -557,7 +764,9 @@ mod world { keys: Span, layout: dojo::database::introspect::Layout ) { - assert_can_write(@self, model, get_caller_address()); + assert( + self.can_write_model(model, get_caller_address()), Errors::NO_MODEL_WRITE_ACCESS + ); self._delete_model_data(model, keys, layout); EventEmitter::emit(ref self, StoreDelRecord { table: model, keys }); @@ -605,10 +814,7 @@ mod world { /// * `new_class_hash` - The new world class hash. fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { assert(new_class_hash.is_non_zero(), 'invalid class_hash'); - assert( - IWorld::is_owner(@self, get_tx_info().unbox().account_contract_address, WORLD), - Errors::OWNER_ONLY_UPGRADE, - ); + assert(self.is_account_world_owner(), Errors::OWNER_ONLY_UPGRADE); // upgrade to new_class_hash replace_class_syscall(new_class_hash).unwrap(); @@ -674,37 +880,117 @@ mod world { } } - /// Asserts that the current caller can write to the model. - /// - /// # Arguments - /// - /// * `resource` - The selector of the resource being written to. - /// * `caller` - The selector of the caller writing. - fn assert_can_write(self: @ContractState, resource: felt252, caller: ContractAddress) { - assert( - IWorld::is_writer(self, resource, caller) || is_account_owner(self, resource), - 'not writer' - ); - } - - /// Verifies if the calling account is owner of the resource or the - /// owner of the world. - /// - /// # Arguments - /// - /// * `resource` - The selector of the resource being verified. - /// - /// # Returns - /// - /// * `bool` - True if the calling account is the owner of the resource or the owner of the world, - /// false otherwise. - fn is_account_owner(self: @ContractState, resource: felt252) -> bool { - IWorld::is_owner(self, get_tx_info().unbox().account_contract_address, resource) - || IWorld::is_owner(self, get_tx_info().unbox().account_contract_address, WORLD) - } - #[generate_trait] impl Self of SelfTrait { + #[inline(always)] + fn _get_account_address(self: @ContractState) -> ContractAddress { + get_tx_info().unbox().account_contract_address + } + + /// Verifies if the calling account is owner of the resource or the + /// owner of the world. + /// + /// # Arguments + /// + /// * `resource` - The selector of the resource being verified. + /// + /// # Returns + /// + /// * `bool` - True if the calling account is the owner of the resource or the owner of the world, + /// false otherwise. + #[inline(always)] + fn is_account_owner(self: @ContractState, resource: felt252) -> bool { + IWorld::is_owner(self, self._get_account_address(), resource) + || self.is_account_world_owner() + } + + /// Verifies if the calling account has write access to the resource. + /// + /// # Arguments + /// + /// * `resource` - The selector of the resource being verified. + /// + /// # Returns + /// + /// * `bool` - True if the calling account has write access to the resource, + /// false otherwise. + #[inline(always)] + fn is_account_writer(self: @ContractState, resource: felt252) -> bool { + IWorld::is_writer(self, resource, self._get_account_address()) + } + + /// Verifies if the calling account is the world owner. + /// + /// # Returns + /// + /// * `bool` - True if the calling account is the world owner, false otherwise. + #[inline(always)] + fn is_account_world_owner(self: @ContractState) -> bool { + IWorld::is_owner(self, self._get_account_address(), WORLD) + } + + /// Indicates if the provided namespace is already registered + #[inline(always)] + fn _is_namespace_registered(self: @ContractState, namespace_selector: felt252) -> bool { + // TODO: use match self.resources... directly when https://github.com/starkware-libs/cairo/pull/5743 fixed + let resource: ResourceData = self.resources.read(namespace_selector); + match resource { + ResourceData::Namespace => true, + _ => false + } + } + + /// Check model write access. + /// That means, check if: + /// - the calling contract has the writer role for the model OR, + /// - the calling account has the owner and/or writer role for the model OR, + /// - the calling contract has the writer role for the model namespace OR + /// - the calling account has the owner and/or writer role for the model namespace. + /// + /// # Arguments + /// * `model_id` - the model selector to check. + /// * `model_address` - the model contract address. + /// * `contract` - the calling contract. + /// + /// # Returns + /// `true` if the write access is allowed, false otherwise. + /// + fn _check_model_write_access( + self: @ContractState, + model_id: felt252, + model_address: ContractAddress, + contract: ContractAddress + ) -> bool { + if !self.is_writer(model_id, contract) + && !self.is_account_owner(model_id) + && !self.is_account_writer(model_id) { + let model = IModelDispatcher { contract_address: model_address }; + self._check_basic_write_access(model.namespace_selector(), contract) + } else { + true + } + } + + /// Check basic resource write access. + /// That means, check if: + /// - the calling contract has the writer role for the resource OR, + /// - the calling account has the owner and/or writer role for the resource. + /// + /// # Arguments + /// * `resource_id` - the resource selector to check. + /// * `contract` - the calling contract. + /// + /// # Returns + /// `true` if the write access is allowed, false otherwise. + /// + fn _check_basic_write_access( + self: @ContractState, resource_id: felt252, contract: ContractAddress + ) -> bool { + self.is_writer(resource_id, contract) + || self.is_account_owner(resource_id) + || self.is_account_writer(resource_id) + } + /// Write a new model record. /// /// # Arguments diff --git a/crates/dojo-core/src/world_test.cairo b/crates/dojo-core/src/world_test.cairo index 634309cd61..955ad4a8a6 100644 --- a/crates/dojo-core/src/world_test.cairo +++ b/crates/dojo-core/src/world_test.cairo @@ -11,8 +11,9 @@ use dojo::benchmarks; use dojo::config::interface::{IConfigDispatcher, IConfigDispatcherImpl}; use dojo::world::{ IWorldDispatcher, IWorldDispatcherTrait, world, IUpgradeableWorld, IUpgradeableWorldDispatcher, - IUpgradeableWorldDispatcherTrait, ResourceMetadata + IUpgradeableWorldDispatcherTrait, ResourceMetadata, }; +use dojo::world::world::NamespaceRegistered; use dojo::database::introspect::{Introspect, Layout, FieldLayout}; use dojo::database::MAX_ARRAY_LENGTH; use dojo::test_utils::{spawn_test_world, deploy_with_world_address, assert_array}; @@ -43,6 +44,16 @@ struct Foo { b: u128, } +#[derive(Copy, Drop, Serde)] +#[dojo::model(namespace: "another_namespace")] +struct Buzz { + #[key] + caller: ContractAddress, + a: felt252, + b: u128, +} + + fn create_foo() -> Span { array![1, 2].span() } @@ -248,6 +259,8 @@ fn get_key_test() -> Span { trait IMetadataOnly { fn selector(self: @T) -> felt252; fn name(self: @T) -> ByteArray; + fn namespace(self: @T) -> ByteArray; + fn namespace_selector(self: @T) -> felt252; } #[starknet::contract] @@ -258,7 +271,15 @@ mod resource_metadata_malicious { #[abi(embed_v0)] impl InvalidModelName of super::IMetadataOnly { fn selector(self: @ContractState) -> felt252 { - selector!("ResourceMetadata") + dojo::model::Model::::selector() + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::utils::hash(@Self::namespace(self)) } fn name(self: @ContractState) -> ByteArray { @@ -302,7 +323,7 @@ mod bar { .world .read() .delete_entity( - selector!("Foo"), + dojo::model::Model::::selector(), array![get_caller_address().into()].span(), dojo::model::Model::::layout() ); @@ -408,7 +429,7 @@ fn test_model_class_hash_getter() { let world = deploy_world(); world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); - let (foo_class_hash, _) = world.model(selector!("Foo")); + let (foo_class_hash, _) = world.model(dojo::model::Model::::selector()); assert(foo_class_hash == foo::TEST_CLASS_HASH.try_into().unwrap(), 'foo wrong class hash'); } @@ -423,6 +444,66 @@ fn test_legacy_model_class_hash_getter() { assert(foo_class_hash == foo::TEST_CLASS_HASH.try_into().unwrap(), 'foo wrong class hash'); } +#[test] +fn test_register_namespace() { + let world = deploy_world(); + + let caller = starknet::contract_address_const::<0xb0b>(); + starknet::testing::set_account_contract_address(caller); + + drop_all_events(world.contract_address); + + let namespace = "namespace"; + let hash = dojo::utils::hash(@namespace); + + world.register_namespace(namespace); + + assert(world.is_owner(caller, hash), 'namespace not registered'); + + assert_eq!( + starknet::testing::pop_log(world.contract_address), + Option::Some(NamespaceRegistered { namespace: "namespace", hash }) + ); +} + +#[test] +fn test_register_namespace_already_registered_same_caller() { + let world = deploy_world(); + + let caller = starknet::contract_address_const::<0xb0b>(); + starknet::testing::set_account_contract_address(caller); + + let namespace = "namespace"; + let hash = dojo::utils::hash(@namespace); + + world.register_namespace(namespace); + + drop_all_events(world.contract_address); + + world.register_namespace("namespace"); + + assert(world.is_owner(caller, hash), 'namespace not registered'); + + let event = starknet::testing::pop_log_raw(world.contract_address); + assert(event.is_none(), 'unexpected event'); +} + +#[test] +#[should_panic(expected: ('namespace already registered', 'ENTRYPOINT_FAILED',))] +fn test_register_namespace_already_registered_other_caller() { + let world = deploy_world(); + + let account = starknet::contract_address_const::<0xb0b>(); + starknet::testing::set_account_contract_address(account); + + world.register_namespace("namespace"); + + let another_account = starknet::contract_address_const::<0xa11ce>(); + starknet::testing::set_account_contract_address(another_account); + + world.register_namespace("namespace"); +} + #[test] #[available_gas(6000000)] fn test_emit() { @@ -472,7 +553,7 @@ fn test_set_entity_unauthorized() { // Utils fn deploy_world() -> IWorldDispatcher { - spawn_test_world(array![]) + spawn_test_world("dojo", array![]) } #[test] @@ -492,13 +573,13 @@ fn test_set_metadata_world() { #[test] #[available_gas(60000000)] fn test_set_metadata_model_writer() { - let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let world = spawn_test_world("dojo", array![foo::TEST_CLASS_HASH],); let bar_contract = IbarDispatcher { contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) }; - world.grant_writer(selector!("Foo"), bar_contract.contract_address); + world.grant_writer(dojo::model::Model::::selector(), bar_contract.contract_address); let bob = starknet::contract_address_const::<0xb0b>(); starknet::testing::set_account_contract_address(bob); @@ -507,18 +588,18 @@ fn test_set_metadata_model_writer() { bar_contract.set_foo(1337, 1337); let metadata = ResourceMetadata { - resource_id: selector!("Foo"), metadata_uri: format!("ipfs:bob") + resource_id: dojo::model::Model::::selector(), metadata_uri: format!("ipfs:bob") }; // A system that has write access on a model should be able to update the metadata. // This follows conventional ACL model. world.set_metadata(metadata.clone()); - assert(world.metadata(selector!("Foo")) == metadata, 'bad metadata'); + assert(world.metadata(dojo::model::Model::::selector()) == metadata, 'bad metadata'); } #[test] #[available_gas(60000000)] -#[should_panic(expected: ('not writer', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('no write access', 'ENTRYPOINT_FAILED',))] fn test_set_metadata_same_model_rules() { let world = deploy_world(); @@ -543,6 +624,9 @@ fn test_metadata_update_owner_only() { let bob = starknet::contract_address_const::<0xb0b>(); starknet::testing::set_contract_address(bob); + + world.grant_owner(bob, dojo::utils::hash(@"dojo")); + starknet::testing::set_account_contract_address(bob); world.register_model(resource_metadata_malicious::TEST_CLASS_HASH.try_into().unwrap()); @@ -552,24 +636,26 @@ fn test_metadata_update_owner_only() { #[available_gas(6000000)] fn test_owner() { let world = deploy_world(); + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + let foo_selector = dojo::model::Model::::selector(); let alice = starknet::contract_address_const::<0x1337>(); let bob = starknet::contract_address_const::<0x1338>(); assert(!world.is_owner(alice, 0), 'should not be owner'); - assert(!world.is_owner(bob, 42), 'should not be owner'); + assert(!world.is_owner(bob, foo_selector), 'should not be owner'); world.grant_owner(alice, 0); assert(world.is_owner(alice, 0), 'should be owner'); - world.grant_owner(bob, 42); - assert(world.is_owner(bob, 42), 'should be owner'); + world.grant_owner(bob, foo_selector); + assert(world.is_owner(bob, foo_selector), 'should be owner'); world.revoke_owner(alice, 0); assert(!world.is_owner(alice, 0), 'should not be owner'); - world.revoke_owner(bob, 42); - assert(!world.is_owner(bob, 42), 'should not be owner'); + world.revoke_owner(bob, foo_selector); + assert(!world.is_owner(bob, foo_selector), 'should not be owner'); } #[test] @@ -579,7 +665,7 @@ fn test_set_owner_fails_for_non_owner() { let world = deploy_world(); let alice = starknet::contract_address_const::<0x1337>(); - starknet::testing::set_contract_address(alice); + starknet::testing::set_account_contract_address(alice); world.revoke_owner(alice, 0); assert(!world.is_owner(alice, 0), 'should not be owner'); @@ -591,28 +677,39 @@ fn test_set_owner_fails_for_non_owner() { #[available_gas(6000000)] fn test_writer() { let world = deploy_world(); + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + let foo_selector = dojo::model::Model::::selector(); - assert(!world.is_writer(42, 69.try_into().unwrap()), 'should not be writer'); + assert(!world.is_writer(foo_selector, 69.try_into().unwrap()), 'should not be writer'); - world.grant_writer(42, 69.try_into().unwrap()); - assert(world.is_writer(42, 69.try_into().unwrap()), 'should be writer'); + world.grant_writer(foo_selector, 69.try_into().unwrap()); + assert(world.is_writer(foo_selector, 69.try_into().unwrap()), 'should be writer'); - world.revoke_writer(42, 69.try_into().unwrap()); - assert(!world.is_writer(42, 69.try_into().unwrap()), 'should not be writer'); + world.revoke_writer(foo_selector, 69.try_into().unwrap()); + assert(!world.is_writer(foo_selector, 69.try_into().unwrap()), 'should not be writer'); +} + +#[test] +#[should_panic(expected: ('resource not registered', 'ENTRYPOINT_FAILED'))] +fn test_writer_not_registered_resource() { + let world = deploy_world(); + + // 42 is not a registered resource ID + world.grant_writer(42, 69.try_into().unwrap()); } #[test] #[available_gas(6000000)] #[should_panic] fn test_system_not_writer_fail() { - let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let world = spawn_test_world("dojo", array![foo::TEST_CLASS_HASH],); let bar_address = deploy_with_world_address(bar::TEST_CLASS_HASH, world); let bar_contract = IbarDispatcher { contract_address: bar_address }; // Caller is not owner now - let caller = starknet::contract_address_const::<0x1337>(); - starknet::testing::set_account_contract_address(caller); + let account = starknet::contract_address_const::<0xb0b>(); + starknet::testing::set_account_contract_address(account); // Should panic, system not writer bar_contract.set_foo(25, 16); @@ -620,13 +717,13 @@ fn test_system_not_writer_fail() { #[test] fn test_system_writer_access() { - let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let world = spawn_test_world("dojo", array![foo::TEST_CLASS_HASH],); let bar_address = deploy_with_world_address(bar::TEST_CLASS_HASH, world); let bar_contract = IbarDispatcher { contract_address: bar_address }; - world.grant_writer(selector!("Foo"), bar_address); - assert(world.is_writer(selector!("Foo"), bar_address), 'should be writer'); + world.grant_writer(dojo::model::Model::::selector(), bar_address); + assert(world.is_writer(dojo::model::Model::::selector(), bar_address), 'should be writer'); // Caller is not owner now let caller = starknet::contract_address_const::<0x1337>(); @@ -653,14 +750,14 @@ fn test_set_writer_fails_for_non_owner() { #[test] fn test_execute_multiple_worlds() { // Deploy world contract - let world1 = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let world1 = spawn_test_world("dojo", array![foo::TEST_CLASS_HASH],); let bar1_contract = IbarDispatcher { contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world1) }; // Deploy another world contract - let world2 = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let world2 = spawn_test_world("dojo", array![foo::TEST_CLASS_HASH],); let bar2_contract = IbarDispatcher { contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world2) @@ -682,7 +779,7 @@ fn test_execute_multiple_worlds() { #[test] #[available_gas(60000000)] fn bench_execute() { - let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let world = spawn_test_world("dojo", array![foo::TEST_CLASS_HASH],); let bar_contract = IbarDispatcher { contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) }; @@ -704,7 +801,7 @@ fn bench_execute() { #[test] fn bench_execute_complex() { - let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let world = spawn_test_world("dojo", array![benchmarks::character::TEST_CLASS_HASH],); let bar_contract = IbarDispatcher { contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) }; @@ -725,7 +822,6 @@ fn bench_execute_complex() { assert(data.heigth == 1337, 'data not stored'); } - #[starknet::interface] trait IWorldUpgrade { fn hello(self: @TContractState) -> felt252; @@ -844,6 +940,9 @@ trait IDojoInit { #[dojo::contract] mod test_contract {} +#[dojo::contract(namespace: "buzz_namespace")] +mod buzz_contract {} + #[test] #[available_gas(6000000)] #[should_panic(expected: ('Only world can init', 'ENTRYPOINT_FAILED'))] @@ -862,14 +961,12 @@ fn test_can_call_init() { fn test_set_entity_with_fixed_layout() { let world = deploy_world(); world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); - - let selector = selector!("foo"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_foo(); let layout = dojo::model::Model::::layout(); world.set_entity(selector, get_key_test(), values, layout); - let read_values = world.entity(selector, keys, layout); assert_array(read_values, values); } @@ -879,7 +976,7 @@ fn test_set_entity_with_struct_layout() { let world = deploy_world(); world.register_model(struct_simple_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_simple_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_simple_model(); let layout = dojo::model::Model::::layout(); @@ -895,7 +992,7 @@ fn test_set_entity_with_struct_tuple_layout() { let world = deploy_world(); world.register_model(struct_with_tuple::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_with_tuple"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_with_tuple(); let layout = dojo::model::Model::::layout(); @@ -911,7 +1008,7 @@ fn test_set_entity_with_struct_enum_layout() { let world = deploy_world(); world.register_model(struct_with_enum::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_with_enum"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_with_enum_first_variant(); let layout = dojo::model::Model::::layout(); @@ -935,7 +1032,7 @@ fn test_set_entity_with_struct_simple_array_layout() { let world = deploy_world(); world.register_model(struct_simple_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_simple_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_simple_array_model(); let layout = dojo::model::Model::::layout(); @@ -951,7 +1048,7 @@ fn test_set_entity_with_struct_complex_array_layout() { let world = deploy_world(); world.register_model(struct_complex_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_complex_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_complex_array_model(); let layout = dojo::model::Model::::layout(); @@ -967,7 +1064,7 @@ fn test_set_entity_with_struct_layout_and_byte_array() { let world = deploy_world(); world.register_model(struct_byte_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_byte_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_byte_array_model(); let layout = dojo::model::Model::::layout(); @@ -983,7 +1080,7 @@ fn test_set_entity_with_nested_elements() { let world = deploy_world(); world.register_model(struct_nested_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_nested_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_nested_model(); let layout = dojo::model::Model::::layout(); @@ -1010,7 +1107,7 @@ fn test_set_entity_with_struct_generics_enum_layout() { let world = deploy_world(); world.register_model(struct_with_generic::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_with_generic"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_generic_first_variant(); let layout = dojo::model::Model::::layout(); @@ -1033,13 +1130,12 @@ fn test_set_entity_with_struct_generics_enum_layout() { fn test_delete_entity_with_fixed_layout() { let world = deploy_world(); world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); - - let selector = selector!("foo"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_foo(); let layout = dojo::model::Model::::layout(); - world.set_entity(selector, keys, values, layout); + world.set_entity(selector, get_key_test(), values, layout); world.delete_entity(selector, keys, layout); @@ -1054,7 +1150,7 @@ fn test_delete_entity_with_simple_struct_layout() { let world = deploy_world(); world.register_model(struct_simple_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_simple_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_simple_model(); let layout = dojo::model::Model::::layout(); @@ -1074,7 +1170,7 @@ fn test_delete_entity_with_struct_simple_array_layout() { let world = deploy_world(); world.register_model(struct_simple_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_simple_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_simple_array_model(); let layout = dojo::model::Model::::layout(); @@ -1097,7 +1193,7 @@ fn test_delete_entity_with_complex_array_struct_layout() { let world = deploy_world(); world.register_model(struct_complex_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_complex_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_complex_array_model(); @@ -1121,7 +1217,7 @@ fn test_delete_entity_with_struct_tuple_layout() { let world = deploy_world(); world.register_model(struct_with_tuple::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_with_tuple"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_with_tuple(); let layout = dojo::model::Model::::layout(); @@ -1142,7 +1238,7 @@ fn test_delete_entity_with_struct_enum_layout() { let world = deploy_world(); world.register_model(struct_with_enum::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_with_enum"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_with_enum_first_variant(); let layout = dojo::model::Model::::layout(); @@ -1164,7 +1260,7 @@ fn test_delete_entity_with_struct_layout_and_byte_array() { let world = deploy_world(); world.register_model(struct_byte_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_byte_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_byte_array_model(); let layout = dojo::model::Model::::layout(); @@ -1185,7 +1281,7 @@ fn test_delete_entity_with_nested_elements() { let world = deploy_world(); world.register_model(struct_nested_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_nested_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_nested_model(); let layout = dojo::model::Model::::layout(); @@ -1206,7 +1302,7 @@ fn test_delete_entity_with_struct_generics_enum_layout() { let world = deploy_world(); world.register_model(struct_with_generic::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_with_generic"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values = create_struct_generic_first_variant(); let layout = dojo::model::Model::::layout(); @@ -1234,7 +1330,10 @@ fn test_set_entity_with_unexpected_array_model_layout() { world .set_entity( - selector!("struct_simple_array_model"), array![].span(), array![].span(), layout + dojo::model::Model::::selector(), + array![].span(), + array![].span(), + layout ); } @@ -1250,7 +1349,10 @@ fn test_set_entity_with_unexpected_tuple_model_layout() { world .set_entity( - selector!("struct_simple_array_model"), array![].span(), array![].span(), layout + dojo::model::Model::::selector(), + array![].span(), + array![].span(), + layout ); } @@ -1264,7 +1366,10 @@ fn test_delete_entity_with_unexpected_array_model_layout() { array![dojo::database::introspect::Introspect::::layout()].span() ); - world.delete_entity(selector!("struct_simple_array_model"), array![].span(), layout); + world + .delete_entity( + dojo::model::Model::::selector(), array![].span(), layout + ); } #[test] @@ -1277,7 +1382,10 @@ fn test_delete_entity_with_unexpected_tuple_model_layout() { array![dojo::database::introspect::Introspect::::layout()].span() ); - world.delete_entity(selector!("struct_simple_array_model"), array![].span(), layout); + world + .delete_entity( + dojo::model::Model::::selector(), array![].span(), layout + ); } #[test] @@ -1290,7 +1398,7 @@ fn test_get_entity_with_unexpected_array_model_layout() { array![dojo::database::introspect::Introspect::::layout()].span() ); - world.entity(selector!("struct_simple_array_model"), array![].span(), layout); + world.entity(dojo::model::Model::::selector(), array![].span(), layout); } #[test] @@ -1303,7 +1411,7 @@ fn test_get_entity_with_unexpected_tuple_model_layout() { array![dojo::database::introspect::Introspect::::layout()].span() ); - world.entity(selector!("struct_simple_array_model"), array![].span(), layout); + world.entity(dojo::model::Model::::selector(), array![].span(), layout); } @@ -1311,22 +1419,13 @@ fn test_get_entity_with_unexpected_tuple_model_layout() { #[should_panic(expected: ('Invalid values length', 'ENTRYPOINT_FAILED',))] fn test_set_entity_with_bad_values_length_error_for_array_layout() { let world = deploy_world(); + world.register_model(struct_simple_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("a_selector"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); - let layout = Layout::Struct( - array![ - FieldLayout { - selector: selector!("a"), - layout: Layout::Array( - array![dojo::database::introspect::Introspect::::layout()].span() - ) - }, - ] - .span() - ); + let layout = dojo::model::Model::::layout(); - world.set_entity(selector, keys, array![].span(), layout); + world.set_entity(selector, keys, array![1].span(), layout); } #[test] @@ -1335,7 +1434,7 @@ fn test_set_entity_with_too_big_array_length() { let world = deploy_world(); world.register_model(struct_simple_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_simple_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values: Span = array![ 1, MAX_ARRAY_LENGTH.try_into().unwrap() + 1, 10, 20, 30, 40, 2 @@ -1352,7 +1451,7 @@ fn test_set_entity_with_struct_layout_and_bad_byte_array_length() { let world = deploy_world(); world.register_model(struct_byte_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_byte_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values: Span = array![ 1, MAX_ARRAY_LENGTH.try_into().unwrap(), 'first', 'second', 'third', 'pending', 7 @@ -1369,10 +1468,201 @@ fn test_set_entity_with_struct_layout_and_bad_value_length_for_byte_array() { let world = deploy_world(); world.register_model(struct_byte_array_model::TEST_CLASS_HASH.try_into().unwrap()); - let selector = selector!("struct_byte_array_model"); + let selector = dojo::model::Model::::selector(); let keys = get_key_test(); let values: Span = array![1, 3, 'first', 'second', 'third', 'pending'].span(); let layout = dojo::model::Model::::layout(); world.set_entity(selector, keys, values, layout); } + +fn write_foo_record(world: dojo::world::IWorldDispatcher) { + let selector = dojo::model::Model::::selector(); + let values = create_foo(); + let layout = dojo::model::Model::::layout(); + + world.set_entity(selector, get_key_test(), values, layout); +} + +#[test] +fn test_write_model_for_namespace_owner() { + let world = deploy_world(); + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + + let account = starknet::contract_address_const::<0xb0b>(); + let contract = starknet::contract_address_const::<0xdeadbeef>(); + + // the caller account is a model namespace owner + world.grant_owner(account, dojo::model::Model::::namespace_selector()); + starknet::testing::set_account_contract_address(account); + starknet::testing::set_contract_address(contract); + + write_foo_record(world); +} + +#[test] +fn test_write_model_for_model_owner() { + let world = deploy_world(); + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + + // the caller account is a model owner + let account = starknet::contract_address_const::<0xb0b>(); + let contract = starknet::contract_address_const::<0xdeadbeef>(); + + world.grant_owner(account, dojo::model::Model::::selector()); + starknet::testing::set_account_contract_address(account); + starknet::testing::set_contract_address(contract); + + write_foo_record(world); +} + +#[test] +fn test_write_model_for_namespace_writer() { + let world = deploy_world(); + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + + let account = starknet::contract_address_const::<0xb0b>(); + let contract = starknet::contract_address_const::<0xdeadbeef>(); + + world.grant_writer(dojo::model::Model::::namespace_selector(), contract); + + // the account does not own anything + starknet::testing::set_account_contract_address(account); + starknet::testing::set_contract_address(contract); + + write_foo_record(world); +} + +#[test] +fn test_write_model_for_model_writer() { + let world = deploy_world(); + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + + let account = starknet::contract_address_const::<0xb0b>(); + let contract = starknet::contract_address_const::<0xdeadbeef>(); + + world.grant_writer(dojo::model::Model::::selector(), contract); + + // the account does not own anything + starknet::testing::set_account_contract_address(account); + starknet::testing::set_contract_address(contract); + + write_foo_record(world); +} + +#[test] +fn test_write_namespace_for_namespace_owner() { + let world = deploy_world(); + + let account = starknet::contract_address_const::<0xb0b>(); + let contract = starknet::contract_address_const::<0xdeadbeef>(); + + world.grant_owner(account, dojo::model::Model::::namespace_selector()); + + // the account owns the Foo model namespace so it should be able to deploy + // and register the model. + starknet::testing::set_account_contract_address(account); + starknet::testing::set_contract_address(contract); + + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); +} + +#[test] +fn test_write_namespace_for_namespace_writer() { + let world = deploy_world(); + + let account = starknet::contract_address_const::<0xb0b>(); + let contract = starknet::contract_address_const::<0xdeadbeef>(); + + world.grant_writer(dojo::model::Model::::namespace_selector(), account); + + // the account has write access to the Foo model namespace so it should be able + // to deploy and register the model. + starknet::testing::set_account_contract_address(account); + starknet::testing::set_contract_address(contract); + + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); +} + +#[test] +#[should_panic(expected: ('no model write access', 'ENTRYPOINT_FAILED',))] +fn test_write_model_no_write_access() { + let world = deploy_world(); + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + + // the caller account does not own the model nor the model namespace nor the world + let account = starknet::contract_address_const::<0xb0b>(); + starknet::testing::set_account_contract_address(account); + + // the contract is not a writer for the model nor for the model namespace + let contract = starknet::contract_address_const::<0xdeadbeef>(); + starknet::testing::set_contract_address(contract); + + write_foo_record(world); +} + +#[test] +#[should_panic(expected: ('namespace not registered', 'ENTRYPOINT_FAILED',))] +fn test_register_model_with_unregistered_namespace() { + let world = deploy_world(); + world.register_model(buzz::TEST_CLASS_HASH.try_into().unwrap()); +} + +#[test] +fn test_deploy_contract_for_namespace_owner() { + let world = deploy_world(); + + let account = starknet::contract_address_const::<0xb0b>(); + world.grant_owner(account, dojo::utils::hash(@"dojo")); + + // the account owns the 'test_contract' namespace so it should be able to deploy + // and register the model. + starknet::testing::set_account_contract_address(account); + + world + .deploy_contract( + 'salt1', test_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span() + ); +} + +#[test] +fn test_deploy_contract_for_namespace_writer() { + let world = deploy_world(); + + let account = starknet::contract_address_const::<0xb0b>(); + world.grant_writer(dojo::utils::hash(@"dojo"), account); + + // the account has write access to the 'test_contract' namespace so it should be able + // to deploy and register the model. + starknet::testing::set_account_contract_address(account); + + world + .deploy_contract( + 'salt1', test_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span() + ); +} + +#[test] +#[should_panic(expected: ('namespace not registered', 'ENTRYPOINT_FAILED',))] +fn test_deploy_contract_with_unregistered_namespace() { + let world = deploy_world(); + world + .deploy_contract( + 'salt1', buzz_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span() + ); +} + +#[test] +#[should_panic(expected: ('no namespace write access', 'ENTRYPOINT_FAILED',))] +fn test_deploy_contract_no_namespace_write_access() { + let world = deploy_world(); + + let account = starknet::contract_address_const::<0xb0b>(); + starknet::testing::set_account_contract_address(account); + + world + .deploy_contract( + 'salt1', test_contract::TEST_CLASS_HASH.try_into().unwrap(), array![].span() + ); +} + diff --git a/crates/dojo-lang/Cargo.toml b/crates/dojo-lang/Cargo.toml index 2f7b17357a..e9bf9fa806 100644 --- a/crates/dojo-lang/Cargo.toml +++ b/crates/dojo-lang/Cargo.toml @@ -11,6 +11,7 @@ testing = [ ] [dependencies] anyhow.workspace = true +cainome.workspace = true cairo-lang-compiler.workspace = true cairo-lang-debug.workspace = true cairo-lang-defs.workspace = true @@ -32,12 +33,13 @@ camino.workspace = true convert_case.workspace = true directories = "5" dojo-types = { path = "../dojo-types" } -dojo-world = { path = "../dojo-world", features = [ "manifest" ] } +dojo-world = { path = "../dojo-world", features = [ "manifest", "metadata" ] } indoc.workspace = true itertools.workspace = true lazy_static.workspace = true num-traits.workspace = true once_cell.workspace = true +regex.workspace = true salsa.workspace = true scarb.workspace = true semver.workspace = true @@ -46,6 +48,7 @@ serde_json.workspace = true serde_with.workspace = true smol_str.workspace = true starknet.workspace = true +starknet-crypto.workspace = true thiserror.workspace = true toml.workspace = true tracing.workspace = true diff --git a/crates/dojo-lang/src/compiler.rs b/crates/dojo-lang/src/compiler.rs index a0eac5529b..97eeff00b9 100644 --- a/crates/dojo-lang/src/compiler.rs +++ b/crates/dojo-lang/src/compiler.rs @@ -1,4 +1,5 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::fs; use std::io::Write; use std::iter::zip; use std::ops::DerefMut; @@ -6,7 +7,7 @@ use std::ops::DerefMut; use anyhow::{anyhow, Context, Result}; use cairo_lang_compiler::db::RootDatabase; use cairo_lang_defs::db::DefsGroup; -use cairo_lang_defs::ids::{ModuleId, ModuleItemId}; +use cairo_lang_defs::ids::{ModuleId, ModuleItemId, TopLevelLanguageElementId}; use cairo_lang_filesystem::db::FilesGroup; use cairo_lang_filesystem::ids::{CrateId, CrateLongId}; use cairo_lang_formatter::format_string; @@ -17,17 +18,20 @@ use cairo_lang_starknet::plugin::aux_data::StarkNetContractAuxData; use cairo_lang_starknet_classes::abi; use cairo_lang_starknet_classes::contract_class::ContractClass; use cairo_lang_utils::UpcastMut; -use camino::{Utf8Path, Utf8PathBuf}; +use camino::Utf8PathBuf; use convert_case::{Case, Casing}; +use dojo_world::contracts::naming; use dojo_world::manifest::{ AbiFormat, Class, ComputedValueEntrypoint, DojoContract, DojoModel, Manifest, ManifestMethods, - ABIS_DIR, BASE_CONTRACT_NAME, BASE_DIR, CONTRACTS_DIR, MANIFESTS_DIR, MODELS_DIR, - WORLD_CONTRACT_NAME, + ABIS_DIR, BASE_CONTRACT_TAG, BASE_DIR, BASE_QUALIFIED_PATH, CONTRACTS_DIR, MANIFESTS_DIR, + MODELS_DIR, WORLD_CONTRACT_TAG, WORLD_QUALIFIED_PATH, }; +use dojo_world::metadata::get_default_namespace_from_ws; use itertools::Itertools; use scarb::compiler::helpers::{build_compiler_config, collect_main_crate_ids}; use scarb::compiler::{CairoCompilationUnit, CompilationUnitAttributes, Compiler}; use scarb::core::{PackageName, TargetKind, Workspace}; +use scarb::flock::Filesystem; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use smol_str::SmolStr; @@ -36,13 +40,11 @@ use starknet::core::types::Felt; use tracing::{debug, trace, trace_span}; use crate::inline_macros::utils::{SYSTEM_READS, SYSTEM_WRITES}; -use crate::plugin::{ComputedValuesAuxData, DojoAuxData}; +use crate::plugin::{ComputedValuesAuxData, DojoAuxData, Model}; use crate::semantics::utils::find_module_rw; const CAIRO_PATH_SEPARATOR: &str = "::"; -pub const SOURCES_DIR: &str = "src"; - pub(crate) const LOG_TARGET: &str = "dojo_lang::compiler"; #[cfg(test)] @@ -84,7 +86,8 @@ impl Compiler for DojoCompiler { ) -> Result<()> { let props: Props = unit.main_component().target_props()?; let target_dir = unit.target_dir(ws); - let sources_dir = target_dir.child(Utf8Path::new(SOURCES_DIR)); + + let default_namespace = get_default_namespace_from_ws(ws); let compiler_config = build_compiler_config(&unit, ws); @@ -111,44 +114,30 @@ impl Compiler for DojoCompiler { compile_prepared_db(db, &contracts, compiler_config)? }; - // (contract name, class hash) - let mut compiled_classes: HashMap)> = HashMap::new(); + let mut compiled_classes: HashMap = HashMap::new(); for (decl, class) in zip(contracts, classes) { - let contract_full_path = decl.module_id().full_path(db.upcast_mut()); - - // save expanded contract source file - if let Ok(file_id) = db.module_main_file(decl.module_id()) { - if let Some(file_content) = db.file_content(file_id) { - let src_file_name = format!("{contract_full_path}.cairo").replace("::", "_"); - - let mut file = - sources_dir.open_rw(src_file_name.clone(), "source file", ws.config())?; - file.write(format_string(db, file_content.to_string()).as_bytes()) - .with_context(|| { - format!("failed to serialize contract source: {contract_full_path}") - })?; - } else { - return Err(anyhow!("failed to get source file content: {contract_full_path}")); - } - } else { - return Err(anyhow!("failed to get source file: {contract_full_path}")); - } - - // save JSON artifact file - let file_name = format!("{contract_full_path}.json"); - let mut file = target_dir.open_rw(file_name.clone(), "class file", ws.config())?; - serde_json::to_writer_pretty(file.deref_mut(), &class).with_context(|| { - format!("failed to serialize contract artifact: {contract_full_path}") - })?; + // note that the qualified path is in snake case while + // the `full_path()` method of StructId uses the original struct name case. + // (see in `get_dojo_model_artifacts`) + let qualified_path = decl.module_id().full_path(db.upcast_mut()); let class_hash = compute_class_hash_of_contract_class(&class).with_context(|| { - format!("problem computing class hash for contract `{contract_full_path}`") + format!("problem computing class hash for contract `{}`", qualified_path.clone()) })?; - compiled_classes.insert(contract_full_path.into(), (class_hash, class.abi)); + + compiled_classes.insert(qualified_path, (class_hash, class)); } - update_manifest(db, ws, &main_crate_ids, compiled_classes, props.build_external_contracts)?; + update_files( + db, + ws, + &target_dir, + &main_crate_ids, + compiled_classes, + props.build_external_contracts, + &default_namespace, + )?; Ok(()) } } @@ -201,8 +190,8 @@ fn find_project_contracts( pub fn collect_core_crate_ids(db: &RootDatabase) -> Vec { [ - ContractSelector(BASE_CONTRACT_NAME.to_string()), - ContractSelector(WORLD_CONTRACT_NAME.to_string()), + ContractSelector(BASE_QUALIFIED_PATH.to_string()), + ContractSelector(WORLD_QUALIFIED_PATH.to_string()), ] .iter() .map(|selector| selector.package().into()) @@ -223,12 +212,14 @@ pub fn collect_external_crate_ids( .collect::>() } -fn update_manifest( +fn update_files( db: &RootDatabase, ws: &Workspace<'_>, + target_dir: &Filesystem, crate_ids: &[CrateId], - compiled_artifacts: HashMap)>, + compiled_artifacts: HashMap, external_contracts: Option>, + default_namespace: &str, ) -> anyhow::Result<()> { let profile_name = ws.current_profile().expect("Scarb profile expected to be defined.").to_string(); @@ -239,40 +230,40 @@ fn update_manifest( let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); fn get_compiled_artifact_from_map<'a>( - artifacts: &'a HashMap)>, - artifact_name: &str, - ) -> anyhow::Result<&'a (Felt, Option)> { - artifacts.get(artifact_name).context(format!( - "Contract `{artifact_name}` not found. Did you include `dojo` as a dependency?", + artifacts: &'a HashMap, + qualified_artifact_path: &str, + ) -> anyhow::Result<&'a (Felt, ContractClass)> { + artifacts.get(qualified_artifact_path).context(format!( + "Contract `{qualified_artifact_path}` not found. Did you include `dojo` as a \ + dependency?", )) } let mut crate_ids = crate_ids.to_vec(); - let (hash, abi) = get_compiled_artifact_from_map(&compiled_artifacts, WORLD_CONTRACT_NAME)?; - write_manifest_and_abi( - &relative_manifests_dir, - &relative_abis_dir, - &manifest_dir, - &mut Manifest::new( - // abi path will be written by `write_manifest` - Class { class_hash: *hash, abi: None, original_class_hash: *hash }, - WORLD_CONTRACT_NAME.into(), - ), - abi, - )?; - - let (hash, _) = get_compiled_artifact_from_map(&compiled_artifacts, BASE_CONTRACT_NAME)?; - write_manifest_and_abi( - &relative_manifests_dir, - &relative_abis_dir, - &manifest_dir, - &mut Manifest::new( - Class { class_hash: *hash, abi: None, original_class_hash: *hash }, - BASE_CONTRACT_NAME.into(), - ), - &None, - )?; + for (qualified_path, tag) in + [(WORLD_QUALIFIED_PATH, WORLD_CONTRACT_TAG), (BASE_QUALIFIED_PATH, BASE_CONTRACT_TAG)] + { + let (hash, class) = get_compiled_artifact_from_map(&compiled_artifacts, qualified_path)?; + let filename = naming::get_filename_from_tag(tag); + write_manifest_and_abi( + &relative_manifests_dir, + &relative_abis_dir, + &manifest_dir, + &mut Manifest::new( + // abi path will be written by `write_manifest` + Class { + class_hash: *hash, + abi: None, + original_class_hash: *hash, + tag: tag.to_string(), + }, + filename.clone(), + ), + &class.abi, + )?; + save_json_artifact_file(ws, target_dir, class, &filename, tag)?; + } let mut models = BTreeMap::new(); let mut contracts = BTreeMap::new(); @@ -292,33 +283,43 @@ fn update_manifest( .filter_map(|info| info.as_ref().map(|i| &i.aux_data)) .filter_map(|aux_data| aux_data.as_ref().map(|aux_data| aux_data.0.as_any())) { - if let Some(aux_data) = aux_data.downcast_ref::() { - contracts.extend(get_dojo_contract_artifacts( - db, - module_id, - aux_data, - &compiled_artifacts, - )?); - } if let Some(aux_data) = aux_data.downcast_ref::() { get_dojo_computed_values(db, module_id, aux_data, &mut computed); - } + } else if let Some(dojo_aux_data) = aux_data.downcast_ref::() { + for system in &dojo_aux_data.systems { + contracts.extend(get_dojo_contract_artifacts( + db, + module_id, + &naming::get_tag(&system.namespace, &system.name), + &compiled_artifacts, + )?); + } - if let Some(dojo_aux_data) = aux_data.downcast_ref::() { models.extend(get_dojo_model_artifacts( db, - dojo_aux_data, + &dojo_aux_data.models, *module_id, &compiled_artifacts, )?); + } else if let Some(aux_data) = aux_data.downcast_ref::() { + contracts.extend(get_dojo_contract_artifacts( + db, + module_id, + &naming::get_tag(default_namespace, &aux_data.contract_name), + &compiled_artifacts, + )?); } } } } + // `get_dojo_computed_values()` uses the module name as contract name to build the `computed` + // variable. That means, the namespace of the contract is not taken into account, + // but should be retrieved from the dojo::contract attribute. computed.into_iter().for_each(|(contract, computed_value_entrypoint)| { - let contract_data = - contracts.get_mut(&contract).expect("Error: Computed value contract doesn't exist."); + let contract_data = contracts + .get_mut(&contract.to_string()) + .expect("Error: Computed value contract doesn't exist."); contract_data.0.inner.computed = computed_value_entrypoint; }); @@ -326,24 +327,49 @@ fn update_manifest( contracts.remove(model.0.as_str()); } - for (_, (manifest, abi)) in contracts.iter_mut() { + let contracts_dir = target_dir.child(CONTRACTS_DIR); + if !contracts.is_empty() && !contracts_dir.exists() { + fs::create_dir_all(contracts_dir.path_unchecked())?; + } + + for (_, (manifest, class, module_id)) in contracts.iter_mut() { write_manifest_and_abi( &relative_manifests_dir.join(CONTRACTS_DIR), &relative_abis_dir.join(CONTRACTS_DIR), &manifest_dir, manifest, - abi, + &class.abi, + )?; + + let filename = naming::get_filename_from_tag(&manifest.inner.tag); + save_expanded_source_file( + ws, + *module_id, + db, + &contracts_dir, + &filename, + &manifest.inner.tag, )?; + save_json_artifact_file(ws, &contracts_dir, class, &filename, &manifest.inner.tag)?; } - for (_, (manifest, abi)) in models.iter_mut() { + let models_dir = target_dir.child(MODELS_DIR); + if !models.is_empty() && !models_dir.exists() { + fs::create_dir_all(models_dir.path_unchecked())?; + } + + for (_, (manifest, class, module_id)) in models.iter_mut() { write_manifest_and_abi( &relative_manifests_dir.join(MODELS_DIR), &relative_abis_dir.join(MODELS_DIR), &manifest_dir, manifest, - abi, + &class.abi, )?; + + let filename = naming::get_filename_from_tag(&manifest.inner.tag); + save_expanded_source_file(ws, *module_id, db, &models_dir, &filename, &manifest.inner.tag)?; + save_json_artifact_file(ws, &models_dir, class, &filename, &manifest.inner.tag)?; } Ok(()) @@ -354,42 +380,42 @@ fn update_manifest( #[allow(clippy::type_complexity)] fn get_dojo_model_artifacts( db: &RootDatabase, - aux_data: &DojoAuxData, + aux_data: &Vec, module_id: ModuleId, - compiled_classes: &HashMap)>, -) -> anyhow::Result, Option)>> { - let mut models = HashMap::with_capacity(aux_data.models.len()); - - let module_name = module_id.full_path(db); - let module_name = module_name.as_str(); + compiled_classes: &HashMap, +) -> anyhow::Result, ContractClass, ModuleId)>> { + let mut models = HashMap::with_capacity(aux_data.len()); - for model in &aux_data.models { - if let Ok(Some(ModuleItemId::Struct(_))) = + for model in aux_data { + if let Ok(Some(ModuleItemId::Struct(struct_id))) = db.module_item_by_name(module_id, model.name.clone().into()) { - let model_contract_name = model.name.to_case(Case::Snake); - let model_full_name = format!("{module_name}::{}", &model_contract_name); + // The `struct_id` full_path() method uses the original struct name case while + // snake case was used to build `compiled_classes` in `compile()`. + let qualified_path = struct_id.full_path(db).to_case(Case::Snake); + let compiled_class = compiled_classes.get(&qualified_path).cloned(); + let tag = naming::get_tag(&model.namespace, &model.name); - let compiled_class = compiled_classes.get(model_full_name.as_str()).cloned(); - - if let Some((class_hash, abi)) = compiled_class { + if let Some((class_hash, class)) = compiled_class { models.insert( - model_full_name.clone(), + qualified_path.clone(), ( Manifest::new( DojoModel { + tag: tag.clone(), class_hash, abi: None, members: model.members.clone(), original_class_hash: class_hash, }, - model_full_name.into(), + naming::get_filename_from_tag(&tag), ), - abi, + class, + module_id, ), ); } else { - println!("Model {} not found in target.", model_full_name.clone()); + println!("Model {} not found in target.", tag.clone()); } } } @@ -414,7 +440,7 @@ fn get_dojo_computed_values( computed_vals.push(ComputedValueEntrypoint { contract: module_name, entrypoint: aux_data.entrypoint.clone(), - model: aux_data.model.clone(), + tag: aux_data.tag.clone(), }) } } @@ -423,21 +449,19 @@ fn get_dojo_computed_values( fn get_dojo_contract_artifacts( db: &RootDatabase, module_id: &ModuleId, - aux_data: &StarkNetContractAuxData, - compiled_classes: &HashMap)>, -) -> anyhow::Result, Option)>> { - let contract_name = &aux_data.contract_name; - + tag: &str, + compiled_classes: &HashMap, +) -> anyhow::Result, ContractClass, ModuleId)>> { let mut result = HashMap::new(); - if !matches!(contract_name.as_ref(), "world" | "resource_metadata" | "base") { - let module_name: SmolStr = module_id.full_path(db).into(); + if !matches!(naming::get_name_from_tag(tag).as_str(), "world" | "resource_metadata" | "base") { + let qualified_path = module_id.full_path(db).to_string(); - if let Some((class_hash, abi)) = compiled_classes.get(&module_name as &str) { + if let Some((class_hash, class)) = compiled_classes.get(&qualified_path) { let reads = SYSTEM_READS .lock() .unwrap() - .get(&module_name as &str) + .get(&qualified_path as &str) // should use tag instead of qualified_path .map_or_else(Vec::new, |models| { models.clone().into_iter().collect::>().into_iter().collect() }); @@ -445,21 +469,22 @@ fn get_dojo_contract_artifacts( let writes = SYSTEM_WRITES .lock() .unwrap() - .get(&module_name as &str) + .get(&qualified_path as &str) // should use tag instead of qualified_path .map_or_else(Vec::new, |write_ops| find_module_rw(db, module_id, write_ops)); let manifest = Manifest::new( DojoContract { + tag: tag.to_string(), writes, reads, class_hash: *class_hash, original_class_hash: *class_hash, ..Default::default() }, - module_name.clone(), + naming::get_filename_from_tag(tag), ); - result.insert(module_name, (manifest, abi.clone())); + result.insert(qualified_path.to_string(), (manifest, class.clone(), *module_id)); } } @@ -476,10 +501,10 @@ fn write_manifest_and_abi( where T: Serialize + DeserializeOwned + ManifestMethods, { - let name = manifest.name.to_string().replace("::", "_"); - - let relative_manifest_path = relative_manifest_dir.join(name.clone()).with_extension("toml"); - let relative_abi_path = relative_abis_dir.join(name.clone()).with_extension("json"); + let relative_manifest_path = + relative_manifest_dir.join(manifest.manifest_name.clone()).with_extension("toml"); + let relative_abi_path = + relative_abis_dir.join(manifest.manifest_name.clone()).with_extension("json"); if abi.is_some() { manifest.inner.set_abi(Some(AbiFormat::Path(relative_abi_path.clone()))); @@ -514,3 +539,43 @@ where } Ok(()) } + +fn save_expanded_source_file( + ws: &Workspace<'_>, + module_id: ModuleId, + db: &RootDatabase, + contract_dir: &Filesystem, + contract_basename: &str, + contract_tag: &str, +) -> anyhow::Result<()> { + if let Ok(file_id) = db.module_main_file(module_id) { + if let Some(file_content) = db.file_content(file_id) { + let src_file_name = format!("{contract_basename}.cairo"); + + let mut file = + contract_dir.open_rw(src_file_name.clone(), "source file", ws.config())?; + file.write(format_string(db, file_content.to_string()).as_bytes()) + .with_context(|| format!("failed to serialize contract source: {contract_tag}"))?; + } else { + return Err(anyhow!("failed to get source file content: {contract_tag}")); + } + } else { + return Err(anyhow!("failed to get source file: {contract_tag}")); + } + + Ok(()) +} + +fn save_json_artifact_file( + ws: &Workspace<'_>, + contract_dir: &Filesystem, + contract_class: &ContractClass, + contract_basename: &str, + contract_tag: &str, +) -> anyhow::Result<()> { + let mut file = + contract_dir.open_rw(format!("{contract_basename}.json"), "class file", ws.config())?; + serde_json::to_writer_pretty(file.deref_mut(), &contract_class) + .with_context(|| format!("failed to serialize contract artifact: {contract_tag}"))?; + Ok(()) +} diff --git a/crates/dojo-lang/src/compiler_test.rs b/crates/dojo-lang/src/compiler_test.rs index 6357a72649..01d71a6075 100644 --- a/crates/dojo-lang/src/compiler_test.rs +++ b/crates/dojo-lang/src/compiler_test.rs @@ -1,4 +1,5 @@ use dojo_test_utils::compiler::build_test_config; +use scarb::compiler::Profile; use scarb::core::TargetKind; use scarb::ops::{CompileOpts, FeaturesOpts, FeaturesSelector}; @@ -6,7 +7,9 @@ use crate::scarb_internal; #[test] fn test_compiler_cairo_features() { - let config = build_test_config("./src/manifest_test_data/compiler_cairo/Scarb.toml").unwrap(); + let config = + build_test_config("./src/manifest_test_data/compiler_cairo/Scarb.toml", Profile::DEV) + .unwrap(); let features_opts = FeaturesOpts { features: FeaturesSelector::AllFeatures, no_default_features: false }; diff --git a/crates/dojo-lang/src/contract.rs b/crates/dojo-lang/src/contract.rs index 1e4e5147e0..0bd61a7611 100644 --- a/crates/dojo-lang/src/contract.rs +++ b/crates/dojo-lang/src/contract.rs @@ -5,18 +5,28 @@ use cairo_lang_defs::plugin::{ DynGeneratedFileAuxData, PluginDiagnostic, PluginGeneratedFile, PluginResult, }; use cairo_lang_diagnostics::Severity; -use cairo_lang_syntax::node::ast::MaybeModuleBody; +use cairo_lang_syntax::node::ast::{ + ArgClause, Expr, MaybeModuleBody, OptionArgListParenthesized, OptionReturnTypeClause, +}; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::helpers::QueryAttrs; use cairo_lang_syntax::node::{ast, ids, Terminal, TypedStablePtr, TypedSyntaxNode}; use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; use dojo_types::system::Dependency; +use dojo_world::contracts::naming; -use crate::plugin::{DojoAuxData, SystemAuxData}; +use crate::plugin::{DojoAuxData, SystemAuxData, DOJO_CONTRACT_ATTR}; use crate::syntax::world_param::{self, WorldParamInjectionKind}; use crate::syntax::{self_param, utils as syntax_utils}; +use crate::utils::is_name_valid; const DOJO_INIT_FN: &str = "dojo_init"; +const CONTRACT_NAMESPACE: &str = "namespace"; + +#[derive(Clone, Default)] +pub struct ContractParameters { + namespace: Option, +} pub struct DojoContract { diagnostics: Vec, @@ -24,14 +34,48 @@ pub struct DojoContract { } impl DojoContract { - pub fn from_module(db: &dyn SyntaxGroup, module_ast: ast::ItemModule) -> PluginResult { + pub fn from_module( + db: &dyn SyntaxGroup, + module_ast: &ast::ItemModule, + package_id: String, + ) -> PluginResult { let name = module_ast.name(db).text(db); - let mut system = DojoContract { diagnostics: vec![], dependencies: HashMap::new() }; + let mut diagnostics = vec![]; + let parameters = get_parameters(db, module_ast, &mut diagnostics); + + let mut system = DojoContract { diagnostics, dependencies: HashMap::new() }; + let mut has_event = false; let mut has_storage = false; let mut has_init = false; + let contract_namespace = match parameters.namespace { + Some(x) => x.to_string(), + None => package_id, + }; + + for (id, value) in [("name", &name.to_string()), ("namespace", &contract_namespace)] { + if !is_name_valid(value) { + return PluginResult { + code: None, + diagnostics: vec![PluginDiagnostic { + stable_ptr: module_ast.stable_ptr().0, + message: format!( + "The contract {id} '{value}' can only contain characters (a-z/A-Z), \ + numbers (0-9) and underscore (_)" + ), + severity: Severity::Error, + }], + remove_original_item: false, + }; + } + } + + let contract_tag = naming::get_tag(&contract_namespace, &name); + let contract_name_selector = naming::compute_bytearray_hash(&name); + let contract_namespace_selector = naming::compute_bytearray_hash(&contract_namespace); + if let MaybeModuleBody::Some(body) = module_ast.body(db) { let mut body_nodes: Vec<_> = body .items(db) @@ -101,25 +145,40 @@ impl DojoContract { body_nodes.append(&mut system.create_storage()) } - let mut builder = PatchBuilder::new(db, &module_ast); - builder.add_modified(RewriteNode::interpolate_patched( - " + let mut builder = PatchBuilder::new(db, module_ast); + builder.add_modified(RewriteNode::Mapped { + node: Box::new(RewriteNode::interpolate_patched( + " #[starknet::contract] mod $name$ { use dojo::world; use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; - + use dojo::contract::IContract; component!(path: dojo::components::upgradeable::upgradeable, storage: \ - upgradeable, event: UpgradeableEvent); + upgradeable, event: UpgradeableEvent); #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - '$name$' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + \"$name$\" + } + fn selector(self: @ContractState) -> felt252 { + $contract_name_selector$ + } + + fn namespace(self: @ContractState) -> ByteArray { + \"$contract_namespace$\" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + $contract_namespace_selector$ + } + + fn tag(self: @ContractState) -> ByteArray { + \"$contract_tag$\" } } @@ -132,16 +191,31 @@ impl DojoContract { #[abi(embed_v0)] impl UpgradableImpl = \ - dojo::components::upgradeable::upgradeable::UpgradableImpl; + dojo::components::upgradeable::upgradeable::UpgradableImpl; $body$ } ", - &UnorderedHashMap::from([ - ("name".to_string(), RewriteNode::Text(name.to_string())), - ("body".to_string(), RewriteNode::new_modified(body_nodes)), - ]), - )); + &UnorderedHashMap::from([ + ("name".to_string(), RewriteNode::Text(name.to_string())), + ( + "contract_name_selector".to_string(), + RewriteNode::Text(contract_name_selector.to_string()), + ), + ("body".to_string(), RewriteNode::new_modified(body_nodes)), + ( + "contract_namespace".to_string(), + RewriteNode::Text(contract_namespace.clone()), + ), + ( + "contract_namespace_selector".to_string(), + RewriteNode::Text(contract_namespace_selector.to_string()), + ), + ("contract_tag".to_string(), RewriteNode::Text(contract_tag)), + ]), + )), + origin: module_ast.as_syntax_node().span_without_trivia(db), + }); let (code, code_mappings) = builder.build(); @@ -153,6 +227,7 @@ impl DojoContract { models: vec![], systems: vec![SystemAuxData { name, + namespace: contract_namespace.clone(), dependencies: system.dependencies.values().cloned().collect(), }], events: vec![], @@ -173,7 +248,14 @@ impl DojoContract { fn_ast: &ast::FunctionWithBody, ) -> Vec { let fn_decl = fn_ast.declaration(db); - let fn_name = fn_decl.name(db).text(db); + + if let OptionReturnTypeClause::ReturnTypeClause(_) = fn_decl.signature(db).ret_ty(db) { + self.diagnostics.push(PluginDiagnostic { + stable_ptr: fn_ast.stable_ptr().untyped(), + message: "The dojo_init function cannot have a return type.".to_string(), + severity: Severity::Error, + }); + } let (params_str, was_world_injected) = self.rewrite_parameters( db, @@ -181,39 +263,66 @@ impl DojoContract { fn_ast.stable_ptr().untyped(), ); - let mut world_read = ""; - if was_world_injected { - world_read = "let world = self.world_dispatcher.read();"; - } - - let body = fn_ast.body(db).as_syntax_node().get_text(db); + let trait_node = RewriteNode::interpolate_patched( + "#[starknet::interface] + trait IDojoInit { + fn dojo_init($params_str$); + } + ", + &UnorderedHashMap::from([( + "params_str".to_string(), + RewriteNode::Text(params_str.clone()), + )]), + ); - let node = RewriteNode::interpolate_patched( + let impl_node = RewriteNode::Text( " - #[starknet::interface] - trait IDojoInit { - fn $name$($params_str$); - } + #[abi(embed_v0)] + impl IDojoInitImpl of IDojoInit { + " + .to_string(), + ); - #[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())), - ]), + let declaration_node = RewriteNode::Mapped { + node: Box::new(RewriteNode::Text(format!("fn dojo_init({}) {{", params_str))), + origin: fn_ast.declaration(db).as_syntax_node().span_without_trivia(db), + }; + + let world_line_node = if was_world_injected { + RewriteNode::Text("let world = self.world_dispatcher.read();".to_string()) + } else { + RewriteNode::empty() + }; + + let assert_world_caller_node = RewriteNode::Text( + "assert(starknet::get_caller_address() == self.world().contract_address, 'Only world \ + can init');" + .to_string(), ); - vec![node] + let func_nodes = fn_ast + .body(db) + .statements(db) + .elements(db) + .iter() + .map(|e| RewriteNode::Mapped { + node: Box::new(RewriteNode::from(e.as_syntax_node())), + origin: e.as_syntax_node().span_without_trivia(db), + }) + .collect::>(); + + let mut nodes = vec![ + trait_node, + impl_node, + declaration_node, + world_line_node, + assert_world_caller_node, + ]; + nodes.extend(func_nodes); + // Close the init function + close the impl block. + nodes.push(RewriteNode::Text("}\n}".to_string())); + + nodes } pub fn merge_event( @@ -366,22 +475,6 @@ impl DojoContract { (params.join(", "), world_injection != WorldParamInjectionKind::None) } - /// Rewrites function statements by adding the reading of `world` at first statement. - pub fn rewrite_statements( - &mut self, - db: &dyn SyntaxGroup, - statement_list: ast::StatementList, - ) -> String { - let mut statements = statement_list - .elements(db) - .iter() - .map(|e| e.as_syntax_node().get_text(db)) - .collect::>(); - - statements.insert(0, "let world = self.world_dispatcher.read();\n".to_string()); - statements.join("") - } - /// Rewrites function declaration by: /// * adding `self` parameter if missing, /// * removing `world` if present as first parameter (self excluded), @@ -395,7 +488,9 @@ impl DojoContract { fn_ast: ast::FunctionWithBody, has_generate_trait: bool, ) -> Vec { - let mut rewritten_fn = RewriteNode::from_ast(&fn_ast); + let fn_name = fn_ast.declaration(db).name(db).text(db); + let return_type = + fn_ast.declaration(db).signature(db).ret_ty(db).as_syntax_node().get_text(db); let (params_str, was_world_injected) = self.rewrite_parameters( db, @@ -403,6 +498,31 @@ impl DojoContract { fn_ast.stable_ptr().untyped(), ); + let declaration_node = RewriteNode::Mapped { + node: Box::new(RewriteNode::Text(format!( + "fn {}({}) {} {{", + fn_name, params_str, return_type + ))), + origin: fn_ast.declaration(db).as_syntax_node().span_without_trivia(db), + }; + + let world_line_node = if was_world_injected { + RewriteNode::Text("let world = self.world_dispatcher.read();".to_string()) + } else { + RewriteNode::empty() + }; + + let func_nodes = fn_ast + .body(db) + .statements(db) + .elements(db) + .iter() + .map(|e| RewriteNode::Mapped { + node: Box::new(RewriteNode::from(e.as_syntax_node())), + origin: e.as_syntax_node().span_without_trivia(db), + }) + .collect::>(); + if has_generate_trait && was_world_injected { self.diagnostics.push(PluginDiagnostic { stable_ptr: fn_ast.stable_ptr().untyped(), @@ -413,26 +533,11 @@ impl DojoContract { }); } - // We always rewrite the params as the self parameter is added based on the - // world mutability. - let rewritten_params = rewritten_fn - .modify_child(db, ast::FunctionWithBody::INDEX_DECLARATION) - .modify_child(db, ast::FunctionDeclaration::INDEX_SIGNATURE) - .modify_child(db, ast::FunctionSignature::INDEX_PARAMETERS); - rewritten_params.set_str(params_str); - - // If the world was injected, we also need to rewrite the statements of the function - // to ensure the `world` injection is effective. - if was_world_injected { - let rewritten_statements = rewritten_fn - .modify_child(db, ast::FunctionWithBody::INDEX_BODY) - .modify_child(db, ast::ExprBlock::INDEX_STATEMENTS); - - rewritten_statements - .set_str(self.rewrite_statements(db, fn_ast.body(db).statements(db))); - } + let mut nodes = vec![declaration_node, world_line_node]; + nodes.extend(func_nodes); + nodes.push(RewriteNode::Text("}".to_string())); - vec![rewritten_fn] + nodes } /// Rewrites all the functions of a Impl block. @@ -441,6 +546,17 @@ impl DojoContract { let has_generate_trait = !generate_attrs.is_empty(); if let ast::MaybeImplBody::Some(body) = impl_ast.body(db) { + // We shouldn't have generic param in the case of contract's endpoints. + let impl_node = RewriteNode::Mapped { + node: Box::new(RewriteNode::Text(format!( + "{} impl {} of {} {{", + impl_ast.attributes(db).as_syntax_node().get_text(db), + impl_ast.name(db).as_syntax_node().get_text(db), + impl_ast.trait_path(db).as_syntax_node().get_text(db), + ))), + origin: impl_ast.as_syntax_node().span_without_trivia(db), + }; + let body_nodes: Vec<_> = body .items(db) .elements(db) @@ -453,26 +569,121 @@ impl DojoContract { }) .collect(); - let mut builder = PatchBuilder::new(db, &impl_ast); - builder.add_modified(RewriteNode::interpolate_patched( - "$body$", - &UnorderedHashMap::from([( - "body".to_string(), - RewriteNode::new_modified(body_nodes), - )]), - )); + let body_node = RewriteNode::Mapped { + node: Box::new(RewriteNode::interpolate_patched( + "$body$", + &UnorderedHashMap::from([( + "body".to_string(), + RewriteNode::new_modified(body_nodes), + )]), + )), + origin: impl_ast.as_syntax_node().span_without_trivia(db), + }; - let mut rewritten_impl = RewriteNode::from_ast(&impl_ast); - let rewritten_items = rewritten_impl - .modify_child(db, ast::ItemImpl::INDEX_BODY) - .modify_child(db, ast::ImplBody::INDEX_ITEMS); + return vec![impl_node, body_node, RewriteNode::Text("}".to_string())]; + } - let (code, _) = builder.build(); + vec![RewriteNode::Copied(impl_ast.as_syntax_node())] + } +} - rewritten_items.set_str(code); - return vec![rewritten_impl]; +/// Get the contract namespace from the `Expr` parameter. +fn get_contract_namespace( + db: &dyn SyntaxGroup, + arg_value: Expr, + diagnostics: &mut Vec, +) -> Option { + match arg_value { + Expr::ShortString(ss) => Some(ss.string_value(db).unwrap()), + Expr::String(s) => Some(s.string_value(db).unwrap()), + _ => { + diagnostics.push(PluginDiagnostic { + message: format!( + "The argument '{}' of dojo::contract must be a string", + CONTRACT_NAMESPACE + ), + stable_ptr: arg_value.stable_ptr().untyped(), + severity: Severity::Error, + }); + Option::None } + } +} - vec![RewriteNode::Copied(impl_ast.as_syntax_node())] +/// Get parameters of the dojo::contract attribute. +/// +/// Parameters: +/// * db: The semantic database. +/// * module_ast: The AST of the contract module. +/// * diagnostics: vector of compiler diagnostics. +/// +/// Returns: +/// * A [`ContractParameters`] object containing all the dojo::contract parameters with their +/// default values if not set in the code. +fn get_parameters( + db: &dyn SyntaxGroup, + module_ast: &ast::ItemModule, + diagnostics: &mut Vec, +) -> ContractParameters { + let mut parameters = ContractParameters::default(); + let mut processed_args: HashMap = HashMap::new(); + + if let OptionArgListParenthesized::ArgListParenthesized(arguments) = + module_ast.attributes(db).query_attr(db, DOJO_CONTRACT_ATTR).first().unwrap().arguments(db) + { + arguments.arguments(db).elements(db).iter().for_each(|a| match a.arg_clause(db) { + ArgClause::Named(x) => { + let arg_name = x.name(db).text(db).to_string(); + let arg_value = x.value(db); + + if processed_args.contains_key(&arg_name) { + diagnostics.push(PluginDiagnostic { + message: format!("Too many '{}' attributes for dojo::contract", arg_name), + stable_ptr: module_ast.stable_ptr().untyped(), + severity: Severity::Error, + }); + } else { + processed_args.insert(arg_name.clone(), true); + + match arg_name.as_str() { + CONTRACT_NAMESPACE => { + parameters.namespace = + get_contract_namespace(db, arg_value, diagnostics); + } + _ => { + diagnostics.push(PluginDiagnostic { + message: format!( + "Unexpected argument '{}' for dojo::contract", + arg_name + ), + stable_ptr: x.stable_ptr().untyped(), + severity: Severity::Warning, + }); + } + } + } + } + ArgClause::Unnamed(arg) => { + let arg_name = arg.value(db).as_syntax_node().get_text(db); + + diagnostics.push(PluginDiagnostic { + message: format!("Unexpected argument '{}' for dojo::contract", arg_name), + stable_ptr: arg.stable_ptr().untyped(), + severity: Severity::Warning, + }); + } + ArgClause::FieldInitShorthand(x) => { + diagnostics.push(PluginDiagnostic { + message: format!( + "Unexpected argument '{}' for dojo::contract", + x.name(db).name(db).text(db).to_string() + ), + stable_ptr: x.stable_ptr().untyped(), + severity: Severity::Warning, + }); + } + }) } + + parameters } diff --git a/crates/dojo-lang/src/interface.rs b/crates/dojo-lang/src/interface.rs index 1fc64b6759..b601ada9d3 100644 --- a/crates/dojo-lang/src/interface.rs +++ b/crates/dojo-lang/src/interface.rs @@ -40,30 +40,36 @@ impl DojoInterface { }) .collect(); - builder.add_modified(RewriteNode::interpolate_patched( - " + builder.add_modified(RewriteNode::Mapped { + node: Box::new(RewriteNode::interpolate_patched( + " #[starknet::interface] trait $name$ { $body$ } ", - &UnorderedHashMap::from([ - ("name".to_string(), RewriteNode::Text(name.to_string())), - ("body".to_string(), RewriteNode::new_modified(body_nodes)), - ]), - )); + &UnorderedHashMap::from([ + ("name".to_string(), RewriteNode::Text(name.to_string())), + ("body".to_string(), RewriteNode::new_modified(body_nodes)), + ]), + )), + origin: trait_ast.as_syntax_node().span_without_trivia(db), + }); } else { // empty trait - builder.add_modified(RewriteNode::interpolate_patched( - " + builder.add_modified(RewriteNode::Mapped { + node: Box::new(RewriteNode::interpolate_patched( + " #[starknet::interface] trait $name$ {} ", - &UnorderedHashMap::from([( - "name".to_string(), - RewriteNode::Text(name.to_string()), - )]), - )); + &UnorderedHashMap::from([( + "name".to_string(), + RewriteNode::Text(name.to_string()), + )]), + )), + origin: trait_ast.as_syntax_node().span_without_trivia(db), + }); } let (code, code_mappings) = builder.build(); @@ -135,11 +141,9 @@ impl DojoInterface { db: &dyn SyntaxGroup, fn_ast: ast::TraitItemFunction, ) -> Vec { - let mut rewritten_fn = RewriteNode::from_ast(&fn_ast); - let rewritten_params = rewritten_fn - .modify_child(db, ast::TraitItemFunction::INDEX_DECLARATION) - .modify_child(db, ast::FunctionDeclaration::INDEX_SIGNATURE) - .modify_child(db, ast::FunctionSignature::INDEX_PARAMETERS); + let fn_name = fn_ast.declaration(db).name(db).text(db); + let return_type = + fn_ast.declaration(db).signature(db).ret_ty(db).as_syntax_node().get_text(db); let params_str = self.rewrite_parameters( db, @@ -147,7 +151,14 @@ impl DojoInterface { fn_ast.stable_ptr().untyped(), ); - rewritten_params.set_str(params_str); - vec![rewritten_fn] + let declaration_node = RewriteNode::Mapped { + node: Box::new(RewriteNode::Text(format!( + "fn {}({}) {};", + fn_name, params_str, return_type + ))), + origin: fn_ast.declaration(db).as_syntax_node().span_without_trivia(db), + }; + + vec![declaration_node] } } diff --git a/crates/dojo-lang/src/lib.rs b/crates/dojo-lang/src/lib.rs index 4f7e0e4bca..4ed4b70c41 100644 --- a/crates/dojo-lang/src/lib.rs +++ b/crates/dojo-lang/src/lib.rs @@ -14,6 +14,7 @@ pub mod plugin; pub mod print; pub mod semantics; pub mod syntax; +pub mod utils; pub(crate) mod version; // Copy of non pub functions from scarb + extension. diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/Scarb.toml b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/Scarb.toml index 971e710469..f22ec5c45f 100644 --- a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/Scarb.toml +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/Scarb.toml @@ -18,6 +18,8 @@ build-external-contracts = [ ] [tool.dojo.world] description = "Cairo compiler features" name = "compiler_cairo_features" +seed = "compiler_cairo_features" +namespace = "ccf" [tool.dojo.env] rpc_url = "http://localhost:5050/" diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/compiler_cairo_cairo_24_cairo_v240.json b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/ccf-cairo_v240-8d921297.json similarity index 100% rename from crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/compiler_cairo_cairo_24_cairo_v240.json rename to crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/ccf-cairo_v240-8d921297.json diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/compiler_cairo_cairo_26_cairo_v260.json b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/ccf-cairo_v260-465ec7fe.json similarity index 100% rename from crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/compiler_cairo_cairo_26_cairo_v260.json rename to crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/contracts/ccf-cairo_v260-465ec7fe.json diff --git a/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_mock_token_mock_token.json b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo-base.json similarity index 67% rename from examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_mock_token_mock_token.json rename to crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo-base.json index f4bde33e6d..ee9ceaac66 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_mock_token_mock_token.json +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo-base.json @@ -1,26 +1,4 @@ [ - { - "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", @@ -53,24 +31,6 @@ } ] }, - { - "type": "impl", - "name": "IDojoInitImpl", - "interface_name": "dojo_examples::mock_token::mock_token::IDojoInit" - }, - { - "type": "interface", - "name": "dojo_examples::mock_token::mock_token::IDojoInit", - "items": [ - { - "type": "function", - "name": "dojo_init", - "inputs": [], - "outputs": [], - "state_mutability": "view" - } - ] - }, { "type": "impl", "name": "UpgradableImpl", @@ -94,6 +54,11 @@ } ] }, + { + "type": "constructor", + "name": "constructor", + "inputs": [] + }, { "type": "event", "name": "dojo::components::upgradeable::upgradeable::Upgraded", @@ -120,13 +85,13 @@ }, { "type": "event", - "name": "dojo_examples::mock_token::mock_token::Event", + "name": "dojo::base::base::Event", "kind": "enum", "variants": [ { "name": "UpgradeableEvent", "type": "dojo::components::upgradeable::upgradeable::Event", - "kind": "nested" + "kind": "flat" } ] } 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.json similarity index 89% rename from crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo_world_world.json rename to crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/abis/base/dojo-world.json index c1a2447839..0e5e96210e 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.json @@ -194,6 +194,18 @@ "outputs": [], "state_mutability": "external" }, + { + "type": "function", + "name": "register_namespace", + "inputs": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray" + } + ], + "outputs": [], + "state_mutability": "external" + }, { "type": "function", "name": "deploy_contract", @@ -401,7 +413,7 @@ "name": "is_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -421,7 +433,7 @@ "name": "grant_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -437,7 +449,7 @@ "name": "revoke_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -447,6 +459,66 @@ ], "outputs": [], "state_mutability": "external" + }, + { + "type": "function", + "name": "can_write_resource", + "inputs": [ + { + "name": "resource_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_model", + "inputs": [ + { + "name": "model_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_namespace", + "inputs": [ + { + "name": "namespace_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" } ] }, @@ -692,6 +764,16 @@ "name": "address", "type": "core::starknet::contract_address::ContractAddress", "kind": "data" + }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "name", + "type": "core::byte_array::ByteArray", + "kind": "data" } ] }, @@ -741,6 +823,23 @@ } ] }, + { + "type": "event", + "name": "dojo::world::world::NamespaceRegistered", + "kind": "struct", + "members": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "hash", + "type": "core::felt252", + "kind": "data" + } + ] + }, { "type": "event", "name": "dojo::world::world::ModelRegistered", @@ -751,6 +850,11 @@ "type": "core::byte_array::ByteArray", "kind": "data" }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash", @@ -818,7 +922,7 @@ "kind": "struct", "members": [ { - "name": "model", + "name": "resource", "type": "core::felt252", "kind": "data" }, @@ -956,6 +1060,11 @@ "type": "dojo::world::world::MetadataUpdate", "kind": "nested" }, + { + "name": "NamespaceRegistered", + "type": "dojo::world::world::NamespaceRegistered", + "kind": "nested" + }, { "name": "ModelRegistered", "type": "dojo::world::world::ModelRegistered", 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/ccf-cairo_v240-8d921297.toml similarity index 66% rename from crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_24_cairo_v240.toml rename to crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/ccf-cairo_v240-8d921297.toml index d253e1bd66..be3fb94735 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/ccf-cairo_v240-8d921297.toml @@ -2,9 +2,10 @@ kind = "DojoContract" class_hash = "0x7468fbf6e47eb66fd898a4a68bbe801560fdd42b0d6909ec4f75fb38c613702" original_class_hash = "0x7468fbf6e47eb66fd898a4a68bbe801560fdd42b0d6909ec4f75fb38c613702" base_class_hash = "0x0" -abi = "manifests/dev/abis/base/contracts/compiler_cairo_cairo_24_cairo_v240.json" +abi = "manifests/dev/abis/base/contracts/ccf-cairo_v240-8d921297.json" reads = [] writes = [] computed = [] init_calldata = [] -name = "compiler_cairo::cairo_24::cairo_v240" +tag = "ccf-cairo_v240" +manifest_name = "ccf-cairo_v240-8d921297" 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/ccf-cairo_v260-465ec7fe.toml similarity index 66% rename from crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/compiler_cairo_cairo_26_cairo_v260.toml rename to crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/contracts/ccf-cairo_v260-465ec7fe.toml index 4ac01d6fdb..f4443a58d4 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/ccf-cairo_v260-465ec7fe.toml @@ -2,9 +2,10 @@ kind = "DojoContract" class_hash = "0x4bbfcdc8d95ecfa332201e21f615e98b4d12e08f77d176761e32bb34e3bc333" original_class_hash = "0x4bbfcdc8d95ecfa332201e21f615e98b4d12e08f77d176761e32bb34e3bc333" base_class_hash = "0x0" -abi = "manifests/dev/abis/base/contracts/compiler_cairo_cairo_26_cairo_v260.json" +abi = "manifests/dev/abis/base/contracts/ccf-cairo_v260-465ec7fe.json" reads = [] writes = [] computed = [] init_calldata = [] -name = "compiler_cairo::cairo_26::cairo_v260" +tag = "ccf-cairo_v260" +manifest_name = "ccf-cairo_v260-465ec7fe" diff --git a/examples/spawn-and-move/manifests/release/base/dojo_base_base.toml b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo-base.toml similarity index 66% rename from examples/spawn-and-move/manifests/release/base/dojo_base_base.toml rename to crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo-base.toml index 6c4b5de67e..4e2fc2e0bb 100644 --- a/examples/spawn-and-move/manifests/release/base/dojo_base_base.toml +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo-base.toml @@ -1,4 +1,6 @@ kind = "Class" class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" original_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -name = "dojo::base::base" +abi = "manifests/dev/abis/base/dojo-base.json" +tag = "dojo-base" +manifest_name = "dojo-base" diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo-world.toml b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo-world.toml new file mode 100644 index 0000000000..94821ba3f7 --- /dev/null +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo-world.toml @@ -0,0 +1,6 @@ +kind = "Class" +class_hash = "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2" +original_class_hash = "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2" +abi = "manifests/dev/abis/base/dojo-world.json" +tag = "dojo-world" +manifest_name = "dojo-world" 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 deleted file mode 100644 index 2555ec3eda..0000000000 --- a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_world_world.toml +++ /dev/null @@ -1,5 +0,0 @@ -kind = "Class" -class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -original_class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -abi = "manifests/dev/abis/base/dojo_world_world.json" -name = "dojo::world::world" diff --git a/crates/dojo-lang/src/model.rs b/crates/dojo-lang/src/model.rs index dab07e5efe..29da3a0603 100644 --- a/crates/dojo-lang/src/model.rs +++ b/crates/dojo-lang/src/model.rs @@ -9,22 +9,25 @@ use cairo_lang_syntax::node::helpers::QueryAttrs; use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode}; use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; use convert_case::{Case, Casing}; +use dojo_world::contracts::naming; use dojo_world::manifest::Member; -use starknet::core::utils::get_selector_from_name; use crate::plugin::{DojoAuxData, Model, DOJO_MODEL_ATTR}; +use crate::utils::is_name_valid; const DEFAULT_MODEL_VERSION: u8 = 1; const MODEL_VERSION_NAME: &str = "version"; +const MODEL_NAMESPACE: &str = "namespace"; struct ModelParameters { version: u8, + namespace: Option, } impl Default for ModelParameters { fn default() -> ModelParameters { - ModelParameters { version: DEFAULT_MODEL_VERSION } + ModelParameters { version: DEFAULT_MODEL_VERSION, namespace: Option::None } } } @@ -73,6 +76,29 @@ fn get_model_version( } } +/// Get the model namespace from the `Expr` parameter. +fn get_model_namespace( + db: &dyn SyntaxGroup, + arg_value: Expr, + diagnostics: &mut Vec, +) -> Option { + match arg_value { + Expr::ShortString(ss) => Some(ss.string_value(db).unwrap()), + Expr::String(s) => Some(s.string_value(db).unwrap()), + _ => { + diagnostics.push(PluginDiagnostic { + message: format!( + "The argument '{}' of dojo::model must be a string", + MODEL_NAMESPACE + ), + stable_ptr: arg_value.stable_ptr().untyped(), + severity: Severity::Error, + }); + Option::None + } + } +} + /// Get parameters of the dojo::model attribute. /// /// Note: dojo::model attribute has already been checked so there is one and only one attribute. @@ -114,6 +140,9 @@ fn get_model_parameters( MODEL_VERSION_NAME => { parameters.version = get_model_version(db, arg_value, diagnostics); } + MODEL_NAMESPACE => { + parameters.namespace = get_model_namespace(db, arg_value, diagnostics); + } _ => { diagnostics.push(PluginDiagnostic { message: format!( @@ -163,17 +192,47 @@ pub fn handle_model_struct( db: &dyn SyntaxGroup, aux_data: &mut DojoAuxData, struct_ast: ItemStruct, + package_id: String, ) -> (RewriteNode, Vec) { let mut diagnostics = vec![]; let parameters = get_model_parameters(db, struct_ast.clone(), &mut diagnostics); let model_name = struct_ast.name(db).as_syntax_node().get_text(db).trim().to_string(); + let model_namespace = match parameters.namespace { + Option::Some(x) => x, + Option::None => package_id, + }; + + for (id, value) in [("name", &model_name), ("namespace", &model_namespace)] { + if !is_name_valid(value) { + return ( + RewriteNode::empty(), + vec![PluginDiagnostic { + stable_ptr: struct_ast.name(db).stable_ptr().0, + message: format!( + "The model {id} '{value}' can only contain characters (a-z/A-Z), numbers \ + (0-9) and underscore (_)" + ) + .to_string(), + severity: Severity::Error, + }], + ); + } + } + + let model_tag = naming::get_tag(&model_namespace, &model_name); + let model_name_hash = naming::compute_bytearray_hash(&model_name); + let model_namespace_hash = naming::compute_bytearray_hash(&model_namespace); + let (model_version, model_selector) = match parameters.version { 0 => (RewriteNode::Text("0".to_string()), RewriteNode::Text(format!("\"{model_name}\""))), _ => ( RewriteNode::Text(DEFAULT_MODEL_VERSION.to_string()), - RewriteNode::Text(get_selector_from_name(model_name.as_str()).unwrap().to_string()), + RewriteNode::Text( + naming::compute_model_selector_from_hash(model_namespace_hash, model_name_hash) + .to_string(), + ), ), }; @@ -242,7 +301,11 @@ pub fn handle_model_struct( members.iter().filter_map(|m| serialize_member(m, false)).collect::<_>(); let name = struct_ast.name(db).text(db); - aux_data.models.push(Model { name: name.to_string(), members: members.to_vec() }); + aux_data.models.push(Model { + name: name.to_string(), + namespace: model_namespace.clone(), + members: members.to_vec(), + }); ( RewriteNode::interpolate_patched( @@ -250,8 +313,12 @@ pub fn handle_model_struct( impl $type_name$Model of dojo::model::Model<$type_name$> { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: \ dojo::database::introspect::Layout) -> $type_name$ { - let values = dojo::world::IWorldDispatcherTrait::entity(world, $model_selector$, keys, \ - layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -292,6 +359,21 @@ impl $type_name$Model of dojo::model::Model<$type_name$> { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + \"$model_namespace$\" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + $model_namespace_hash$ + } + + #[inline(always)] + fn tag() -> ByteArray { + \"$model_tag$\" + } + #[inline(always)] fn keys(self: @$type_name$) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -360,7 +442,19 @@ mod $contract_name$ { fn version(self: @ContractState) -> u8 { dojo::model::Model::<$type_name$>::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::<$type_name$>::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::<$type_name$>::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::<$type_name$>::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::<$type_name$>::size() } @@ -393,6 +487,12 @@ mod $contract_name$ { ("serialized_values".to_string(), RewriteNode::new_modified(serialized_values)), ("model_version".to_string(), model_version), ("model_selector".to_string(), model_selector), + ("model_namespace".to_string(), RewriteNode::Text(model_namespace.clone())), + ( + "model_namespace_hash".to_string(), + RewriteNode::Text(model_namespace_hash.to_string()), + ), + ("model_tag".to_string(), RewriteNode::Text(model_tag.clone())), ]), ), diagnostics, diff --git a/crates/dojo-lang/src/plugin.rs b/crates/dojo-lang/src/plugin.rs index 2da354fded..37b91398a8 100644 --- a/crates/dojo-lang/src/plugin.rs +++ b/crates/dojo-lang/src/plugin.rs @@ -16,6 +16,7 @@ use cairo_lang_syntax::node::helpers::QueryAttrs; use cairo_lang_syntax::node::ids::SyntaxStablePtrId; use cairo_lang_syntax::node::{ast, Terminal, TypedSyntaxNode}; use dojo_types::system::Dependency; +use dojo_world::contracts::naming; use dojo_world::manifest::Member; use scarb::compiler::plugin::builtin::BuiltinStarkNetPlugin; use scarb::compiler::plugin::{CairoPlugin, CairoPluginInstance}; @@ -34,6 +35,7 @@ use crate::interface::DojoInterface; use crate::introspect::{handle_introspect_enum, handle_introspect_struct}; use crate::model::handle_model_struct; use crate::print::{handle_print_enum, handle_print_struct}; +use crate::utils::get_package_id; pub const DOJO_CONTRACT_ATTR: &str = "dojo::contract"; pub const DOJO_INTERFACE_ATTR: &str = "dojo::interface"; @@ -46,12 +48,14 @@ pub const DOJO_PACKED_ATTR: &str = "IntrospectPacked"; #[derive(Clone, Debug, PartialEq)] pub struct Model { pub name: String, + pub namespace: String, pub members: Vec, } #[derive(Debug, PartialEq, Eq)] pub struct SystemAuxData { pub name: SmolStr, + pub namespace: String, pub dependencies: Vec, } @@ -81,7 +85,7 @@ pub struct ComputedValuesAuxData { // Name of entrypoint to get computed value pub entrypoint: SmolStr, // Model to bind to - pub model: Option, + pub tag: Option, } impl GeneratedFileAuxData for ComputedValuesAuxData { @@ -103,9 +107,14 @@ pub const PACKAGE_NAME: &str = "dojo_plugin"; pub struct BuiltinDojoPlugin; impl BuiltinDojoPlugin { - fn handle_mod(&self, db: &dyn SyntaxGroup, module_ast: ast::ItemModule) -> PluginResult { + fn handle_mod( + &self, + db: &dyn SyntaxGroup, + module_ast: ast::ItemModule, + package_id: String, + ) -> PluginResult { if module_ast.has_attr(db, DOJO_CONTRACT_ATTR) { - return DojoContract::from_module(db, module_ast); + return DojoContract::from_module(db, &module_ast, package_id); } PluginResult::default() @@ -131,7 +140,12 @@ impl BuiltinDojoPlugin { } } - fn handle_fn(&self, db: &dyn SyntaxGroup, fn_ast: ast::FunctionWithBody) -> PluginResult { + fn handle_fn( + &self, + db: &dyn SyntaxGroup, + fn_ast: ast::FunctionWithBody, + package_id: String, + ) -> PluginResult { let attrs = fn_ast.attributes(db).query_attr(db, "computed"); if attrs.is_empty() { return PluginResult::default(); @@ -154,10 +168,11 @@ impl BuiltinDojoPlugin { let fn_name = fn_decl.name(db).text(db); let params = fn_decl.signature(db).parameters(db); let param_els = params.elements(db); - let mut model = None; + let mut tag = None; if args.len() == 1 { let model_name = args[0].text(db); - model = Some(model_name.clone()); + tag = Some(naming::get_tag(&model_name, &package_id)); + let model_type_node = param_els[1].type_clause(db).ty(db); if let ast::Expr::Path(model_type_path) = model_type_node { let model_type = model_type_path @@ -167,7 +182,7 @@ impl BuiltinDojoPlugin { .unwrap() .as_syntax_node() .get_text(db); - if model_type != model_name { + if model_type != model_name.clone() { return self.result_with_diagnostic( model_type_path.stable_ptr().0, "Computed functions second parameter should be the model.".into(), @@ -195,7 +210,7 @@ impl BuiltinDojoPlugin { name: fn_name.clone(), content: "".into(), aux_data: Some(DynGeneratedFileAuxData::new(ComputedValuesAuxData { - model, + tag, entrypoint: fn_name, })), code_mappings: vec![], @@ -323,8 +338,25 @@ impl MacroPlugin for BuiltinDojoPlugin { item_ast: ast::ModuleItem, _metadata: &MacroPluginMetadata<'_>, ) -> PluginResult { + let package_id = match get_package_id(db) { + Option::Some(x) => x, + Option::None => { + return PluginResult { + code: Option::None, + diagnostics: vec![PluginDiagnostic { + stable_ptr: item_ast.stable_ptr().0, + message: "Unable to find the package ID. Be sure to have a 'package.name' \ + field in your Scarb.toml file." + .into(), + severity: Severity::Error, + }], + remove_original_item: false, + }; + } + }; + match item_ast { - ast::ModuleItem::Module(module_ast) => self.handle_mod(db, module_ast), + ast::ModuleItem::Module(module_ast) => self.handle_mod(db, module_ast, package_id), ast::ModuleItem::Trait(trait_ast) => self.handle_trait(db, trait_ast), ast::ModuleItem::Enum(enum_ast) => { let aux_data = DojoAuxData::default(); @@ -464,7 +496,7 @@ impl MacroPlugin for BuiltinDojoPlugin { match model_attrs.len().cmp(&1) { Ordering::Equal => { let (model_rewrite_nodes, model_diagnostics) = - handle_model_struct(db, &mut aux_data, struct_ast.clone()); + handle_model_struct(db, &mut aux_data, struct_ast.clone(), package_id); rewrite_nodes.push(model_rewrite_nodes); diagnostics.extend(model_diagnostics); } @@ -502,7 +534,7 @@ impl MacroPlugin for BuiltinDojoPlugin { remove_original_item: false, } } - ast::ModuleItem::FreeFunction(fn_ast) => self.handle_fn(db, fn_ast), + ast::ModuleItem::FreeFunction(fn_ast) => self.handle_fn(db, fn_ast, package_id), _ => PluginResult::default(), } } diff --git a/crates/dojo-lang/src/plugin_test_data/model b/crates/dojo-lang/src/plugin_test_data/model index 092bee2438..620747c774 100644 --- a/crates/dojo-lang/src/plugin_test_data/model +++ b/crates/dojo-lang/src/plugin_test_data/model @@ -61,6 +61,27 @@ struct Modelv0 { v: Vec3, } +#[dojo::model(namespace: 'MyNamespace')] +struct ModelWithBadNamespaceFormat { + #[key] + id: felt252, + v: Vec3, +} + +#[dojo::model(namespace: 'my_namespace')] +struct ModelWithShortStringNamespace { + #[key] + id: felt252, + v: Vec3, +} + +#[dojo::model(namespace: "my_namespace")] +struct ModelWithStringNamespace { + #[key] + id: felt252, + v: Vec3, +} + #[dojo::model] struct Position { #[key] @@ -195,6 +216,27 @@ struct Modelv0 { v: Vec3, } +#[dojo::model(namespace: 'MyNamespace')] +struct ModelWithBadNamespaceFormat { + #[key] + id: felt252, + v: Vec3, +} + +#[dojo::model(namespace: 'my_namespace')] +struct ModelWithShortStringNamespace { + #[key] + id: felt252, + v: Vec3, +} + +#[dojo::model(namespace: "my_namespace")] +struct ModelWithStringNamespace { + #[key] + id: felt252, + v: Vec3, +} + #[dojo::model] struct Position { #[key] @@ -355,7 +397,12 @@ dojo::database::introspect::Member { impl BadModelMultipleVersionsModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> BadModelMultipleVersions { - let values = dojo::world::IWorldDispatcherTrait::entity(world, "BadModelMultipleVersions", keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -395,6 +442,21 @@ impl BadModelMultipleVersionsModel of dojo::model::Model ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-BadModelMultipleVersions" + } + #[inline(always)] fn keys(self: @BadModelMultipleVersions) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -463,7 +525,19 @@ mod bad_model_multiple_versions { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -531,7 +605,12 @@ dojo::database::introspect::Member { impl BadModelBadVersionTypeModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> BadModelBadVersionType { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 537246025350462187868458942885902086202425585541495898150877285881426926523, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -563,7 +642,7 @@ impl BadModelBadVersionTypeModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 537246025350462187868458942885902086202425585541495898150877285881426926523 + 1257444965375069225632929782137899434201314657712945575000402601057577969467 } #[inline(always)] @@ -571,6 +650,21 @@ impl BadModelBadVersionTypeModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-BadModelBadVersionType" + } + #[inline(always)] fn keys(self: @BadModelBadVersionType) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -639,7 +733,19 @@ mod bad_model_bad_version_type { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -707,7 +813,12 @@ dojo::database::introspect::Member { impl BadModelNoVersionValueModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> BadModelNoVersionValue { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 510884764440804248641145470106421715520498065640762121627610646122289418442, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -739,7 +850,7 @@ impl BadModelNoVersionValueModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 510884764440804248641145470106421715520498065640762121627610646122289418442 + 3373097771824053354196231469173076752789704424742154674493295341772858104830 } #[inline(always)] @@ -747,6 +858,21 @@ impl BadModelNoVersionValueModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-BadModelNoVersionValue" + } + #[inline(always)] fn keys(self: @BadModelNoVersionValue) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -815,7 +941,19 @@ mod bad_model_no_version_value { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -883,7 +1021,12 @@ dojo::database::introspect::Member { impl BadModelUnexpectedArgWithValueModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> BadModelUnexpectedArgWithValue { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 609798817400410974332175542242790740792842310475593325422194044359199941027, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -915,7 +1058,7 @@ impl BadModelUnexpectedArgWithValueModel of dojo::model::Model felt252 { - 609798817400410974332175542242790740792842310475593325422194044359199941027 + 3590405629214068034212593672218295377527112441964389853941077460616482660871 } #[inline(always)] @@ -923,6 +1066,21 @@ impl BadModelUnexpectedArgWithValueModel of dojo::model::Model ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-BadModelUnexpectedArgWithValue" + } + #[inline(always)] fn keys(self: @BadModelUnexpectedArgWithValue) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -991,7 +1149,19 @@ mod bad_model_unexpected_arg_with_value { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -1059,7 +1229,12 @@ dojo::database::introspect::Member { impl BadModelUnexpectedArgModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> BadModelUnexpectedArg { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 949097735839867165379372628344008784789919991740118948112868710933218930849, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -1091,7 +1266,7 @@ impl BadModelUnexpectedArgModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 949097735839867165379372628344008784789919991740118948112868710933218930849 + 1096230073778795894347893795076822536130165745015118585791241737691598280119 } #[inline(always)] @@ -1099,6 +1274,21 @@ impl BadModelUnexpectedArgModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-BadModelUnexpectedArg" + } + #[inline(always)] fn keys(self: @BadModelUnexpectedArg) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -1167,7 +1357,19 @@ mod bad_model_unexpected_arg { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -1235,7 +1437,12 @@ dojo::database::introspect::Member { impl BadModelNotSupportedVersionModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> BadModelNotSupportedVersion { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 1024306429832444281478160321067399481685861686361100808176928576017034183663, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -1252,12 +1459,636 @@ impl BadModelNotSupportedVersionModel of dojo::model::Model::unwrap(entity) + core::option::OptionTrait::::unwrap(entity) + } + + #[inline(always)] + fn name() -> ByteArray { + "BadModelNotSupportedVersion" + } + + #[inline(always)] + fn version() -> u8 { + 1 + } + + #[inline(always)] + fn selector() -> felt252 { + 2618259498257511987977711074037130039442899457131070784132500210658539158686 + } + + #[inline(always)] + fn instance_selector(self: @BadModelNotSupportedVersion) -> felt252 { + Self::selector() + } + + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-BadModelNotSupportedVersion" + } + + #[inline(always)] + fn keys(self: @BadModelNotSupportedVersion) -> Span { + let mut serialized = core::array::ArrayTrait::new(); + core::array::ArrayTrait::append(ref serialized, *self.id); + core::array::ArrayTrait::span(@serialized) + } + + #[inline(always)] + fn values(self: @BadModelNotSupportedVersion) -> Span { + let mut serialized = core::array::ArrayTrait::new(); + core::serde::Serde::serialize(self.v, ref serialized); + core::array::ArrayTrait::span(@serialized) + } + + #[inline(always)] + fn layout() -> dojo::database::introspect::Layout { + dojo::database::introspect::Introspect::::layout() + } + + #[inline(always)] + fn instance_layout(self: @BadModelNotSupportedVersion) -> dojo::database::introspect::Layout { + Self::layout() + } + + #[inline(always)] + fn packed_size() -> Option { + let layout = Self::layout(); + + match layout { + dojo::database::introspect::Layout::Fixed(layout) => { + let mut span_layout = layout; + Option::Some(dojo::packing::calculate_packed_size(ref span_layout)) + }, + dojo::database::introspect::Layout::Struct(_) => Option::None, + dojo::database::introspect::Layout::Array(_) => Option::None, + dojo::database::introspect::Layout::Tuple(_) => Option::None, + dojo::database::introspect::Layout::Enum(_) => Option::None, + dojo::database::introspect::Layout::ByteArray => Option::None, + } + } +} + +#[starknet::interface] +trait Ibad_model_not_supported_version { + fn ensure_abi(self: @T, model: BadModelNotSupportedVersion); +} + +#[starknet::contract] +mod bad_model_not_supported_version { + use super::BadModelNotSupportedVersion; + use super::Ibad_model_not_supported_version; + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + impl DojoModelImpl of dojo::model::IModel{ + fn selector(self: @ContractState) -> felt252 { + dojo::model::Model::::selector() + } + + fn name(self: @ContractState) -> ByteArray { + dojo::model::Model::::name() + } + + fn version(self: @ContractState) -> u8 { + dojo::model::Model::::version() + } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + + fn unpacked_size(self: @ContractState) -> Option { + dojo::database::introspect::Introspect::::size() + } + + fn packed_size(self: @ContractState) -> Option { + dojo::model::Model::::packed_size() + } + + fn layout(self: @ContractState) -> dojo::database::introspect::Layout { + dojo::model::Model::::layout() + } + + fn schema(self: @ContractState) -> dojo::database::introspect::Ty { + dojo::database::introspect::Introspect::::ty() + } + } + + #[abi(embed_v0)] + impl bad_model_not_supported_versionImpl of Ibad_model_not_supported_version{ + fn ensure_abi(self: @ContractState, model: BadModelNotSupportedVersion) { + } + } +} + +impl Modelv0Introspect<> of dojo::database::introspect::Introspect> { + #[inline(always)] + fn size() -> Option { + dojo::database::introspect::Introspect::::size() + } + + fn layout() -> dojo::database::introspect::Layout { + dojo::database::introspect::Layout::Struct( + array![ + dojo::database::introspect::FieldLayout { + selector: 578691550836206188651404750433984985630363913126316857592149308417275000080, + layout: dojo::database::introspect::Introspect::::layout() + } + ].span() + ) + } + + #[inline(always)] + fn ty() -> dojo::database::introspect::Ty { + dojo::database::introspect::Ty::Struct( + dojo::database::introspect::Struct { + name: 'Modelv0', + attrs: array![].span(), + children: array![ + dojo::database::introspect::Member { + name: 'id', + attrs: array!['key'].span(), + ty: dojo::database::introspect::Introspect::::ty() + }, +dojo::database::introspect::Member { + name: 'v', + attrs: array![].span(), + ty: dojo::database::introspect::Introspect::::ty() + } + + ].span() + } + ) + } +} + +impl Modelv0Model of dojo::model::Model { + fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> Modelv0 { + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); + + // TODO: Generate method to deserialize from keys / values directly to avoid + // serializing to intermediate array. + let mut serialized = core::array::ArrayTrait::new(); + core::array::serialize_array_helper(keys, ref serialized); + core::array::serialize_array_helper(values, ref serialized); + let mut serialized = core::array::ArrayTrait::span(@serialized); + + let entity = core::serde::Serde::::deserialize(ref serialized); + + if core::option::OptionTrait::::is_none(@entity) { + panic!( + "Model `Modelv0`: deserialization failed. Ensure the length of the keys tuple is matching the number of #[key] fields in the model struct." + ); + } + + core::option::OptionTrait::::unwrap(entity) + } + + #[inline(always)] + fn name() -> ByteArray { + "Modelv0" + } + + #[inline(always)] + fn version() -> u8 { + 0 + } + + #[inline(always)] + fn selector() -> felt252 { + "Modelv0" + } + + #[inline(always)] + fn instance_selector(self: @Modelv0) -> felt252 { + Self::selector() + } + + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-Modelv0" + } + + #[inline(always)] + fn keys(self: @Modelv0) -> Span { + let mut serialized = core::array::ArrayTrait::new(); + core::array::ArrayTrait::append(ref serialized, *self.id); + core::array::ArrayTrait::span(@serialized) + } + + #[inline(always)] + fn values(self: @Modelv0) -> Span { + let mut serialized = core::array::ArrayTrait::new(); + core::serde::Serde::serialize(self.v, ref serialized); + core::array::ArrayTrait::span(@serialized) + } + + #[inline(always)] + fn layout() -> dojo::database::introspect::Layout { + dojo::database::introspect::Introspect::::layout() + } + + #[inline(always)] + fn instance_layout(self: @Modelv0) -> dojo::database::introspect::Layout { + Self::layout() + } + + #[inline(always)] + fn packed_size() -> Option { + let layout = Self::layout(); + + match layout { + dojo::database::introspect::Layout::Fixed(layout) => { + let mut span_layout = layout; + Option::Some(dojo::packing::calculate_packed_size(ref span_layout)) + }, + dojo::database::introspect::Layout::Struct(_) => Option::None, + dojo::database::introspect::Layout::Array(_) => Option::None, + dojo::database::introspect::Layout::Tuple(_) => Option::None, + dojo::database::introspect::Layout::Enum(_) => Option::None, + dojo::database::introspect::Layout::ByteArray => Option::None, + } + } +} + +#[starknet::interface] +trait Imodelv_0 { + fn ensure_abi(self: @T, model: Modelv0); +} + +#[starknet::contract] +mod modelv_0 { + use super::Modelv0; + use super::Imodelv_0; + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + impl DojoModelImpl of dojo::model::IModel{ + fn selector(self: @ContractState) -> felt252 { + dojo::model::Model::::selector() + } + + fn name(self: @ContractState) -> ByteArray { + dojo::model::Model::::name() + } + + fn version(self: @ContractState) -> u8 { + dojo::model::Model::::version() + } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + + fn unpacked_size(self: @ContractState) -> Option { + dojo::database::introspect::Introspect::::size() + } + + fn packed_size(self: @ContractState) -> Option { + dojo::model::Model::::packed_size() + } + + fn layout(self: @ContractState) -> dojo::database::introspect::Layout { + dojo::model::Model::::layout() + } + + fn schema(self: @ContractState) -> dojo::database::introspect::Ty { + dojo::database::introspect::Introspect::::ty() + } + } + + #[abi(embed_v0)] + impl modelv_0Impl of Imodelv_0{ + fn ensure_abi(self: @ContractState, model: Modelv0) { + } + } +} + +impl ModelWithBadNamespaceFormatIntrospect<> of dojo::database::introspect::Introspect> { + #[inline(always)] + fn size() -> Option { + dojo::database::introspect::Introspect::::size() + } + + fn layout() -> dojo::database::introspect::Layout { + dojo::database::introspect::Layout::Struct( + array![ + dojo::database::introspect::FieldLayout { + selector: 578691550836206188651404750433984985630363913126316857592149308417275000080, + layout: dojo::database::introspect::Introspect::::layout() + } + ].span() + ) + } + + #[inline(always)] + fn ty() -> dojo::database::introspect::Ty { + dojo::database::introspect::Ty::Struct( + dojo::database::introspect::Struct { + name: 'ModelWithBadNamespaceFormat', + attrs: array![].span(), + children: array![ + dojo::database::introspect::Member { + name: 'id', + attrs: array!['key'].span(), + ty: dojo::database::introspect::Introspect::::ty() + }, +dojo::database::introspect::Member { + name: 'v', + attrs: array![].span(), + ty: dojo::database::introspect::Introspect::::ty() + } + + ].span() + } + ) + } +} + +impl ModelWithBadNamespaceFormatModel of dojo::model::Model { + fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> ModelWithBadNamespaceFormat { + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); + + // TODO: Generate method to deserialize from keys / values directly to avoid + // serializing to intermediate array. + let mut serialized = core::array::ArrayTrait::new(); + core::array::serialize_array_helper(keys, ref serialized); + core::array::serialize_array_helper(values, ref serialized); + let mut serialized = core::array::ArrayTrait::span(@serialized); + + let entity = core::serde::Serde::::deserialize(ref serialized); + + if core::option::OptionTrait::::is_none(@entity) { + panic!( + "Model `ModelWithBadNamespaceFormat`: deserialization failed. Ensure the length of the keys tuple is matching the number of #[key] fields in the model struct." + ); + } + + core::option::OptionTrait::::unwrap(entity) + } + + #[inline(always)] + fn name() -> ByteArray { + "ModelWithBadNamespaceFormat" + } + + #[inline(always)] + fn version() -> u8 { + 1 + } + + #[inline(always)] + fn selector() -> felt252 { + 3498075185010572568869958326439183660947774785526793544998939163671395407352 + } + + #[inline(always)] + fn instance_selector(self: @ModelWithBadNamespaceFormat) -> felt252 { + Self::selector() + } + + #[inline(always)] + fn namespace() -> ByteArray { + "MyNamespace" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 2518247443458975119180817677753479732439290689944304814934908400279022945373 + } + + #[inline(always)] + fn tag() -> ByteArray { + "MyNamespace-ModelWithBadNamespaceFormat" + } + + #[inline(always)] + fn keys(self: @ModelWithBadNamespaceFormat) -> Span { + let mut serialized = core::array::ArrayTrait::new(); + core::array::ArrayTrait::append(ref serialized, *self.id); + core::array::ArrayTrait::span(@serialized) + } + + #[inline(always)] + fn values(self: @ModelWithBadNamespaceFormat) -> Span { + let mut serialized = core::array::ArrayTrait::new(); + core::serde::Serde::serialize(self.v, ref serialized); + core::array::ArrayTrait::span(@serialized) + } + + #[inline(always)] + fn layout() -> dojo::database::introspect::Layout { + dojo::database::introspect::Introspect::::layout() + } + + #[inline(always)] + fn instance_layout(self: @ModelWithBadNamespaceFormat) -> dojo::database::introspect::Layout { + Self::layout() + } + + #[inline(always)] + fn packed_size() -> Option { + let layout = Self::layout(); + + match layout { + dojo::database::introspect::Layout::Fixed(layout) => { + let mut span_layout = layout; + Option::Some(dojo::packing::calculate_packed_size(ref span_layout)) + }, + dojo::database::introspect::Layout::Struct(_) => Option::None, + dojo::database::introspect::Layout::Array(_) => Option::None, + dojo::database::introspect::Layout::Tuple(_) => Option::None, + dojo::database::introspect::Layout::Enum(_) => Option::None, + dojo::database::introspect::Layout::ByteArray => Option::None, + } + } +} + +#[starknet::interface] +trait Imodel_with_bad_namespace_format { + fn ensure_abi(self: @T, model: ModelWithBadNamespaceFormat); +} + +#[starknet::contract] +mod model_with_bad_namespace_format { + use super::ModelWithBadNamespaceFormat; + use super::Imodel_with_bad_namespace_format; + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + impl DojoModelImpl of dojo::model::IModel{ + fn selector(self: @ContractState) -> felt252 { + dojo::model::Model::::selector() + } + + fn name(self: @ContractState) -> ByteArray { + dojo::model::Model::::name() + } + + fn version(self: @ContractState) -> u8 { + dojo::model::Model::::version() + } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + + fn unpacked_size(self: @ContractState) -> Option { + dojo::database::introspect::Introspect::::size() + } + + fn packed_size(self: @ContractState) -> Option { + dojo::model::Model::::packed_size() + } + + fn layout(self: @ContractState) -> dojo::database::introspect::Layout { + dojo::model::Model::::layout() + } + + fn schema(self: @ContractState) -> dojo::database::introspect::Ty { + dojo::database::introspect::Introspect::::ty() + } + } + + #[abi(embed_v0)] + impl model_with_bad_namespace_formatImpl of Imodel_with_bad_namespace_format{ + fn ensure_abi(self: @ContractState, model: ModelWithBadNamespaceFormat) { + } + } +} + +impl ModelWithShortStringNamespaceIntrospect<> of dojo::database::introspect::Introspect> { + #[inline(always)] + fn size() -> Option { + dojo::database::introspect::Introspect::::size() + } + + fn layout() -> dojo::database::introspect::Layout { + dojo::database::introspect::Layout::Struct( + array![ + dojo::database::introspect::FieldLayout { + selector: 578691550836206188651404750433984985630363913126316857592149308417275000080, + layout: dojo::database::introspect::Introspect::::layout() + } + ].span() + ) + } + + #[inline(always)] + fn ty() -> dojo::database::introspect::Ty { + dojo::database::introspect::Ty::Struct( + dojo::database::introspect::Struct { + name: 'ModelWithShortStringNamespace', + attrs: array![].span(), + children: array![ + dojo::database::introspect::Member { + name: 'id', + attrs: array!['key'].span(), + ty: dojo::database::introspect::Introspect::::ty() + }, +dojo::database::introspect::Member { + name: 'v', + attrs: array![].span(), + ty: dojo::database::introspect::Introspect::::ty() + } + + ].span() + } + ) + } +} + +impl ModelWithShortStringNamespaceModel of dojo::model::Model { + fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> ModelWithShortStringNamespace { + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); + + // TODO: Generate method to deserialize from keys / values directly to avoid + // serializing to intermediate array. + let mut serialized = core::array::ArrayTrait::new(); + core::array::serialize_array_helper(keys, ref serialized); + core::array::serialize_array_helper(values, ref serialized); + let mut serialized = core::array::ArrayTrait::span(@serialized); + + let entity = core::serde::Serde::::deserialize(ref serialized); + + if core::option::OptionTrait::::is_none(@entity) { + panic!( + "Model `ModelWithShortStringNamespace`: deserialization failed. Ensure the length of the keys tuple is matching the number of #[key] fields in the model struct." + ); + } + + core::option::OptionTrait::::unwrap(entity) } #[inline(always)] fn name() -> ByteArray { - "BadModelNotSupportedVersion" + "ModelWithShortStringNamespace" } #[inline(always)] @@ -1267,23 +2098,38 @@ impl BadModelNotSupportedVersionModel of dojo::model::Model felt252 { - 1024306429832444281478160321067399481685861686361100808176928576017034183663 + 643350075018191729855964658181798951445581630505240307235771395728709379388 } #[inline(always)] - fn instance_selector(self: @BadModelNotSupportedVersion) -> felt252 { + fn instance_selector(self: @ModelWithShortStringNamespace) -> felt252 { Self::selector() } #[inline(always)] - fn keys(self: @BadModelNotSupportedVersion) -> Span { + fn namespace() -> ByteArray { + "my_namespace" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 1685136890688416384941629523783652800960468745356230625531475538826800548713 + } + + #[inline(always)] + fn tag() -> ByteArray { + "my_namespace-ModelWithShortStringNamespace" + } + + #[inline(always)] + fn keys(self: @ModelWithShortStringNamespace) -> Span { let mut serialized = core::array::ArrayTrait::new(); core::array::ArrayTrait::append(ref serialized, *self.id); core::array::ArrayTrait::span(@serialized) } #[inline(always)] - fn values(self: @BadModelNotSupportedVersion) -> Span { + fn values(self: @ModelWithShortStringNamespace) -> Span { let mut serialized = core::array::ArrayTrait::new(); core::serde::Serde::serialize(self.v, ref serialized); core::array::ArrayTrait::span(@serialized) @@ -1291,11 +2137,11 @@ impl BadModelNotSupportedVersionModel of dojo::model::Model dojo::database::introspect::Layout { - dojo::database::introspect::Introspect::::layout() + dojo::database::introspect::Introspect::::layout() } #[inline(always)] - fn instance_layout(self: @BadModelNotSupportedVersion) -> dojo::database::introspect::Layout { + fn instance_layout(self: @ModelWithShortStringNamespace) -> dojo::database::introspect::Layout { Self::layout() } @@ -1318,14 +2164,14 @@ impl BadModelNotSupportedVersionModel of dojo::model::Model { - fn ensure_abi(self: @T, model: BadModelNotSupportedVersion); +trait Imodel_with_short_string_namespace { + fn ensure_abi(self: @T, model: ModelWithShortStringNamespace); } #[starknet::contract] -mod bad_model_not_supported_version { - use super::BadModelNotSupportedVersion; - use super::Ibad_model_not_supported_version; +mod model_with_short_string_namespace { + use super::ModelWithShortStringNamespace; + use super::Imodel_with_short_string_namespace; #[storage] struct Storage {} @@ -1333,42 +2179,54 @@ mod bad_model_not_supported_version { #[abi(embed_v0)] impl DojoModelImpl of dojo::model::IModel{ fn selector(self: @ContractState) -> felt252 { - dojo::model::Model::::selector() + dojo::model::Model::::selector() } fn name(self: @ContractState) -> ByteArray { - dojo::model::Model::::name() + dojo::model::Model::::name() } fn version(self: @ContractState) -> u8 { - dojo::model::Model::::version() + dojo::model::Model::::version() + } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { - dojo::database::introspect::Introspect::::size() + dojo::database::introspect::Introspect::::size() } fn packed_size(self: @ContractState) -> Option { - dojo::model::Model::::packed_size() + dojo::model::Model::::packed_size() } fn layout(self: @ContractState) -> dojo::database::introspect::Layout { - dojo::model::Model::::layout() + dojo::model::Model::::layout() } fn schema(self: @ContractState) -> dojo::database::introspect::Ty { - dojo::database::introspect::Introspect::::ty() + dojo::database::introspect::Introspect::::ty() } } #[abi(embed_v0)] - impl bad_model_not_supported_versionImpl of Ibad_model_not_supported_version{ - fn ensure_abi(self: @ContractState, model: BadModelNotSupportedVersion) { + impl model_with_short_string_namespaceImpl of Imodel_with_short_string_namespace{ + fn ensure_abi(self: @ContractState, model: ModelWithShortStringNamespace) { } } } -impl Modelv0Introspect<> of dojo::database::introspect::Introspect> { +impl ModelWithStringNamespaceIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> Option { dojo::database::introspect::Introspect::::size() @@ -1389,7 +2247,7 @@ impl Modelv0Introspect<> of dojo::database::introspect::Introspect> { fn ty() -> dojo::database::introspect::Ty { dojo::database::introspect::Ty::Struct( dojo::database::introspect::Struct { - name: 'Modelv0', + name: 'ModelWithStringNamespace', attrs: array![].span(), children: array![ dojo::database::introspect::Member { @@ -1409,9 +2267,14 @@ dojo::database::introspect::Member { } } -impl Modelv0Model of dojo::model::Model { - fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> Modelv0 { - let values = dojo::world::IWorldDispatcherTrait::entity(world, "Modelv0", keys, layout); +impl ModelWithStringNamespaceModel of dojo::model::Model { + fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> ModelWithStringNamespace { + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -1420,46 +2283,61 @@ impl Modelv0Model of dojo::model::Model { core::array::serialize_array_helper(values, ref serialized); let mut serialized = core::array::ArrayTrait::span(@serialized); - let entity = core::serde::Serde::::deserialize(ref serialized); + let entity = core::serde::Serde::::deserialize(ref serialized); - if core::option::OptionTrait::::is_none(@entity) { + if core::option::OptionTrait::::is_none(@entity) { panic!( - "Model `Modelv0`: deserialization failed. Ensure the length of the keys tuple is matching the number of #[key] fields in the model struct." + "Model `ModelWithStringNamespace`: deserialization failed. Ensure the length of the keys tuple is matching the number of #[key] fields in the model struct." ); } - core::option::OptionTrait::::unwrap(entity) + core::option::OptionTrait::::unwrap(entity) } #[inline(always)] fn name() -> ByteArray { - "Modelv0" + "ModelWithStringNamespace" } #[inline(always)] fn version() -> u8 { - 0 + 1 } #[inline(always)] fn selector() -> felt252 { - "Modelv0" + 2567055065785696374111447326195815858786390804996225951953791904194802101726 } #[inline(always)] - fn instance_selector(self: @Modelv0) -> felt252 { + fn instance_selector(self: @ModelWithStringNamespace) -> felt252 { Self::selector() } #[inline(always)] - fn keys(self: @Modelv0) -> Span { + fn namespace() -> ByteArray { + "my_namespace" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 1685136890688416384941629523783652800960468745356230625531475538826800548713 + } + + #[inline(always)] + fn tag() -> ByteArray { + "my_namespace-ModelWithStringNamespace" + } + + #[inline(always)] + fn keys(self: @ModelWithStringNamespace) -> Span { let mut serialized = core::array::ArrayTrait::new(); core::array::ArrayTrait::append(ref serialized, *self.id); core::array::ArrayTrait::span(@serialized) } #[inline(always)] - fn values(self: @Modelv0) -> Span { + fn values(self: @ModelWithStringNamespace) -> Span { let mut serialized = core::array::ArrayTrait::new(); core::serde::Serde::serialize(self.v, ref serialized); core::array::ArrayTrait::span(@serialized) @@ -1467,11 +2345,11 @@ impl Modelv0Model of dojo::model::Model { #[inline(always)] fn layout() -> dojo::database::introspect::Layout { - dojo::database::introspect::Introspect::::layout() + dojo::database::introspect::Introspect::::layout() } #[inline(always)] - fn instance_layout(self: @Modelv0) -> dojo::database::introspect::Layout { + fn instance_layout(self: @ModelWithStringNamespace) -> dojo::database::introspect::Layout { Self::layout() } @@ -1494,14 +2372,14 @@ impl Modelv0Model of dojo::model::Model { } #[starknet::interface] -trait Imodelv_0 { - fn ensure_abi(self: @T, model: Modelv0); +trait Imodel_with_string_namespace { + fn ensure_abi(self: @T, model: ModelWithStringNamespace); } #[starknet::contract] -mod modelv_0 { - use super::Modelv0; - use super::Imodelv_0; +mod model_with_string_namespace { + use super::ModelWithStringNamespace; + use super::Imodel_with_string_namespace; #[storage] struct Storage {} @@ -1509,37 +2387,49 @@ mod modelv_0 { #[abi(embed_v0)] impl DojoModelImpl of dojo::model::IModel{ fn selector(self: @ContractState) -> felt252 { - dojo::model::Model::::selector() + dojo::model::Model::::selector() } fn name(self: @ContractState) -> ByteArray { - dojo::model::Model::::name() + dojo::model::Model::::name() } fn version(self: @ContractState) -> u8 { - dojo::model::Model::::version() + dojo::model::Model::::version() + } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { - dojo::database::introspect::Introspect::::size() + dojo::database::introspect::Introspect::::size() } fn packed_size(self: @ContractState) -> Option { - dojo::model::Model::::packed_size() + dojo::model::Model::::packed_size() } fn layout(self: @ContractState) -> dojo::database::introspect::Layout { - dojo::model::Model::::layout() + dojo::model::Model::::layout() } fn schema(self: @ContractState) -> dojo::database::introspect::Ty { - dojo::database::introspect::Introspect::::ty() + dojo::database::introspect::Introspect::::ty() } } #[abi(embed_v0)] - impl modelv_0Impl of Imodelv_0{ - fn ensure_abi(self: @ContractState, model: Modelv0) { + impl model_with_string_namespaceImpl of Imodel_with_string_namespace{ + fn ensure_abi(self: @ContractState, model: ModelWithStringNamespace) { } } } @@ -1587,7 +2477,12 @@ dojo::database::introspect::Member { impl PositionModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> Position { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 71955415935911354973604243446792437686714331401564029050333759332065302780, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -1619,7 +2514,7 @@ impl PositionModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 71955415935911354973604243446792437686714331401564029050333759332065302780 + 2479520712854223096864276571913189358268637670027015185814050381675363770524 } #[inline(always)] @@ -1627,6 +2522,21 @@ impl PositionModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-Position" + } + #[inline(always)] fn keys(self: @Position) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -1695,7 +2605,19 @@ mod position { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -1763,7 +2685,12 @@ impl RolesIntrospect<> of dojo::database::introspect::Introspect> { impl RolesModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> Roles { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 419336544500304321788712222515554985736095774968511843121228046452992595089, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -1795,7 +2722,7 @@ impl RolesModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 419336544500304321788712222515554985736095774968511843121228046452992595089 + 855657559905271410348750655782338503830053919297069309307754778237415879343 } #[inline(always)] @@ -1803,6 +2730,21 @@ impl RolesModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-Roles" + } + #[inline(always)] fn keys(self: @Roles) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -1871,7 +2813,19 @@ mod roles { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -1931,7 +2885,12 @@ impl OnlyKeyModelIntrospect<> of dojo::database::introspect::Introspect { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> OnlyKeyModel { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 95348903287879133251404472882179060573633188436503276991594023731017124802, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -1963,7 +2922,7 @@ impl OnlyKeyModelModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 95348903287879133251404472882179060573633188436503276991594023731017124802 + 185833973124429845784881936399707635120594083624714634478397191250435112638 } #[inline(always)] @@ -1971,6 +2930,21 @@ impl OnlyKeyModelModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-OnlyKeyModel" + } + #[inline(always)] fn keys(self: @OnlyKeyModel) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -2039,7 +3013,19 @@ mod only_key_model { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -2099,7 +3085,12 @@ impl U256KeyModelIntrospect<> of dojo::database::introspect::Introspect { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> U256KeyModel { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 1012572617804599336461526165833773117831255507291742736404237332578427043161, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -2131,7 +3122,7 @@ impl U256KeyModelModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 1012572617804599336461526165833773117831255507291742736404237332578427043161 + 313949624866121544885414428820758126526635902180652023772135974434319323806 } #[inline(always)] @@ -2139,6 +3130,21 @@ impl U256KeyModelModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-U256KeyModel" + } + #[inline(always)] fn keys(self: @U256KeyModel) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -2207,7 +3213,19 @@ mod u_256_key_model { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -2280,7 +3298,12 @@ dojo::database::introspect::Member { impl PlayerModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> Player { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 711011379911436309259372467342761500657237775100998141763491044473508065524, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -2312,7 +3335,7 @@ impl PlayerModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 711011379911436309259372467342761500657237775100998141763491044473508065524 + 3236595220905041720254406300307550499454912078227837571413280031443759061272 } #[inline(always)] @@ -2320,6 +3343,21 @@ impl PlayerModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-Player" + } + #[inline(always)] fn keys(self: @Player) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -2388,7 +3426,19 @@ mod player { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -2470,7 +3520,12 @@ dojo::database::introspect::Member { impl ModelWithSimpleArrayModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> ModelWithSimpleArray { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 1666797686724122591687516812540504658474050953055875240069335038116740262482, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -2502,7 +3557,7 @@ impl ModelWithSimpleArrayModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 1666797686724122591687516812540504658474050953055875240069335038116740262482 + 3015573168128997707047545520976347917082964620628387533056114714397499472255 } #[inline(always)] @@ -2510,6 +3565,21 @@ impl ModelWithSimpleArrayModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-ModelWithSimpleArray" + } + #[inline(always)] fn keys(self: @ModelWithSimpleArray) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -2578,7 +3648,19 @@ mod model_with_simple_array { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -2656,7 +3738,12 @@ dojo::database::introspect::Member { impl ModelWithByteArrayModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> ModelWithByteArray { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 141311514815198970479576122039175676312992855455532566618126930677198428029, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -2688,7 +3775,7 @@ impl ModelWithByteArrayModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 141311514815198970479576122039175676312992855455532566618126930677198428029 + 1495813766814131974177657304275361143893541941399872042870855060582891570868 } #[inline(always)] @@ -2696,6 +3783,21 @@ impl ModelWithByteArrayModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-ModelWithByteArray" + } + #[inline(always)] fn keys(self: @ModelWithByteArray) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -2764,7 +3866,19 @@ mod model_with_byte_array { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -2846,7 +3960,12 @@ dojo::database::introspect::Member { impl ModelWithComplexArrayModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> ModelWithComplexArray { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 897000934674353152443993618748644314791405506479389733109858158050692281536, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -2878,7 +3997,7 @@ impl ModelWithComplexArrayModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 897000934674353152443993618748644314791405506479389733109858158050692281536 + 36330270687495804813979499325154873801411429162367231362587376627277795482 } #[inline(always)] @@ -2886,6 +4005,21 @@ impl ModelWithComplexArrayModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-ModelWithComplexArray" + } + #[inline(always)] fn keys(self: @ModelWithComplexArray) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -2954,7 +4088,19 @@ mod model_with_complex_array { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -3043,7 +4189,12 @@ dojo::database::introspect::Introspect::::ty() impl ModelWithTupleModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> ModelWithTuple { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 337716500732518130569836528184281758241439084973964029720107667501574962806, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -3075,7 +4226,7 @@ impl ModelWithTupleModel of dojo::model::Model { #[inline(always)] fn selector() -> felt252 { - 337716500732518130569836528184281758241439084973964029720107667501574962806 + 2968932965114787437968555813812827702683268243780347884633996291362924490633 } #[inline(always)] @@ -3083,6 +4234,21 @@ impl ModelWithTupleModel of dojo::model::Model { Self::selector() } + #[inline(always)] + fn namespace() -> ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-ModelWithTuple" + } + #[inline(always)] fn keys(self: @ModelWithTuple) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -3151,7 +4317,19 @@ mod model_with_tuple { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -3249,7 +4427,12 @@ dojo::database::introspect::Introspect::::ty() impl ModelWithTupleNoPrimitivesModel of dojo::model::Model { fn entity(world: dojo::world::IWorldDispatcher, keys: Span, layout: dojo::database::introspect::Layout) -> ModelWithTupleNoPrimitives { - let values = dojo::world::IWorldDispatcherTrait::entity(world, 1607855252168926989989190762728138559179794826576658643328244347189144524311, keys, layout); + let values = dojo::world::IWorldDispatcherTrait::entity( + world, + Self::selector(), + keys, + layout + ); // TODO: Generate method to deserialize from keys / values directly to avoid // serializing to intermediate array. @@ -3281,7 +4464,7 @@ impl ModelWithTupleNoPrimitivesModel of dojo::model::Model felt252 { - 1607855252168926989989190762728138559179794826576658643328244347189144524311 + 1303002480984182254910512996281734914702552284127358821084351389028444664680 } #[inline(always)] @@ -3289,6 +4472,21 @@ impl ModelWithTupleNoPrimitivesModel of dojo::model::Model ByteArray { + "dojo_plugin" + } + + #[inline(always)] + fn namespace_selector() -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + #[inline(always)] + fn tag() -> ByteArray { + "dojo_plugin-ModelWithTupleNoPrimitives" + } + #[inline(always)] fn keys(self: @ModelWithTupleNoPrimitives) -> Span { let mut serialized = core::array::ArrayTrait::new(); @@ -3357,7 +4555,19 @@ mod model_with_tuple_no_primitives { fn version(self: @ContractState) -> u8 { dojo::model::Model::::version() } + + fn namespace(self: @ContractState) -> ByteArray { + dojo::model::Model::::namespace() + } + fn namespace_selector(self: @ContractState) -> felt252 { + dojo::model::Model::::namespace_selector() + } + + fn tag(self: @ContractState) -> ByteArray { + dojo::model::Model::::tag() + } + fn unpacked_size(self: @ContractState) -> Option { dojo::database::introspect::Introspect::::size() } @@ -3419,22 +4629,22 @@ error: dojo::model version 2 not supported ^ error: Model must define at least one #[key] attribute - --> test_src/lib.cairo:66:8 + --> test_src/lib.cairo:87:8 struct Roles { ^***^ error: Model must define at least one member that is not a key - --> test_src/lib.cairo:71:8 + --> test_src/lib.cairo:92:8 struct OnlyKeyModel { ^**********^ error: Model must define at least one member that is not a key - --> test_src/lib.cairo:77:8 + --> test_src/lib.cairo:98:8 struct U256KeyModel { ^**********^ error: Key is only supported for core types that are 1 felt long once serialized. `u256` is a struct of 2 u128, hence not supported. - --> test_src/lib.cairo:77:8 + --> test_src/lib.cairo:98:8 struct U256KeyModel { ^**********^ @@ -3475,51 +4685,66 @@ error: Unsupported attribute. error: Unsupported attribute. --> test_src/lib.cairo:58:1 +#[dojo::model(namespace: 'MyNamespace')] +^**************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:65:1 +#[dojo::model(namespace: 'my_namespace')] +^***************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:72:1 +#[dojo::model(namespace: "my_namespace")] +^***************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:79:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:65:1 + --> test_src/lib.cairo:86:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:70:1 + --> test_src/lib.cairo:91:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:76:1 + --> test_src/lib.cairo:97:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:82:1 + --> test_src/lib.cairo:103:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:94:1 + --> test_src/lib.cairo:115:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:102:1 + --> test_src/lib.cairo:123:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:110:1 + --> test_src/lib.cairo:131:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:118:1 + --> test_src/lib.cairo:139:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:126:1 + --> test_src/lib.cairo:147:1 #[dojo::model] ^************^ @@ -3630,150 +4855,195 @@ error: Unsupported attribute. error: Unsupported attribute. --> test_src/lib.cairo:58:1 +#[dojo::model(namespace: 'MyNamespace')] +^**************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:58:1 +#[dojo::model(namespace: 'MyNamespace')] +^**************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:58:1 +#[dojo::model(namespace: 'MyNamespace')] +^**************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:65:1 +#[dojo::model(namespace: 'my_namespace')] +^***************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:65:1 +#[dojo::model(namespace: 'my_namespace')] +^***************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:65:1 +#[dojo::model(namespace: 'my_namespace')] +^***************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:72:1 +#[dojo::model(namespace: "my_namespace")] +^***************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:72:1 +#[dojo::model(namespace: "my_namespace")] +^***************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:72:1 +#[dojo::model(namespace: "my_namespace")] +^***************************************^ + +error: Unsupported attribute. + --> test_src/lib.cairo:79:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:58:1 + --> test_src/lib.cairo:79:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:58:1 + --> test_src/lib.cairo:79:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:65:1 + --> test_src/lib.cairo:86:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:65:1 + --> test_src/lib.cairo:86:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:65:1 + --> test_src/lib.cairo:86:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:70:1 + --> test_src/lib.cairo:91:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:70:1 + --> test_src/lib.cairo:91:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:70:1 + --> test_src/lib.cairo:91:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:76:1 + --> test_src/lib.cairo:97:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:76:1 + --> test_src/lib.cairo:97:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:76:1 + --> test_src/lib.cairo:97:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:82:1 + --> test_src/lib.cairo:103:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:82:1 + --> test_src/lib.cairo:103:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:82:1 + --> test_src/lib.cairo:103:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:94:1 + --> test_src/lib.cairo:115:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:94:1 + --> test_src/lib.cairo:115:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:94:1 + --> test_src/lib.cairo:115:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:102:1 + --> test_src/lib.cairo:123:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:102:1 + --> test_src/lib.cairo:123:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:102:1 + --> test_src/lib.cairo:123:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:110:1 + --> test_src/lib.cairo:131:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:110:1 + --> test_src/lib.cairo:131:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:110:1 + --> test_src/lib.cairo:131:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:118:1 + --> test_src/lib.cairo:139:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:118:1 + --> test_src/lib.cairo:139:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:118:1 + --> test_src/lib.cairo:139:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:126:1 + --> test_src/lib.cairo:147:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:126:1 + --> test_src/lib.cairo:147:1 #[dojo::model] ^************^ error: Unsupported attribute. - --> test_src/lib.cairo:126:1 + --> test_src/lib.cairo:147:1 #[dojo::model] ^************^ diff --git a/crates/dojo-lang/src/plugin_test_data/system b/crates/dojo-lang/src/plugin_test_data/system index 1cc4ec33cc..645bb25f34 100644 --- a/crates/dojo-lang/src/plugin_test_data/system +++ b/crates/dojo-lang/src/plugin_test_data/system @@ -4,7 +4,17 @@ test_expand_plugin //! > cairo_code -#[dojo::contract] +#[dojo::contract(namespace: "My@Namespace")] +mod bad_namespace_format { + use traits::Into; + use dojo::world::Context; + + fn execute(ctx: Context, name: felt252) { + return (); + } +} + +#[dojo::contract(namespace: 'my_namespace')] mod spawn { use traits::Into; use dojo::world::Context; @@ -14,7 +24,7 @@ mod spawn { } } -#[dojo::contract] +#[dojo::contract(namespace: "my_namespace")] mod proxy { fn execute(value: felt252) -> felt252 { value @@ -345,95 +355,95 @@ mod ctxnamed { } //! > expected_diagnostics +error: The contract namespace 'My@Namespace' can only contain characters (a-z/A-Z), numbers (0-9) and underscore (_) + --> test_src/lib.cairo:1:1 +#[dojo::contract(namespace: "My@Namespace")] +^******************************************^ + error: Unsupported attribute. - --> test_src/lib.cairo:42:1 + --> test_src/lib.cairo:52:1 #[starknet::component] ^********************^ error: Unsupported attribute. - --> test_src/lib.cairo:48:1 + --> test_src/lib.cairo:58:1 #[starknet::component] ^********************^ error: Anything other than functions is not supported in a dojo::interface - --> test_src/lib.cairo:80:5 + --> test_src/lib.cairo:90:5 const ONE: u8; ^************^ error: World parameter must be the first parameter. - --> test_src/lib.cairo:101:5 + --> test_src/lib.cairo:111:5 fn do_with_self_and_world(self: @ContractState, world: @IWorldDispatcher) -> felt252; ^***********************************************************************************^ error: World parameter must be the first parameter. - --> test_src/lib.cairo:102:5 + --> test_src/lib.cairo:112:5 fn do_with_ref_self_and_world(ref self: ContractState, ref world: IWorldDispatcher) -> felt252; ^*********************************************************************************************^ error: You cannot use `self` and `world` parameters together. - --> test_src/lib.cairo:103:5 + --> test_src/lib.cairo:113:5 fn do_with_self_and_world_inv(world: @IWorldDispatcher, self: @ContractState) -> felt252; ^***************************************************************************************^ error: You cannot use `self` and `world` parameters together. - --> test_src/lib.cairo:104:5 + --> test_src/lib.cairo:114:5 fn do_with_ref_self_and_world_inv( ^********************************^ error: World parameter must be the first parameter. - --> test_src/lib.cairo:111:5 + --> test_src/lib.cairo:121:5 fn do_with_world_not_first(vec: Vec2, ref world: IWorldDispatcher) -> felt252; ^****************************************************************************^ error: World parameter must be the first parameter. - --> test_src/lib.cairo:118:9 + --> test_src/lib.cairo:128:9 fn do_with_self_and_world(self: @ContractState, world: @IWorldDispatcher) -> felt252 { ^************************************************************************************^ error: World parameter must be the first parameter. - --> test_src/lib.cairo:122:9 + --> test_src/lib.cairo:132:9 fn do_with_ref_self_and_world( ^****************************^ error: You cannot use `self` and `world` parameters together. - --> test_src/lib.cairo:128:9 + --> test_src/lib.cairo:138:9 fn do_with_self_and_world_inv(world: @IWorldDispatcher, self: @ContractState) -> felt252 { ^****************************************************************************************^ error: You cannot use `self` and `world` parameters together. - --> test_src/lib.cairo:132:9 + --> test_src/lib.cairo:142:9 fn do_with_ref_self_and_world_inv( ^********************************^ error: World parameter must be the first parameter. - --> test_src/lib.cairo:148:9 + --> test_src/lib.cairo:158:9 fn do_with_world_not_first(vec: Vec2, ref world: IWorldDispatcher) -> felt252 { ^*****************************************************************************^ error: You cannot use `world` and `#[generate_trait]` together. Use `self` instead. - --> test_src/lib.cairo:155:9 + --> test_src/lib.cairo:165:9 fn bad_func_using_generate(world: @IWorldDispatcher) -> felt252 { ^***************************************************************^ error: World parameter must be a snapshot if `ref` is not used. - --> test_src/lib.cairo:214:5 + --> test_src/lib.cairo:224:5 fn dojo_init( ^***********^ -error: Unsupported attribute. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ - error: Unsupported attribute. --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:18:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unsupported attribute. --> test_src/lib.cairo:28:1 @@ -441,446 +451,455 @@ error: Unsupported attribute. ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:82:5 - #[my_attr] - ^********^ + --> test_src/lib.cairo:64:1 +#[dojo::contract] +^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:44:5 + --> test_src/lib.cairo:54:5 #[storage] ^********^ error: Unsupported attribute. - --> test_src/lib.cairo:50:5 + --> test_src/lib.cairo:60:5 #[storage] ^********^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:11:1 +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:11:1 +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:11:1 +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:11:1 +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:11:1 +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:11:1 +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:11:1 +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:1:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:11:1 +#[dojo::contract(namespace: 'my_namespace')] +^******************************************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:11:1 -#[dojo::contract] -^***************^ + --> test_src/lib.cairo:21:1 +#[dojo::contract(namespace: "my_namespace")] +^******************************************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:18:1 + --> test_src/lib.cairo:28:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:18:1 + --> test_src/lib.cairo:28:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:18:1 + --> test_src/lib.cairo:28:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:18:1 + --> test_src/lib.cairo:28:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:18:1 + --> test_src/lib.cairo:28:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:18:1 + --> test_src/lib.cairo:28:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:18:1 + --> test_src/lib.cairo:28:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:18:1 + --> test_src/lib.cairo:28:1 #[dojo::contract] ^***************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:28:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:28:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:28:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:28:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:28:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:28:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:28:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:28:1 + --> test_src/lib.cairo:38:1 #[dojo::contract] ^***************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:56:5 + --> test_src/lib.cairo:66:5 component!(path: testcomponent1, storage: testcomponent1_storage, event: testcomponent1_event); ^*********************************************************************************************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:57:5 + --> test_src/lib.cairo:67:5 component!(path: testcomponent2, storage: testcomponent2_storage, event: testcomponent2_event); ^*********************************************************************************************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:54:1 + --> test_src/lib.cairo:64:1 #[dojo::contract] ^***************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:116:5 + --> test_src/lib.cairo:126:5 #[abi(embed_v0)] ^**************^ error: Generated trait must have generic args matching the impl's generic params. - --> test_src/lib.cairo:154:29 - impl InternalImplBad of InternalUtils { - ^**************************^ + --> test_src/lib.cairo:163:5 + #[generate_trait] + ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:114:1 + --> test_src/lib.cairo:124:1 #[dojo::contract] ^***************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:168:5 + --> test_src/lib.cairo:178:5 #[abi(embed_v0)] ^**************^ error: Unsupported attribute. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:161:1 + --> test_src/lib.cairo:171:1 #[dojo::contract] ^***************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:212:1 + --> test_src/lib.cairo:222:1 #[dojo::contract] ^***************^ error: Unknown inline item macro: 'component'. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ error: Unsupported attribute. - --> test_src/lib.cairo:229:1 + --> test_src/lib.cairo:239:1 #[dojo::contract] ^***************^ //! > expanded_cairo_code +#[dojo::contract(namespace: "My@Namespace")] +mod bad_namespace_format { + use traits::Into; + use dojo::world::Context; + + fn execute(ctx: Context, name: felt252) { + return (); + } +} #[starknet::component] mod testcomponent1 { @@ -900,12 +919,27 @@ mod testcomponent2 { use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'spawn' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "spawn" + } + fn selector(self: @ContractState) -> felt252 { + 2776321589048333240377325502911505147617911439383944762363370901236132332849 + } + + fn namespace(self: @ContractState) -> ByteArray { + "my_namespace" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 1685136890688416384941629523783652800960468745356230625531475538826800548713 + } + + fn tag(self: @ContractState) -> ByteArray { + "my_namespace-spawn" } } @@ -960,12 +994,27 @@ impl EventDrop of core::traits::Drop::; use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'proxy' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "proxy" + } + fn selector(self: @ContractState) -> felt252 { + 379211399603323842291430789821178524592027629543381998047225121027704003915 + } + + fn namespace(self: @ContractState) -> ByteArray { + "my_namespace" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 1685136890688416384941629523783652800960468745356230625531475538826800548713 + } + + fn tag(self: @ContractState) -> ByteArray { + "my_namespace-proxy" } } @@ -1017,12 +1066,27 @@ impl EventDrop of core::traits::Drop::; use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'ctxnamed' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "ctxnamed" + } + fn selector(self: @ContractState) -> felt252 { + 1120614286815912604239420768209466007446460277061516527925636408561239543041 + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo_plugin" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + fn tag(self: @ContractState) -> ByteArray { + "dojo_plugin-ctxnamed" } } @@ -1077,12 +1141,27 @@ impl EventDrop of core::traits::Drop::; use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'withevent' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "withevent" + } + fn selector(self: @ContractState) -> felt252 { + 2196275886623691942883456540767114081898043897719876113518403014973431884540 + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo_plugin" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + fn tag(self: @ContractState) -> ByteArray { + "dojo_plugin-withevent" } } @@ -1138,12 +1217,27 @@ impl TestEventDrop of core::traits::Drop::; use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'withcomponent' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "withcomponent" + } + fn selector(self: @ContractState) -> felt252 { + 999850881662666048155666650426666132968026605763740651763663703103974774091 + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo_plugin" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + fn tag(self: @ContractState) -> ByteArray { + "dojo_plugin-withcomponent" } } @@ -1197,40 +1291,19 @@ impl EventDrop of core::traits::Drop::; #[starknet::interface] trait IFaultyTrait { - - #[my_attr] - fn do_with_attrs(self: @TContractState, p1: u8) -> u16; - + fn do_with_attrs(self: @TContractState, p1: u8) -> u16; } #[starknet::interface] trait INominalTrait { - fn do_no_param(self: @TContractState) -> felt252; - fn do_no_param_but_world(self: @TContractState) -> felt252; - fn do_no_param_but_world_ref(ref self: TContractState) -> felt252; - fn do_params_no_world(self: @TContractState, p1: felt252, p2: u8) -> felt252; - fn do_params_and_world(self: @TContractState, p2: u8) -> felt252; - fn do_params_and_world_ref(ref self: TContractState, p2: u8) -> felt252; - - fn do_with_self(self: @ContractState) -> felt252; - fn do_with_ref_self(ref self: ContractState) -> felt252; - + fn do_no_param(self: @TContractState) -> felt252;fn do_no_param_but_world(self: @TContractState) -> felt252;fn do_no_param_but_world_ref(ref self: TContractState) -> felt252;fn do_params_no_world(self: @TContractState, p1: felt252, p2: u8) -> felt252;fn do_params_and_world(self: @TContractState, p2: u8) -> felt252;fn do_params_and_world_ref(ref self: TContractState, p2: u8) -> felt252;fn do_with_self(self: @ContractState) -> felt252;fn do_with_ref_self(ref self: ContractState) -> felt252; } #[starknet::interface] trait IFaultyTrait { - fn do_with_self_and_world(self: @ContractState, world: @IWorldDispatcher) -> felt252; - fn do_with_ref_self_and_world(ref self: ContractState, ref world: IWorldDispatcher) -> felt252; - fn do_with_self_and_world_inv(self: @TContractState, self: @ContractState) -> felt252; - fn do_with_ref_self_and_world_inv( -ref self: TContractState, ref self: ContractState - ) -> felt252; - fn do_with_several_world_dispatchers( -self: @TContractState, vec: Vec2, ref another_world: IWorldDispatcher - ) -> felt252; - fn do_with_world_not_named_world(self: @TContractState, another_world: @IWorldDispatcher) -> felt252; - fn do_with_world_not_first(self: @TContractState, vec: Vec2, ref world: IWorldDispatcher) -> felt252; - + fn do_with_self_and_world(self: @ContractState, world: @IWorldDispatcher) -> felt252;fn do_with_ref_self_and_world(ref self: ContractState, ref world: IWorldDispatcher) -> felt252;fn do_with_self_and_world_inv(self: @TContractState, self: @ContractState) -> felt252;fn do_with_ref_self_and_world_inv(ref self: TContractState, ref self: ContractState +) -> felt252;fn do_with_several_world_dispatchers(self: @TContractState, vec: Vec2, ref another_world: IWorldDispatcher +) -> felt252;fn do_with_world_not_named_world(self: @TContractState, another_world: @IWorldDispatcher) -> felt252;fn do_with_world_not_first(self: @TContractState, vec: Vec2, ref world: IWorldDispatcher) -> felt252; } #[starknet::contract] @@ -1239,12 +1312,27 @@ self: @TContractState, vec: Vec2, ref another_world: IWorldDispatcher use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'MyFaultyContract' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "MyFaultyContract" + } + fn selector(self: @ContractState) -> felt252 { + 3439743807865759203015037013733809315384408083697880336790137144257764256358 + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo_plugin" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + fn tag(self: @ContractState) -> ByteArray { + "dojo_plugin-MyFaultyContract" } } @@ -1259,52 +1347,19 @@ self: @TContractState, vec: Vec2, ref another_world: IWorldDispatcher impl UpgradableImpl = dojo::components::upgradeable::upgradeable::UpgradableImpl; #[abi(embed_v0)] - impl TestFaultyImpl of IFaultyTrait { - fn do_with_self_and_world(self: @ContractState) -> felt252 { - 'land' - } - - fn do_with_ref_self_and_world( - ref self: ContractState ) -> felt252 { - 'land' - } - - fn do_with_self_and_world_inv(self: @ContractState, self: @ContractState) -> felt252 { -let world = self.world_dispatcher.read(); - 'land' - } - - fn do_with_ref_self_and_world_inv( -ref self: ContractState, ref self: ContractState - ) -> felt252 { -let world = self.world_dispatcher.read(); - 'land' - } - - fn do_with_several_world_dispatchers( -self: @ContractState, vec: Vec2, ref another_world: IWorldDispatcher - ) -> felt252 { -let world = self.world_dispatcher.read(); - 'land' - } - - fn do_with_world_not_named_world(self: @ContractState, another_world: @IWorldDispatcher) -> felt252 { - 'land' - } - - fn do_with_world_not_first(self: @ContractState, vec: Vec2) -> felt252 { - 'land' - } - } - + impl TestFaultyImpl of IFaultyTrait {fn do_with_self_and_world(self: @ContractState) -> felt252 { 'land' +}fn do_with_ref_self_and_world( ref self: ContractState) -> felt252 { 'land' +}fn do_with_self_and_world_inv(self: @ContractState, self: @ContractState) -> felt252 {let world = self.world_dispatcher.read(); 'land' +}fn do_with_ref_self_and_world_inv(ref self: ContractState, ref self: ContractState +) -> felt252 {let world = self.world_dispatcher.read(); 'land' +}fn do_with_several_world_dispatchers(self: @ContractState, vec: Vec2, ref another_world: IWorldDispatcher +) -> felt252 {let world = self.world_dispatcher.read(); 'land' +}fn do_with_world_not_named_world(self: @ContractState, another_world: @IWorldDispatcher) -> felt252 { 'land' +}fn do_with_world_not_first(self: @ContractState, vec: Vec2) -> felt252 { 'land' +}} #[generate_trait] - impl InternalImplBad of InternalUtils { - fn bad_func_using_generate(self: @ContractState) -> felt252 { -let world = self.world_dispatcher.read(); - 'land' - } - } - + impl InternalImplBad of InternalUtils {fn bad_func_using_generate(self: @ContractState) -> felt252 {let world = self.world_dispatcher.read(); 'land' +}} #[starknet::interface] trait IDojoInit { fn dojo_init(self: @ContractState); @@ -1329,9 +1384,8 @@ let world = self.world_dispatcher.read(); #[substorage(v0)] upgradeable: dojo::components::upgradeable::upgradeable::Storage, } - trait InternalUtils{ - fn bad_func_using_generate(self: @ContractState) -> felt252; - } + trait InternalUtils{fn bad_func_using_generate(self: @ContractState) -> felt252; +} impl EventDrop of core::traits::Drop::; } @@ -1342,12 +1396,27 @@ impl EventDrop of core::traits::Drop::; use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'MyNominalContract' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "MyNominalContract" + } + fn selector(self: @ContractState) -> felt252 { + 1172628497499445169993220559087829230676589931723677494032285307738203070578 + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo_plugin" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + fn tag(self: @ContractState) -> ByteArray { + "dojo_plugin-MyNominalContract" } } @@ -1367,44 +1436,15 @@ impl EventDrop of core::traits::Drop::; } #[abi(embed_v0)] - impl TestNominalImpl of INominalTrait { - fn do_no_param(self: @ContractState) -> felt252 { - 'land' - } - - fn do_no_param_but_world(self: @ContractState) -> felt252 { -let world = self.world_dispatcher.read(); - 'land' - } - - fn do_no_param_but_world_ref(ref self: ContractState) -> felt252 { -let world = self.world_dispatcher.read(); - 'land' - } - - fn do_params_no_world(self: @ContractState, p1: felt252, p2: u8) -> felt252 { - 'land' - } - - fn do_params_and_world(self: @ContractState, p2: u8) -> felt252 { -let world = self.world_dispatcher.read(); - 'land' - } - - fn do_params_and_world_ref(ref self: ContractState, p2: u8) -> felt252 { -let world = self.world_dispatcher.read(); - 'land' - } - - fn do_with_self(self: @ContractState) -> felt252 { - 'land' - } - - fn do_with_ref_self(ref self: ContractState) -> felt252 { - 'land' - } - } - + impl TestNominalImpl of INominalTrait {fn do_no_param(self: @ContractState) -> felt252 { 'land' +}fn do_no_param_but_world(self: @ContractState) -> felt252 {let world = self.world_dispatcher.read(); 'land' +}fn do_no_param_but_world_ref(ref self: ContractState) -> felt252 {let world = self.world_dispatcher.read(); 'land' +}fn do_params_no_world(self: @ContractState, p1: felt252, p2: u8) -> felt252 { 'land' +}fn do_params_and_world(self: @ContractState, p2: u8) -> felt252 {let world = self.world_dispatcher.read(); 'land' +}fn do_params_and_world_ref(ref self: ContractState, p2: u8) -> felt252 {let world = self.world_dispatcher.read(); 'land' +}fn do_with_self(self: @ContractState) -> felt252 { 'land' +}fn do_with_ref_self(ref self: ContractState) -> felt252 { 'land' +}} #[generate_trait] impl ImplInternalNoContractState of InternalNoContractState { fn func1(world: IWorldDispatcher) -> felt252 { @@ -1451,12 +1491,27 @@ impl EventDrop of core::traits::Drop::; use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'init_test' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "init_test" + } + fn selector(self: @ContractState) -> felt252 { + 1673722391095039242574232979635399085098867563460816803458086922970092209409 + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo_plugin" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + fn tag(self: @ContractState) -> ByteArray { + "dojo_plugin-init_test" } } @@ -1470,31 +1525,23 @@ impl EventDrop of core::traits::Drop::; #[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 + #[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!( + } + + #[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 { @@ -1517,12 +1564,27 @@ impl EventDrop of core::traits::Drop::; use dojo::world::IWorldDispatcher; use dojo::world::IWorldDispatcherTrait; use dojo::world::IWorldProvider; - use dojo::world::IDojoResourceProvider; + use dojo::contract::IContract; #[abi(embed_v0)] - impl DojoResourceProviderImpl of IDojoResourceProvider { - fn dojo_resource(self: @ContractState) -> felt252 { - 'no_init_test' + impl ContractImpl of IContract { + fn contract_name(self: @ContractState) -> ByteArray { + "no_init_test" + } + fn selector(self: @ContractState) -> felt252 { + 2188407785440294979090510634648735461326850740369373015349552249835679156474 + } + + fn namespace(self: @ContractState) -> ByteArray { + "dojo_plugin" + } + + fn namespace_selector(self: @ContractState) -> felt252 { + 3437408695301308226171664635441698996501144546809569617702850025816833723775 + } + + fn tag(self: @ContractState) -> ByteArray { + "dojo_plugin-no_init_test" } } diff --git a/crates/dojo-lang/src/utils.rs b/crates/dojo-lang/src/utils.rs new file mode 100644 index 0000000000..d46f10a7b7 --- /dev/null +++ b/crates/dojo-lang/src/utils.rs @@ -0,0 +1,47 @@ +use std::fs; + +use cairo_lang_filesystem::ids::Directory; +use cairo_lang_syntax::node::db::SyntaxGroup; +use regex::Regex; +use toml::Table; + +/// Check if the provided name follows the format rules. +pub fn is_name_valid(name: &str) -> bool { + Regex::new(r"^[a-zA-Z0-9_]+$").unwrap().is_match(name) +} + +// parse the configuration file of the first crate to extract +// the main package Id (so the name field of the package section of the Scarb.toml file) +// TODO: Ask to Scarb team to expose this package Id information with the new macro system. +pub fn get_package_id(db: &dyn SyntaxGroup) -> Option { + let crates = db.crates(); + + if crates.is_empty() { + return Option::None; + } + + let configuration = match db.crate_config(crates[0]) { + Option::Some(cfg) => cfg, + Option::None => return Option::None, + }; + + if let Directory::Real(path) = configuration.root { + let config_path = path.parent().unwrap().join("Scarb.toml"); + let config_content = match fs::read_to_string(config_path) { + Ok(x) => x, + Err(_) => return Option::None, + }; + let config = match config_content.parse::() { + Ok(x) => x, + Err(_) => return Option::None, + }; + + if config.contains_key("package") + && config["package"].as_table().unwrap().contains_key("name") + { + return Some(config["package"]["name"].as_str().unwrap().to_string()); + }; + } + + Option::None +} diff --git a/crates/dojo-test-utils/build.rs b/crates/dojo-test-utils/build.rs index 17232238fe..44f01457b9 100644 --- a/crates/dojo-test-utils/build.rs +++ b/crates/dojo-test-utils/build.rs @@ -6,22 +6,27 @@ fn main() { use dojo_lang::compiler::DojoCompiler; use dojo_lang::plugin::CairoPluginRepository; use dojo_lang::scarb_internal::compile_workspace; - use scarb::compiler::CompilerRepository; + use scarb::compiler::{CompilerRepository, Profile}; use scarb::core::{Config, TargetKind}; use scarb::ops::{CompileOpts, FeaturesOpts, FeaturesSelector}; use scarb_ui::Verbosity; - let project_paths = ["../../examples/spawn-and-move", "../torii/types-test"]; + let project_paths = [ + ("../../examples/spawn-and-move", Profile::DEV), + ("../torii/types-test", Profile::DEV), + ("../../examples/spawn-and-move", Profile::RELEASE), + ]; - project_paths.iter().for_each(|path| compile(path)); + project_paths.iter().for_each(|(path, profile)| compile(path, profile)); println!("cargo:rerun-if-changed=../../examples"); println!("cargo:rerun-if-changed=../torii/types-test"); println!("cargo:rerun-if-changed=../dojo-lang/src"); println!("cargo:rerun-if-changed=../../bin/sozo/src"); - fn compile(path: &str) { - let target_path = Utf8PathBuf::from_path_buf(format!("{}/target", path).into()).unwrap(); + fn compile(path: &str, profile: &Profile) { + let target_path = + Utf8PathBuf::from_path_buf(format!("{}/target/{}", path, profile).into()).unwrap(); if target_path.exists() { return; } @@ -41,6 +46,7 @@ fn main() { .ui_verbosity(Verbosity::Verbose) .log_filter_directive(env::var_os("SCARB_LOG")) .compilers(compilers) + .profile(profile.to_owned()) .cairo_plugins(cairo_plugins.into()) .build() .unwrap(); diff --git a/crates/dojo-test-utils/src/compiler.rs b/crates/dojo-test-utils/src/compiler.rs index a48ef13435..e355673982 100644 --- a/crates/dojo-test-utils/src/compiler.rs +++ b/crates/dojo-test-utils/src/compiler.rs @@ -8,7 +8,7 @@ use camino::{Utf8Path, Utf8PathBuf}; use dojo_lang::compiler::DojoCompiler; use dojo_lang::plugin::CairoPluginRepository; use dojo_lang::scarb_internal::{compile_workspace, CompileInfo}; -use scarb::compiler::{CompilationUnit, CompilerRepository}; +use scarb::compiler::{CompilationUnit, CompilerRepository, Profile}; use scarb::core::{Config, TargetKind}; use scarb::ops; use scarb::ops::{CompileOpts, FeaturesOpts, FeaturesSelector}; @@ -20,7 +20,11 @@ use toml::{Table, Value}; /// # Returns /// /// A [`Config`] object loaded from the spawn-and-moves Scarb.toml file. -pub fn copy_tmp_config(source_project_dir: &Utf8PathBuf, dojo_core_path: &Utf8PathBuf) -> Config { +pub fn copy_tmp_config( + source_project_dir: &Utf8PathBuf, + dojo_core_path: &Utf8PathBuf, + profile: Profile, +) -> Config { let temp_project_dir = Utf8PathBuf::from( assert_fs::TempDir::new().unwrap().to_path_buf().to_string_lossy().to_string(), ); @@ -30,7 +34,8 @@ pub fn copy_tmp_config(source_project_dir: &Utf8PathBuf, dojo_core_path: &Utf8Pa // Copy all the files, including manifests. As we will not re-build, mostly only migrate. copy_project_temp(source_project_dir, &temp_project_dir, dojo_core_path, &[]).unwrap(); - build_test_config(&temp_project_path).unwrap_or_else(|c| panic!("Error loading config: {c:?}")) + build_test_config(&temp_project_path, profile) + .unwrap_or_else(|c| panic!("Error loading config: {c:?}")) } /// Copies a project to a new location, excluding the manifests @@ -48,6 +53,7 @@ pub fn copy_build_project_temp( source_project_path: &str, dojo_core_path: &str, do_build: bool, + profile: Profile, ) -> (Utf8PathBuf, Config, Option) { let source_project_dir = Utf8PathBuf::from(source_project_path).parent().unwrap().to_path_buf(); @@ -64,7 +70,7 @@ pub fn copy_build_project_temp( copy_project_temp(&source_project_dir, &temp_project_dir, &dojo_core_path, &ignore_dirs) .unwrap(); - let config = build_test_config(&temp_project_path).unwrap(); + let config = build_test_config(&temp_project_path, profile).unwrap(); let features_opts = FeaturesOpts { features: FeaturesSelector::AllFeatures, no_default_features: false }; @@ -165,7 +171,7 @@ pub fn copy_project_temp( /// # Arguments /// /// * `path` - The path to the Scarb.toml file to build the config for. -pub fn build_test_config(path: &str) -> anyhow::Result { +pub fn build_test_config(path: &str, profile: Profile) -> anyhow::Result { let mut compilers = CompilerRepository::empty(); compilers.add(Box::new(DojoCompiler)).unwrap(); @@ -180,12 +186,15 @@ pub fn build_test_config(path: &str) -> anyhow::Result { .ui_verbosity(Verbosity::Verbose) .log_filter_directive(env::var_os("SCARB_LOG")) .compilers(compilers) + .profile(profile) .cairo_plugins(cairo_plugins.into()) .build() } pub fn corelib() -> PathBuf { - let config = build_test_config("./src/manifest_test_data/spawn-and-move/Scarb.toml").unwrap(); + let config = + build_test_config("./src/manifest_test_data/spawn-and-move/Scarb.toml", Profile::DEV) + .unwrap(); let ws = ops::read_workspace(config.manifest_path(), &config).unwrap(); let resolve = ops::resolve_workspace(&ws).unwrap(); diff --git a/crates/dojo-test-utils/src/migration.rs b/crates/dojo-test-utils/src/migration.rs index c57f4f75b3..fff72ec87c 100644 --- a/crates/dojo-test-utils/src/migration.rs +++ b/crates/dojo-test-utils/src/migration.rs @@ -11,6 +11,7 @@ pub fn prepare_migration( manifest_dir: Utf8PathBuf, target_dir: Utf8PathBuf, skip_migration: Option>, + default_namespace: &str, ) -> Result { // In testing, profile name is always dev. let profile_name = "dev"; @@ -21,18 +22,18 @@ pub fn prepare_migration( .unwrap(); if let Some(skip_manifests) = skip_migration { - manifest.remove_items(skip_manifests); + manifest.remove_tags(skip_manifests); } - let overlay_manifest = OverlayManifest::load_from_path( - &manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(OVERLAYS_DIR), - ) - .unwrap(); + let overlay_dir = manifest_dir.join(OVERLAYS_DIR).join(profile_name); - manifest.merge(overlay_manifest); + if overlay_dir.exists() { + let overlay_manifest = OverlayManifest::load_from_path(&overlay_dir, &manifest).unwrap(); + manifest.merge(overlay_manifest); + } let mut world = WorldDiff::compute(manifest, None); - world.update_order().unwrap(); + world.update_order(default_namespace).unwrap(); let mut strat = prepare_for_migration(None, felt!("0x12345"), &target_dir, world).unwrap(); strat.resolve_variable(strat.world_address().unwrap()).unwrap(); @@ -45,6 +46,7 @@ pub fn prepare_migration_with_world_and_seed( target_dir: Utf8PathBuf, world_address: Option, seed: &str, + default_namespace: &str, ) -> Result { // In testing, profile name is always dev. let profile_name = "dev"; @@ -54,15 +56,14 @@ pub fn prepare_migration_with_world_and_seed( ) .unwrap(); - let overlay_manifest = OverlayManifest::load_from_path( - &manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(OVERLAYS_DIR), - ) - .unwrap(); - - manifest.merge(overlay_manifest); + let overlay_dir = manifest_dir.join(OVERLAYS_DIR).join(profile_name); + if overlay_dir.exists() { + let overlay_manifest = OverlayManifest::load_from_path(&overlay_dir, &manifest).unwrap(); + manifest.merge(overlay_manifest); + } let mut world = WorldDiff::compute(manifest, None); - world.update_order().unwrap(); + world.update_order(default_namespace).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/lib.rs b/crates/dojo-types/src/lib.rs index 31b65f3332..3ced1a5883 100644 --- a/crates/dojo-types/src/lib.rs +++ b/crates/dojo-types/src/lib.rs @@ -16,12 +16,12 @@ pub mod system; pub struct WorldMetadata { pub world_address: Felt, pub world_class_hash: Felt, - pub models: HashMap, + pub models: HashMap, } impl WorldMetadata { /// Retrieves the metadata of a model. - pub fn model(&self, name: impl AsRef) -> Option<&ModelMetadata> { - self.models.get(name.as_ref()) + pub fn model(&self, model: &Felt) -> Option<&ModelMetadata> { + self.models.get(model) } } diff --git a/crates/dojo-types/src/schema.rs b/crates/dojo-types/src/schema.rs index 162ce295c1..a9a15b5969 100644 --- a/crates/dojo-types/src/schema.rs +++ b/crates/dojo-types/src/schema.rs @@ -27,6 +27,7 @@ impl Member { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ModelMetadata { pub schema: Ty, + pub namespace: String, pub name: String, pub packed_size: u32, pub unpacked_size: u32, diff --git a/crates/dojo-world/Cargo.toml b/crates/dojo-world/Cargo.toml index 3c440ef24d..0dd118523f 100644 --- a/crates/dojo-world/Cargo.toml +++ b/crates/dojo-world/Cargo.toml @@ -35,6 +35,7 @@ scarb = { workspace = true, optional = true } tokio = { version = "1.32.0", features = [ "time" ], default-features = false, optional = true } toml.workspace = true url = { workspace = true, optional = true } +walkdir = "2.5.0" [dev-dependencies] assert_fs.workspace = true @@ -48,6 +49,6 @@ tokio.workspace = true [features] contracts = [ "dep:dojo-types", "dep:http", "dep:num-traits" ] -manifest = [ "contracts", "dep:dojo-types", "dep:url" ] +manifest = [ "contracts", "dep:dojo-types", "dep:url", "dep:scarb" ] metadata = [ "dep:ipfs-api-backend-hyper", "dep:scarb", "dep:url" ] -migration = [ "dep:tokio" ] +migration = [ "dep:tokio", "dep:scarb" ] diff --git a/crates/dojo-world/src/contracts/abi/world.rs b/crates/dojo-world/src/contracts/abi/world.rs index 4f9016025e..f681f1ab97 100644 --- a/crates/dojo-world/src/contracts/abi/world.rs +++ b/crates/dojo-world/src/contracts/abi/world.rs @@ -200,6 +200,18 @@ abigen!( "outputs": [], "state_mutability": "external" }, + { + "type": "function", + "name": "register_namespace", + "inputs": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray" + } + ], + "outputs": [], + "state_mutability": "external" + }, { "type": "function", "name": "deploy_contract", @@ -407,7 +419,7 @@ abigen!( "name": "is_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -427,7 +439,7 @@ abigen!( "name": "grant_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -443,7 +455,7 @@ abigen!( "name": "revoke_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -453,6 +465,66 @@ abigen!( ], "outputs": [], "state_mutability": "external" + }, + { + "type": "function", + "name": "can_write_resource", + "inputs": [ + { + "name": "resource_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_model", + "inputs": [ + { + "name": "model_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_namespace", + "inputs": [ + { + "name": "namespace_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" } ] }, @@ -698,6 +770,16 @@ abigen!( "name": "address", "type": "core::starknet::contract_address::ContractAddress", "kind": "data" + }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "name", + "type": "core::byte_array::ByteArray", + "kind": "data" } ] }, @@ -747,6 +829,23 @@ abigen!( } ] }, + { + "type": "event", + "name": "dojo::world::world::NamespaceRegistered", + "kind": "struct", + "members": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "hash", + "type": "core::felt252", + "kind": "data" + } + ] + }, { "type": "event", "name": "dojo::world::world::ModelRegistered", @@ -757,6 +856,11 @@ abigen!( "type": "core::byte_array::ByteArray", "kind": "data" }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash", @@ -824,7 +928,7 @@ abigen!( "kind": "struct", "members": [ { - "name": "model", + "name": "resource", "type": "core::felt252", "kind": "data" }, @@ -962,6 +1066,11 @@ abigen!( "type": "dojo::world::world::MetadataUpdate", "kind": "nested" }, + { + "name": "NamespaceRegistered", + "type": "dojo::world::world::NamespaceRegistered", + "kind": "nested" + }, { "name": "ModelRegistered", "type": "dojo::world::world::ModelRegistered", diff --git a/crates/dojo-world/src/contracts/mod.rs b/crates/dojo-world/src/contracts/mod.rs index 9174da838a..d70cf4fd80 100644 --- a/crates/dojo-world/src/contracts/mod.rs +++ b/crates/dojo-world/src/contracts/mod.rs @@ -1,6 +1,7 @@ pub mod abi; pub mod cairo_utils; pub mod model; +pub mod naming; pub mod world; pub use world::{WorldContract, WorldContractReader}; diff --git a/crates/dojo-world/src/contracts/model.rs b/crates/dojo-world/src/contracts/model.rs index 902bf4c558..64182c8dba 100644 --- a/crates/dojo-world/src/contracts/model.rs +++ b/crates/dojo-world/src/contracts/model.rs @@ -8,12 +8,13 @@ use dojo_types::primitive::{Primitive, PrimitiveError}; use dojo_types::schema::{Enum, EnumOption, Member, Struct, Ty}; use starknet::core::types::Felt; use starknet::core::utils::{ - cairo_short_string_to_felt, get_selector_from_name, parse_cairo_short_string, - CairoShortStringToFeltError, NonAsciiNameError, ParseCairoShortStringError, + cairo_short_string_to_felt, parse_cairo_short_string, CairoShortStringToFeltError, + NonAsciiNameError, ParseCairoShortStringError, }; use starknet::providers::{Provider, ProviderError}; use super::abi::world::Layout; +use super::naming; use crate::contracts::WorldContractReader; #[cfg(test)] @@ -46,15 +47,16 @@ pub enum ModelError { Packing(#[from] PackingError), #[error(transparent)] Cainome(#[from] CainomeError), + #[error("{0}")] + TagError(String), } // TODO: to update to match with new model interface #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] pub trait ModelReader { - // TODO: kept for compatibility but should be removed - // because it returns the model name hash and not the model name itself. - fn name(&self) -> String; + fn namespace(&self) -> &str; + fn name(&self) -> &str; fn selector(&self) -> Felt; fn class_hash(&self) -> Felt; fn contract_address(&self) -> Felt; @@ -65,8 +67,12 @@ pub trait ModelReader { } pub struct ModelRPCReader<'a, P: Provider + Sync + Send> { - /// The name of the model - name: Felt, + /// Namespace of the model + namespace: String, + /// Name of the model + name: String, + /// The selector of the model + selector: Felt, /// The class hash of the model class_hash: Felt, /// The contract address of the model @@ -82,13 +88,14 @@ where P: Provider + Sync + Send, { pub async fn new( + namespace: &str, name: &str, world: &'a WorldContractReader

, ) -> Result, ModelError> { - let name = get_selector_from_name(name)?; + let model_selector = naming::compute_model_selector_from_names(namespace, name); let (class_hash, contract_address) = - world.model(&name).block_id(world.block_id).call().await?; + world.model(&model_selector).block_id(world.block_id).call().await?; // World Cairo contract won't raise an error in case of unknown/unregistered // model so raise an error here in case of zero address. @@ -99,10 +106,12 @@ where let model_reader = ModelContractReader::new(contract_address.into(), world.provider()); Ok(Self { + namespace: namespace.into(), + name: name.into(), world_reader: world, class_hash: class_hash.into(), contract_address: contract_address.into(), - name, + selector: model_selector, model_reader, }) } @@ -137,12 +146,16 @@ impl<'a, P> ModelReader for ModelRPCReader<'a, P> where P: Provider + Sync + Send, { - fn name(&self) -> String { - self.name.to_string() + fn namespace(&self) -> &str { + &self.namespace + } + + fn name(&self) -> &str { + &self.name } fn selector(&self) -> Felt { - self.name + self.selector } fn class_hash(&self) -> Felt { diff --git a/crates/dojo-world/src/contracts/model_test.rs b/crates/dojo-world/src/contracts/model_test.rs index 2fd460b729..5fa132f905 100644 --- a/crates/dojo-world/src/contracts/model_test.rs +++ b/crates/dojo-world/src/contracts/model_test.rs @@ -3,6 +3,7 @@ use dojo_test_utils::compiler; use dojo_types::primitive::Primitive; use dojo_types::schema::{Enum, EnumOption, Member, Struct, Ty}; use katana_runner::KatanaRunner; +use scarb::compiler::Profile; use starknet::accounts::ConnectedAccount; use starknet::macros::felt; @@ -20,6 +21,7 @@ async fn test_model() { let config = compiler::copy_tmp_config( &Utf8PathBuf::from("../../examples/spawn-and-move"), &Utf8PathBuf::from("../dojo-core"), + Profile::DEV, ); let manifest_dir = config.manifest_path().parent().unwrap(); @@ -29,12 +31,19 @@ async fn test_model() { let dojo_metadata = dojo_metadata_from_workspace(&ws).expect("No current package with dojo metadata found."); - let world_address = - deploy_world(&runner, &manifest_dir.into(), &target_dir, dojo_metadata.skip_migration) - .await; + let default_namespace = ws.current_package().unwrap().id.name.to_string(); + + let world_address = deploy_world( + &runner, + &manifest_dir.into(), + &target_dir, + dojo_metadata.skip_migration, + &default_namespace, + ) + .await; let world = WorldContractReader::new(world_address, provider); - let position = world.model_reader("Position").await.unwrap(); + let position = world.model_reader("dojo_examples", "Position").await.unwrap(); let schema = position.schema().await.unwrap(); assert_eq!( @@ -72,10 +81,10 @@ async fn test_model() { assert_eq!( position.class_hash(), - felt!("0x027942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff") + felt!("0x059e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34") ); - let moves = world.model_reader("Moves").await.unwrap(); + let moves = world.model_reader("dojo_examples", "Moves").await.unwrap(); let schema = moves.schema().await.unwrap(); assert_eq!( diff --git a/crates/dojo-world/src/contracts/naming.rs b/crates/dojo-world/src/contracts/naming.rs new file mode 100644 index 0000000000..5f9141a323 --- /dev/null +++ b/crates/dojo-world/src/contracts/naming.rs @@ -0,0 +1,146 @@ +use anyhow::{anyhow, Result}; +use cainome::cairo_serde::{ByteArray, CairoSerde}; +use starknet::core::types::Felt; +use starknet_crypto::poseidon_hash_many; + +pub const CONTRACT_NAME_SEPARATOR: &str = "::"; +pub const TAG_SEPARATOR: char = '-'; +pub const SELECTOR_CHUNK_SIZE: usize = 8; + +pub fn capitalize(s: &str) -> String { + let mut c = s.chars(); + match c.next() { + None => String::new(), + Some(f) => f.to_uppercase().collect::() + c.as_str(), + } +} + +pub fn get_name_from_tag(tag: &str) -> String { + let parts: Vec<&str> = tag.split(TAG_SEPARATOR).collect(); + parts.last().unwrap().to_string() +} + +pub fn get_namespace_from_tag(tag: &str) -> String { + let parts: Vec<&str> = tag.split(TAG_SEPARATOR).collect(); + parts.first().unwrap().to_string() +} + +pub fn get_tag(namespace: &str, name: &str) -> String { + format!("{namespace}{TAG_SEPARATOR}{name}") +} + +/// Get the namespace and the name of a world element from its tag. +pub fn split_tag(tag: &str) -> Result<(String, String)> { + let parts: Vec<&str> = tag.split(TAG_SEPARATOR).collect(); + match parts.len() { + 2 => Ok((parts[0].to_string(), parts[1].to_string())), + _ => Err(anyhow!( + "Unexpected tag. Expected format: {TAG_SEPARATOR} or " + )), + } +} + +pub fn ensure_namespace(tag: &str, default_namespace: &str) -> String { + if tag.contains(TAG_SEPARATOR) { tag.to_string() } else { get_tag(default_namespace, tag) } +} + +pub fn get_filename_from_tag(tag: &str) -> String { + if [format!("dojo{TAG_SEPARATOR}world").as_str(), format!("dojo{TAG_SEPARATOR}base").as_str()] + .contains(&tag) + { + return tag.to_string(); + } + + let mut selector = format!("{:x}", compute_model_selector_from_tag(tag)); + selector.truncate(SELECTOR_CHUNK_SIZE); + + format!("{tag}{TAG_SEPARATOR}{selector}") +} + +pub fn compute_bytearray_hash(namespace: &str) -> Felt { + let ba = ByteArray::from_string(namespace).unwrap(); + poseidon_hash_many(&ByteArray::cairo_serialize(&ba)) +} + +pub fn compute_model_selector_from_tag(tag: &str) -> Felt { + let (namespace, name) = split_tag(tag).unwrap(); + compute_model_selector_from_names(&namespace, &name) +} + +pub fn compute_model_selector_from_names(namespace: &str, model_name: &str) -> Felt { + compute_model_selector_from_hash( + compute_bytearray_hash(namespace), + compute_bytearray_hash(model_name), + ) +} + +pub fn compute_model_selector_from_hash(namespace_hash: Felt, model_hash: Felt) -> Felt { + poseidon_hash_many(&[namespace_hash, model_hash]) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_split_tag_success() { + assert_eq!( + split_tag("namespace-name").unwrap(), + ("namespace".to_string(), "name".to_string()) + ); + } + + #[test] + fn test_split_tag_invalid_chars() { + assert!(split_tag("invalid:namespace").is_err()); + assert!(split_tag("invalid namespace").is_err()); + assert!(split_tag("inv-alid-namespace").is_err()); + } + + #[test] + fn test_ensure_namespace_success() { + assert_eq!(ensure_namespace("namespace-name", "default"), "namespace-name"); + assert_eq!(ensure_namespace("name", "default"), "default-name"); + } + + #[test] + fn test_get_filename_from_tag_success() { + assert_eq!(get_filename_from_tag("dojo-world"), "dojo-world"); + assert_eq!(get_filename_from_tag("dojo-base"), "dojo-base"); + + let tag = "namespace-model"; + let filename = get_filename_from_tag(tag); + assert!(filename.starts_with(tag)); + assert_eq!(filename.split(TAG_SEPARATOR).count(), 3); + } + + #[test] + fn test_compute_bytearray_hash_success() { + let hash = compute_bytearray_hash("test"); + assert_eq!( + hash, + Felt::from_hex("0x2ca96bf6e71766195fa290b97c50f073b218d4e8c6948c899e3b07d754d6760") + .unwrap() + ); + } + + #[test] + fn test_compute_model_selector_from_tag_success() { + let selector = compute_model_selector_from_tag("namespace-model"); + assert_eq!( + selector, + Felt::from_hex("0x6cfe11a346c1bb31de8f454d65880454952e22d9adc2374fe67734196e0cbcb") + .unwrap() + ); + } + + #[test] + fn test_compute_model_selector_from_names_success() { + let selector = compute_model_selector_from_names("namespace", "model"); + assert_eq!( + selector, + Felt::from_hex("0x6cfe11a346c1bb31de8f454d65880454952e22d9adc2374fe67734196e0cbcb") + .unwrap() + ); + } +} diff --git a/crates/dojo-world/src/contracts/world.rs b/crates/dojo-world/src/contracts/world.rs index 8a639c421c..4b017ef5ee 100644 --- a/crates/dojo-world/src/contracts/world.rs +++ b/crates/dojo-world/src/contracts/world.rs @@ -7,6 +7,7 @@ pub use abigen::world::{ use starknet::providers::Provider; use super::model::{ModelError, ModelRPCReader}; +use super::naming; #[cfg(test)] #[path = "world_test.rs"] @@ -22,7 +23,20 @@ impl

WorldContractReader

where P: Provider + Sync + Send, { - pub async fn model_reader(&self, name: &str) -> Result, ModelError> { - ModelRPCReader::new(name, self).await + pub async fn model_reader_with_tag( + &self, + tag: &str, + ) -> Result, ModelError> { + let (namespace, name) = + naming::split_tag(tag).map_err(|e| ModelError::TagError(e.to_string()))?; + ModelRPCReader::new(&namespace, &name, self).await + } + + pub async fn model_reader( + &self, + namespace: &str, + name: &str, + ) -> Result, ModelError> { + ModelRPCReader::new(namespace, name, self).await } } diff --git a/crates/dojo-world/src/contracts/world_test.rs b/crates/dojo-world/src/contracts/world_test.rs index 91ee527423..387ab779f6 100644 --- a/crates/dojo-world/src/contracts/world_test.rs +++ b/crates/dojo-world/src/contracts/world_test.rs @@ -3,6 +3,7 @@ use std::time::Duration; use camino::Utf8PathBuf; use dojo_test_utils::compiler; use katana_runner::KatanaRunner; +use scarb::compiler::Profile; use starknet::accounts::{Account, ConnectedAccount}; use starknet::core::types::{BlockId, BlockTag, Felt}; @@ -12,6 +13,7 @@ use crate::metadata::dojo_metadata_from_workspace; use crate::migration::strategy::prepare_for_migration; use crate::migration::world::WorldDiff; use crate::migration::{Declarable, Deployable, TxnConfig}; +use crate::utils::TransactionExt; #[tokio::test(flavor = "multi_thread")] async fn test_world_contract_reader() { @@ -19,7 +21,11 @@ async fn test_world_contract_reader() { let config = compiler::copy_tmp_config( &Utf8PathBuf::from("../../examples/spawn-and-move"), &Utf8PathBuf::from("../dojo-core"), + Profile::DEV, ); + let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); + + let default_namespace = ws.current_package().unwrap().id.name.to_string(); let manifest_dir = config.manifest_path().parent().unwrap(); let target_dir = manifest_dir.join("target").join("dev"); @@ -38,6 +44,7 @@ async fn test_world_contract_reader() { &manifest_dir.to_path_buf(), &target_dir.to_path_buf(), dojo_metadata.skip_migration, + &default_namespace, ) .await; @@ -49,6 +56,7 @@ pub async fn deploy_world( manifest_dir: &Utf8PathBuf, target_dir: &Utf8PathBuf, skip_migration: Option>, + default_namespace: &str, ) -> Felt { // Dev profile is used by default for testing: let profile_name = "dev"; @@ -59,18 +67,17 @@ pub async fn deploy_world( .unwrap(); if let Some(skip_manifests) = skip_migration { - manifest.remove_items(skip_manifests); + manifest.remove_tags(skip_manifests); } - let overlay_manifest = OverlayManifest::load_from_path( - &manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(OVERLAYS_DIR), - ) - .unwrap(); - - manifest.merge(overlay_manifest); + let overlay_dir = manifest_dir.join(OVERLAYS_DIR).join(profile_name); + if overlay_dir.exists() { + let overlay_manifest = OverlayManifest::load_from_path(&overlay_dir, &manifest).unwrap(); + manifest.merge(overlay_manifest); + } let mut world = WorldDiff::compute(manifest.clone(), None); - world.update_order().unwrap(); + world.update_order(default_namespace).unwrap(); let account = sequencer.account(0); @@ -79,10 +86,7 @@ pub async fn deploy_world( strategy.resolve_variable(strategy.world_address().unwrap()).unwrap(); let base_class_hash = - strategy.base.unwrap().declare(&account, &TxnConfig::default()).await.unwrap().class_hash; - - // wait for the tx to be mined - tokio::time::sleep(Duration::from_millis(250)).await; + strategy.base.unwrap().declare(&account, &TxnConfig::init_wait()).await.unwrap().class_hash; let world_address = strategy .world @@ -91,7 +95,7 @@ pub async fn deploy_world( manifest.clone().world.inner.class_hash, vec![base_class_hash], &account, - &TxnConfig::default(), + &TxnConfig::init_wait(), ) .await .unwrap() @@ -99,24 +103,28 @@ pub async fn deploy_world( let mut declare_output = vec![]; for model in strategy.models { - let res = model.declare(&account, &TxnConfig::default()).await.unwrap(); + let res = model.declare(&account, &TxnConfig::init_wait()).await.unwrap(); declare_output.push(res); } - // wait for the tx to be mined - tokio::time::sleep(Duration::from_millis(250)).await; - let world = WorldContract::new(world_address, &account); + world + .register_namespace(&cainome::cairo_serde::ByteArray::from_string("dojo_examples").unwrap()) + .send_with_cfg(&TxnConfig::init_wait()) + .await + .unwrap(); + + // Wondering why the `init_wait` is not enough and causes a nonce error. + // May be to a delay to create the block as we are in instant mining. + tokio::time::sleep(Duration::from_millis(2000)).await; + let calls = declare_output .iter() .map(|o| world.register_model_getcall(&o.class_hash.into())) .collect::>(); - let _ = account.execute_v1(calls).send().await.unwrap(); - - // wait for the tx to be mined - tokio::time::sleep(Duration::from_millis(250)).await; + let _ = account.execute_v1(calls).send_with_cfg(&TxnConfig::init_wait()).await.unwrap(); for contract in strategy.contracts { let declare_res = contract.declare(&account, &TxnConfig::default()).await.unwrap(); @@ -126,15 +134,12 @@ pub async fn deploy_world( declare_res.class_hash, base_class_hash, &account, - &TxnConfig::default(), + &TxnConfig::init_wait(), &contract.diff.init_calldata, ) .await .unwrap(); } - // wait for the tx to be mined - tokio::time::sleep(Duration::from_millis(250)).await; - world_address } diff --git a/crates/dojo-world/src/manifest/manifest_test.rs b/crates/dojo-world/src/manifest/manifest_test.rs index 67b541796f..a26e60af08 100644 --- a/crates/dojo-world/src/manifest/manifest_test.rs +++ b/crates/dojo-world/src/manifest/manifest_test.rs @@ -5,8 +5,8 @@ use camino::Utf8PathBuf; use dojo_test_utils::compiler; use dojo_test_utils::rpc::MockJsonRpcTransport; use katana_runner::KatanaRunner; +use scarb::compiler::Profile; use serde_json::json; -use smol_str::SmolStr; use starknet::accounts::ConnectedAccount; use starknet::core::types::contract::AbiEntry; use starknet::core::types::{EmittedEvent, Felt}; @@ -17,6 +17,7 @@ use super::{ parse_contracts_events, AbiFormat, BaseManifest, DojoContract, DojoModel, OverlayDojoContract, OverlayManifest, }; +use crate::contracts::naming::{get_filename_from_tag, get_tag}; use crate::contracts::world::test::deploy_world; use crate::manifest::{ parse_models_events, AbstractManifestError, DeploymentManifest, Manifest, OverlayClass, @@ -55,57 +56,39 @@ async fn manifest_from_remote_throw_error_on_not_deployed() { fn parse_registered_model_events() { let expected_models = vec![ Manifest::new( - DojoModel { class_hash: felt!("0x5555"), ..Default::default() }, - "Model1".into(), + DojoModel { + tag: get_tag("ns", "modelA"), + class_hash: felt!("0x5555"), + ..Default::default() + }, + get_filename_from_tag(&get_tag("ns", "modelA")), ), Manifest::new( - DojoModel { class_hash: felt!("0x6666"), ..Default::default() }, - "Model2".into(), + DojoModel { + tag: get_tag("ns", "modelB"), + class_hash: felt!("0x6666"), + ..Default::default() + }, + get_filename_from_tag(&get_tag("ns", "modelB")), ), ]; - let selector = selector!("ModelRegistered"); - let events = vec![ - EmittedEvent { - data: { - let mut data = - ByteArray::cairo_serialize(&ByteArray::from_string("Model1").unwrap()); - data.extend(vec![felt!("0x5555"), felt!("0xbeef"), felt!("0xa1"), felt!("0")]); - data - }, - keys: vec![selector], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, - EmittedEvent { - data: { - let mut data = - ByteArray::cairo_serialize(&ByteArray::from_string("Model1").unwrap()); - data.extend(vec![felt!("0xbeef"), felt!("0"), felt!("0xa1"), felt!("0xa1")]); - data - }, - keys: vec![selector], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, - EmittedEvent { - data: { - let mut data = - ByteArray::cairo_serialize(&ByteArray::from_string("Model2").unwrap()); - data.extend(vec![felt!("0x6666"), felt!("0"), felt!("0xa3"), felt!("0")]); - data - }, - keys: vec![selector], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, + build_model_registered_event( + vec![felt!("0x5555"), felt!("0xbeef"), felt!("0xa1"), felt!("0")], + "ns", + "modelA", + ), + build_model_registered_event( + vec![felt!("0xbeef"), felt!("0"), felt!("0xa1"), felt!("0xa1")], + "ns", + "modelA", + ), + build_model_registered_event( + vec![felt!("0x6666"), felt!("0"), felt!("0xa3"), felt!("0")], + "ns", + "modelB", + ), ]; let actual_models = parse_models_events(events); @@ -122,53 +105,35 @@ fn parse_deployed_contracts_events_without_upgrade() { DojoContract { class_hash: felt!("0x1"), address: Some(felt!("0x123")), + tag: get_tag("ns1", "c1"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns1", "c1")), ), Manifest::new( DojoContract { class_hash: felt!("0x2"), address: Some(felt!("0x456")), + tag: get_tag("ns2", "c2"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns2", "c2")), ), Manifest::new( DojoContract { class_hash: felt!("0x3"), address: Some(felt!("0x789")), + tag: get_tag("ns3", "c3"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns3", "c3")), ), ]; let events = vec![ - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x1"), felt!("0x123")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x2"), felt!("0x456")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x3"), felt!("0x789")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, + build_deploy_event(vec![felt!("0x0"), felt!("0x1"), felt!("0x123")], "ns1", "c1"), + build_deploy_event(vec![felt!("0x0"), felt!("0x2"), felt!("0x456")], "ns2", "c2"), + build_deploy_event(vec![felt!("0x0"), felt!("0x3"), felt!("0x789")], "ns3", "c3"), ]; let actual_contracts = parse_contracts_events(events, vec![]); @@ -182,53 +147,35 @@ fn parse_deployed_contracts_events_with_upgrade() { DojoContract { class_hash: felt!("0x69"), address: Some(felt!("0x123")), + tag: get_tag("ns1", "c1"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns1", "c1")), ), Manifest::new( DojoContract { class_hash: felt!("0x2"), address: Some(felt!("0x456")), + tag: get_tag("ns2", "c2"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns2", "c2")), ), Manifest::new( DojoContract { class_hash: felt!("0x88"), address: Some(felt!("0x789")), + tag: get_tag("ns3", "c3"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns3", "c3")), ), ]; let deployed_events = vec![ - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x1"), felt!("0x123")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x2"), felt!("0x456")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x3"), felt!("0x789")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, + build_deploy_event(vec![felt!("0x0"), felt!("0x1"), felt!("0x123")], "ns1", "c1"), + build_deploy_event(vec![felt!("0x0"), felt!("0x2"), felt!("0x456")], "ns2", "c2"), + build_deploy_event(vec![felt!("0x0"), felt!("0x3"), felt!("0x789")], "ns3", "c3"), ]; let upgrade_events = vec![ @@ -277,53 +224,35 @@ fn events_without_block_number_arent_parsed() { DojoContract { class_hash: felt!("0x66"), address: Some(felt!("0x123")), + tag: get_tag("ns1", "c1"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns1", "c1")), ), Manifest::new( DojoContract { class_hash: felt!("0x2"), address: Some(felt!("0x456")), + tag: get_tag("ns2", "c2"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns2", "c2")), ), Manifest::new( DojoContract { class_hash: felt!("0x3"), address: Some(felt!("0x789")), + tag: get_tag("ns3", "c3"), ..Default::default() }, - "".into(), + get_filename_from_tag(&get_tag("ns3", "c3")), ), ]; let deployed_events = vec![ - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x1"), felt!("0x123")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x2"), felt!("0x456")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, - EmittedEvent { - data: vec![felt!("0x0"), felt!("0x3"), felt!("0x789")], - keys: vec![], - block_hash: Default::default(), - from_address: Default::default(), - block_number: Default::default(), - transaction_hash: Default::default(), - }, + build_deploy_event(vec![felt!("0x0"), felt!("0x1"), felt!("0x123")], "ns1", "c1"), + build_deploy_event(vec![felt!("0x0"), felt!("0x2"), felt!("0x456")], "ns2", "c2"), + build_deploy_event(vec![felt!("0x0"), felt!("0x3"), felt!("0x789")], "ns3", "c3"), ]; // only the first upgrade event has a block number and is parsed @@ -381,7 +310,7 @@ fn fetch_remote_manifest() { // Build a completely new project in it's own directory. let (temp_project_dir, config, _) = - compiler::copy_build_project_temp(source_project, dojo_core_path, true); + compiler::copy_build_project_temp(source_project, dojo_core_path, true, Profile::DEV); let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); let dojo_metadata = @@ -389,12 +318,15 @@ fn fetch_remote_manifest() { let artifacts_path = temp_project_dir.join(format!("target/{profile_name}")); + let default_namespace = ws.current_package().unwrap().id.name.to_string(); + let world_address = config.tokio_handle().block_on(async { deploy_world( &runner, &temp_project_dir, &artifacts_path, dojo_metadata.skip_migration.clone(), + &default_namespace, ) .await }); @@ -405,15 +337,16 @@ fn fetch_remote_manifest() { .unwrap(); if let Some(skip_manifests) = dojo_metadata.skip_migration { - local_manifest.remove_items(skip_manifests); + local_manifest.remove_tags(skip_manifests); } - let overlay_manifest = OverlayManifest::load_from_path( - &temp_project_dir.join(MANIFESTS_DIR).join(profile_name).join(OVERLAYS_DIR), - ) - .unwrap(); + let overlay_dir = temp_project_dir.join(OVERLAYS_DIR).join(profile_name); + if overlay_dir.exists() { + let overlay_manifest = + OverlayManifest::load_from_path(&overlay_dir, &local_manifest).unwrap(); - local_manifest.merge(overlay_manifest); + local_manifest.merge(overlay_manifest); + } let remote_manifest = config.tokio_handle().block_on(async { DeploymentManifest::load_from_remote(provider, world_address).await.unwrap() @@ -497,46 +430,46 @@ fn test_abi_format_load_abi_string() -> Result<(), Box> { fn overlay_merge_for_contract_and_model_work_as_expected() { let other = OverlayManifest { contracts: vec![ - OverlayDojoContract { name: "othercontract1".into(), ..Default::default() }, - OverlayDojoContract { name: "othercontract2".into(), ..Default::default() }, - OverlayDojoContract { name: "existingcontract".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:othercontract1".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:othercontract2".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:existingcontract".into(), ..Default::default() }, ], models: vec![ - OverlayDojoModel { name: "othermodel1".into(), ..Default::default() }, - OverlayDojoModel { name: "othermodel2".into(), ..Default::default() }, - OverlayDojoModel { name: "existingmodel".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:othermodel1".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:othermodel2".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:existingmodel".into(), ..Default::default() }, ], ..Default::default() }; let mut current = OverlayManifest { contracts: vec![ - OverlayDojoContract { name: "currentcontract1".into(), ..Default::default() }, - OverlayDojoContract { name: "currentcontract2".into(), ..Default::default() }, - OverlayDojoContract { name: "existingcontract".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:currentcontract1".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:currentcontract2".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:existingcontract".into(), ..Default::default() }, ], models: vec![ - OverlayDojoModel { name: "currentmodel1".into(), ..Default::default() }, - OverlayDojoModel { name: "currentmodel2".into(), ..Default::default() }, - OverlayDojoModel { name: "existingmodel".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:currentmodel1".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:currentmodel2".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:existingmodel".into(), ..Default::default() }, ], ..Default::default() }; let expected = OverlayManifest { contracts: vec![ - OverlayDojoContract { name: "currentcontract1".into(), ..Default::default() }, - OverlayDojoContract { name: "currentcontract2".into(), ..Default::default() }, - OverlayDojoContract { name: "existingcontract".into(), ..Default::default() }, - OverlayDojoContract { name: "othercontract1".into(), ..Default::default() }, - OverlayDojoContract { name: "othercontract2".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:currentcontract1".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:currentcontract2".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:existingcontract".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:othercontract1".into(), ..Default::default() }, + OverlayDojoContract { tag: "ns:othercontract2".into(), ..Default::default() }, ], models: vec![ - OverlayDojoModel { name: "currentmodel1".into(), ..Default::default() }, - OverlayDojoModel { name: "currentmodel2".into(), ..Default::default() }, - OverlayDojoModel { name: "existingmodel".into(), ..Default::default() }, - OverlayDojoModel { name: "othermodel1".into(), ..Default::default() }, - OverlayDojoModel { name: "othermodel2".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:currentmodel1".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:currentmodel2".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:existingmodel".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:othermodel1".into(), ..Default::default() }, + OverlayDojoModel { tag: "ns:othermodel2".into(), ..Default::default() }, ], ..Default::default() }; @@ -551,11 +484,11 @@ fn overlay_merge_for_world_work_as_expected() { // when other.world is none and current.world is some let other = OverlayManifest { ..Default::default() }; let mut current = OverlayManifest { - world: Some(OverlayClass { name: "world".into(), ..Default::default() }), + world: Some(OverlayClass { tag: "dojo:world".to_string(), ..Default::default() }), ..Default::default() }; let expected = OverlayManifest { - world: Some(OverlayClass { name: "world".into(), ..Default::default() }), + world: Some(OverlayClass { tag: "dojo:world".to_string(), ..Default::default() }), ..Default::default() }; current.merge(other); @@ -564,12 +497,12 @@ fn overlay_merge_for_world_work_as_expected() { // when other.world is some and current.world is none let other = OverlayManifest { - world: Some(OverlayClass { name: "world".into(), ..Default::default() }), + world: Some(OverlayClass { tag: "dojo:world".to_string(), ..Default::default() }), ..Default::default() }; let mut current = OverlayManifest { ..Default::default() }; let expected = OverlayManifest { - world: Some(OverlayClass { name: "world".into(), ..Default::default() }), + world: Some(OverlayClass { tag: "dojo:world".to_string(), ..Default::default() }), ..Default::default() }; @@ -578,15 +511,15 @@ fn overlay_merge_for_world_work_as_expected() { // when other.world is some and current.world is some let other = OverlayManifest { - world: Some(OverlayClass { name: "worldother".into(), ..Default::default() }), + world: Some(OverlayClass { tag: "dojo:worldother".to_string(), ..Default::default() }), ..Default::default() }; let mut current = OverlayManifest { - world: Some(OverlayClass { name: "worldcurrent".into(), ..Default::default() }), + world: Some(OverlayClass { tag: "dojo:worldcurrent".to_string(), ..Default::default() }), ..Default::default() }; let expected = OverlayManifest { - world: Some(OverlayClass { name: "worldcurrent".into(), ..Default::default() }), + world: Some(OverlayClass { tag: "dojo:worldcurrent".to_string(), ..Default::default() }), ..Default::default() }; @@ -607,11 +540,11 @@ fn overlay_merge_for_base_work_as_expected() { // when other.base is none and current.base is some let other = OverlayManifest { ..Default::default() }; let mut current = OverlayManifest { - base: Some(OverlayClass { name: "base".into(), ..Default::default() }), + base: Some(OverlayClass { tag: "dojo:base".to_string(), ..Default::default() }), ..Default::default() }; let expected = OverlayManifest { - base: Some(OverlayClass { name: "base".into(), ..Default::default() }), + base: Some(OverlayClass { tag: "dojo:base".to_string(), ..Default::default() }), ..Default::default() }; current.merge(other); @@ -620,12 +553,12 @@ fn overlay_merge_for_base_work_as_expected() { // when other.base is some and current.base is none let other = OverlayManifest { - base: Some(OverlayClass { name: "base".into(), ..Default::default() }), + base: Some(OverlayClass { tag: "dojo:base".to_string(), ..Default::default() }), ..Default::default() }; let mut current = OverlayManifest { ..Default::default() }; let expected = OverlayManifest { - base: Some(OverlayClass { name: "base".into(), ..Default::default() }), + base: Some(OverlayClass { tag: "dojo:base".to_string(), ..Default::default() }), ..Default::default() }; @@ -634,15 +567,15 @@ fn overlay_merge_for_base_work_as_expected() { // when other.base is some and current.base is some let other = OverlayManifest { - base: Some(OverlayClass { name: "baseother".into(), ..Default::default() }), + base: Some(OverlayClass { tag: "dojo:baseother".to_string(), ..Default::default() }), ..Default::default() }; let mut current = OverlayManifest { - base: Some(OverlayClass { name: "basecurrent".into(), ..Default::default() }), + base: Some(OverlayClass { tag: "dojo:basecurrent".to_string(), ..Default::default() }), ..Default::default() }; let expected = OverlayManifest { - base: Some(OverlayClass { name: "basecurrent".into(), ..Default::default() }), + base: Some(OverlayClass { tag: "dojo:basecurrent".to_string(), ..Default::default() }), ..Default::default() }; @@ -660,34 +593,75 @@ fn overlay_merge_for_base_work_as_expected() { #[test] fn base_manifest_remove_items_work_as_expected() { - let contracts = ["c1", "c2", "c3"]; - let models = ["m1", "m2", "m3"]; + let contracts = ["ns:c1", "ns:c2", "ns:c3"]; + let models = ["ns:m1", "ns:m2", "ns:m3"]; - let world = Manifest { name: "world".into(), inner: Default::default() }; - let base = Manifest { name: "base".into(), inner: Default::default() }; + let world = Manifest { manifest_name: "world".into(), inner: Default::default() }; + let base = Manifest { manifest_name: "dojo-base".to_string(), inner: Default::default() }; let contracts = contracts .iter() - .map(|c| Manifest { name: SmolStr::from(*c), inner: Default::default() }) + .map(|c| Manifest { + manifest_name: c.to_string(), + inner: DojoContract { tag: c.to_string(), ..Default::default() }, + }) .collect(); let models = models .iter() - .map(|c| Manifest { name: SmolStr::from(*c), inner: Default::default() }) + .map(|c| Manifest { + manifest_name: c.to_string(), + inner: DojoModel { tag: c.to_string(), ..Default::default() }, + }) .collect(); let mut base = BaseManifest { contracts, models, world, base }; - base.remove_items(vec!["c1".to_string(), "c3".to_string(), "m2".to_string()]); + base.remove_tags(vec!["ns:c1".to_string(), "ns:c3".to_string(), "ns:m2".to_string()]); assert_eq!(base.contracts.len(), 1); assert_eq!( - base.contracts.iter().map(|c| c.name.clone().into()).collect::>(), - vec!["c2"] + base.contracts.iter().map(|c| c.manifest_name.clone()).collect::>(), + vec!["ns:c2"] ); assert_eq!(base.models.len(), 2); assert_eq!( - base.models.iter().map(|c| c.name.clone().into()).collect::>(), - vec!["m1", "m3"] + base.models.iter().map(|c| c.manifest_name.clone()).collect::>(), + vec!["ns:m1", "ns:m3"] ); } + +fn serialize_bytearray(s: &str) -> Vec { + let ba = ByteArray::from_string(s).unwrap(); + ByteArray::cairo_serialize(&ba) +} + +fn build_model_registered_event(values: Vec, namespace: &str, model: &str) -> EmittedEvent { + let mut data = ByteArray::cairo_serialize(&ByteArray::from_string(model).unwrap()); + data.extend(ByteArray::cairo_serialize(&ByteArray::from_string(namespace).unwrap())); + data.extend(values); + + EmittedEvent { + data, + keys: vec![selector!("ModelRegistered")], + block_hash: Default::default(), + from_address: Default::default(), + block_number: Default::default(), + transaction_hash: Default::default(), + } +} + +fn build_deploy_event(values: Vec, ns: &str, name: &str) -> EmittedEvent { + let mut data = values.to_vec(); + data.extend(serialize_bytearray(ns).iter()); + data.extend(serialize_bytearray(name).iter()); + + EmittedEvent { + data, + keys: vec![], + block_hash: Default::default(), + from_address: Default::default(), + block_number: Default::default(), + transaction_hash: Default::default(), + } +} diff --git a/crates/dojo-world/src/manifest/mod.rs b/crates/dojo-world/src/manifest/mod.rs index 1aa721bd6c..876a1f814e 100644 --- a/crates/dojo-world/src/manifest/mod.rs +++ b/crates/dojo-world/src/manifest/mod.rs @@ -1,27 +1,26 @@ use std::collections::HashMap; +use std::path::PathBuf; use std::{fs, io}; use anyhow::Result; -use cainome::cairo_serde::Error as CainomeError; +use cainome::cairo_serde::{ByteArray, CairoSerde, Error as CainomeError}; use camino::Utf8PathBuf; use serde::de::DeserializeOwned; -use smol_str::SmolStr; -use starknet::core::types::{ - BlockId, BlockTag, EmittedEvent, EventFilter, Felt, FunctionCall, StarknetError, -}; +use serde::Serialize; +use starknet::core::types::{BlockId, BlockTag, EmittedEvent, EventFilter, Felt, StarknetError}; use starknet::core::utils::{ - parse_cairo_short_string, starknet_keccak, CairoShortStringToFeltError, - ParseCairoShortStringError, + starknet_keccak, CairoShortStringToFeltError, ParseCairoShortStringError, }; -use starknet::macros::selector; use starknet::providers::{Provider, ProviderError}; use thiserror::Error; use toml; +use toml::Table; use tracing::error; +use walkdir::WalkDir; use crate::contracts::model::ModelError; use crate::contracts::world::WorldEvent; -use crate::contracts::WorldContractReader; +use crate::contracts::{naming, WorldContractReader}; #[cfg(test)] #[path = "manifest_test.rs"] @@ -35,12 +34,14 @@ pub use types::{ OverlayDojoContract, OverlayDojoModel, OverlayManifest, WorldContract, WorldMetadata, }; -pub const WORLD_CONTRACT_NAME: &str = "dojo::world::world"; -pub const BASE_CONTRACT_NAME: &str = "dojo::base::base"; -pub const RESOURCE_METADATA_CONTRACT_NAME: &str = "dojo::resource_metadata::resource_metadata"; -pub const RESOURCE_METADATA_MODEL_NAME: &str = "0x5265736f757263654d65746164617461"; +pub const WORLD_CONTRACT_TAG: &str = "dojo-world"; +pub const BASE_CONTRACT_TAG: &str = "dojo-base"; + +pub const WORLD_QUALIFIED_PATH: &str = "dojo::world::world"; +pub const BASE_QUALIFIED_PATH: &str = "dojo::base::base"; pub const MANIFESTS_DIR: &str = "manifests"; +pub const TARGET_DIR: &str = "target"; pub const BASE_DIR: &str = "base"; pub const OVERLAYS_DIR: &str = "overlays"; pub const DEPLOYMENTS_DIR: &str = "deployments"; @@ -75,6 +76,12 @@ pub enum AbstractManifestError { AbiError(String), #[error(transparent)] Json(#[from] serde_json::Error), + #[error("Duplicated manifest : {0}")] + DuplicatedManifest(String), + #[error("{0}")] + TagError(String), + #[error("{0}")] + UnknownTarget(String), } impl From> for Manifest { @@ -86,7 +93,7 @@ impl From> for Manifest { original_class_hash: value.inner.original_class_hash, ..Default::default() }, - value.name, + value.manifest_name, ) } } @@ -105,44 +112,41 @@ impl From for DeploymentManifest { impl BaseManifest { /// Load the manifest from a file at the given path. pub fn load_from_path(path: &Utf8PathBuf) -> Result { - let contract_dir = path.join(CONTRACTS_DIR); - let model_dir = path.join(MODELS_DIR); - let world: Manifest = toml::from_str(&fs::read_to_string( - path.join(WORLD_CONTRACT_NAME.replace("::", "_")).with_extension("toml"), + path.join(naming::get_filename_from_tag(WORLD_CONTRACT_TAG)).with_extension("toml"), )?)?; let base: Manifest = toml::from_str(&fs::read_to_string( - path.join(BASE_CONTRACT_NAME.replace("::", "_")).with_extension("toml"), + path.join(naming::get_filename_from_tag(BASE_CONTRACT_TAG)).with_extension("toml"), )?)?; - let contracts = elements_from_path::(&contract_dir)?; - let models = elements_from_path::(&model_dir)?; + let contracts = elements_from_path::(&path.join(CONTRACTS_DIR))?; + let models = elements_from_path::(&path.join(MODELS_DIR))?; Ok(Self { world, base, contracts, models }) } - /// Given a list of contract or model names, remove those from the manifest. - pub fn remove_items(&mut self, items: Vec) { - self.contracts.retain(|contract| !items.contains(&contract.name.to_string())); - self.models.retain(|model| !items.contains(&model.name.to_string())); + /// Given a list of contract or model tags, remove those from the manifest. + pub fn remove_tags(&mut self, tags: Vec) { + self.contracts.retain(|contract| !tags.contains(&contract.inner.tag)); + self.models.retain(|model| !tags.contains(&model.inner.tag)); } pub fn merge(&mut self, overlay: OverlayManifest) { let mut base_map = HashMap::new(); for contract in self.contracts.iter_mut() { - base_map.insert(contract.name.clone(), contract); + base_map.insert(contract.inner.tag.clone(), contract); } for contract in overlay.contracts { - if let Some(manifest) = base_map.get_mut(&contract.name) { + if let Some(manifest) = base_map.get_mut(&contract.tag) { manifest.inner.merge(contract); } else { error!( "OverlayManifest configured for contract \"{}\", but contract is not present \ in BaseManifest.", - contract.name + contract.tag ); } } @@ -156,40 +160,119 @@ impl BaseManifest { } } -impl OverlayManifest { - pub fn load_from_path(path: &Utf8PathBuf) -> Result { - fs::create_dir_all(path)?; +#[derive(Clone, Debug, Copy)] +enum ManifestKind { + BaseClass, + WorldClass, + Contract, + Model, +} - let mut world: Option = None; +impl OverlayManifest { + fn build_kind_from_tags(base_manifest: &BaseManifest) -> HashMap { + let mut kind_from_tags = HashMap::::new(); - let world_path = path.join(WORLD_CONTRACT_NAME.replace("::", "_")).with_extension("toml"); + kind_from_tags.insert(WORLD_CONTRACT_TAG.to_string(), ManifestKind::WorldClass); + kind_from_tags.insert(BASE_CONTRACT_TAG.to_string(), ManifestKind::BaseClass); - if world_path.exists() { - world = Some(toml::from_str(&fs::read_to_string(world_path)?)?); + for model in base_manifest.models.as_slice() { + kind_from_tags.insert(model.inner.tag.clone(), ManifestKind::Model); } - let mut base: Option = None; - let base_path = path.join(BASE_CONTRACT_NAME.replace("::", "_")).with_extension("toml"); - - if base_path.exists() { - base = Some(toml::from_str(&fs::read_to_string(base_path)?)?); + for contract in base_manifest.contracts.as_slice() { + kind_from_tags.insert(contract.inner.tag.clone(), ManifestKind::Contract); } - let contract_dir = path.join(CONTRACTS_DIR); - let contracts = if contract_dir.exists() { - overlay_elements_from_path::(&contract_dir)? - } else { - vec![] - }; + kind_from_tags + } - let model_dir = path.join(MODELS_DIR); - let models = if model_dir.exists() { - overlay_elements_from_path::(&model_dir)? - } else { - vec![] + fn load_overlay( + path: &PathBuf, + kind: ManifestKind, + overlays: &mut OverlayManifest, + ) -> Result<(), AbstractManifestError> { + match kind { + ManifestKind::BaseClass => { + let overlay: OverlayClass = toml::from_str(&fs::read_to_string(path)?)?; + overlays.base = Some(overlay); + } + ManifestKind::WorldClass => { + let overlay: OverlayClass = toml::from_str(&fs::read_to_string(path)?)?; + overlays.world = Some(overlay); + } + ManifestKind::Model => { + let overlay: OverlayDojoModel = toml::from_str(&fs::read_to_string(path)?)?; + overlays.models.push(overlay); + } + ManifestKind::Contract => { + let overlay: OverlayDojoContract = toml::from_str(&fs::read_to_string(path)?)?; + overlays.contracts.push(overlay); + } }; - Ok(Self { world, base, contracts, models }) + Ok(()) + } + + pub fn load_from_path( + path: &Utf8PathBuf, + base_manifest: &BaseManifest, + ) -> Result { + fs::create_dir_all(path)?; + + let kind_from_tags = Self::build_kind_from_tags(base_manifest); + let mut loaded_tags = HashMap::::new(); + let mut overlays = OverlayManifest::default(); + + for entry in WalkDir::new(path).into_iter() { + let entry = match entry { + Ok(e) => e, + Err(e) => return Err(AbstractManifestError::IO(e.into())), + }; + let file_path = entry.path(); + let file_name = entry.file_name().to_string_lossy().to_string(); + + if !file_name.clone().ends_with(".toml") { + continue; + } + + // an overlay file must contain a 'tag' key. + let toml_data = toml::from_str::

(&fs::read_to_string(file_path)?)?; + if !toml_data.contains_key("tag") { + return Err(AbstractManifestError::TagError(format!( + "The overlay '{file_name}' must contain the 'tag' key." + ))); + } + + // the tag key must be a string + let tag = match toml_data["tag"].as_str() { + Some(x) => x.to_string(), + None => { + return Err(AbstractManifestError::TagError(format!( + "The tag key of the overlay '{file_name}' must be a string." + ))); + } + }; + + // an overlay must target an existing class/model/contract + if !kind_from_tags.contains_key(&tag) { + return Err(AbstractManifestError::UnknownTarget(format!( + "The tag '{tag}' of the overlay '{file_name}' does not target an existing \ + class/model/contract." + ))); + } + + // a same tag cannot be used in multiple overlays. + if loaded_tags.contains_key(&tag) { + return Err(AbstractManifestError::DuplicatedManifest(format!( + "The tag '{tag}' is used in multiple overlays." + ))); + } + + Self::load_overlay(&file_path.to_path_buf(), kind_from_tags[&tag], &mut overlays)?; + loaded_tags.insert(tag, true); + } + + Ok(overlays) } /// Writes `Self` to overlay manifests folder. @@ -202,18 +285,25 @@ impl OverlayManifest { if let Some(ref world) = self.world { let world = toml::to_string(world)?; let file_name = - path.join(WORLD_CONTRACT_NAME.replace("::", "_")).with_extension("toml"); + path.join(naming::get_filename_from_tag(WORLD_CONTRACT_TAG)).with_extension("toml"); fs::write(file_name, world)?; } if let Some(ref base) = self.base { let base = toml::to_string(base)?; - let file_name = path.join(BASE_CONTRACT_NAME.replace("::", "_")).with_extension("toml"); + let file_name = + path.join(naming::get_filename_from_tag(BASE_CONTRACT_TAG)).with_extension("toml"); fs::write(file_name, base)?; } - overlay_dojo_contracts_to_path(&path.join(CONTRACTS_DIR), self.contracts.as_slice())?; - overlay_dojo_model_to_path(&path.join(MODELS_DIR), self.models.as_slice())?; + overlay_to_path::( + &path.join(CONTRACTS_DIR), + self.contracts.as_slice(), + |c| c.tag.clone(), + )?; + overlay_to_path::(&path.join(MODELS_DIR), self.models.as_slice(), |m| { + m.tag.clone() + })?; Ok(()) } @@ -229,14 +319,14 @@ impl OverlayManifest { } for other_contract in other.contracts { - let found = self.contracts.iter().find(|c| c.name == other_contract.name); + let found = self.contracts.iter().find(|c| c.tag == other_contract.tag); if found.is_none() { self.contracts.push(other_contract); } } for other_model in other.models { - let found = self.models.iter().find(|m| m.name == other_model.name); + let found = self.models.iter().find(|m| m.tag == other_model.tag); if found.is_none() { self.models.push(other_model); } @@ -257,7 +347,8 @@ impl DeploymentManifest { self.world.inner.seed = previous.world.inner.seed; self.contracts.iter_mut().for_each(|contract| { - let previous_contract = previous.contracts.iter().find(|c| c.name == contract.name); + let previous_contract = + previous.contracts.iter().find(|c| c.manifest_name == contract.manifest_name); if let Some(previous_contract) = previous_contract { if previous_contract.inner.base_class_hash != Felt::ZERO { contract.inner.base_class_hash = previous_contract.inner.base_class_hash; @@ -342,15 +433,16 @@ impl DeploymentManifest { class_hash: world_class_hash, ..Default::default() }, - WORLD_CONTRACT_NAME.into(), + naming::get_filename_from_tag(WORLD_CONTRACT_TAG), ), base: Manifest::new( Class { class_hash: base_class_hash, abi: None, original_class_hash: base_class_hash, + tag: BASE_CONTRACT_TAG.to_string(), }, - BASE_CONTRACT_NAME.into(), + naming::get_filename_from_tag(BASE_CONTRACT_TAG), ), }) } @@ -413,27 +505,8 @@ where let models = parse_models_events(registered_models_events); let mut contracts = parse_contracts_events(contract_deployed_events, contract_upgraded_events); - // fetch contracts name for contract in &mut contracts { - let name = match provider - .call( - FunctionCall { - calldata: vec![], - entry_point_selector: selector!("dojo_resource"), - contract_address: contract.inner.address.expect("qed; missing address"), - }, - BlockId::Tag(BlockTag::Latest), - ) - .await - { - Ok(res) => parse_cairo_short_string(&res[0])?.into(), - - Err(ProviderError::StarknetError(StarknetError::ContractError(_))) => SmolStr::from(""), - - Err(err) => return Err(err.into()), - }; - - contract.name = name; + contract.manifest_name = naming::get_filename_from_tag(&contract.inner.tag); } Ok((models, contracts)) @@ -511,6 +584,18 @@ fn parse_contracts_events( let mut class_hash = data.next().expect("class hash is missing from event"); let address = data.next().expect("addresss is missing from event"); + let str_data = data.as_slice(); + let namespace = + ByteArray::cairo_deserialize(str_data, 0).expect("namespace is missing from event"); + let offset = ByteArray::cairo_serialized_size(&namespace); + let name = + ByteArray::cairo_deserialize(str_data, offset).expect("name is missing from event"); + + let tag = naming::get_tag( + &namespace.to_string().expect("ASCII encoded namespace"), + &name.to_string().expect("ASCII encoded name"), + ); + if let Some(upgrade) = upgradeds.get(&address) { class_hash = *upgrade; } @@ -520,9 +605,10 @@ fn parse_contracts_events( address: Some(address), class_hash, abi: None, + tag: tag.clone(), ..Default::default() }, - Default::default(), + naming::get_filename_from_tag(&tag), ) }) .collect() @@ -543,23 +629,25 @@ fn parse_models_events(events: Vec) -> Vec> { } }; - // TODO: Safely unwrap? - let model_name = model_event.name.to_string().unwrap(); - if let Some(current_class_hash) = models.get_mut(&model_name) { + let model_name = model_event.name.to_string().expect("ASCII encoded name"); + let namespace = model_event.namespace.to_string().expect("ASCII encoded namespace"); + let model_tag = naming::get_tag(&namespace, &model_name); + + if let Some(current_class_hash) = models.get_mut(&model_tag) { if current_class_hash == &model_event.prev_class_hash.into() { *current_class_hash = model_event.class_hash.into(); } } else { - models.insert(model_name, model_event.class_hash.into()); + models.insert(model_tag, model_event.class_hash.into()); } } // TODO: include address of the model in the manifest. models .into_iter() - .map(|(name, class_hash)| Manifest:: { - inner: DojoModel { class_hash, abi: None, ..Default::default() }, - name: name.into(), + .map(|(tag, class_hash)| Manifest:: { + inner: DojoModel { tag: tag.clone(), class_hash, abi: None, ..Default::default() }, + manifest_name: naming::get_filename_from_tag(&tag), }) .collect() } @@ -591,48 +679,20 @@ where Ok(elements) } -fn overlay_elements_from_path(path: &Utf8PathBuf) -> Result, AbstractManifestError> +fn overlay_to_path( + path: &Utf8PathBuf, + elements: &[T], + get_tag: fn(&T) -> String, +) -> Result<(), AbstractManifestError> where - T: DeserializeOwned, + T: Serialize, { - let mut elements = vec![]; - - for entry in path.read_dir()? { - let entry = entry?; - let path = entry.path(); - if path.is_file() { - let manifest: T = toml::from_str(&fs::read_to_string(path)?)?; - elements.push(manifest); - } else { - continue; - } - } - - Ok(elements) -} - -fn overlay_dojo_contracts_to_path( - path: &Utf8PathBuf, - elements: &[OverlayDojoContract], -) -> Result<(), AbstractManifestError> { fs::create_dir_all(path)?; for element in elements { - let path = path.join(element.name.replace("::", "_")).with_extension("toml"); - fs::write(path, toml::to_string(&element)?)?; - } - Ok(()) -} - -fn overlay_dojo_model_to_path( - path: &Utf8PathBuf, - elements: &[OverlayDojoModel], -) -> Result<(), AbstractManifestError> { - fs::create_dir_all(path)?; - - for element in elements { - let path = path.join(element.name.replace("::", "_")).with_extension("toml"); - fs::write(path, toml::to_string(&element)?)?; + let filename = naming::get_filename_from_tag(&get_tag(element)); + let path = path.join(filename).with_extension("toml"); + fs::write(path, toml::to_string(element)?)?; } Ok(()) } @@ -661,6 +721,8 @@ impl ManifestMethods for DojoContract { } fn merge(&mut self, old: Self::OverlayType) { + // ignore name and namespace + if let Some(class_hash) = old.original_class_hash { self.original_class_hash = class_hash; } diff --git a/crates/dojo-world/src/manifest/types.rs b/crates/dojo-world/src/manifest/types.rs index b576c6e557..f9d51b8326 100644 --- a/crates/dojo-world/src/manifest/types.rs +++ b/crates/dojo-world/src/manifest/types.rs @@ -45,7 +45,7 @@ pub struct OverlayManifest { pub models: Vec, } -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Default, Deserialize, Debug)] #[cfg_attr(test, derive(PartialEq))] pub struct Manifest where @@ -53,7 +53,9 @@ where { #[serde(flatten)] pub inner: T, - pub name: SmolStr, + + // name of the manifest which is used as filename + pub manifest_name: String, } // Utility methods thats needs to be implemented by manifest types @@ -75,8 +77,8 @@ impl Manifest where T: ManifestMethods, { - pub fn new(inner: T, name: SmolStr) -> Self { - Self { inner, name } + pub fn new(inner: T, manifest_name: String) -> Self { + Self { inner, manifest_name } } } @@ -103,6 +105,7 @@ pub struct DojoContract { pub computed: Vec, #[serde(default)] pub init_calldata: Vec, + pub tag: String, } /// Represents a declaration of a model. @@ -117,6 +120,7 @@ pub struct DojoModel { #[serde_as(as = "UfeHex")] pub original_class_hash: Felt, pub abi: Option, + pub tag: String, } #[serde_as] @@ -148,13 +152,14 @@ pub struct Class { #[serde_as(as = "UfeHex")] pub original_class_hash: Felt, pub abi: Option, + pub tag: String, } #[serde_as] #[derive(Clone, Default, Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(PartialEq))] pub struct OverlayDojoContract { - pub name: SmolStr, + pub tag: String, pub original_class_hash: Option, pub reads: Option>, pub writes: Option>, @@ -165,7 +170,7 @@ pub struct OverlayDojoContract { #[derive(Clone, Default, Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(PartialEq))] pub struct OverlayDojoModel { - pub name: SmolStr, + pub tag: String, pub original_class_hash: Option, } @@ -181,7 +186,7 @@ pub struct OverlayContract { #[derive(Clone, Default, Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(PartialEq))] pub struct OverlayClass { - pub name: SmolStr, + pub tag: String, pub original_class_hash: Option, } @@ -206,7 +211,7 @@ pub struct ComputedValueEntrypoint { // Name of entrypoint to get computed value pub entrypoint: SmolStr, // Component to compute for - pub model: Option, + pub tag: Option, } impl From for Member { diff --git a/crates/dojo-world/src/metadata.rs b/crates/dojo-world/src/metadata.rs index d778476a2f..31774c5a3e 100644 --- a/crates/dojo-world/src/metadata.rs +++ b/crates/dojo-world/src/metadata.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::io::Cursor; use std::path::PathBuf; -use anyhow::Result; +use anyhow::{anyhow, Context, Result}; use camino::Utf8PathBuf; use ipfs_api_backend_hyper::{IpfsApi, IpfsClient, TryFromUri}; use scarb::core::{ManifestMetadata, Workspace}; @@ -10,7 +10,8 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::json; use url::Url; -use crate::manifest::{BaseManifest, WORLD_CONTRACT_NAME}; +use crate::contracts::naming; +use crate::manifest::{BaseManifest, CONTRACTS_DIR, MODELS_DIR, WORLD_CONTRACT_TAG}; #[cfg(test)] #[path = "metadata_test.rs"] @@ -23,17 +24,15 @@ pub const IPFS_PASSWORD: &str = "12290b883db9138a8ae3363b6739d220"; // copy constants from dojo-lang to avoid circular dependency pub const MANIFESTS_DIR: &str = "manifests"; pub const ABIS_DIR: &str = "abis"; -pub const SOURCES_DIR: &str = "src"; pub const BASE_DIR: &str = "base"; -fn build_artifact_from_name( - source_dir: &Utf8PathBuf, +fn build_artifact_from_filename( abi_dir: &Utf8PathBuf, - element_name: &str, + source_dir: &Utf8PathBuf, + filename: &str, ) -> ArtifactMetadata { - let sanitized_name = element_name.replace("::", "_"); - let abi_file = abi_dir.join(format!("{sanitized_name}.json")); - let src_file = source_dir.join(format!("{sanitized_name}.cairo")); + let abi_file = abi_dir.join(format!("{filename}.json")); + let src_file = source_dir.join(format!("{filename}.cairo")); ArtifactMetadata { abi: if abi_file.exists() { Some(Uri::File(abi_file.into_std_path_buf())) } else { None }, @@ -45,6 +44,21 @@ fn build_artifact_from_name( } } +/// Get the default namespace from the workspace. +/// +/// # Arguments +/// +/// * `ws`: the workspace. +/// +/// # Returns +/// +/// A [`String`] object containing the namespace. +pub fn get_default_namespace_from_ws(ws: &Workspace<'_>) -> String { + let metadata = dojo_metadata_from_workspace(ws) + .expect("Namespace key is already checked by the parsing of the Scarb.toml file."); + metadata.world.namespace +} + /// Build world metadata with data read from the project configuration. /// /// # Arguments @@ -54,27 +68,17 @@ fn build_artifact_from_name( /// # Returns /// /// A [`WorldMetadata`] object initialized with project metadata. -pub fn project_to_world_metadata(project_metadata: Option) -> WorldMetadata { - if let Some(m) = project_metadata { - WorldMetadata { - name: m.name, - description: m.description, - cover_uri: m.cover_uri, - icon_uri: m.icon_uri, - website: m.website, - socials: m.socials, - ..Default::default() - } - } else { - WorldMetadata { - name: None, - description: None, - cover_uri: None, - icon_uri: None, - website: None, - socials: None, - ..Default::default() - } +pub fn project_to_world_metadata(m: ProjectWorldMetadata) -> WorldMetadata { + WorldMetadata { + name: m.name, + description: m.description, + cover_uri: m.cover_uri, + icon_uri: m.icon_uri, + website: m.website, + socials: m.socials, + seed: m.seed, + namespace: m.namespace, + ..Default::default() } } @@ -85,21 +89,24 @@ pub fn project_to_world_metadata(project_metadata: Option) /// /// # Returns /// A [`DojoMetadata`] object containing all Dojo metadata. -pub fn dojo_metadata_from_workspace(ws: &Workspace<'_>) -> Option { +pub fn dojo_metadata_from_workspace(ws: &Workspace<'_>) -> Result { let profile = ws.config().profile(); let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); let manifest_dir = manifest_dir.join(MANIFESTS_DIR).join(profile.as_str()); - let target_dir = ws.target_dir().path_existent().unwrap(); - let sources_dir = target_dir.join(profile.as_str()).join(SOURCES_DIR); - let abis_dir = manifest_dir.join(ABIS_DIR).join(BASE_DIR); + let abi_dir = manifest_dir.join(ABIS_DIR).join(BASE_DIR); + let source_dir = ws.target_dir().path_existent().unwrap(); + let source_dir = source_dir.join(profile.as_str()); let project_metadata = if let Ok(current_package) = ws.current_package() { - current_package.manifest.metadata.dojo() + current_package.manifest.metadata.dojo()? } else { // On workspaces, dojo metadata are not accessible because if no current package is defined // (being the only package or using --package). - return None; + return Err(anyhow!( + "No current package with dojo metadata found, this subcommand is not yet support for \ + workspaces." + )); }; let mut dojo_metadata = DojoMetadata { @@ -108,7 +115,11 @@ pub fn dojo_metadata_from_workspace(ws: &Workspace<'_>) -> Option ..Default::default() }; - let world_artifact = build_artifact_from_name(&sources_dir, &abis_dir, WORLD_CONTRACT_NAME); + let world_artifact = build_artifact_from_filename( + &abi_dir, + &source_dir, + &naming::get_filename_from_tag(WORLD_CONTRACT_TAG), + ); // inialize Dojo world metadata with world metadata coming from project configuration dojo_metadata.world = project_to_world_metadata(project_metadata.world); @@ -118,30 +129,34 @@ pub fn dojo_metadata_from_workspace(ws: &Workspace<'_>) -> Option if manifest_dir.join(BASE_DIR).exists() { if let Ok(manifest) = BaseManifest::load_from_path(&manifest_dir.join(BASE_DIR)) { for model in manifest.models { - let name = model.name.to_string(); + let tag = model.inner.tag.clone(); + let abi_model_dir = abi_dir.join(MODELS_DIR); + let source_model_dir = source_dir.join(MODELS_DIR); dojo_metadata.resources_artifacts.insert( - name.clone(), + tag.clone(), ResourceMetadata { - name: name.clone(), - artifacts: build_artifact_from_name( - &sources_dir, - &abis_dir.join("models"), - &name, + name: tag.clone(), + artifacts: build_artifact_from_filename( + &abi_model_dir, + &source_model_dir, + &naming::get_filename_from_tag(&tag), ), }, ); } for contract in manifest.contracts { - let name = contract.name.to_string(); + let tag = contract.inner.tag.clone(); + let abi_contract_dir = abi_dir.join(CONTRACTS_DIR); + let source_contract_dir = source_dir.join(CONTRACTS_DIR); dojo_metadata.resources_artifacts.insert( - name.clone(), + tag.clone(), ResourceMetadata { - name: name.clone(), - artifacts: build_artifact_from_name( - &sources_dir, - &abis_dir.join("contracts"), - &name, + name: tag.clone(), + artifacts: build_artifact_from_filename( + &abi_contract_dir, + &source_contract_dir, + &naming::get_filename_from_tag(&tag), ), }, ); @@ -149,13 +164,13 @@ pub fn dojo_metadata_from_workspace(ws: &Workspace<'_>) -> Option } } - Some(dojo_metadata) + Ok(dojo_metadata) } /// Metadata coming from project configuration (Scarb.toml) #[derive(Default, Deserialize, Debug, Clone)] pub struct ProjectMetadata { - pub world: Option, + pub world: ProjectWorldMetadata, pub env: Option, pub skip_migration: Option>, } @@ -233,6 +248,8 @@ impl Uri { #[derive(Default, Serialize, Deserialize, Debug, Clone)] pub struct ProjectWorldMetadata { pub name: Option, + pub seed: String, + pub namespace: String, pub description: Option, pub cover_uri: Option, pub icon_uri: Option, @@ -244,6 +261,8 @@ pub struct ProjectWorldMetadata { #[derive(Default, Serialize, Deserialize, Debug, Clone)] pub struct WorldMetadata { pub name: Option, + pub seed: String, + pub namespace: String, pub description: Option, pub cover_uri: Option, pub icon_uri: Option, @@ -410,16 +429,23 @@ impl DojoMetadata { } trait MetadataExt { - fn dojo(&self) -> ProjectMetadata; + fn dojo(&self) -> Result; } impl MetadataExt for ManifestMetadata { - fn dojo(&self) -> ProjectMetadata { - self.tool_metadata + fn dojo(&self) -> Result { + let metadata = self + .tool_metadata .as_ref() .and_then(|e| e.get("dojo")) - .cloned() - .map(|v| v.try_into::().unwrap_or_default()) - .unwrap_or_default() + // TODO: see if we can make error more descriptive + .ok_or_else(|| anyhow!("Some of the fields in [tool.dojo] are required."))? + .clone(); + + let project_metadata: ProjectMetadata = metadata + .try_into() + .with_context(|| "Project metadata (i.e. [tool.dojo]) is not properly configured.")?; + + Ok(project_metadata) } } diff --git a/crates/dojo-world/src/metadata_test.rs b/crates/dojo-world/src/metadata_test.rs index b8f430072e..adf9156617 100644 --- a/crates/dojo-world/src/metadata_test.rs +++ b/crates/dojo-world/src/metadata_test.rs @@ -3,12 +3,15 @@ use std::fs; use camino::Utf8PathBuf; use dojo_test_utils::compiler; +use scarb::compiler::Profile; use scarb::ops; use url::Url; +use crate::contracts::naming::{get_filename_from_tag, TAG_SEPARATOR}; +use crate::manifest::{CONTRACTS_DIR, MODELS_DIR, WORLD_CONTRACT_TAG}; use crate::metadata::{ dojo_metadata_from_workspace, ArtifactMetadata, ProjectMetadata, Uri, WorldMetadata, ABIS_DIR, - BASE_DIR, MANIFESTS_DIR, SOURCES_DIR, + BASE_DIR, MANIFESTS_DIR, }; #[test] @@ -30,6 +33,8 @@ cover_uri = "file://example_cover.png" icon_uri = "file://example_icon.png" website = "https://dojoengine.org" socials.x = "https://x.com/dojostarknet" +seed = "dojo_examples" +namespace = "dojo_examples" "#, ) .unwrap(); @@ -53,8 +58,7 @@ socials.x = "https://x.com/dojostarknet" Some("0x0248cacaeac64c45be0c19ee8727e0bb86623ca7fa3f0d431a6c55e200697e5a") ); - assert!(metadata.world.is_some()); - let world = metadata.world.unwrap(); + let world = metadata.world; assert_eq!(world.name(), Some("example")); assert_eq!(world.description(), Some("example world")); @@ -62,12 +66,16 @@ socials.x = "https://x.com/dojostarknet" assert_eq!(world.icon_uri, Some(Uri::File("example_icon.png".into()))); assert_eq!(world.website, Some(Url::parse("https://dojoengine.org").unwrap())); assert_eq!(world.socials.unwrap().get("x"), Some(&"https://x.com/dojostarknet".to_string())); + assert_eq!(world.seed, String::from("dojo_examples")); + assert_eq!(world.namespace, String::from("dojo_examples")); } #[tokio::test] async fn world_metadata_hash_and_upload() { let meta = WorldMetadata { + namespace: "dojo_examples".to_string(), name: Some("Test World".to_string()), + seed: String::from("dojo_examples"), description: Some("A world used for testing".to_string()), cover_uri: Some(Uri::File("src/metadata_test_data/cover.png".into())), icon_uri: Some(Uri::File("src/metadata_test_data/cover.png".into())), @@ -97,6 +105,8 @@ world_address = "0x0248cacaeac64c45be0c19ee8727e0bb86623ca7fa3f0d431a6c55e200697 [world] name = "example" description = "example world" +seed = "dojo_examples" +namespace = "dojo_examples" cover_uri = "file://example_cover.png" icon_uri = "file://example_icon.png" website = "https://dojoengine.org" @@ -105,12 +115,14 @@ website = "https://dojoengine.org" ) .unwrap(); - assert!(metadata.world.is_some()); + assert!(metadata.world.socials.is_none()); } #[tokio::test] async fn get_full_dojo_metadata_from_workspace() { - let config = compiler::build_test_config("../../examples/spawn-and-move/Scarb.toml").unwrap(); + let config = + compiler::build_test_config("../../examples/spawn-and-move/Scarb.toml", Profile::DEV) + .unwrap(); let ws = ops::read_workspace(config.manifest_path(), &config) .unwrap_or_else(|op| panic!("Error building workspace: {op:?}")); @@ -118,7 +130,7 @@ async fn get_full_dojo_metadata_from_workspace() { let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); let manifest_dir = manifest_dir.join(MANIFESTS_DIR).join(profile.as_str()); let target_dir = ws.target_dir().path_existent().unwrap(); - let sources_dir = target_dir.join(profile.as_str()).join(SOURCES_DIR); + let target_dir = target_dir.join(profile.as_str()); let abis_dir = manifest_dir.join(ABIS_DIR).join(BASE_DIR); let dojo_metadata = @@ -144,10 +156,6 @@ async fn get_full_dojo_metadata_from_workspace() { ); assert!(env.world_address.is_some()); - assert_eq!( - env.world_address.unwrap(), - "0x07efebb0c2d4cc285d48a97a7174def3be7fdd6b7bd29cca758fa2e17e03ef30" - ); assert!(env.keystore_path.is_none()); assert!(env.keystore_password.is_none()); @@ -164,86 +172,81 @@ async fn get_full_dojo_metadata_from_workspace() { assert!(dojo_metadata.world.website.is_none()); assert!(dojo_metadata.world.socials.is_none()); - check_artifact( - dojo_metadata.world.artifacts, - "dojo_world_world".to_string(), - &abis_dir, - &sources_dir, + let world_filename = get_filename_from_tag(WORLD_CONTRACT_TAG); + assert!(dojo_metadata.world.artifacts.abi.is_some(), "No abi for {world_filename}"); + let abi = dojo_metadata.world.artifacts.abi.unwrap(); + assert_eq!( + abi, + Uri::File(abis_dir.join(format!("{world_filename}.json")).into()), + "Bad abi for {world_filename}", ); let artifacts = get_artifacts_from_manifest(&manifest_dir); - for (abi_subdir, name) in artifacts { - let resource = dojo_metadata.resources_artifacts.get(&name); - assert!(resource.is_some(), "bad resource metadata for {}", name); - let resource = resource.unwrap(); + for (subdir, filename) in artifacts { + let tag = get_tag_from_filename(&filename); + let resource = dojo_metadata.resources_artifacts.get(&tag); - let sanitized_name = name.replace("::", "_"); + assert!(resource.is_some(), "bad resource metadata for {}", tag); + let resource = resource.unwrap(); check_artifact( resource.artifacts.clone(), - sanitized_name, - &abis_dir.join(abi_subdir), - &sources_dir, + filename, + &abis_dir.join(subdir), + &target_dir.join(subdir), ); } } fn check_artifact( artifact: ArtifactMetadata, - name: String, + basename: String, abis_dir: &Utf8PathBuf, - sources_dir: &Utf8PathBuf, + source_dir: &Utf8PathBuf, ) { - assert!(artifact.abi.is_some()); + assert!(artifact.abi.is_some(), "No abi for {}", basename); let abi = artifact.abi.unwrap(); assert_eq!( abi, - Uri::File(abis_dir.join(format!("{name}.json")).into()), + Uri::File(abis_dir.join(format!("{basename}.json")).into()), "Bad abi for {}", - name + basename ); - assert!(artifact.source.is_some()); + assert!(artifact.source.is_some(), "No source for {}", basename); let source = artifact.source.unwrap(); assert_eq!( source, - Uri::File(sources_dir.join(format!("{name}.cairo")).into()), + Uri::File(source_dir.join(format!("{basename}.cairo")).into()), "Bad source for {}", - name + basename ); } -fn get_artifacts_from_manifest(manifest_dir: &Utf8PathBuf) -> Vec<(String, String)> { - let contracts_dir = manifest_dir.join(BASE_DIR).join("contracts"); - let models_dir = manifest_dir.join(BASE_DIR).join("models"); +fn get_artifacts_from_manifest(manifest_dir: &Utf8PathBuf) -> Vec<(&str, String)> { + let contracts_dir = manifest_dir.join(BASE_DIR).join(CONTRACTS_DIR); + let models_dir = manifest_dir.join(BASE_DIR).join(MODELS_DIR); let mut artifacts = vec![]; // models for entry in fs::read_dir(models_dir).unwrap().flatten() { - let name = entry.path().file_stem().unwrap().to_string_lossy().to_string(); - let name = name.replace("_models_", "::models::"); - // 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::"); - - let name = name.replace("_mock_token_", "::mock_token::"); - let name = name.replace("::mock_token_", "::mock_token::"); - artifacts.push(("models".to_string(), name)); + let filename = entry.path().file_stem().unwrap().to_string_lossy().to_string(); + artifacts.push((MODELS_DIR, filename)); } // contracts 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::"); - let name = name.replace("_mock_token_", "::mock_token::"); - artifacts.push(("contracts".to_string(), name)); + let filename = entry.path().file_stem().unwrap().to_string_lossy().to_string(); + artifacts.push((CONTRACTS_DIR, filename)); } artifacts } + +fn get_tag_from_filename(filename: &str) -> String { + let parts = filename.split(TAG_SEPARATOR).collect::>(); + assert!(parts.len() >= 2); + format!("{}{TAG_SEPARATOR}{}", parts[0], parts[1]) +} diff --git a/crates/dojo-world/src/migration/class.rs b/crates/dojo-world/src/migration/class.rs index a342e6eb01..d49561a2d2 100644 --- a/crates/dojo-world/src/migration/class.rs +++ b/crates/dojo-world/src/migration/class.rs @@ -9,7 +9,8 @@ use super::{Declarable, MigrationType, StateDiff}; /// Represents differences between a local and remote class. #[derive(Debug, Default, Clone)] pub struct ClassDiff { - pub name: String, + // The tag is used to identify the corresponding artifact produced by the compiler. + pub tag: String, pub local_class_hash: Felt, pub original_class_hash: Felt, pub remote_class_hash: Option, @@ -27,7 +28,7 @@ impl StateDiff for ClassDiff { impl Display for ClassDiff { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{}:", self.name)?; + writeln!(f, "{}:", self.tag)?; writeln!(f, " Local: {:#x}", self.local_class_hash)?; if let Some(remote) = self.remote_class_hash { diff --git a/crates/dojo-world/src/migration/contract.rs b/crates/dojo-world/src/migration/contract.rs index 6a0bb9ce35..2339454f1a 100644 --- a/crates/dojo-world/src/migration/contract.rs +++ b/crates/dojo-world/src/migration/contract.rs @@ -11,7 +11,8 @@ pub type DeclareOutput = DeclareTransactionResult; /// Represents differences between a local and remote contract. #[derive(Debug, Default, Clone)] pub struct ContractDiff { - pub name: String, + // The tag is used to identify the corresponding artifact produced by the compiler. + pub tag: String, pub local_class_hash: Felt, pub original_class_hash: Felt, pub base_class_hash: Felt, @@ -31,7 +32,7 @@ impl StateDiff for ContractDiff { impl Display for ContractDiff { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{}:", self.name)?; + writeln!(f, "{}:", self.tag)?; writeln!(f, " Local Class Hash: {:#x}", self.local_class_hash)?; writeln!(f, " Original Class Hash: {:#x}", self.original_class_hash)?; writeln!(f, " Base Class Hash: {:#x}", self.base_class_hash)?; diff --git a/crates/dojo-world/src/migration/mod.rs b/crates/dojo-world/src/migration/mod.rs index e67dbafc32..f63349e62c 100644 --- a/crates/dojo-world/src/migration/mod.rs +++ b/crates/dojo-world/src/migration/mod.rs @@ -36,7 +36,7 @@ pub struct DeployOutput { // base class hash at time of deployment pub base_class_hash: Felt, pub was_upgraded: bool, - pub name: Option, + pub tag: Option, } #[derive(Clone, Debug)] @@ -51,7 +51,7 @@ pub struct UpgradeOutput { pub struct RegisterOutput { pub transaction_hash: Felt, pub declare_output: Vec, - pub registered_model_names: Vec, + pub registered_models: Vec, } #[derive(Debug, Error)] @@ -241,7 +241,7 @@ pub trait Deployable: Declarable + Sync { declare, base_class_hash, was_upgraded, - name: None, + tag: None, }) } @@ -306,7 +306,7 @@ pub trait Deployable: Declarable + Sync { declare, base_class_hash: Felt::default(), was_upgraded: false, - name: None, + tag: None, }) } diff --git a/crates/dojo-world/src/migration/strategy.rs b/crates/dojo-world/src/migration/strategy.rs index f0e79d8867..3e221d27ba 100644 --- a/crates/dojo-world/src/migration/strategy.rs +++ b/crates/dojo-world/src/migration/strategy.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::fs; use std::path::PathBuf; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{anyhow, Context, Result}; use camino::Utf8PathBuf; use starknet::core::types::Felt; use starknet::core::utils::{cairo_short_string_to_felt, get_contract_address}; @@ -12,6 +12,8 @@ use super::class::{ClassDiff, ClassMigration}; use super::contract::{ContractDiff, ContractMigration}; use super::world::WorldDiff; use super::MigrationType; +use crate::contracts::naming; +use crate::manifest::{CONTRACTS_DIR, MODELS_DIR}; #[derive(Debug, Clone)] pub enum MigrationMetadata { @@ -75,7 +77,7 @@ impl MigrationStrategy { match dependency_contract { MigrationMetadata::Contract(c) => { let contract_address = get_contract_address( - generate_salt(&c.name), + generate_salt(&naming::get_name_from_tag(&c.tag)), c.base_class_hash, &[], world_address, @@ -107,26 +109,11 @@ pub fn prepare_for_migration( diff: WorldDiff, ) -> Result { let mut metadata = HashMap::new(); - let entries = fs::read_dir(target_dir).with_context(|| { - format!( - "Failed trying to read target directory ({target_dir})\nNOTE: build files are profile \ - specified so make sure to run build command with correct profile. For e.g. `sozo -P \ - my_profile build`" - ) - })?; - let mut artifact_paths = HashMap::new(); - for entry in entries.flatten() { - let file_name = entry.file_name(); - let file_name_str = file_name.to_string_lossy(); - if file_name_str == "manifest.json" || !file_name_str.ends_with(".json") { - continue; - } - - let name = file_name_str.trim_end_matches(".json").to_string(); - artifact_paths.insert(name, entry.path()); - } + read_artifact_paths(target_dir, &mut artifact_paths)?; + read_artifact_paths(&target_dir.join(MODELS_DIR), &mut artifact_paths)?; + read_artifact_paths(&target_dir.join(CONTRACTS_DIR), &mut artifact_paths)?; // We don't need to care if a contract has already been declared or not, because // the migration strategy will take care of that. @@ -155,16 +142,6 @@ pub fn prepare_for_migration( Felt::ZERO, ); - if let Some(world_address) = world_address { - if world_address != generated_world_address { - println!("generated_world_address: {:?}", generated_world_address); - bail!( - "Calculated world address doesn't match provided world address.\nIf you are \ - deploying with custom seed make sure `world_address` is correctly configured \ - (or not set) `Scarb.toml`" - ) - } - } world.contract_address = generated_world_address; } @@ -199,7 +176,8 @@ fn evaluate_class_to_migrate( Ok(None) } _ => { - let path = find_artifact_path(class.name.as_str(), artifact_paths)?; + let path = + find_artifact_path(&naming::get_filename_from_tag(&class.tag), artifact_paths)?; Ok(Some(ClassMigration { diff: class.clone(), artifact_path: path.clone() })) } } @@ -214,17 +192,18 @@ fn evaluate_contracts_to_migrate( let mut comps_to_migrate = vec![]; for c in contracts { - metadata.insert(c.name.clone(), MigrationMetadata::Contract(c.clone())); + metadata.insert(c.tag.clone(), MigrationMetadata::Contract(c.clone())); match c.remote_class_hash { Some(remote) if remote == c.local_class_hash && !world_contract_will_migrate => { continue; } _ => { - let path = find_artifact_path(c.name.as_str(), artifact_paths)?; + let path = + find_artifact_path(&naming::get_filename_from_tag(&c.tag), artifact_paths)?; comps_to_migrate.push(ContractMigration { diff: c.clone(), artifact_path: path.clone(), - salt: generate_salt(&c.name), + salt: generate_salt(&naming::get_name_from_tag(&c.tag)), ..Default::default() }); } @@ -243,7 +222,8 @@ fn evaluate_contract_to_migrate( || contract.remote_class_hash.is_none() || matches!(contract.remote_class_hash, Some(remote_hash) if remote_hash != contract.local_class_hash) { - let path = find_artifact_path(&contract.name, artifact_paths)?; + let path = + find_artifact_path(&naming::get_filename_from_tag(&contract.tag), artifact_paths)?; Ok(Some(ContractMigration { diff: contract.clone(), @@ -256,12 +236,12 @@ fn evaluate_contract_to_migrate( } fn find_artifact_path<'a>( - contract_name: &str, + artifact_name: &str, artifact_paths: &'a HashMap, ) -> Result<&'a PathBuf> { artifact_paths - .get(contract_name) - .with_context(|| anyhow!("missing contract artifact for `{}` contract", contract_name)) + .get(artifact_name) + .with_context(|| anyhow!("missing contract artifact for `{}` contract", artifact_name)) } pub fn generate_salt(value: &str) -> Felt { @@ -277,3 +257,29 @@ pub fn generate_salt(value: &str) -> Felt { .collect::>(), ) } + +fn read_artifact_paths( + input_dir: &Utf8PathBuf, + artifact_paths: &mut HashMap, +) -> Result<()> { + let entries = fs::read_dir(input_dir).with_context(|| { + format!( + "Failed trying to read target directory ({input_dir})\nNOTE: build files are profile \ + specified so make sure to run build command with correct profile. For e.g. `sozo -P \ + my_profile build`" + ) + })?; + + for entry in entries.flatten() { + let file_name = entry.file_name(); + let file_name_str = file_name.to_string_lossy(); + if file_name_str == "manifest.json" || !file_name_str.ends_with(".json") { + continue; + } + + let artifact_name = file_name_str.trim_end_matches(".json").to_string(); + artifact_paths.insert(artifact_name, entry.path()); + } + + Ok(()) +} diff --git a/crates/dojo-world/src/migration/world.rs b/crates/dojo-world/src/migration/world.rs index a19a7084a4..9a703f1338 100644 --- a/crates/dojo-world/src/migration/world.rs +++ b/crates/dojo-world/src/migration/world.rs @@ -3,15 +3,15 @@ use std::mem; use std::str::FromStr; use anyhow::{bail, Result}; -use convert_case::{Case, Casing}; -use starknet::core::types::Felt; +use starknet_crypto::Felt; use topological_sort::TopologicalSort; use super::class::ClassDiff; use super::contract::ContractDiff; use super::StateDiff; +use crate::contracts::naming; use crate::manifest::{ - BaseManifest, DeploymentManifest, ManifestMethods, BASE_CONTRACT_NAME, WORLD_CONTRACT_NAME, + BaseManifest, DeploymentManifest, ManifestMethods, BASE_CONTRACT_TAG, WORLD_CONTRACT_TAG, }; #[cfg(test)] @@ -33,23 +33,14 @@ impl WorldDiff { .models .iter() .map(|model| ClassDiff { - name: model.name.to_string(), + tag: model.inner.tag.to_string(), local_class_hash: *model.inner.class_hash(), original_class_hash: *model.inner.original_class_hash(), remote_class_hash: remote.as_ref().and_then(|m| { - // Remote models are detected from events, where only the struct - // name (pascal case) is emitted. - // Local models uses the fully qualified name of the model, - // always in snake_case from cairo compiler. - let model_name = model - .name - .split("::") - .last() - .unwrap_or(&model.name) - .from_case(Case::Snake) - .to_case(Case::Pascal); - - m.models.iter().find(|e| e.name == model_name).map(|s| *s.inner.class_hash()) + m.models + .iter() + .find(|e| e.manifest_name == model.manifest_name) + .map(|s| *s.inner.class_hash()) }), }) .collect::>(); @@ -68,7 +59,7 @@ impl WorldDiff { }; ContractDiff { - name: contract.name.to_string(), + tag: contract.inner.tag.to_string(), local_class_hash: *contract.inner.class_hash(), original_class_hash: *contract.inner.original_class_hash(), base_class_hash, @@ -84,14 +75,14 @@ impl WorldDiff { .collect::>(); let base = ClassDiff { - name: BASE_CONTRACT_NAME.into(), + tag: BASE_CONTRACT_TAG.to_string(), local_class_hash: *local.base.inner.class_hash(), original_class_hash: *local.base.inner.original_class_hash(), remote_class_hash: remote.as_ref().map(|m| *m.base.inner.class_hash()), }; let world = ContractDiff { - name: WORLD_CONTRACT_NAME.into(), + tag: WORLD_CONTRACT_TAG.to_string(), local_class_hash: *local.world.inner.class_hash(), original_class_hash: *local.world.inner.original_class_hash(), base_class_hash: *local.base.inner.class_hash(), @@ -114,19 +105,24 @@ impl WorldDiff { count } - pub fn update_order(&mut self) -> Result<()> { - let mut ts = TopologicalSort::<&str>::new(); + pub fn update_order(&mut self, default_namespace: &str) -> Result<()> { + let mut ts = TopologicalSort::::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); + ts.insert(contract.tag.clone()); for field in &contract.init_calldata { if let Some(dependency) = field.strip_prefix("$contract_address:") { - ts.add_dependency(dependency, curr_name); + ts.add_dependency( + naming::ensure_namespace(dependency, default_namespace), + contract.tag.clone(), + ); } else if let Some(dependency) = field.strip_prefix("$class_hash:") { - ts.add_dependency(dependency, curr_name); + ts.add_dependency( + naming::ensure_namespace(dependency, default_namespace), + contract.tag.clone(), + ); } else { // verify its a field element match Felt::from_str(field) { @@ -157,8 +153,8 @@ impl WorldDiff { let mut new_contracts = vec![]; - for c_name in calculated_order { - let contract = match self.contracts.iter().find(|c| c.name == c_name) { + for tag in calculated_order { + let contract = match self.contracts.iter().find(|c| c.tag == tag) { Some(c) => c, None => bail!("Unidentified contract found in `init_calldata`"), }; diff --git a/crates/dojo-world/src/migration/world_test.rs b/crates/dojo-world/src/migration/world_test.rs index e84e1b2cec..94c85a51ef 100644 --- a/crates/dojo-world/src/migration/world_test.rs +++ b/crates/dojo-world/src/migration/world_test.rs @@ -1,28 +1,29 @@ use starknet::macros::felt; use super::*; +use crate::contracts::naming::{get_filename_from_tag, get_tag}; use crate::manifest::{BaseManifest, Class, DojoContract, DojoModel, Manifest}; #[test] fn no_diff_when_local_and_remote_are_equal() { let world_contract = Manifest::new( Class { class_hash: 66_u32.into(), ..Default::default() }, - WORLD_CONTRACT_NAME.into(), + get_filename_from_tag(WORLD_CONTRACT_TAG), ); let base_contract = Manifest::new( Class { class_hash: 77_u32.into(), ..Default::default() }, - BASE_CONTRACT_NAME.into(), + get_filename_from_tag(BASE_CONTRACT_TAG), ); let models = vec![Manifest::new( DojoModel { members: vec![], class_hash: 11_u32.into(), ..Default::default() }, - "dojo_mock::models::model".into(), + "dojo_mock-model".into(), )]; let remote_models = vec![Manifest::new( DojoModel { members: vec![], class_hash: 11_u32.into(), ..Default::default() }, - "Model".into(), + "dojo_mock-model".into(), )]; let local = @@ -33,8 +34,6 @@ fn no_diff_when_local_and_remote_are_equal() { let diff = WorldDiff::compute(local, Some(remote)); - println!("{:?}", diff); - assert_eq!(diff.count_diffs(), 0); } @@ -42,52 +41,75 @@ fn no_diff_when_local_and_remote_are_equal() { fn diff_when_local_and_remote_are_different() { let world_contract = Manifest::new( Class { class_hash: 66_u32.into(), ..Default::default() }, - WORLD_CONTRACT_NAME.into(), + get_filename_from_tag(WORLD_CONTRACT_TAG), ); let base_contract = Manifest::new( Class { class_hash: 77_u32.into(), ..Default::default() }, - BASE_CONTRACT_NAME.into(), + get_filename_from_tag(BASE_CONTRACT_TAG), ); let models = vec![ Manifest::new( - DojoModel { members: vec![], class_hash: felt!("0x11"), ..Default::default() }, - "dojo_mock::models::model".into(), + DojoModel { + tag: get_tag("dojo_mock", "model"), + members: vec![], + class_hash: felt!("0x11"), + ..Default::default() + }, + get_filename_from_tag(&get_tag("dojo_mock", "model")), ), Manifest::new( - DojoModel { members: vec![], class_hash: felt!("0x22"), ..Default::default() }, - "dojo_mock::models::model_2".into(), + DojoModel { + tag: get_tag("dojo_mock", "model2"), + members: vec![], + class_hash: felt!("0x22"), + ..Default::default() + }, + get_filename_from_tag(&get_tag("dojo_mock", "model2")), ), ]; let remote_models = vec![ Manifest::new( - DojoModel { members: vec![], class_hash: felt!("0x11"), ..Default::default() }, - "Model".into(), + DojoModel { + tag: get_tag("dojo_mock", "model"), + members: vec![], + class_hash: felt!("0x11"), + ..Default::default() + }, + get_filename_from_tag(&get_tag("dojo_mock", "model")), ), Manifest::new( - DojoModel { members: vec![], class_hash: felt!("0x33"), ..Default::default() }, - "Model2".into(), + DojoModel { + tag: get_tag("dojo_mock", "model2"), + + members: vec![], + class_hash: felt!("0x33"), + ..Default::default() + }, + get_filename_from_tag(&get_tag("dojo_mock", "model2")), ), ]; let contracts = vec![ Manifest::new( DojoContract { + tag: get_tag("dojo_mock", "my_contract"), class_hash: felt!("0x1111"), address: Some(felt!("0x2222")), ..DojoContract::default() }, - "dojo_mock::contracts::my_contract".into(), + get_filename_from_tag(&get_tag("dojo_mock", "my_contract")), ), Manifest::new( DojoContract { + tag: get_tag("dojo_mock", "my_contract2"), class_hash: felt!("0x3333"), address: Some(felt!("4444")), ..DojoContract::default() }, - "dojo_mock::contracts::my_contract_2".into(), + get_filename_from_tag(&get_tag("dojo_mock", "my_contract2")), ), ]; @@ -102,27 +124,27 @@ fn diff_when_local_and_remote_are_different() { let diff = WorldDiff::compute(local, Some(remote)); assert_eq!(diff.count_diffs(), 3); - 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")); + assert!(diff.models.iter().any(|m| m.tag == get_tag("dojo_mock", "model2"))); + assert!(diff.contracts.iter().any(|c| c.tag == get_tag("dojo_mock", "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"]), + ("ns", "c4", vec!["$contract_address:ns-c1", "0x0"]), + ("ns", "c3", vec!["0x0"]), + ("ns", "c5", vec!["$contract_address:ns-c4", "0x0"]), + ("ns", "c7", vec!["$contract_address:ns-c4", "0x0"]), + ("ns", "c2", vec!["0x0"]), + ("ns", "c6", vec!["$contract_address:ns-c4", "$contract_address:ns-c3", "0x0"]), + ("ns", "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(), + init_calldata: calldata.2.iter().map(|c| c.to_string()).collect(), + tag: get_tag(calldata.0, calldata.1), ..Default::default() }); } @@ -134,31 +156,31 @@ fn updating_order_as_expected() { models: vec![], }; - diff.update_order().unwrap(); + diff.update_order("ns").unwrap(); - let expected_order = ["c1", "c2", "c3", "c4", "c5", "c6", "c7"]; + let expected_order = ["ns-c1", "ns-c2", "ns-c3", "ns-c4", "ns-c5", "ns-c6", "ns-c7"]; for (i, contract) in diff.contracts.iter().enumerate() { - assert_eq!(contract.name, expected_order[i]); + assert_eq!(contract.tag, 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"]), + ("ns", "c4", vec!["$contract_address:ns-c1", "$contract_address:ns-c6", "0x0"]), + ("ns", "c3", vec!["0x0"]), + ("ns", "c5", vec!["$contract_address:ns-c4", "0x0"]), + ("ns", "c7", vec!["$contract_address:ns-c4", "0x0"]), + ("ns", "c2", vec!["0x0"]), + ("ns", "c6", vec!["$contract_address:ns-c4", "$contract_address:ns-c3", "0x0"]), + ("ns", "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(), + init_calldata: calldata.2.iter().map(|c| c.to_string()).collect(), + tag: get_tag(calldata.0, calldata.1), ..Default::default() }); } @@ -170,5 +192,5 @@ fn updating_order_when_cyclic_dependency_fail() { models: vec![], }; - assert!(diff.update_order().is_err_and(|e| e.to_string().contains("Cyclic"))); + assert!(diff.update_order("ns").is_err_and(|e| e.to_string().contains("Cyclic"))); } diff --git a/crates/dojo-world/src/utils.rs b/crates/dojo-world/src/utils.rs index 16c7d59a64..4fd2c47800 100644 --- a/crates/dojo-world/src/utils.rs +++ b/crates/dojo-world/src/utils.rs @@ -3,6 +3,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; +use anyhow::Result; use futures::FutureExt; use starknet::accounts::{ AccountDeploymentV1, AccountError, AccountFactory, AccountFactoryError, ConnectedAccount, diff --git a/crates/katana/core/tests/sequencer.rs b/crates/katana/core/tests/sequencer.rs index 4d3ffb1494..a50047eb47 100644 --- a/crates/katana/core/tests/sequencer.rs +++ b/crates/katana/core/tests/sequencer.rs @@ -103,7 +103,14 @@ async fn test_increase_next_block_timestamp() { let block2_timestamp = BlockProvider::block(provider, block2.into()).unwrap().unwrap().header.timestamp; - assert_eq!(block2_timestamp, block1_timestamp + 1000, "timestamp should be updated"); + // Depending on the current time and the machine we run on, we may have 1 sec difference + // between the expected and actual timestamp. + // We take this possible delay in account to have the test more robust for now, + // but it may due to how the timestamp is updated in the sequencer. + assert!( + block2_timestamp == block1_timestamp + 1000 || block2_timestamp == block1_timestamp + 1001, + "timestamp should be updated" + ); } // #[tokio::test] diff --git a/crates/sozo/ops/Cargo.toml b/crates/sozo/ops/Cargo.toml index 1d70e1d491..f88f3c6f04 100644 --- a/crates/sozo/ops/Cargo.toml +++ b/crates/sozo/ops/Cargo.toml @@ -30,6 +30,7 @@ dojo-lang.workspace = true dojo-types.workspace = true dojo-world = { workspace = true, features = [ "contracts", "metadata", "migration" ] } futures.workspace = true +itertools.workspace = true num-bigint = "0.4.6" num-traits.workspace = true rpassword.workspace = true @@ -53,3 +54,4 @@ assert_fs.workspace = true dojo-test-utils = { workspace = true, features = [ "build-examples" ] } ipfs-api-backend-hyper = { git = "https://github.com/ferristseng/rust-ipfs-api", rev = "af2c17f7b19ef5b9898f458d97a90055c3605633", features = [ "with-hyper-rustls" ] } katana-runner.workspace = true +tee = "0.1.0" \ No newline at end of file diff --git a/crates/sozo/ops/src/auth.rs b/crates/sozo/ops/src/auth.rs index 5abdaec4a2..f9a1942aeb 100644 --- a/crates/sozo/ops/src/auth.rs +++ b/crates/sozo/ops/src/auth.rs @@ -2,96 +2,108 @@ use std::str::FromStr; use anyhow::{Context, Result}; use dojo_world::contracts::model::ModelError; +use dojo_world::contracts::naming::{ + compute_bytearray_hash, compute_model_selector_from_tag, ensure_namespace, +}; use dojo_world::contracts::world::WorldContract; -use dojo_world::contracts::{cairo_utils, WorldContractReader}; +use dojo_world::contracts::WorldContractReader; use dojo_world::migration::TxnConfig; use dojo_world::utils::TransactionExt; use scarb_ui::Ui; use starknet::accounts::{Account, ConnectedAccount}; use starknet::core::types::{BlockId, BlockTag, Felt}; -use starknet::core::utils::{get_selector_from_name, parse_cairo_short_string}; use crate::utils; #[derive(Debug, Clone, PartialEq)] pub enum ResourceType { Contract(String), - Model(Felt), + Namespace(String), + Model(String), +} + +impl FromStr for ResourceType { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let parts = s.split_once(':'); + let resource = match parts { + Some(("contract", name)) | Some(("c", name)) => { + ResourceType::Contract(name.to_string()) + } + Some(("model", name)) | Some(("m", name)) => ResourceType::Model(name.to_string()), + Some(("namespace", name)) | Some(("ns", name)) => { + ResourceType::Namespace(name.to_string()) + } + _ => anyhow::bail!( + "Resource is expected to be in the format `resource_type:resource_name`: `sozo \ + auth grant owner resource_type:resource_name,0x1234`" + ), + }; + Ok(resource) + } } #[derive(Debug, Clone, PartialEq)] -pub struct ModelContract { - pub model: Felt, - pub contract: String, +pub struct ResourceWriter { + pub resource: ResourceType, + pub tag_or_address: String, } -impl FromStr for ModelContract { +impl FromStr for ResourceWriter { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let parts: Vec<&str> = s.split(',').collect(); - let (model, contract) = match parts.as_slice() { - [model, contract] => (model, contract), + let (resource, tag_or_address) = match parts.as_slice() { + [resource, tag_or_address] => (resource, tag_or_address.to_string()), _ => anyhow::bail!( - "Model and contract address are expected to be comma separated: `sozo auth grant \ - writer model_name,0x1234`" + "Resource and contract are expected to be comma separated: `sozo auth grant \ + writer model:model_name,0x1234`" ), }; - let model = cairo_utils::str_to_felt(model) - .map_err(|_| anyhow::anyhow!("Invalid model name: {}", model))?; - - Ok(ModelContract { model, contract: contract.to_string() }) + let resource = ResourceType::from_str(resource)?; + Ok(ResourceWriter { resource, tag_or_address }) } } #[derive(Debug, Clone, PartialEq)] -pub struct OwnerResource { +pub struct ResourceOwner { pub resource: ResourceType, pub owner: Felt, } -impl FromStr for OwnerResource { +impl FromStr for ResourceOwner { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let parts: Vec<&str> = s.split(',').collect(); - let (resource_part, owner_part) = match parts.as_slice() { - [resource, owner] => (*resource, *owner), + let (resource, owner) = match parts.as_slice() { + [resource, owner] => (resource, owner), _ => anyhow::bail!( - "Owner and resource are expected to be comma separated: `sozo auth grant owner \ + "Resource and owner are expected to be comma separated: `sozo auth grant owner \ resource_type:resource_name,0x1234`" ), }; - let owner = Felt::from_hex(owner_part) - .map_err(|_| anyhow::anyhow!("Invalid owner address: {}", owner_part))?; + let owner = Felt::from_hex(owner) + .map_err(|_| anyhow::anyhow!("Invalid owner address: {}", owner))?; - let resource_parts = resource_part.split_once(':'); - let resource = match resource_parts { - Some(("contract", name)) => ResourceType::Contract(name.to_string()), - Some(("model", name)) => { - let model = cairo_utils::str_to_felt(name) - .map_err(|_| anyhow::anyhow!("Invalid model name: {}", name))?; - ResourceType::Model(model) - } - _ => anyhow::bail!( - "Resource is expected to be in the format `resource_type:resource_name`: `sozo \ - auth grant owner resource_type:resource_name,0x1234`" - ), - }; + let resource = ResourceType::from_str(resource)?; - Ok(OwnerResource { owner, resource }) + Ok(ResourceOwner { owner, resource }) } } pub async fn grant_writer<'a, A>( ui: &'a Ui, world: &WorldContract, - models_contracts: Vec, + new_writers: Vec, txn_config: TxnConfig, + default_namespace: &str, ) -> Result<()> where A: ConnectedAccount + Sync + Send, @@ -99,30 +111,12 @@ where { let mut calls = Vec::new(); - let world_reader = WorldContractReader::new(world.address, world.account.provider()) - .with_block(BlockId::Tag(BlockTag::Pending)); - - // TODO: Is some models have version 0 (using the name of the struct instead of the selector), - // we're not able to distinguish that. - // Should we add the version into the `ModelContract` struct? Can we always know that? - for mc in models_contracts { - let model_name = parse_cairo_short_string(&mc.model)?; - let model_selector = get_selector_from_name(&model_name)?; - - match world_reader.model_reader(&model_name).await { - Ok(_) => { - let contract = utils::get_contract_address(world, mc.contract).await?; - calls.push(world.grant_writer_getcall(&model_selector, &contract.into())); - } - - Err(ModelError::ModelNotFound) => { - ui.print(format!("Unknown model '{}' => IGNORED", model_name)); - } - - Err(err) => { - return Err(err.into()); - } - } + for new_writer in new_writers { + let resource_selector = + get_resource_selector(ui, world, &new_writer.resource, default_namespace).await?; + let contract_address = + utils::get_contract_address(world, new_writer.tag_or_address).await?; + calls.push(world.grant_writer_getcall(&resource_selector, &contract_address.into())); } if !calls.is_empty() { @@ -149,30 +143,19 @@ where pub async fn grant_owner( ui: &Ui, world: &WorldContract, - owners_resources: Vec, + new_owners: Vec, txn_config: TxnConfig, + default_namespace: &str, ) -> Result<()> where A: ConnectedAccount + Sync + Send + 'static, { let mut calls = Vec::new(); - for or in owners_resources { - let resource = match &or.resource { - ResourceType::Model(name) => { - // TODO: Is some models have version 0 (using the name of the struct instead of the - // selector), we're not able to distinguish that. - // Should we add the version into the `ModelContract` struct? Can we always know - // that? - let model_name = parse_cairo_short_string(name)?; - get_selector_from_name(&model_name)? - } - ResourceType::Contract(name_or_address) => { - utils::get_contract_address(world, name_or_address.clone()).await? - } - }; - - calls.push(world.grant_owner_getcall(&or.owner.into(), &resource)); + for new_owner in new_owners { + let resource_selector = + get_resource_selector(ui, world, &new_owner.resource, default_namespace).await?; + calls.push(world.grant_owner_getcall(&new_owner.owner.into(), &resource_selector)); } let res = world @@ -197,38 +180,21 @@ where pub async fn revoke_writer( ui: &Ui, world: &WorldContract, - models_contracts: Vec, + new_writers: Vec, txn_config: TxnConfig, + default_namespace: &str, ) -> Result<()> where A: ConnectedAccount + Sync + Send + 'static, { let mut calls = Vec::new(); - let mut world_reader = WorldContractReader::new(world.address, world.account.provider()); - world_reader.set_block(BlockId::Tag(BlockTag::Pending)); - - for mc in models_contracts { - // TODO: Is some models have version 0 (using the name of the struct instead of the - // selector), we're not able to distinguish that. - // Should we add the version into the `ModelContract` struct? Can we always know that? - let model_name = parse_cairo_short_string(&mc.model)?; - let model_selector = get_selector_from_name(&model_name)?; - - match world_reader.model_reader(&model_name).await { - Ok(_) => { - let contract = utils::get_contract_address(world, mc.contract).await?; - calls.push(world.revoke_writer_getcall(&model_selector, &contract.into())); - } - - Err(ModelError::ModelNotFound) => { - ui.print(format!("Unknown model '{}' => IGNORED", model_name)); - } - - Err(err) => { - return Err(err.into()); - } - } + for new_writer in new_writers { + let resource_selector = + get_resource_selector(ui, world, &new_writer.resource, default_namespace).await?; + let contract_address = + utils::get_contract_address(world, new_writer.tag_or_address).await?; + calls.push(world.revoke_writer_getcall(&resource_selector, &contract_address.into())); } if !calls.is_empty() { @@ -255,30 +221,19 @@ where pub async fn revoke_owner( ui: &Ui, world: &WorldContract, - owners_resources: Vec, + new_owners: Vec, txn_config: TxnConfig, + default_namespace: &str, ) -> Result<()> where A: ConnectedAccount + Sync + Send + 'static, { let mut calls = Vec::new(); - for or in owners_resources { - let resource = match &or.resource { - ResourceType::Model(name) => { - // TODO: Is some models have version 0 (using the name of the struct instead of the - // selector), we're not able to distinguish that. - // Should we add the version into the `ModelContract` struct? Can we always know - // that? - let model_name = parse_cairo_short_string(name)?; - get_selector_from_name(&model_name)? - } - ResourceType::Contract(name_or_address) => { - utils::get_contract_address(world, name_or_address.clone()).await? - } - }; - - calls.push(world.revoke_owner_getcall(&or.owner.into(), &resource)); + for new_owner in new_owners { + let resource_selector = + get_resource_selector(ui, world, &new_owner.resource, default_namespace).await?; + calls.push(world.revoke_owner_getcall(&new_owner.owner.into(), &resource_selector)); } let res = world @@ -299,3 +254,50 @@ where Ok(()) } + +async fn get_resource_selector( + ui: &Ui, + world: &WorldContract, + resource: &ResourceType, + default_namespace: &str, +) -> Result +where + A: ConnectedAccount + Sync + Send, + ::SignError: 'static, +{ + let world_reader = WorldContractReader::new(world.address, world.account.provider()) + .with_block(BlockId::Tag(BlockTag::Pending)); + + let resource_selector = match resource { + ResourceType::Contract(tag_or_address) => { + let tag_or_address = if tag_or_address.starts_with("0x") { + tag_or_address.to_string() + } else { + ensure_namespace(tag_or_address, default_namespace) + }; + utils::get_contract_address(world, tag_or_address).await? + } + ResourceType::Model(tag_or_name) => { + // TODO: Is some models have version 0 (using the name of the struct instead of the + // selector), we're not able to distinguish that. + // Should we add the version into the `ModelContract` struct? Can we always know that? + let tag = ensure_namespace(tag_or_name, default_namespace); + + // be sure that the model exists + match world_reader.model_reader_with_tag(&tag).await { + Err(ModelError::ModelNotFound) => { + ui.print(format!("Unknown model '{}' => IGNORED", tag)); + } + Err(err) => { + return Err(err.into()); + } + _ => {} + }; + + compute_model_selector_from_tag(&tag) + } + ResourceType::Namespace(name) => compute_bytearray_hash(name), + }; + + Ok(resource_selector) +} diff --git a/crates/sozo/ops/src/call.rs b/crates/sozo/ops/src/call.rs index 3f3a125f40..251cc07819 100644 --- a/crates/sozo/ops/src/call.rs +++ b/crates/sozo/ops/src/call.rs @@ -8,12 +8,12 @@ use crate::utils::{get_contract_address_from_reader, parse_block_id}; pub async fn call( world_reader: WorldContractReader

, - contract: String, + tag_or_address: String, entrypoint: String, calldata: Vec, block_id: Option, ) -> Result<()> { - let contract_address = get_contract_address_from_reader(&world_reader, contract).await?; + let contract_address = get_contract_address_from_reader(&world_reader, tag_or_address).await?; let block_id = if let Some(block_id) = block_id { parse_block_id(block_id)? } else { diff --git a/crates/sozo/ops/src/events.rs b/crates/sozo/ops/src/events.rs index 4c7073929a..4092613eb0 100644 --- a/crates/sozo/ops/src/events.rs +++ b/crates/sozo/ops/src/events.rs @@ -2,10 +2,15 @@ use std::collections::{HashMap, VecDeque}; use std::fs; use anyhow::{anyhow, Result}; +use cainome::cairo_serde::{ByteArray, CairoSerde}; use cainome::parser::tokens::{CompositeInner, CompositeInnerKind, CoreBasic, Token}; use cainome::parser::AbiParser; use camino::Utf8PathBuf; -use dojo_world::manifest::{AbiFormat, DeploymentManifest, ManifestMethods, MANIFESTS_DIR}; +use dojo_world::contracts::naming::get_filename_from_tag; +use dojo_world::manifest::{ + AbiFormat, DeploymentManifest, ManifestMethods, BASE_CONTRACT_TAG, MANIFESTS_DIR, TARGET_DIR, + WORLD_CONTRACT_TAG, +}; use starknet::core::types::{BlockId, EventFilter, Felt}; use starknet::core::utils::{parse_cairo_short_string, starknet_keccak}; use starknet::providers::jsonrpc::HttpTransport; @@ -33,15 +38,13 @@ pub async fn parse( continuation_token: Option, event_filter: EventFilter, json: bool, - manifest_dir: &Utf8PathBuf, + project_dir: &Utf8PathBuf, profile_name: &str, ) -> Result<()> { let events_map = if !json { - let deployed_manifest = manifest_dir - .join(MANIFESTS_DIR) - .join(profile_name) - .join("manifest") - .with_extension("toml"); + let manifest_dir = project_dir.join(MANIFESTS_DIR).join(profile_name); + let target_dir = project_dir.join(TARGET_DIR).join(profile_name); + let deployed_manifest = manifest_dir.join("manifest").with_extension("toml"); if !deployed_manifest.exists() { return Err(anyhow!("Run scarb migrate before running this command")); @@ -49,7 +52,8 @@ pub async fn parse( Some(extract_events( &DeploymentManifest::load_from_path(&deployed_manifest)?, - manifest_dir, + &manifest_dir, + &target_dir, )?) } else { None @@ -74,7 +78,8 @@ fn is_event(token: &Token) -> bool { fn extract_events( manifest: &DeploymentManifest, - manifest_dir: &Utf8PathBuf, + project_dir: &Utf8PathBuf, + target_dir: &Utf8PathBuf, ) -> Result>> { fn process_abi( events: &mut HashMap>, @@ -102,24 +107,26 @@ fn extract_events( for contract in &manifest.contracts { if let Some(AbiFormat::Path(abi_path)) = contract.inner.abi() { - let full_abi_path = manifest_dir.join(abi_path); + let full_abi_path = project_dir.join(abi_path); process_abi(&mut events_map, &full_abi_path)?; } } for model in &manifest.models { if let Some(AbiFormat::Path(abi_path)) = model.inner.abi() { - let full_abi_path = manifest_dir.join(abi_path); + let full_abi_path = project_dir.join(abi_path); process_abi(&mut events_map, &full_abi_path)?; } } // Read the world and base ABI from scarb artifacts as the // manifest does not include them (at least base is not included). - let world_abi_path = manifest_dir.join("target/dev/dojo::world::world.json"); + let world_abi_path = + target_dir.join(format!("{}.json", get_filename_from_tag(WORLD_CONTRACT_TAG))); process_abi(&mut events_map, &world_abi_path)?; - let base_abi_path = manifest_dir.join("target/dev/dojo::base::base.json"); + let base_abi_path = + target_dir.join(format!("{}.json", get_filename_from_tag(BASE_CONTRACT_TAG))); process_abi(&mut events_map, &base_abi_path)?; Ok(events_map) @@ -208,6 +215,16 @@ fn process_inners( let formatted_value = match &inner.token { Token::CoreBasic(ref cb) => parse_core_basic(cb, &value, true)?, + Token::Composite(c) => { + if c.type_path.eq("core::byte_array::ByteArray") { + data.push_front(value); + let bytearray = ByteArray::cairo_deserialize(data.as_mut_slices().0, 0)?; + data.drain(0..ByteArray::cairo_serialized_size(&bytearray)); + ByteArray::to_string(&bytearray)? + } else { + return Err(anyhow!("Unhandled Composite token")); + } + } Token::Array(ref array) => { let length = value .to_string() @@ -244,7 +261,7 @@ fn process_inners( mod tests { use cainome::parser::tokens::{Array, Composite, CompositeInner, CompositeType}; use camino::Utf8Path; - use dojo_world::manifest::{BaseManifest, BASE_DIR}; + use dojo_world::manifest::{BaseManifest, BASE_DIR, WORLD_QUALIFIED_PATH}; use starknet::core::types::EmittedEvent; use super::*; @@ -252,22 +269,23 @@ mod tests { #[test] fn extract_events_work_as_expected() { let profile_name = "dev"; - let manifest_dir = Utf8Path::new("../../../examples/spawn-and-move").to_path_buf(); - let manifest = BaseManifest::load_from_path( - &manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(BASE_DIR), - ) - .unwrap() - .into(); - let result = extract_events(&manifest, &manifest_dir).unwrap(); + let project_dir = Utf8Path::new("../../../examples/spawn-and-move").to_path_buf(); + let manifest_dir = project_dir.join(MANIFESTS_DIR).join(profile_name); + println!("manifest_dir {:?}", manifest_dir); + let target_dir = project_dir.join(TARGET_DIR).join(profile_name); + println!("target dir {:?}", target_dir); + let manifest = BaseManifest::load_from_path(&manifest_dir.join(BASE_DIR)).unwrap().into(); + + let result = extract_events(&manifest, &project_dir, &target_dir).unwrap(); // we are just collecting all events from manifest file so just verifying count should work - assert_eq!(result.len(), 15); + assert_eq!(result.len(), 16); } #[test] fn test_core_basic() { let composite = Composite { - type_path: "dojo::world::world::TestEvent".to_string(), + type_path: format!("{WORLD_QUALIFIED_PATH}::TestEvent"), inners: vec![ CompositeInner { index: 0, @@ -375,10 +393,11 @@ mod tests { transaction_hash: Felt::from_hex("0x789").unwrap(), }; - let expected_output = "Event name: dojo::world::world::TestEvent\nfelt252: 0x5465737431 \ - \"Test1\"\nbool: true\nu8: 1\nu16: 2\nu32: 3\nu64: 4\nu128: \ - 5\nusize: 6\nclass_hash: 0x54657374\ncontract_address: 0x54657374\n" - .to_string(); + let expected_output = format!( + "Event name: {WORLD_QUALIFIED_PATH}::TestEvent\nfelt252: 0x5465737431 \ + \"Test1\"\nbool: true\nu8: 1\nu16: 2\nu32: 3\nu64: 4\nu128: 5\nusize: 6\nclass_hash: \ + 0x54657374\ncontract_address: 0x54657374\n" + ); let actual_output_option = parse_event(event, &events_map).expect("Failed to parse event"); @@ -391,7 +410,7 @@ mod tests { #[test] fn test_array() { let composite = Composite { - type_path: "dojo::world::world::StoreDelRecord".to_string(), + type_path: format!("{WORLD_QUALIFIED_PATH}::StoreDelRecord"), inners: vec![ CompositeInner { index: 0, @@ -440,9 +459,10 @@ mod tests { transaction_hash: Felt::from_hex("0x789").unwrap(), }; - let expected_output = "Event name: dojo::world::world::StoreDelRecord\ntable: 0x54657374 \ - \"Test\"\nkeys: [0x5465737431, 0x5465737432, 0x5465737433]\n" - .to_string(); + let expected_output = format!( + "Event name: {WORLD_QUALIFIED_PATH}::StoreDelRecord\ntable: 0x54657374 \ + \"Test\"\nkeys: [0x5465737431, 0x5465737432, 0x5465737433]\n" + ); let actual_output_option = parse_event(event, &events_map).expect("Failed to parse event"); @@ -455,7 +475,7 @@ mod tests { #[test] fn test_custom_event() { let composite = Composite { - type_path: "dojo::world::world::CustomEvent".to_string(), + type_path: format!("{WORLD_QUALIFIED_PATH}::CustomEvent"), inners: vec![ CompositeInner { index: 0, @@ -514,9 +534,10 @@ mod tests { transaction_hash: Felt::from_hex("0x789").unwrap(), }; - let expected_output = "Event name: dojo::world::world::CustomEvent\nkey_1: 3\nkey_2: \ - 0x5465737431 \"Test1\"\ndata_1: 1\ndata_2: 2\n" - .to_string(); + let expected_output = format!( + "Event name: {WORLD_QUALIFIED_PATH}::CustomEvent\nkey_1: 3\nkey_2: 0x5465737431 \ + \"Test1\"\ndata_1: 1\ndata_2: 2\n" + ); let actual_output_option = parse_event(event, &events_map).expect("Failed to parse event"); @@ -529,7 +550,7 @@ mod tests { #[test] fn test_zero_felt() { let composite = Composite { - type_path: "dojo::world::world::StoreDelRecord".to_string(), + type_path: format!("{WORLD_QUALIFIED_PATH}::StoreDelRecord"), inners: vec![ CompositeInner { index: 0, @@ -578,9 +599,10 @@ mod tests { transaction_hash: Felt::from_hex("0x789").unwrap(), }; - let expected_output = "Event name: dojo::world::world::StoreDelRecord\ntable: 0x0\nkeys: \ - [0x0, 0x1, 0x2]\n" - .to_string(); + let expected_output = format!( + "Event name: {WORLD_QUALIFIED_PATH}::StoreDelRecord\ntable: 0x0\nkeys: [0x0, 0x1, \ + 0x2]\n" + ); let actual_output_option = parse_event(event, &events_map).expect("Failed to parse event"); diff --git a/crates/sozo/ops/src/execute.rs b/crates/sozo/ops/src/execute.rs index 5fc69a9727..24dd6c6ee2 100644 --- a/crates/sozo/ops/src/execute.rs +++ b/crates/sozo/ops/src/execute.rs @@ -11,7 +11,7 @@ use crate::utils; pub async fn execute( ui: &Ui, - contract: String, + tag_or_address: String, entrypoint: String, calldata: Vec, world: &WorldContract, @@ -20,7 +20,7 @@ pub async fn execute( where A: ConnectedAccount + Sync + Send + 'static, { - let contract_address = utils::get_contract_address(world, contract).await?; + let contract_address = utils::get_contract_address(world, tag_or_address).await?; let res = world .account .execute_v1(vec![Call { diff --git a/crates/sozo/ops/src/migration/auto_auth.rs b/crates/sozo/ops/src/migration/auto_auth.rs index df213e144c..5e040f8ea5 100644 --- a/crates/sozo/ops/src/migration/auto_auth.rs +++ b/crates/sozo/ops/src/migration/auto_auth.rs @@ -1,5 +1,7 @@ +use std::str::FromStr; + use anyhow::Result; -use dojo_world::contracts::{cairo_utils, WorldContract}; +use dojo_world::contracts::WorldContract; use dojo_world::manifest::BaseManifest; use dojo_world::migration::TxnConfig; use scarb::core::Workspace; @@ -8,7 +10,7 @@ use starknet::accounts::ConnectedAccount; use super::ui::MigrationUi; use super::MigrationOutput; -use crate::auth::{grant_writer, ModelContract}; +use crate::auth::{grant_writer, ResourceType, ResourceWriter}; pub async fn auto_authorize( ws: &Workspace<'_>, @@ -16,6 +18,7 @@ pub async fn auto_authorize( txn_config: &TxnConfig, local_manifest: &BaseManifest, migration_output: &MigrationOutput, + default_namespace: &str, ) -> Result<()> where A: ConnectedAccount + Sync + Send, @@ -26,15 +29,15 @@ where ui.print(" "); ui.print_step(6, "🖋️", "Authorizing Models to Systems (based on overlay)..."); ui.print(" "); - let models_contracts = compute_models_contracts(&ui, local_manifest, migration_output)?; - grant_writer(&ui, world, models_contracts, *txn_config).await + let new_writers = compute_writers(&ui, local_manifest, migration_output)?; + grant_writer(&ui, world, new_writers, *txn_config, default_namespace).await } -pub fn compute_models_contracts( +pub fn compute_writers( ui: &Ui, local_manifest: &BaseManifest, migration_output: &MigrationOutput, -) -> Result> { +) -> Result> { let mut res = vec![]; let local_contracts = &local_manifest.contracts; @@ -43,21 +46,21 @@ pub fn compute_models_contracts( // Find that contract from local_manifest based on its name. let contract = local_contracts .iter() - .find(|c| migrated_contract.name == c.name) + .find(|c| migrated_contract.tag == c.inner.tag) .expect("we know this contract exists"); ui.print_sub(format!( "Authorizing {} for Models: {:?}", - contract.name, contract.inner.writes + contract.inner.tag, contract.inner.writes )); - // Read all the models that its supposed to write and collect them in a Vec + // Read all the models that its supposed to write and collect them in a Vec // so we can call `grant_writer` on all of them. - for model in &contract.inner.writes { - let model = cairo_utils::str_to_felt(model)?; - let contract_addr_str = format!("{:#x}", migrated_contract.contract_address); + for model_tag in &contract.inner.writes { + let resource = ResourceType::from_str(format!("model:{model_tag}").as_str())?; + let tag_or_address = format!("{:#x}", migrated_contract.contract_address); - res.push(ModelContract { model, contract: contract_addr_str }); + res.push(ResourceWriter { resource, tag_or_address }); } } diff --git a/crates/sozo/ops/src/migration/migrate.rs b/crates/sozo/ops/src/migration/migrate.rs index d948f9305c..e2fcfcecbc 100644 --- a/crates/sozo/ops/src/migration/migrate.rs +++ b/crates/sozo/ops/src/migration/migrate.rs @@ -1,8 +1,12 @@ use std::path::Path; use anyhow::{anyhow, bail, Context, Result}; +use cainome::cairo_serde::ByteArray; use camino::Utf8PathBuf; use dojo_world::contracts::abi::world; +use dojo_world::contracts::naming::{ + compute_model_selector_from_tag, get_name_from_tag, get_namespace_from_tag, +}; use dojo_world::contracts::{cairo_utils, WorldContract}; use dojo_world::manifest::{ AbiFormat, BaseManifest, DeploymentManifest, DojoContract, DojoModel, Manifest, @@ -19,6 +23,7 @@ use dojo_world::migration::{ }; use dojo_world::utils::{TransactionExt, TransactionWaiter}; use futures::future; +use itertools::Itertools; use scarb::core::Workspace; use scarb_ui::Ui; use starknet::accounts::ConnectedAccount; @@ -213,11 +218,21 @@ where let world_address = strategy.world_address()?; + // register namespaces + let mut namespaces = + strategy.models.iter().map(|m| get_namespace_from_tag(&m.diff.tag)).collect::>(); + namespaces.extend( + strategy.contracts.iter().map(|c| get_namespace_from_tag(&c.diff.tag)).collect::>(), + ); + namespaces = namespaces.into_iter().unique().collect::>(); + + register_namespaces(&namespaces, world_address, &migrator, &ui, &txn_config).await?; + // Once Torii supports indexing arrays, we should declare and register the // ResourceMetadata model. match register_dojo_models(&strategy.models, world_address, &migrator, &ui, &txn_config).await { Ok(output) => { - migration_output.models = output.registered_model_names; + migration_output.models = output.registered_models; } Err(e) => { ui.anyhow(&e); @@ -305,11 +320,7 @@ where ui.print_step(7, "🌐", "Uploading metadata..."); ui.print(" "); - let dojo_metadata = if let Some(metadata) = dojo_metadata_from_workspace(ws) { - metadata - } else { - return Err(anyhow!("No current package with dojo metadata found.")); - }; + let dojo_metadata = dojo_metadata_from_workspace(ws)?; let mut ipfs = vec![]; let mut resources = vec![]; @@ -329,11 +340,11 @@ where // models if !migration_output.models.is_empty() { - for model_name in migration_output.models { - if let Some(m) = dojo_metadata.resources_artifacts.get(&model_name) { + for model_tag in migration_output.models { + if let Some(m) = dojo_metadata.resources_artifacts.get(&model_tag) { ipfs.push(upload_on_ipfs_and_create_resource( &ui, - get_selector_from_name(&model_name).expect("ASCII model name"), + compute_model_selector_from_tag(&model_tag), m.clone(), )); } @@ -345,7 +356,7 @@ where if !migrated_contracts.is_empty() { for contract in migrated_contracts { - if let Some(m) = dojo_metadata.resources_artifacts.get(&contract.name) { + if let Some(m) = dojo_metadata.resources_artifacts.get(&contract.tag) { ipfs.push(upload_on_ipfs_and_create_resource( &ui, contract.contract_address, @@ -388,10 +399,46 @@ where Ok(()) } +async fn register_namespaces( + namespaces: &[String], + world_address: Felt, + migrator: &A, + ui: &Ui, + txn_config: &TxnConfig, +) -> Result<()> +where + A: ConnectedAccount + Send + Sync, + ::Provider: Send, +{ + ui.print_header(format!("# Namespaces ({})", namespaces.len())); + + let world = WorldContract::new(world_address, migrator); + + let calls = namespaces + .iter() + .map(|ns| { + ui.print(italic_message(&ns).to_string()); + world.register_namespace_getcall(&ByteArray::from_string(ns).unwrap()) + }) + .collect::>(); + + let InvokeTransactionResult { transaction_hash } = + world.account.execute_v1(calls).send_with_cfg(txn_config).await.map_err(|e| { + ui.verbose(format!("{e:?}")); + anyhow!("Failed to register namespace to World: {e}") + })?; + + TransactionWaiter::new(transaction_hash, migrator.provider()).await?; + + ui.print(format!("All namespaces are registered at: {transaction_hash:#x}\n")); + + Ok(()) +} + async fn register_dojo_models( models: &[ClassMigration], world_address: Felt, - migrator: A, + migrator: &A, ui: &Ui, txn_config: &TxnConfig, ) -> Result @@ -403,17 +450,17 @@ where return Ok(RegisterOutput { transaction_hash: Felt::ZERO, declare_output: vec![], - registered_model_names: vec![], + registered_models: vec![], }); } ui.print_header(format!("# Models ({})", models.len())); let mut declare_output = vec![]; - let mut registered_model_names = vec![]; + let mut registered_models = vec![]; for c in models.iter() { - ui.print(italic_message(&c.diff.name).to_string()); + ui.print(italic_message(&c.diff.tag).to_string()); let res = c.declare(&migrator, txn_config).await; match res { @@ -433,7 +480,7 @@ where } Err(e) => { ui.verbose(format!("{e:?}")); - bail!("Failed to declare model {}: {e}", c.diff.name) + bail!("Failed to declare model {}: {e}", c.diff.tag) } } @@ -445,7 +492,7 @@ where let calls = models .iter() .map(|c| { - registered_model_names.push(c.diff.name.clone()); + registered_models.push(c.diff.tag.clone()); world.register_model_getcall(&c.diff.local_class_hash.into()) }) .collect::>(); @@ -458,9 +505,9 @@ where TransactionWaiter::new(transaction_hash, migrator.provider()).await?; - ui.print(format!("All models are registered at: {transaction_hash:#x}")); + ui.print(format!("All models are registered at: {transaction_hash:#x}\n")); - Ok(RegisterOutput { transaction_hash, declare_output, registered_model_names }) + Ok(RegisterOutput { transaction_hash, declare_output, registered_models }) } async fn register_dojo_contracts( @@ -483,8 +530,9 @@ where let mut deploy_output = vec![]; for contract in contracts { - let name = &contract.diff.name; - ui.print(italic_message(name).to_string()); + let tag = &contract.diff.tag; + ui.print(italic_message(tag).to_string()); + match contract .deploy_dojo_contract( world_address, @@ -526,7 +574,7 @@ where ui.print_sub(format!("Contract address: {:#x}", output.contract_address)); } deploy_output.push(Some(ContractMigrationOutput { - name: name.to_string(), + tag: tag.clone(), contract_address: output.contract_address, base_class_hash: output.base_class_hash, })); @@ -541,7 +589,7 @@ where Err(e) => { ui.verbose(format!("{e:?}")); return Err(anyhow!( - "Failed to migrate {name}: {e}. Please also verify init calldata is valid, if \ + "Failed to migrate {tag}: {e}. Please also verify init calldata is valid, if \ any." )); } @@ -577,7 +625,7 @@ where ui.print_hidden_sub(format!("Deploy transaction: {:#x}", val.transaction_hash)); - val.name = Some(contract.diff.name.clone()); + val.tag = Some(contract.diff.tag.clone()); Ok(ContractDeploymentOutput::Output(val)) } Err(MigrationError::ContractAlreadyDeployed(contract_address)) => { @@ -677,17 +725,17 @@ where .await { Ok(current_class_hash) if current_class_hash != contract.diff.local_class_hash => { - return format!("{}: Upgrade", contract.diff.name); + return format!("{}: Upgrade", contract.diff.tag); } Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => { - return format!("{}: Deploy", contract.diff.name); + return format!("{}: Deploy", contract.diff.tag); } Ok(_) => return "Already Deployed".to_string(), - Err(_) => return format!("{}: Deploy", contract.diff.name), + Err(_) => return format!("{}: Deploy", contract.diff.tag), } } } - format!("deploy {}", contract.diff.name) + format!("deploy {}", contract.diff.tag) } pub async fn print_strategy

( @@ -721,7 +769,7 @@ pub async fn print_strategy

( if !&strategy.models.is_empty() { ui.print_header(format!("# Models ({})", &strategy.models.len())); for m in &strategy.models { - ui.print(m.diff.name.to_string()); + ui.print(m.diff.tag.to_string()); ui.print_sub(format!("Class hash: {:#x}", m.diff.local_class_hash)); } } @@ -735,7 +783,7 @@ pub async fn print_strategy

( ui.print(op_name); ui.print_sub(format!("Class hash: {:#x}", c.diff.local_class_hash)); - let salt = generate_salt(&c.diff.name); + let salt = generate_salt(&get_name_from_tag(&c.diff.tag)); let contract_address = get_contract_address(salt, c.diff.base_class_hash, &[], world_address); ui.print_sub(format!("Contract address: {:#x}", contract_address)); @@ -759,10 +807,8 @@ pub async fn update_manifests_and_abis( let ui = ws.config().ui(); ui.print_step(5, "✨", "Updating manifests..."); - let deployed_path = - manifest_dir.join(MANIFESTS_DIR).join(profile_name).join("manifest").with_extension("toml"); - let deployed_path_json = - manifest_dir.join(MANIFESTS_DIR).join(profile_name).join("manifest").with_extension("json"); + let deployed_path = manifest_dir.join("manifest").with_extension("toml"); + let deployed_path_json = manifest_dir.join("manifest").with_extension("json"); let mut local_manifest: DeploymentManifest = local_manifest.into(); @@ -796,7 +842,7 @@ pub async fn update_manifests_and_abis( let local = local_manifest .contracts .iter_mut() - .find(|c| c.name == output.name) + .find(|c| c.inner.tag == output.tag) .expect("contract got migrated, means it should be present here"); local.inner.base_class_hash = output.base_class_hash; @@ -806,7 +852,7 @@ pub async fn update_manifests_and_abis( local_manifest.contracts.iter_mut().for_each(|contract| { if contract.inner.base_class_hash != Felt::ZERO { - let salt = generate_salt(&contract.name); + let salt = generate_salt(&get_name_from_tag(&contract.inner.tag)); contract.inner.address = Some(get_contract_address( salt, contract.inner.base_class_hash, @@ -832,11 +878,7 @@ async fn update_manifest_abis( manifest_dir: &Utf8PathBuf, profile_name: &str, ) { - fs::create_dir_all( - manifest_dir.join(MANIFESTS_DIR).join(profile_name).join(ABIS_DIR).join(DEPLOYMENTS_DIR), - ) - .await - .expect("Failed to create folder"); + fs::create_dir_all(manifest_dir).await.expect("Failed to create folder"); async fn inner_helper( manifest_dir: &Utf8PathBuf, @@ -845,37 +887,25 @@ async fn update_manifest_abis( ) where T: ManifestMethods, { - // for example: - // from: manifests/dev/abis/base/contract/dojo_world_world.json - // to: manifests/dev/abis/deployments/contract/dojo_world_world.json - // - // Unwraps in call to abi is safe because we always write abis for DojoContracts as relative - // path. - // In this relative path, we only what the root from - // ABI directory. - - // manifests/dev/abis/base/contract/dojo_world_world.json + // manifests/dev/abis/base/contract/dojo-world.json -> abis/base/contract/dojo-world.json let base_relative_path = manifest.inner.abi().unwrap().to_path().unwrap(); + let base_relative_path = base_relative_path + .strip_prefix(Utf8PathBuf::new().join(MANIFESTS_DIR).join(profile_name)) + .unwrap(); - // contract/dojo_world_world.json + // abis/base/dojo-world.json -> dojo-world.json let stripped_path = base_relative_path - .strip_prefix( - Utf8PathBuf::new() - .join(MANIFESTS_DIR) - .join(profile_name) - .join(ABIS_DIR) - .join(BASE_DIR), - ) + .strip_prefix(Utf8PathBuf::new().join(ABIS_DIR).join(BASE_DIR)) .unwrap(); - let deployed_relative_path = Utf8PathBuf::new() - .join(MANIFESTS_DIR) - .join(profile_name) - .join(ABIS_DIR) - .join(DEPLOYMENTS_DIR) - .join(stripped_path); + // abis/deployments/dojo-world.json + let deployed_relative_path = + Utf8PathBuf::new().join(ABIS_DIR).join(DEPLOYMENTS_DIR).join(stripped_path); + // /abis/base/dojo-world.json let full_base_path = manifest_dir.join(base_relative_path); + + // /abis/deployments/dojo-world.json let full_deployed_path = manifest_dir.join(deployed_relative_path.clone()); fs::create_dir_all(full_deployed_path.parent().unwrap()) diff --git a/crates/sozo/ops/src/migration/mod.rs b/crates/sozo/ops/src/migration/mod.rs index e234759dff..281bffc1f4 100644 --- a/crates/sozo/ops/src/migration/mod.rs +++ b/crates/sozo/ops/src/migration/mod.rs @@ -1,19 +1,20 @@ +use std::fs; use std::sync::Arc; -use std::{fs, io}; -use anyhow::{anyhow, Context, Result}; -use camino::Utf8PathBuf; +use anyhow::{anyhow, bail, Context, Result}; use dojo_world::contracts::WorldContract; use dojo_world::manifest::{ - DojoContract, DojoModel, Manifest, OverlayClass, OverlayDojoContract, OverlayDojoModel, - OverlayManifest, BASE_CONTRACT_NAME, BASE_DIR, CONTRACTS_DIR, MANIFESTS_DIR, MODELS_DIR, - OVERLAYS_DIR, WORLD_CONTRACT_NAME, + BaseManifest, OverlayClass, OverlayDojoContract, OverlayDojoModel, OverlayManifest, + BASE_CONTRACT_TAG, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR, WORLD_CONTRACT_TAG, }; +use dojo_world::metadata::get_default_namespace_from_ws; use dojo_world::migration::world::WorldDiff; use dojo_world::migration::{DeployOutput, TxnConfig, UpgradeOutput}; use scarb::core::Workspace; use starknet::accounts::ConnectedAccount; use starknet::core::types::Felt; +use starknet::core::utils::{cairo_short_string_to_felt, get_contract_address}; +use starknet_crypto::poseidon_hash_single; mod auto_auth; mod migrate; @@ -42,7 +43,7 @@ pub struct MigrationOutput { #[derive(Debug, Default, Clone)] pub struct ContractMigrationOutput { - pub name: String, + pub tag: String, pub contract_address: Felt, pub base_class_hash: Felt, } @@ -66,31 +67,53 @@ where let ui = ws.config().ui(); // its path to a file so `parent` should never return `None` - let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); + let root_dir = ws.manifest_path().parent().unwrap().to_path_buf(); let profile_name = ws.current_profile().expect("Scarb profile expected to be defined.").to_string(); - let profile_dir = manifest_dir.join(MANIFESTS_DIR).join(&profile_name); + let manifest_dir = root_dir.join(MANIFESTS_DIR).join(&profile_name); + let manifest_base_dir = manifest_dir.join(BASE_DIR); + let overlay_dir = root_dir.join(OVERLAYS_DIR).join(&profile_name); let target_dir = ws.target_dir().path_existent().unwrap(); let target_dir = target_dir.join(ws.config().profile().as_str()); + let default_namespace = get_default_namespace_from_ws(ws); + // Load local and remote World manifests. - let (local_manifest, remote_manifest) = - utils::load_world_manifests(&profile_dir, &account, world_address, &ui, skip_manifests) - .await - .map_err(|e| { - ui.error(e.to_string()); - anyhow!( - "\n Use `sozo clean` to clean your project.\nThen, rebuild your project with \ - `sozo build`.", - ) - })?; + let (local_manifest, remote_manifest) = utils::load_world_manifests( + &manifest_base_dir, + &overlay_dir, + &account, + world_address, + &ui, + skip_manifests, + ) + .await + .map_err(|e| { + ui.error(e.to_string()); + anyhow!( + "\n Use `sozo clean` to clean your project.\nThen, rebuild your project with `sozo \ + build`.", + ) + })?; + + let generated_world_address = get_world_address(&local_manifest, name)?; + if let Some(world_address) = world_address { + if world_address != generated_world_address { + bail!(format!( + "Calculated world address ({:#x}) doesn't match provided world address. If you \ + are deploying with custom seed make sure `world_address` is correctly configured \ + (or not set) `Scarb.toml`", + generated_world_address + )) + } + } // Calculate diff between local and remote World manifests. ui.print_step(2, "🧰", "Evaluating Worlds diff..."); let mut diff = WorldDiff::compute(local_manifest.clone(), remote_manifest.clone()); - diff.update_order()?; + diff.update_order(&default_namespace)?; let total_diffs = diff.count_diffs(); ui.print_sub(format!("Total diffs found: {total_diffs}")); @@ -153,7 +176,15 @@ where let account = Arc::new(account); let world = WorldContract::new(world_address, account.clone()); if let Some(migration_output) = migration_output { - match auto_authorize(ws, &world, &txn_config, &local_manifest, &migration_output).await + match auto_authorize( + ws, + &world, + &txn_config, + &local_manifest, + &migration_output, + &default_namespace, + ) + .await { Ok(()) => { ui.print_sub("Auto authorize completed successfully"); @@ -163,6 +194,7 @@ where } }; + // if !ws.config().offline() { upload_metadata(ws, &account, migration_output.clone(), txn_config).await?; } @@ -172,6 +204,23 @@ where Ok(()) } +fn get_world_address( + local_manifest: &dojo_world::manifest::BaseManifest, + name: &str, +) -> Result { + let name = cairo_short_string_to_felt(name)?; + let salt = poseidon_hash_single(name); + + let generated_world_address = get_contract_address( + salt, + local_manifest.world.inner.original_class_hash, + &[local_manifest.base.inner.class_hash], + Felt::ZERO, + ); + + Ok(generated_world_address) +} + #[allow(dead_code)] enum ContractDeploymentOutput { AlreadyDeployed(Felt), @@ -188,81 +237,53 @@ pub fn generate_overlays(ws: &Workspace<'_>) -> Result<()> { ws.current_profile().expect("Scarb profile expected to be defined.").to_string(); // its path to a file so `parent` should never return `None` - let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf(); - let profile_dir = manifest_dir.join(MANIFESTS_DIR).join(profile_name); - - let base_manifests = profile_dir.join(BASE_DIR); - - let world = OverlayClass { name: WORLD_CONTRACT_NAME.into(), original_class_hash: None }; - let base = OverlayClass { name: BASE_CONTRACT_NAME.into(), original_class_hash: None }; - - // generate default OverlayManifest from base manifests - let contracts = overlay_dojo_contracts_from_path(&base_manifests.join(CONTRACTS_DIR)) - .with_context(|| "Failed to build default DojoContract Overlays from path.")?; - let models = overlay_model_from_path(&base_manifests.join(MODELS_DIR)) - .with_context(|| "Failed to build default DojoModel Overlays from path.")?; - - let default_overlay = - OverlayManifest { world: Some(world), base: Some(base), contracts, models }; - - let overlay_path = profile_dir.join(OVERLAYS_DIR); - - // read existing OverlayManifest from path - let mut overlay_manifest = OverlayManifest::load_from_path(&overlay_path) - .with_context(|| "Failed to load OverlayManifest from path.")?; - - // merge them to get OverlayManifest which contains all the contracts and models from base - // manifests - overlay_manifest.merge(default_overlay); - - overlay_manifest - .write_to_path_nested(&overlay_path) - .with_context(|| "Failed to write OverlayManifest to path.")?; - - Ok(()) -} - -fn overlay_dojo_contracts_from_path(path: &Utf8PathBuf) -> Result> { - let mut elements = vec![]; - - let entries = path - .read_dir()? - .map(|entry| entry.map(|e| e.path())) - .collect::, io::Error>>()?; - - for path in entries { - if path.is_file() { - let manifest: Manifest = toml::from_str(&fs::read_to_string(path)?)?; - - let overlay_manifest = - OverlayDojoContract { name: manifest.name, ..Default::default() }; - elements.push(overlay_manifest); - } else { - continue; - } - } - - Ok(elements) -} + let root_dir = ws.manifest_path().parent().unwrap().to_path_buf(); + let manifest_base_dir = root_dir.join(MANIFESTS_DIR).join(&profile_name).join(BASE_DIR); + let overlay_dir = root_dir.join(OVERLAYS_DIR).join(&profile_name); + + let base_manifest = BaseManifest::load_from_path(&manifest_base_dir)?; + + let default_overlay = OverlayManifest { + world: Some(OverlayClass { + tag: WORLD_CONTRACT_TAG.to_string(), + original_class_hash: None, + }), + base: Some(OverlayClass { tag: BASE_CONTRACT_TAG.to_string(), original_class_hash: None }), + contracts: base_manifest + .contracts + .iter() + .map(|c| OverlayDojoContract { tag: c.inner.tag.clone(), ..Default::default() }) + .collect::>(), + models: base_manifest + .models + .iter() + .map(|m| OverlayDojoModel { tag: m.inner.tag.clone(), ..Default::default() }) + .collect::>(), + }; -fn overlay_model_from_path(path: &Utf8PathBuf) -> Result> { - let mut elements = vec![]; + if overlay_dir.exists() { + // read existing OverlayManifest from path + let mut overlay_manifest = OverlayManifest::load_from_path(&overlay_dir, &base_manifest) + .with_context(|| "Failed to load OverlayManifest from path.")?; - let entries = path - .read_dir()? - .map(|entry| entry.map(|e| e.path())) - .collect::, io::Error>>()?; + // merge them to get OverlayManifest which contains all the contracts and models from base + // manifests + overlay_manifest.merge(default_overlay); - for path in entries { - if path.is_file() { - let manifest: Manifest = toml::from_str(&fs::read_to_string(path)?)?; + // to avoid duplicated overlay manifests, existing overlays must be removed before being + // rewritten by `overlay_manifest.write_to_path_nested()` + fs::remove_dir_all(&overlay_dir)?; + fs::create_dir_all(&overlay_dir)?; - let overlay_manifest = OverlayDojoModel { name: manifest.name, ..Default::default() }; - elements.push(overlay_manifest); - } else { - continue; - } + overlay_manifest + .write_to_path_nested(&overlay_dir) + .with_context(|| "Failed to write OverlayManifest to path.")?; + } else { + fs::create_dir_all(&overlay_dir)?; + default_overlay + .write_to_path_nested(&overlay_dir) + .with_context(|| "Failed to write OverlayManifest to path.")?; } - Ok(elements) + Ok(()) } diff --git a/crates/sozo/ops/src/migration/utils.rs b/crates/sozo/ops/src/migration/utils.rs index 88a52a8e13..dc91f26c1f 100644 --- a/crates/sozo/ops/src/migration/utils.rs +++ b/crates/sozo/ops/src/migration/utils.rs @@ -1,8 +1,7 @@ use anyhow::{anyhow, Result}; use camino::Utf8PathBuf; use dojo_world::manifest::{ - AbstractManifestError, BaseManifest, DeploymentManifest, OverlayManifest, BASE_DIR, - OVERLAYS_DIR, + AbstractManifestError, BaseManifest, DeploymentManifest, OverlayManifest, }; use scarb_ui::Ui; use starknet::accounts::ConnectedAccount; @@ -12,9 +11,10 @@ use super::ui::MigrationUi; /// Loads: /// - `BaseManifest` from filesystem -/// - `DeployedManifest` from onchain dataa if `world_address` is `Some` +/// - `DeployedManifest` from onchain data if `world_address` is `Some` pub(super) async fn load_world_manifests( - profile_dir: &Utf8PathBuf, + manifest_dir: &Utf8PathBuf, + overlay_dir: &Utf8PathBuf, account: A, world_address: Option, ui: &Ui, @@ -26,16 +26,15 @@ where { ui.print_step(1, "🌎", "Building World state..."); - let mut local_manifest = BaseManifest::load_from_path(&profile_dir.join(BASE_DIR)) + let mut local_manifest = BaseManifest::load_from_path(manifest_dir) .map_err(|e| anyhow!("Fail to load local manifest file: {e}."))?; if let Some(skip_manifests) = skip_migration { - local_manifest.remove_items(skip_manifests); + local_manifest.remove_tags(skip_manifests); } - let overlay_path = profile_dir.join(OVERLAYS_DIR); - if overlay_path.exists() { - let overlay_manifest = OverlayManifest::load_from_path(&profile_dir.join(OVERLAYS_DIR)) + if overlay_dir.exists() { + let overlay_manifest = OverlayManifest::load_from_path(overlay_dir, &local_manifest) .map_err(|e| anyhow!("Fail to load overlay manifest file: {e}."))?; // merge user defined changes to base manifest diff --git a/crates/sozo/ops/src/model.rs b/crates/sozo/ops/src/model.rs index 8bf245c3ea..ed3a235234 100644 --- a/crates/sozo/ops/src/model.rs +++ b/crates/sozo/ops/src/model.rs @@ -12,14 +12,14 @@ use starknet::providers::JsonRpcClient; const INDENT: &str = " "; pub async fn model_class_hash( - name: String, + tag: String, world_address: Felt, 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 model = world_reader.model_reader_with_tag(&tag).await?; println!("{:#x}", model.class_hash()); @@ -27,14 +27,14 @@ pub async fn model_class_hash( } pub async fn model_contract_address( - name: String, + tag: String, world_address: Felt, 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 model = world_reader.model_reader_with_tag(&tag).await?; println!("{:#x}", model.contract_address()); @@ -42,14 +42,14 @@ pub async fn model_contract_address( } pub async fn model_layout( - name: String, + tag: String, world_address: Felt, 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 model = world_reader.model_reader_with_tag(&tag).await?; let layout = match model.layout().await { Ok(x) => x, Err(_) => anyhow::bail!( @@ -59,13 +59,13 @@ pub async fn model_layout( }; let schema = model.schema().await?; - deep_print_layout(&name, &layout, &schema); + deep_print_layout(&tag, &layout, &schema); Ok(()) } pub async fn model_schema( - name: String, + tag: String, world_address: Felt, provider: JsonRpcClient, to_json: bool, @@ -73,7 +73,7 @@ pub async fn model_schema( 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 model = world_reader.model_reader_with_tag(&tag).await?; let schema = model.schema().await?; if to_json { @@ -86,7 +86,7 @@ pub async fn model_schema( } pub async fn model_get( - name: String, + tag: String, keys: Vec, world_address: Felt, provider: JsonRpcClient, @@ -98,7 +98,7 @@ pub async fn model_get( 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 model = world_reader.model_reader_with_tag(&tag).await?; let schema = model.schema().await?; let values = model.entity_storage(&keys).await?; diff --git a/crates/sozo/ops/src/register.rs b/crates/sozo/ops/src/register.rs index 8a094d47e8..e6ca81436d 100644 --- a/crates/sozo/ops/src/register.rs +++ b/crates/sozo/ops/src/register.rs @@ -34,20 +34,20 @@ where } }; - let registered_models_names = manifest.models.iter().map(|m| m.name.as_str()); + let registered_models = manifest.models.iter().map(|m| m.inner.tag.clone()); let mut model_class_hashes = HashMap::new(); - for model_name in registered_models_names { - let read_model = world_reader.model_reader(model_name).await?; + for model_tag in registered_models { + let read_model = world_reader.model_reader_with_tag(&model_tag).await?; let class_hash = read_model.class_hash(); - model_class_hashes.insert(class_hash, model_name); + model_class_hashes.insert(class_hash, model_tag); } let mut models_to_register = Vec::new(); for input_model in models { - if let Some(model_name) = model_class_hashes.get(&input_model) { + if let Some(model_tag) = model_class_hashes.get(&input_model) { config.ui().print(format!( "\"{}\" model already registered with the class hash \"{:#x}\"", - model_name, input_model + model_tag, input_model )); } else { models_to_register.push(input_model); diff --git a/crates/sozo/ops/src/tests/auth.rs b/crates/sozo/ops/src/tests/auth.rs index 80df7be990..ef467cdf05 100644 --- a/crates/sozo/ops/src/tests/auth.rs +++ b/crates/sozo/ops/src/tests/auth.rs @@ -1,16 +1,18 @@ +use std::str::FromStr; + use dojo_world::contracts::world::WorldContract; use dojo_world::migration::TxnConfig; use katana_runner::KatanaRunner; use scarb_ui::{OutputFormat, Ui, Verbosity}; use starknet::accounts::{Account, ConnectedAccount}; use starknet::core::types::{BlockId, BlockTag}; -use starknet::core::utils::cairo_short_string_to_felt; use super::setup; -use crate::auth::{self, ModelContract, OwnerResource, ResourceType}; +use crate::auth::{self, ResourceOwner, ResourceType, ResourceWriter}; use crate::execute; -const ACTION_CONTRACT_NAME: &str = "dojo_examples::actions::actions"; +const ACTION_CONTRACT_NAME: &str = "dojo_examples-actions"; +const DEFAULT_NAMESPACE: &str = "dojo_examples"; #[tokio::test(flavor = "multi_thread")] async fn auth_grant_writer_ok() { @@ -29,14 +31,14 @@ async fn auth_grant_writer_ok() { // Account2 does not have the permission to write, but granting // writer to the actions contract allows the execution of it's systems by // any account. - let moves_mc = ModelContract { - model: cairo_short_string_to_felt("Moves").unwrap(), - contract: ACTION_CONTRACT_NAME.to_string(), + let moves_mc = ResourceWriter { + resource: ResourceType::from_str("model:Moves").unwrap(), + tag_or_address: ACTION_CONTRACT_NAME.to_string(), }; - let position_mc = ModelContract { - model: cairo_short_string_to_felt("Position").unwrap(), - contract: ACTION_CONTRACT_NAME.to_string(), + let position_mc = ResourceWriter { + resource: ResourceType::from_str("model:Position").unwrap(), + tag_or_address: ACTION_CONTRACT_NAME.to_string(), }; auth::grant_writer( @@ -44,6 +46,7 @@ async fn auth_grant_writer_ok() { &world, vec![moves_mc, position_mc], TxnConfig { wait: true, ..Default::default() }, + DEFAULT_NAMESPACE, ) .await .unwrap(); @@ -69,14 +72,14 @@ async fn auth_revoke_writer_ok() { // Account2 does not have the permission to write, but granting // writer to the actions contract allows the execution of it's systems by // any account. - let moves_mc = ModelContract { - model: cairo_short_string_to_felt("Moves").unwrap(), - contract: ACTION_CONTRACT_NAME.to_string(), + let moves_mc = ResourceWriter { + resource: ResourceType::from_str("model:Moves").unwrap(), + tag_or_address: ACTION_CONTRACT_NAME.to_string(), }; - let position_mc = ModelContract { - model: cairo_short_string_to_felt("Position").unwrap(), - contract: ACTION_CONTRACT_NAME.to_string(), + let position_mc = ResourceWriter { + resource: ResourceType::from_str("model:Position").unwrap(), + tag_or_address: ACTION_CONTRACT_NAME.to_string(), }; // Here we are granting the permission to write @@ -85,6 +88,7 @@ async fn auth_revoke_writer_ok() { &world, vec![moves_mc.clone(), position_mc.clone()], TxnConfig { wait: true, ..Default::default() }, + DEFAULT_NAMESPACE, ) .await .unwrap(); @@ -98,6 +102,7 @@ async fn auth_revoke_writer_ok() { &world, vec![moves_mc, position_mc], TxnConfig { wait: true, ..Default::default() }, + DEFAULT_NAMESPACE, ) .await .unwrap(); @@ -123,13 +128,13 @@ async fn auth_grant_owner_ok() { // Account2 does not have the permission to write, let's give this account // ownership of both models. - let moves = OwnerResource { - resource: ResourceType::Model(cairo_short_string_to_felt("Moves").unwrap()), + let moves = ResourceOwner { + resource: ResourceType::from_str("model:Moves").unwrap(), owner: account_2_addr, }; - let position = OwnerResource { - resource: ResourceType::Model(cairo_short_string_to_felt("Position").unwrap()), + let position = ResourceOwner { + resource: ResourceType::from_str("model:Position").unwrap(), owner: account_2_addr, }; @@ -138,6 +143,7 @@ async fn auth_grant_owner_ok() { &world, vec![moves, position], TxnConfig { wait: true, ..Default::default() }, + DEFAULT_NAMESPACE, ) .await .unwrap(); @@ -162,13 +168,13 @@ async fn auth_revoke_owner_ok() { // Account2 does not have the permission to write, let's give this account // ownership of both models. - let moves = OwnerResource { - resource: ResourceType::Model(cairo_short_string_to_felt("Moves").unwrap()), + let moves = ResourceOwner { + resource: ResourceType::from_str("model:Moves").unwrap(), owner: account_2_addr, }; - let position = OwnerResource { - resource: ResourceType::Model(cairo_short_string_to_felt("Position").unwrap()), + let position = ResourceOwner { + resource: ResourceType::from_str("model:Position").unwrap(), owner: account_2_addr, }; @@ -177,6 +183,7 @@ async fn auth_revoke_owner_ok() { &world, vec![moves.clone(), position.clone()], TxnConfig { wait: true, ..Default::default() }, + DEFAULT_NAMESPACE, ) .await .unwrap(); @@ -188,6 +195,7 @@ async fn auth_revoke_owner_ok() { &world, vec![moves, position], TxnConfig { wait: true, ..Default::default() }, + DEFAULT_NAMESPACE, ) .await .unwrap(); diff --git a/crates/sozo/ops/src/tests/call.rs b/crates/sozo/ops/src/tests/call.rs index 26c0f3802e..8265a04d42 100644 --- a/crates/sozo/ops/src/tests/call.rs +++ b/crates/sozo/ops/src/tests/call.rs @@ -9,8 +9,11 @@ use starknet::signers::LocalWallet; use super::setup; use crate::{call, utils}; -const CONTRACT_NAME: &str = "dojo_examples::actions::actions"; -const ENTRYPOINT: &str = "tile_terrain"; +const CONTRACT_TAG: &str = "dojo_examples-actions"; +const ENTRYPOINT: &str = "get_player_position"; + +// TODO: we should work on a lazy static init for the runner for all the call tests, +// as the state will not change, we only read and check the result. #[tokio::test] async fn call_with_bad_address() { @@ -65,7 +68,7 @@ async fn call_with_bad_entrypoint() { assert!( call::call( world_reader, - CONTRACT_NAME.to_string(), + CONTRACT_TAG.to_string(), "BadEntryPoint".to_string(), vec![Felt::ZERO, Felt::ZERO], None @@ -84,9 +87,15 @@ async fn call_with_bad_calldata() { let world_reader = WorldContractReader::new(world.address, provider); assert!( - call::call(world_reader, CONTRACT_NAME.to_string(), ENTRYPOINT.to_string(), vec![], None) - .await - .is_err() + call::call( + world_reader, + CONTRACT_TAG.to_string(), + ENTRYPOINT.to_string(), + vec![Felt::ZERO], + None + ) + .await + .is_err() ); } @@ -98,17 +107,11 @@ async fn call_with_contract_name() { let provider = sequencer.provider(); let world_reader = WorldContractReader::new(world.address, provider); - assert!( - call::call( - world_reader, - CONTRACT_NAME.to_string(), - ENTRYPOINT.to_string(), - vec![Felt::ZERO, Felt::ZERO], - None, - ) - .await - .is_ok() - ); + let r = + call::call(world_reader, CONTRACT_TAG.to_string(), ENTRYPOINT.to_string(), vec![], None) + .await; + + assert!(r.is_ok()); } #[tokio::test] @@ -121,7 +124,7 @@ async fn call_with_contract_address() { let contract_address = utils::get_contract_address::< SingleOwnerAccount, LocalWallet>, - >(&world, CONTRACT_NAME.to_string()) + >(&world, CONTRACT_TAG.to_string()) .await .unwrap(); @@ -130,7 +133,7 @@ async fn call_with_contract_address() { world_reader, format!("{:#x}", contract_address), ENTRYPOINT.to_string(), - vec![Felt::ZERO, Felt::ZERO], + vec![], None, ) .await diff --git a/crates/sozo/ops/src/tests/migration.rs b/crates/sozo/ops/src/tests/migration.rs index 16fd117229..50f07a04b3 100644 --- a/crates/sozo/ops/src/tests/migration.rs +++ b/crates/sozo/ops/src/tests/migration.rs @@ -1,16 +1,18 @@ +#![allow(dead_code)] use std::str; use cainome::cairo_serde::ContractAddress; use camino::Utf8Path; use dojo_test_utils::migration::prepare_migration_with_world_and_seed; +use dojo_world::contracts::naming::compute_model_selector_from_tag; use dojo_world::contracts::{WorldContract, WorldContractReader}; use dojo_world::manifest::{ BaseManifest, DeploymentManifest, OverlayManifest, BASE_DIR, MANIFESTS_DIR, OVERLAYS_DIR, - WORLD_CONTRACT_NAME, + WORLD_CONTRACT_TAG, }; use dojo_world::metadata::{ - dojo_metadata_from_workspace, ArtifactMetadata, DojoMetadata, Uri, WorldMetadata, - IPFS_CLIENT_URL, IPFS_PASSWORD, IPFS_USERNAME, + dojo_metadata_from_workspace, get_default_namespace_from_ws, ArtifactMetadata, DojoMetadata, + Uri, WorldMetadata, IPFS_CLIENT_URL, IPFS_PASSWORD, IPFS_USERNAME, }; use dojo_world::migration::strategy::{prepare_for_migration, MigrationMetadata}; use dojo_world::migration::world::WorldDiff; @@ -19,7 +21,6 @@ use futures::TryStreamExt; use ipfs_api_backend_hyper::{HyperBackend, IpfsApi, IpfsClient, TryFromUri}; use katana_runner::{KatanaRunner, KatanaRunnerConfig}; use starknet::core::types::{BlockId, BlockTag, Felt}; -use starknet::core::utils::get_selector_from_name; use starknet::macros::felt; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; @@ -28,6 +29,30 @@ use super::setup; use crate::migration::{auto_authorize, execute_strategy, upload_metadata}; use crate::utils::get_contract_address_from_reader; +#[tokio::test(flavor = "multi_thread")] +async fn default_migrate_no_dry_run() { + let config = setup::load_config(); + let ws = setup::setup_ws(&config); + + let sequencer = KatanaRunner::new().expect("Fail to start runner"); + + let mut account = sequencer.account(0); + account.set_block_id(BlockId::Tag(BlockTag::Pending)); + + let _ = crate::migration::migrate( + &ws, + None, + sequencer.url().to_string(), + account, + "dojo_examples", + false, + TxnConfig::init_wait(), + None, + ) + .await + .is_ok(); +} + #[tokio::test(flavor = "multi_thread")] async fn migrate_with_auto_mine() { let config = setup::load_config(); @@ -101,15 +126,15 @@ async fn metadata_calculated_properly() { let profile_name = ws.current_profile().unwrap().to_string(); let mut manifest = BaseManifest::load_from_path( - &base.to_path_buf().join(MANIFESTS_DIR).join(profile_name).join(BASE_DIR), + &base.to_path_buf().join(MANIFESTS_DIR).join(&profile_name).join(BASE_DIR), ) .unwrap(); - let overlay_manifest = - OverlayManifest::load_from_path(&base.join(MANIFESTS_DIR).join("dev").join(OVERLAYS_DIR)) - .unwrap(); - - manifest.merge(overlay_manifest); + let overlay_dir = base.join(OVERLAYS_DIR).join(&profile_name); + if overlay_dir.exists() { + let overlay_manifest = OverlayManifest::load_from_path(&overlay_dir, &manifest).unwrap(); + manifest.merge(overlay_manifest); + } let world = WorldDiff::compute(manifest, None); @@ -125,7 +150,7 @@ async fn metadata_calculated_properly() { for (key, value) in migration.metadata.iter() { match value { MigrationMetadata::Contract(c) => { - assert_eq!(key, &c.name); + assert_eq!(key, &c.tag); } } } @@ -172,16 +197,18 @@ async fn migration_with_correct_calldata_second_time_work_as_expected() { .await .expect("Failed to load remote manifest"); - let overlay = OverlayManifest::load_from_path( - &base.join(MANIFESTS_DIR).join(&profile_name).join(OVERLAYS_DIR), - ) - .expect("Failed to load overlay"); + let overlay_dir = base.join(OVERLAYS_DIR).join(profile_name); + if overlay_dir.exists() { + let overlay = OverlayManifest::load_from_path(&overlay_dir, &manifest) + .expect("Failed to load overlay"); - // adding correct calldata - manifest.merge(overlay); + // adding correct calldata + manifest.merge(overlay); + } + let default_namespace = get_default_namespace_from_ws(&ws); let mut world = WorldDiff::compute(manifest, Some(remote_manifest)); - world.update_order().expect("Failed to update order"); + world.update_order(&default_namespace).expect("Failed to update order"); let mut migration = prepare_for_migration( Some(world_address), @@ -244,8 +271,6 @@ async fn migration_from_remote() { assert_eq!(local_manifest.models.len(), remote_manifest.models.len()); } -// TODO: remove ignore once IPFS node is running. -#[ignore] #[tokio::test(flavor = "multi_thread")] async fn migrate_with_metadata() { let config = setup::load_config(); @@ -275,7 +300,7 @@ async fn migrate_with_metadata() { // check world metadata let resource = world_reader.metadata(&Felt::ZERO).call().await.unwrap(); - let element_name = WORLD_CONTRACT_NAME.to_string(); + let element_name = WORLD_CONTRACT_TAG.to_string(); let full_uri = resource.metadata_uri.to_string().unwrap(); let resource_bytes = get_ipfs_resource_data(&client, &element_name, &full_uri).await; @@ -289,34 +314,34 @@ async fn migrate_with_metadata() { assert_eq!(metadata.website, dojo_metadata.world.website, ""); assert_eq!(metadata.socials, dojo_metadata.world.socials, ""); - check_artifact_fields( - &client, - &metadata.artifacts, - &dojo_metadata.world.artifacts, - &element_name, - ) - .await; - + // TODO: uncomment when https://github.com/dojoengine/dojo/issues/2137 is fixed. + // check_artifact_fields( + // &client, + // &metadata.artifacts, + // &dojo_metadata.world.artifacts, + // &element_name, + // ) + // .await; // check model metadata - for m in migration.models { - let selector = get_selector_from_name(&m.diff.name).unwrap(); - check_artifact_metadata(&client, &world_reader, selector, &m.diff.name, &dojo_metadata) - .await; - } - + // for m in migration.models { + // let selector = compute_model_selector_from_tag(&m.diff.tag); + // check_artifact_metadata(&client, &world_reader, selector, &m.diff.tag, &dojo_metadata) + // .await; + // } // check contract metadata - for c in migration.contracts { - let contract_address = - get_contract_address_from_reader(&world_reader, c.diff.name.clone()).await.unwrap(); - check_artifact_metadata( - &client, - &world_reader, - contract_address, - &c.diff.name, - &dojo_metadata, - ) - .await; - } + // for c in migration.contracts { + // let contract_address = + // get_contract_address_from_reader(&world_reader, c.diff.tag.clone()).await.unwrap(); + // + // check_artifact_metadata( + // &client, + // &world_reader, + // contract_address, + // &c.diff.tag, + // &dojo_metadata, + // ) + // .await; + // } } #[tokio::test(flavor = "multi_thread")] @@ -332,12 +357,11 @@ async fn migrate_with_auto_authorize() { BaseManifest::load_from_path(&manifest_base.join(MANIFESTS_DIR).join("dev").join(BASE_DIR)) .unwrap(); - let overlay_manifest = OverlayManifest::load_from_path( - &manifest_base.join(MANIFESTS_DIR).join("dev").join(OVERLAYS_DIR), - ) - .unwrap(); - - manifest.merge(overlay_manifest); + let overlay_dir = manifest_base.join(OVERLAYS_DIR).join("dev"); + if overlay_dir.exists() { + let overlay_manifest = OverlayManifest::load_from_path(&overlay_dir, &manifest).unwrap(); + manifest.merge(overlay_manifest); + } let sequencer = KatanaRunner::new().expect("Fail to start runner"); @@ -351,7 +375,9 @@ async fn migrate_with_auto_authorize() { let world_address = migration.world_address().expect("must be present"); let world = WorldContract::new(world_address, account); - let res = auto_authorize(&ws, &world, &txn_config, &manifest, &output).await; + let default_namespace = get_default_namespace_from_ws(&ws); + let res = + auto_authorize(&ws, &world, &txn_config, &manifest, &output, &default_namespace).await; assert!(res.is_ok()); let provider = sequencer.provider(); @@ -360,12 +386,12 @@ async fn migrate_with_auto_authorize() { // check contract metadata for c in migration.contracts { let contract_address = - get_contract_address_from_reader(&world_reader, c.diff.name.clone()).await.unwrap(); + get_contract_address_from_reader(&world_reader, c.diff.tag.clone()).await.unwrap(); - let contract = manifest.contracts.iter().find(|a| a.name == c.diff.name).unwrap(); + let contract = manifest.contracts.iter().find(|a| a.inner.tag == c.diff.tag).unwrap(); for model in &contract.inner.writes { - let model_selector = get_selector_from_name(model).unwrap(); + let model_selector = compute_model_selector_from_tag(model); let contract_address = ContractAddress(contract_address); let is_writer = world_reader.is_writer(&model_selector, &contract_address).call().await.unwrap(); @@ -377,27 +403,25 @@ async fn migrate_with_auto_authorize() { #[tokio::test(flavor = "multi_thread")] async fn migration_with_mismatching_world_address_and_seed() { let config = setup::load_config(); + let ws = setup::setup_ws(&config); let base_dir = config.manifest_path().parent().unwrap().to_path_buf(); let target_dir = base_dir.join("target").join("dev"); - let result = prepare_migration_with_world_and_seed( + let default_namespace = get_default_namespace_from_ws(&ws); + + let strategy = prepare_migration_with_world_and_seed( base_dir, target_dir, - Some(felt!("0x1")), + Some(Felt::ONE), "sozo_test", - ); - - assert!(result.is_err()); - - let error_message = result.unwrap_err().to_string(); + &default_namespace, + ) + .unwrap(); - assert_eq!( - error_message, - "Calculated world address doesn't match provided world address.\nIf you are deploying \ - with custom seed make sure `world_address` is correctly configured (or not set) \ - `Scarb.toml`" - ); + // The strategy.world has it's address set with the seed directly, and not + // from the world address provided by the user. + assert_ne!(strategy.world_address.unwrap(), strategy.world.unwrap().contract_address); } /// Get the hash from a IPFS URI @@ -428,44 +452,34 @@ fn get_hash_from_uri(uri: &str) -> String { /// * `uri` - the IPFS URI of the abi field. /// * `expected_uri` - the URI of the expected file. /// * `field_name` - the field name. -/// * `element_name` - the fully qualified name of the element linked to this field. +/// * `tag` - the tag of the element linked to this field. async fn check_file_field( client: &HyperBackend, uri: &Uri, expected_uri: &Uri, field_name: String, - element_name: &String, + tag: &String, ) { if let Uri::Ipfs(uri) = uri { - let resource_data = get_ipfs_resource_data(client, element_name, uri).await; - assert!( - !resource_data.is_empty(), - "{field_name} IPFS artifact for {} is empty", - element_name - ); + let resource_data = get_ipfs_resource_data(client, tag, uri).await; + assert!(!resource_data.is_empty(), "{field_name} IPFS artifact for {} is empty", tag); if let Uri::File(f) = expected_uri { let file_content = std::fs::read_to_string(f).unwrap(); let resource_content = std::str::from_utf8(&resource_data).unwrap_or_else(|_| { - panic!( - "Unable to stringify resource data for field '{}' of {}", - field_name, element_name - ) + panic!("Unable to stringify resource data for field '{}' of {}", field_name, tag) }); assert!( file_content.eq(&resource_content), "local '{field_name}' content differs from the one uploaded on IPFS for {}", - element_name + tag ); } else { - panic!( - "The field '{field_name}' of {} is not a file (Should never happen !)", - element_name - ); + panic!("The field '{field_name}' of {} is not a file (Should never happen !)", tag); } } else { - panic!("The '{field_name}' field is not an IPFS artifact for {}", element_name); + panic!("The '{field_name}' field is not an IPFS artifact for {}", tag); } } @@ -474,16 +488,16 @@ async fn check_file_field( /// # Arguments /// /// * `raw_data` - resource data as bytes. -/// * `element_name` - name of the element linked to this resource. +/// * `tag` - tag of the element linked to this resource. /// /// # Returns /// /// A [`ArtifactMetadata`] object. -fn resource_bytes_to_metadata(raw_data: &[u8], element_name: &String) -> ArtifactMetadata { +fn resource_bytes_to_metadata(raw_data: &[u8], tag: &String) -> ArtifactMetadata { let data = std::str::from_utf8(raw_data) - .unwrap_or_else(|_| panic!("Unable to stringify raw metadata for {}", element_name)); + .unwrap_or_else(|_| panic!("Unable to stringify raw metadata for {}", tag)); serde_json::from_str(data) - .unwrap_or_else(|_| panic!("Unable to deserialize metadata for {}", element_name)) + .unwrap_or_else(|_| panic!("Unable to deserialize metadata for {}", tag)) } /// Convert resource bytes to a WorldMetadata object. @@ -508,21 +522,17 @@ fn resource_bytes_to_world_metadata(raw_data: &[u8], element_name: &String) -> W /// # Arguments /// /// * `client` - a IPFS client. -/// * `element_name` - the name of the element (model or contract) linked to this artifact. +/// * `tag` - the tag of the element (model or contract) linked to this artifact. /// * `uri` - the IPFS resource URI. /// /// # Returns /// /// A [`Vec`] containing the resource content as bytes. -async fn get_ipfs_resource_data( - client: &HyperBackend, - element_name: &String, - uri: &String, -) -> Vec { +async fn get_ipfs_resource_data(client: &HyperBackend, tag: &String, uri: &String) -> Vec { let hash = get_hash_from_uri(uri); let res = client.cat(&hash).map_ok(|chunk| chunk.to_vec()).try_concat().await; - assert!(res.is_ok(), "Unable to read the IPFS artifact {} for {}", uri, element_name); + assert!(res.is_ok(), "Unable to read the IPFS artifact {} for {}", uri, tag); res.unwrap() } @@ -534,22 +544,26 @@ async fn get_ipfs_resource_data( /// * `client` - a IPFS client. /// * `metadata` - the metadata to check. /// * `expected_metadata` - the metadata values coming from local Dojo metadata. -/// * `element_name` - the name of the element linked to this metadata. +/// * `tag` - the tag of the element linked to this metadata. async fn check_artifact_fields( client: &HyperBackend, metadata: &ArtifactMetadata, expected_metadata: &ArtifactMetadata, - element_name: &String, + tag: &String, ) { - assert!(metadata.abi.is_some(), "'abi' field not set for {}", element_name); + println!("metadata {:?}", metadata); + println!("expected_metadata {:?}", expected_metadata); + + assert!(metadata.abi.is_some(), "'abi' field not set for {}", tag); let abi = metadata.abi.as_ref().unwrap(); let expected_abi = expected_metadata.abi.as_ref().unwrap(); - check_file_field(client, abi, expected_abi, "abi".to_string(), element_name).await; + check_file_field(client, abi, expected_abi, "abi".to_string(), tag).await; - assert!(metadata.source.is_some(), "'source' field not set for {}", element_name); - let source = metadata.source.as_ref().unwrap(); - let expected_source = expected_metadata.source.as_ref().unwrap(); - check_file_field(client, source, expected_source, "source".to_string(), element_name).await; + // For now source are not expended, uncomment when https://github.com/dojoengine/dojo/issues/2137 is fixed. + // assert!(metadata.source.is_some(), "'source' field not set for {}", tag); + // let source = metadata.source.as_ref().unwrap(); + // let expected_source = expected_metadata.source.as_ref().unwrap(); + // check_file_field(client, source, expected_source, "source".to_string(), tag).await; } /// Check the validity of a IPFS artifact metadata. @@ -557,19 +571,19 @@ async fn check_artifact_fields( /// # Arguments /// /// * `client` - a IPFS client. -/// * `element_name` - the fully qualified name of the element linked to the artifact. +/// * `tag` - the tag of the element linked to the artifact. /// * `uri` - the full metadata URI. /// * `expected_metadata` - the expected metadata values coming from local Dojo metadata. async fn check_ipfs_metadata( client: &HyperBackend, - element_name: &String, + tag: &String, uri: &String, expected_metadata: &ArtifactMetadata, ) { - let resource_bytes = get_ipfs_resource_data(client, element_name, uri).await; - let metadata = resource_bytes_to_metadata(&resource_bytes, element_name); + let resource_bytes = get_ipfs_resource_data(client, tag, uri).await; + let metadata = resource_bytes_to_metadata(&resource_bytes, tag); - check_artifact_fields(client, &metadata, expected_metadata, element_name).await; + check_artifact_fields(client, &metadata, expected_metadata, tag).await; } /// Check an artifact metadata read from the resource registry against its value @@ -580,28 +594,24 @@ async fn check_ipfs_metadata( /// * `client` - a IPFS client. /// * `world_reader` - a world reader object. /// * `resource_id` - the resource ID in the resource registry. -/// * `element_name` - the fully qualified name of the element linked to this metadata. +/// * `tag` - the tag of the element linked to this metadata. /// * `dojo_metadata` - local Dojo metadata. async fn check_artifact_metadata( client: &HyperBackend, world_reader: &WorldContractReader

, resource_id: Felt, - element_name: &String, + tag: &String, dojo_metadata: &DojoMetadata, ) { let resource = world_reader.metadata(&resource_id).call().await.unwrap(); - let expected_resource = dojo_metadata.resources_artifacts.get(element_name); - assert!( - expected_resource.is_some(), - "Unable to find local artifact metadata for {}", - element_name - ); + let expected_resource = dojo_metadata.resources_artifacts.get(tag); + assert!(expected_resource.is_some(), "Unable to find local artifact metadata for {}", tag); let expected_resource = expected_resource.unwrap(); check_ipfs_metadata( client, - element_name, + tag, &resource.metadata_uri.to_string().unwrap(), &expected_resource.artifacts, ) diff --git a/crates/sozo/ops/src/tests/setup.rs b/crates/sozo/ops/src/tests/setup.rs index 0606ca74a0..4ddb0f1459 100644 --- a/crates/sozo/ops/src/tests/setup.rs +++ b/crates/sozo/ops/src/tests/setup.rs @@ -3,9 +3,11 @@ use camino::Utf8PathBuf; use dojo_test_utils::compiler; use dojo_test_utils::migration::prepare_migration_with_world_and_seed; use dojo_world::contracts::world::WorldContract; +use dojo_world::metadata::get_default_namespace_from_ws; use dojo_world::migration::strategy::MigrationStrategy; use dojo_world::migration::TxnConfig; use katana_runner::KatanaRunner; +use scarb::compiler::Profile; use scarb::core::{Config, Workspace}; use scarb::ops; use starknet::accounts::SingleOwnerAccount; @@ -31,7 +33,7 @@ pub fn load_config() -> Config { let source_project_dir = Utf8PathBuf::from("../../../examples/spawn-and-move/"); let dojo_core_path = Utf8PathBuf::from("../../dojo-core"); - compiler::copy_tmp_config(&source_project_dir, &dojo_core_path) + compiler::copy_tmp_config(&source_project_dir, &dojo_core_path, Profile::DEV) } /// Setups the workspace for the spawn-and-moves project. @@ -53,11 +55,21 @@ pub fn setup_ws(config: &Config) -> Workspace<'_> { /// /// A [`MigrationStrategy`] to execute to migrate the full spawn-and-moves project. pub fn setup_migration(config: &Config) -> Result { + let ws = setup_ws(config); + let manifest_path = config.manifest_path(); let base_dir = manifest_path.parent().unwrap(); let target_dir = format!("{}/target/dev", base_dir); - prepare_migration_with_world_and_seed(base_dir.into(), target_dir.into(), None, "sozo_test") + let default_namespace = get_default_namespace_from_ws(&ws); + + prepare_migration_with_world_and_seed( + base_dir.into(), + target_dir.into(), + None, + "sozo_test", + &default_namespace, + ) } /// Setups the project by migrating the full spawn-and-moves project. @@ -76,7 +88,9 @@ pub async fn setup( let config = load_config(); let ws = setup_ws(&config); - let migration = setup_migration(&config)?; + let mut migration = setup_migration(&config)?; + let _ = + migration.resolve_variable(migration.world_address().expect("world address must exist")); let mut account = sequencer.account(0); account.set_block_id(BlockId::Tag(BlockTag::Pending)); diff --git a/crates/sozo/ops/src/tests/utils.rs b/crates/sozo/ops/src/tests/utils.rs index 70cb3e03c4..24585a9616 100644 --- a/crates/sozo/ops/src/tests/utils.rs +++ b/crates/sozo/ops/src/tests/utils.rs @@ -7,7 +7,7 @@ use starknet::core::types::{BlockId, BlockTag, Felt}; use super::setup; use crate::utils; -const ACTION_CONTRACT_NAME: &str = "dojo_examples::actions::actions"; +const ACTION_CONTRACT_TAG: &str = "dojo_examples-actions"; #[tokio::test(flavor = "multi_thread")] async fn get_contract_address_from_world() { @@ -16,7 +16,7 @@ async fn get_contract_address_from_world() { let world = setup::setup(&sequencer).await.unwrap(); let contract_address = - utils::get_contract_address(&world, ACTION_CONTRACT_NAME.to_string()).await.unwrap(); + utils::get_contract_address(&world, ACTION_CONTRACT_TAG.to_string()).await.unwrap(); assert!(contract_address != Felt::ZERO); } @@ -43,7 +43,7 @@ async fn get_contract_address_from_world_with_world_reader() { let world_reader = WorldContractReader::new(world.address, provider); let contract_address = - utils::get_contract_address_from_reader(&world_reader, ACTION_CONTRACT_NAME.to_string()) + utils::get_contract_address_from_reader(&world_reader, ACTION_CONTRACT_TAG.to_string()) .await .unwrap(); diff --git a/crates/sozo/ops/src/utils.rs b/crates/sozo/ops/src/utils.rs index aeb5407959..0b6cdd70c4 100644 --- a/crates/sozo/ops/src/utils.rs +++ b/crates/sozo/ops/src/utils.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Result}; use bigdecimal::BigDecimal; -use dojo_world::contracts::{WorldContract, WorldContractReader}; +use dojo_world::contracts::naming::get_name_from_tag; +use dojo_world::contracts::world::{WorldContract, WorldContractReader}; use dojo_world::migration::strategy::generate_salt; use dojo_world::utils::{execution_status_from_receipt, TransactionWaiter}; use scarb_ui::Ui; @@ -15,21 +16,21 @@ use starknet::providers::Provider; /// # Arguments /// /// * `world` - The world's contract connector. -/// * `name_or_address` - A string with a contract name or a hexadecimal address. +/// * `tag_or_address` - A string with a contract tag or a hexadecimal address. /// /// # Returns /// /// A [`Felt`] with the address of the contract on success. pub async fn get_contract_address( world: &WorldContract, - name_or_address: String, + tag_or_address: String, ) -> Result { - if name_or_address.starts_with("0x") { - Felt::from_hex(&name_or_address).map_err(anyhow::Error::from) + if tag_or_address.starts_with("0x") { + Felt::from_hex(&tag_or_address).map_err(anyhow::Error::from) } else { let contract_class_hash = world.base().call().await?; Ok(starknet::core::utils::get_contract_address( - generate_salt(&name_or_address), + generate_salt(&get_name_from_tag(&tag_or_address)), contract_class_hash.into(), &[], world.address, @@ -44,21 +45,21 @@ pub async fn get_contract_address( /// # Arguments /// /// * `world_reader` - The world contract reader. -/// * `name_or_address` - A string with a contract name or a hexadecimal address. +/// * `tag_or_address` - A string with a contract tag or a hexadecimal address. /// /// # Returns /// /// A [`Felt`] with the address of the contract on success. pub async fn get_contract_address_from_reader( world_reader: &WorldContractReader

, - name_or_address: String, + tag_or_address: String, ) -> Result { - if name_or_address.starts_with("0x") { - Felt::from_hex(&name_or_address).map_err(anyhow::Error::from) + if tag_or_address.starts_with("0x") { + Felt::from_hex(&tag_or_address).map_err(anyhow::Error::from) } else { let contract_class_hash = world_reader.base().call().await?; Ok(starknet::core::utils::get_contract_address( - generate_salt(&name_or_address), + generate_salt(&get_name_from_tag(&tag_or_address)), contract_class_hash.into(), &[], world_reader.address, diff --git a/crates/torii/client/src/client/error.rs b/crates/torii/client/src/client/error.rs index 3d163da185..5a036e19cd 100644 --- a/crates/torii/client/src/client/error.rs +++ b/crates/torii/client/src/client/error.rs @@ -1,4 +1,5 @@ use dojo_world::contracts::model::ModelError; +use starknet::core::types::Felt; use starknet::core::utils::{CairoShortStringToFeltError, ParseCairoShortStringError}; use torii_grpc::types::schema::SchemaError; @@ -6,8 +7,10 @@ use torii_grpc::types::schema::SchemaError; pub enum Error { #[error("Subscription service uninitialized")] SubscriptionUninitialized, + #[error("Invalid model name: {0}. Expected format is \"namespace-model\"")] + InvalidModelName(String), #[error("Unknown model: {0}")] - UnknownModel(String), + UnknownModel(Felt), #[error("Parsing error: {0}")] Parse(#[from] ParseError), #[error(transparent)] diff --git a/crates/torii/client/src/client/mod.rs b/crates/torii/client/src/client/mod.rs index d68b73e199..2b5eb88962 100644 --- a/crates/torii/client/src/client/mod.rs +++ b/crates/torii/client/src/client/mod.rs @@ -9,11 +9,10 @@ use std::sync::Arc; use dojo_types::packing::unpack; use dojo_types::schema::Ty; use dojo_types::WorldMetadata; -use dojo_world::contracts::WorldContractReader; +use dojo_world::contracts::{naming, WorldContractReader}; use futures::lock::Mutex; use parking_lot::{RwLock, RwLockReadGuard}; use starknet::core::types::Felt; -use starknet::core::utils::cairo_short_string_to_felt; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; use tokio::sync::RwLock as AsyncRwLock; @@ -175,27 +174,28 @@ impl Client { /// If the requested model is not among the synced models, it will attempt to fetch it from /// the RPC. pub async fn model(&self, keys: &ModelKeysClause) -> Result, Error> { - let Some(mut schema) = self.metadata.read().model(&keys.model).map(|m| m.schema.clone()) + let (namespace, model) = keys.model.split_once('-').unwrap(); + let model_selector = naming::compute_model_selector_from_names(namespace, model); + let Some(mut schema) = + self.metadata.read().model(&model_selector).map(|m| m.schema.clone()) else { return Ok(None); }; if !self.subscribed_models.is_synced(keys) { - let model = self.world_reader.model_reader(&keys.model).await?; + let model = self.world_reader.model_reader(namespace, model).await?; return Ok(Some(model.entity(&keys.keys).await?)); } - let Ok(Some(raw_values)) = self.storage.get_model_storage( - cairo_short_string_to_felt(&keys.model).map_err(ParseError::CairoShortStringToFelt)?, - &keys.keys, - ) else { + let Ok(Some(raw_values)) = self.storage.get_model_storage(model_selector, &keys.keys) + else { return Ok(Some(schema)); }; let layout = self .metadata .read() - .model(&keys.model) + .model(&model_selector) .map(|m| m.layout.clone()) .expect("qed; layout should exist"); @@ -230,7 +230,8 @@ impl Client { /// NOTE: This will establish a new subscription stream with the server. pub async fn add_models_to_sync(&self, models_keys: Vec) -> Result<(), Error> { for keys in &models_keys { - self.initiate_model(&keys.model, keys.keys.clone()).await?; + let (namespace, model) = keys.model.split_once('-').unwrap(); + self.initiate_model(namespace, model, keys.keys.clone()).await?; } self.subscribed_models.add_models(models_keys)?; @@ -279,11 +280,16 @@ impl Client { Ok(stream) } - async fn initiate_model(&self, model: &str, keys: Vec) -> Result<(), Error> { - let model_reader = self.world_reader.model_reader(model).await?; + async fn initiate_model( + &self, + namespace: &str, + model: &str, + keys: Vec, + ) -> Result<(), Error> { + let model_reader = self.world_reader.model_reader(namespace, model).await?; let values = model_reader.entity_storage(&keys).await?; self.storage.set_model_storage( - cairo_short_string_to_felt(model).map_err(ParseError::CairoShortStringToFelt)?, + naming::compute_model_selector_from_names(namespace, model), keys, values, )?; diff --git a/crates/torii/client/src/client/storage.rs b/crates/torii/client/src/client/storage.rs index 7b13f2b180..772b838a93 100644 --- a/crates/torii/client/src/client/storage.rs +++ b/crates/torii/client/src/client/storage.rs @@ -5,9 +5,8 @@ use dojo_types::WorldMetadata; use futures::channel::mpsc::{channel, Receiver, Sender}; use parking_lot::{Mutex, RwLock}; use starknet::core::types::Felt; -use starknet::core::utils::parse_cairo_short_string; -use super::error::{Error, ParseError}; +use super::error::Error; use crate::utils::compute_all_storage_addresses; pub type EntityKeys = Vec; @@ -118,15 +117,12 @@ impl ModelStorage { model: Felt, raw_keys: &[Felt], ) -> Result, Error> { - let model_name = - parse_cairo_short_string(&model).map_err(ParseError::ParseCairoShortString)?; - let model_packed_size = self .metadata .read() - .model(&model_name) + .model(&model) .map(|c| c.packed_size) - .ok_or(Error::UnknownModel(model_name))?; + .ok_or(Error::UnknownModel(model))?; Ok(compute_all_storage_addresses(model, raw_keys, model_packed_size)) } @@ -143,16 +139,17 @@ mod tests { use dojo_types::schema::Ty; use dojo_types::WorldMetadata; + use dojo_world::contracts::naming::compute_model_selector_from_names; use parking_lot::RwLock; - use starknet::core::utils::cairo_short_string_to_felt; use starknet::macros::felt; use crate::utils::compute_all_storage_addresses; fn create_dummy_metadata() -> WorldMetadata { let models = HashMap::from([( - "Position".into(), + compute_model_selector_from_names("Test", "Position"), dojo_types::schema::ModelMetadata { + namespace: "Test".into(), name: "Position".into(), class_hash: felt!("1"), contract_address: felt!("2"), @@ -178,22 +175,20 @@ mod tests { assert!(storage.storage.read().is_empty(), "storage must be empty initially"); - let model = storage.metadata.read().model("Position").cloned().unwrap(); - let expected_storage_addresses = compute_all_storage_addresses( - cairo_short_string_to_felt(&model.name).unwrap(), - &keys, - model.packed_size, - ); + let model_selector = compute_model_selector_from_names("Test", "Position"); + + let model = storage.metadata.read().model(&model_selector).cloned().unwrap(); + let expected_storage_addresses = + compute_all_storage_addresses(model_selector, &keys, model.packed_size); let expected_values = vec![felt!("1"), felt!("2"), felt!("3"), felt!("4")]; - let model_name_in_felt = cairo_short_string_to_felt("Position").unwrap(); storage - .set_model_storage(model_name_in_felt, keys.clone(), expected_values.clone()) + .set_model_storage(model_selector, keys.clone(), expected_values.clone()) .expect("set storage values"); - let actual_values = storage - .get_model_storage(model_name_in_felt, &keys) + let actual_values: Vec = storage + .get_model_storage(model_selector, &keys) .expect("model exist") .expect("values are set"); @@ -201,7 +196,7 @@ mod tests { storage.storage.read().clone().into_keys().collect::>(); assert!( - storage.model_index.read().get(&model_name_in_felt).is_some_and(|e| e.contains(&keys)), + storage.model_index.read().get(&model_selector).is_some_and(|e| e.contains(&keys)), "model keys must be indexed" ); assert!(actual_values == expected_values); diff --git a/crates/torii/client/src/client/subscription.rs b/crates/torii/client/src/client/subscription.rs index 0a02cc67c1..c2a35ef532 100644 --- a/crates/torii/client/src/client/subscription.rs +++ b/crates/torii/client/src/client/subscription.rs @@ -5,15 +5,15 @@ use std::sync::Arc; use std::task::Poll; use dojo_types::WorldMetadata; +use dojo_world::contracts::naming; use futures::channel::mpsc::{self, Receiver, Sender}; use futures_util::StreamExt; use parking_lot::{Mutex, RwLock}; use starknet::core::types::{Felt, StateDiff, StateUpdate}; -use starknet::core::utils::cairo_short_string_to_felt; use torii_grpc::client::ModelDiffsStreaming; use torii_grpc::types::ModelKeysClause; -use crate::client::error::{Error, ParseError}; +use crate::client::error::Error; use crate::client::storage::ModelStorage; use crate::utils::compute_all_storage_addresses; @@ -60,19 +60,20 @@ impl SubscribedModels { return Ok(()); } + let (namespace, model) = + keys.model.split_once('-').ok_or(Error::InvalidModelName(keys.model.clone()))?; + let selector = naming::compute_model_selector_from_names(namespace, model); + let model_packed_size = self .metadata .read() .models - .get(&keys.model) + .get(&selector) .map(|c| c.packed_size) - .ok_or(Error::UnknownModel(keys.model.clone()))?; + .ok_or(Error::UnknownModel(selector))?; - let storage_addresses = compute_all_storage_addresses( - cairo_short_string_to_felt(&keys.model).map_err(ParseError::CairoShortStringToFelt)?, - &keys.keys, - model_packed_size, - ); + let storage_addresses = + compute_all_storage_addresses(selector, &keys.keys, model_packed_size); let storage_lock = &mut self.subscribed_storage_addresses.write(); storage_addresses.into_iter().for_each(|address| { @@ -87,19 +88,20 @@ impl SubscribedModels { return Ok(()); } + let (namespace, model) = + keys.model.split_once('-').ok_or(Error::InvalidModelName(keys.model.clone()))?; + let selector = naming::compute_model_selector_from_names(namespace, model); + let model_packed_size = self .metadata .read() .models - .get(&keys.model) + .get(&selector) .map(|c| c.packed_size) - .ok_or(Error::UnknownModel(keys.model.clone()))?; + .ok_or(Error::UnknownModel(selector))?; - let storage_addresses = compute_all_storage_addresses( - cairo_short_string_to_felt(&keys.model).map_err(ParseError::CairoShortStringToFelt)?, - &keys.keys, - model_packed_size, - ); + let storage_addresses = + compute_all_storage_addresses(selector, &keys.keys, model_packed_size); let storage_lock = &mut self.subscribed_storage_addresses.write(); storage_addresses.iter().for_each(|address| { @@ -243,8 +245,8 @@ mod tests { use dojo_types::schema::Ty; use dojo_types::WorldMetadata; + use dojo_world::contracts::naming::compute_model_selector_from_names; use parking_lot::RwLock; - use starknet::core::utils::cairo_short_string_to_felt; use starknet::macros::felt; use torii_grpc::types::ModelKeysClause; @@ -252,8 +254,9 @@ mod tests { fn create_dummy_metadata() -> WorldMetadata { let components = HashMap::from([( - "Position".into(), + compute_model_selector_from_names("Test", "Position"), dojo_types::schema::ModelMetadata { + namespace: "Test".into(), name: "Position".into(), class_hash: felt!("1"), contract_address: felt!("2"), @@ -269,12 +272,12 @@ mod tests { #[test] fn add_and_remove_subscribed_model() { - let model_name = String::from("Position"); + let model_name = String::from("Test-Position"); let keys = vec![felt!("0x12345")]; let packed_size: u32 = 1; let mut expected_storage_addresses = compute_all_storage_addresses( - cairo_short_string_to_felt(&model_name).unwrap(), + compute_model_selector_from_names("Test", "Position"), &keys, packed_size, ) diff --git a/crates/torii/core/src/cache.rs b/crates/torii/core/src/cache.rs index b795a6f1bf..9afbf0c5e8 100644 --- a/crates/torii/core/src/cache.rs +++ b/crates/torii/core/src/cache.rs @@ -2,16 +2,15 @@ use std::collections::HashMap; use dojo_types::schema::Ty; use sqlx::SqlitePool; +use starknet_crypto::Felt; use tokio::sync::RwLock; use crate::error::{Error, QueryError}; use crate::model::{parse_sql_model_members, SqlModelMember}; -type ModelName = String; - pub struct ModelCache { pool: SqlitePool, - cache: RwLock>, + cache: RwLock>, } impl ModelCache { @@ -19,48 +18,51 @@ impl ModelCache { Self { pool, cache: RwLock::new(HashMap::new()) } } - pub async fn schemas(&self, models: Vec<&str>) -> Result, Error> { - let mut schemas = Vec::with_capacity(models.len()); - for model in models { - schemas.push(self.schema(model).await?); + pub async fn schemas(&self, selectors: &[Felt]) -> Result, Error> { + let mut schemas = Vec::with_capacity(selectors.len()); + for selector in selectors { + schemas.push(self.schema(selector).await?); } Ok(schemas) } - pub async fn schema(&self, model: &str) -> Result { + pub async fn schema(&self, selector: &Felt) -> Result { { let cache = self.cache.read().await; - if let Some(schema) = cache.get(model) { - return Ok(schema.clone()); + if let Some(model) = cache.get(selector).cloned() { + return Ok(model); } } - self.update_schema(model).await + self.update_schema(selector).await } - async fn update_schema(&self, model: &str) -> Result { - let model_name: String = sqlx::query_scalar("SELECT name FROM models WHERE id = ?") - .bind(model) - .fetch_one(&self.pool) - .await?; + async fn update_schema(&self, selector: &Felt) -> Result { + let formatted_selector = format!("{:#x}", selector); + + let (namespace, name): (String, String) = + sqlx::query_as("SELECT namespace, name FROM models WHERE id = ?") + .bind(formatted_selector.clone()) + .fetch_one(&self.pool) + .await?; let model_members: Vec = sqlx::query_as( "SELECT id, model_idx, member_idx, name, type, type_enum, enum_options, key FROM \ model_members WHERE model_id = ? ORDER BY model_idx ASC, member_idx ASC", ) - .bind(model) + .bind(formatted_selector) .fetch_all(&self.pool) .await?; if model_members.is_empty() { - return Err(QueryError::ModelNotFound(model.into()).into()); + return Err(QueryError::ModelNotFound(name.clone()).into()); } - let ty = parse_sql_model_members(&model_name, &model_members); + let schema = parse_sql_model_members(&namespace, &name, &model_members); let mut cache = self.cache.write().await; - cache.insert(model.into(), ty.clone()); + cache.insert(*selector, schema.clone()); - Ok(ty) + Ok(schema) } pub async fn clear(&self) { diff --git a/crates/torii/core/src/error.rs b/crates/torii/core/src/error.rs index 5006d83ded..d86b9c1be3 100644 --- a/crates/torii/core/src/error.rs +++ b/crates/torii/core/src/error.rs @@ -45,4 +45,6 @@ pub enum QueryError { ModelNotFound(String), #[error("exceeds sqlite `JOIN` limit (64)")] SqliteJoinLimit, + #[error("invalid namespaced model: {0}")] + InvalidNamespacedModel(String), } diff --git a/crates/torii/core/src/model.rs b/crates/torii/core/src/model.rs index e9d21749fd..cc449674d6 100644 --- a/crates/torii/core/src/model.rs +++ b/crates/torii/core/src/model.rs @@ -10,14 +10,17 @@ use dojo_world::contracts::model::ModelReader; use sqlx::sqlite::SqliteRow; use sqlx::{Pool, Row, Sqlite}; use starknet::core::types::Felt; -use starknet::core::utils::get_selector_from_name; use super::error::{self, Error}; use crate::error::{ParseError, QueryError}; pub struct ModelSQLReader { + /// Namespace of the model + namespace: String, /// The name of the model name: String, + /// The selector of the model + selector: Felt, /// The class hash of the model class_hash: Felt, /// The contract address of the model @@ -29,8 +32,9 @@ pub struct ModelSQLReader { } impl ModelSQLReader { - pub async fn new(name: &str, pool: Pool) -> Result { - let (name, class_hash, contract_address, packed_size, unpacked_size, layout): ( + pub async fn new(selector: Felt, pool: Pool) -> Result { + let (namespace, name, class_hash, contract_address, packed_size, unpacked_size, layout): ( + String, String, String, String, @@ -38,10 +42,10 @@ impl ModelSQLReader { u32, String, ) = sqlx::query_as( - "SELECT name, class_hash, contract_address, packed_size, unpacked_size, layout FROM \ - models WHERE id = ?", + "SELECT namespace, name, class_hash, contract_address, packed_size, unpacked_size, \ + layout FROM models WHERE id = ?", ) - .bind(name) + .bind(format!("{:#x}", selector)) .fetch_one(&pool) .await?; @@ -51,20 +55,33 @@ impl ModelSQLReader { let layout = serde_json::from_str(&layout).map_err(error::ParseError::FromJsonStr)?; - Ok(Self { name, class_hash, contract_address, pool, packed_size, unpacked_size, layout }) + Ok(Self { + namespace, + name, + selector, + class_hash, + contract_address, + pool, + packed_size, + unpacked_size, + layout, + }) } } #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl ModelReader for ModelSQLReader { - fn name(&self) -> String { - self.name.to_string() + fn namespace(&self) -> &str { + &self.namespace + } + + fn name(&self) -> &str { + &self.name } fn selector(&self) -> Felt { - // this should never fail - get_selector_from_name(&self.name).unwrap() + self.selector } fn class_hash(&self) -> Felt { @@ -76,19 +93,15 @@ impl ModelReader for ModelSQLReader { } async fn schema(&self) -> Result { - // this is temporary until the hash for the model name is precomputed - let model_selector = - get_selector_from_name(&self.name).map_err(error::ParseError::NonAsciiName)?; - let model_members: Vec = sqlx::query_as( "SELECT id, model_idx, member_idx, name, type, type_enum, enum_options, key FROM \ model_members WHERE model_id = ? ORDER BY model_idx ASC, member_idx ASC", ) - .bind(format!("{:#x}", model_selector)) + .bind(format!("{:#x}", self.selector)) .fetch_all(&self.pool) .await?; - Ok(parse_sql_model_members(&self.name, &model_members)) + Ok(parse_sql_model_members(&self.namespace, &self.name, &model_members)) } async fn packed_size(&self) -> Result { @@ -120,7 +133,11 @@ pub struct SqlModelMember { // assume that the model members are sorted by model_idx and member_idx // `id` is the type id of the model member /// A helper function to parse the model members from sql table to `Ty` -pub fn parse_sql_model_members(model: &str, model_members_all: &[SqlModelMember]) -> Ty { +pub fn parse_sql_model_members( + namespace: &str, + model: &str, + model_members_all: &[SqlModelMember], +) -> Ty { fn parse_sql_member(member: &SqlModelMember, model_members_all: &[SqlModelMember]) -> Ty { match member.type_enum.as_str() { "Primitive" => Ty::Primitive(member.r#type.parse().unwrap()), @@ -192,10 +209,10 @@ pub fn parse_sql_model_members(model: &str, model_members_all: &[SqlModelMember] } Ty::Struct(Struct { - name: model.into(), + name: format!("{}-{}", namespace, model), children: model_members_all .iter() - .filter(|m| m.id == model) + .filter(|m| m.id == format!("{}-{}", namespace, model)) .map(|m| Member { key: m.key, name: m.name.to_owned(), @@ -208,7 +225,7 @@ pub fn parse_sql_model_members(model: &str, model_members_all: &[SqlModelMember] /// Creates a query that fetches all models and their nested data. pub fn build_sql_query( - model_schemas: &Vec, + schemas: &Vec, entities_table: &str, entity_relation_column: &str, where_clause: Option<&str>, @@ -227,7 +244,7 @@ pub fn build_sql_query( // struct can be the main entrypoint to our model schema // so we dont format the table name if the path is empty let table_name = - if path.is_empty() { s.name.clone() } else { format!("{}${}", path, name) }; + if path.is_empty() { name.to_string() } else { format!("{}${}", path, name) }; for child in &s.children { parse_ty( @@ -296,14 +313,14 @@ pub fn build_sql_query( is_typed = true; } - selections.push(format!("{}.external_{} AS \"{}.{}\"", path, name, path, name)); + selections.push(format!("[{path}].external_{name} AS \"{path}.{name}\"")); if is_typed { tables.push(table_name); } } _ => { // alias selected columns to avoid conflicts in `JOIN` - selections.push(format!("{}.external_{} AS \"{}.{}\"", path, name, path, name)); + selections.push(format!("[{path}].external_{name} AS \"{path}.{name}\"")); } } } @@ -311,14 +328,13 @@ pub fn build_sql_query( let mut global_selections = Vec::new(); let mut global_tables = Vec::new(); - let mut arrays_queries = HashMap::new(); + let mut arrays_queries: HashMap, Vec)> = HashMap::new(); - for ty in model_schemas { - let schema = ty.as_struct().expect("schema should be struct"); + for model in schemas { parse_ty( "", - &schema.name, - ty, + &model.name(), + model, &mut global_selections, &mut global_tables, &mut arrays_queries, @@ -334,7 +350,7 @@ pub fn build_sql_query( let join_clause = global_tables .into_iter() .map(|table| { - format!(" JOIN {table} ON {entities_table}.id = {table}.{entity_relation_column}") + format!(" JOIN [{table}] ON {entities_table}.id = [{table}].{entity_relation_column}") }) .collect::>() .join(" "); @@ -353,12 +369,13 @@ pub fn build_sql_query( .map(|(idx, table)| { if idx == 0 { format!( - " JOIN {table} ON {entities_table}.id = \ - {table}.{entity_relation_column}" + " JOIN [{table}] ON {entities_table}.id = \ + [{table}].{entity_relation_column}" ) } else { format!( - " JOIN {table} ON {table}.full_array_id = {prev_table}.full_array_id", + " JOIN [{table}] ON [{table}].full_array_id = \ + [{prev_table}].full_array_id", prev_table = tables[idx - 1] ) } @@ -535,7 +552,7 @@ mod tests { fn parse_simple_model_members_to_ty() { let model_members = vec![ SqlModelMember { - id: "Position".into(), + id: "Test-Position".into(), name: "x".into(), r#type: "u256".into(), key: false, @@ -545,7 +562,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "Position".into(), + id: "Test-Position".into(), name: "y".into(), r#type: "u256".into(), key: false, @@ -555,7 +572,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "PlayerConfig".into(), + id: "Test-PlayerConfig".into(), name: "name".into(), r#type: "ByteArray".into(), key: false, @@ -567,7 +584,7 @@ mod tests { ]; let expected_position = Ty::Struct(Struct { - name: "Position".into(), + name: "Test-Position".into(), children: vec![ dojo_types::schema::Member { name: "x".into(), @@ -583,7 +600,7 @@ mod tests { }); let expected_player_config = Ty::Struct(Struct { - name: "PlayerConfig".into(), + name: "Test-PlayerConfig".into(), children: vec![dojo_types::schema::Member { name: "name".into(), key: false, @@ -591,15 +608,18 @@ mod tests { }], }); - assert_eq!(parse_sql_model_members("Position", &model_members), expected_position); - assert_eq!(parse_sql_model_members("PlayerConfig", &model_members), expected_player_config); + assert_eq!(parse_sql_model_members("Test", "Position", &model_members), expected_position); + assert_eq!( + parse_sql_model_members("Test", "PlayerConfig", &model_members), + expected_player_config + ); } #[test] fn parse_complex_model_members_to_ty() { let model_members = vec![ SqlModelMember { - id: "Position".into(), + id: "Test-Position".into(), name: "name".into(), r#type: "felt252".into(), key: false, @@ -609,7 +629,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "Position".into(), + id: "Test-Position".into(), name: "age".into(), r#type: "u8".into(), key: false, @@ -619,7 +639,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "Position".into(), + id: "Test-Position".into(), name: "vec".into(), r#type: "Vec2".into(), key: false, @@ -629,7 +649,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "Position$vec".into(), + id: "Test-Position$vec".into(), name: "x".into(), r#type: "u256".into(), key: false, @@ -639,7 +659,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "Position$vec".into(), + id: "Test-Position$vec".into(), name: "y".into(), r#type: "u256".into(), key: false, @@ -649,7 +669,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "PlayerConfig".into(), + id: "Test-PlayerConfig".into(), name: "favorite_item".into(), r#type: "Option".into(), key: false, @@ -659,7 +679,7 @@ mod tests { enum_options: Some("None,Some".into()), }, SqlModelMember { - id: "PlayerConfig".into(), + id: "Test-PlayerConfig".into(), name: "items".into(), r#type: "Array".into(), key: false, @@ -669,7 +689,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "PlayerConfig$items".into(), + id: "Test-PlayerConfig$items".into(), name: "data".into(), r#type: "PlayerItem".into(), key: false, @@ -679,7 +699,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "PlayerConfig$items$data".into(), + id: "Test-PlayerConfig$items$data".into(), name: "item_id".into(), r#type: "u32".into(), key: false, @@ -689,7 +709,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "PlayerConfig$items$data".into(), + id: "Test-PlayerConfig$items$data".into(), name: "quantity".into(), r#type: "u32".into(), key: false, @@ -699,7 +719,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "PlayerConfig$favorite_item".into(), + id: "Test-PlayerConfig$favorite_item".into(), name: "Some".into(), r#type: "u32".into(), key: false, @@ -709,7 +729,7 @@ mod tests { enum_options: None, }, SqlModelMember { - id: "PlayerConfig$favorite_item".into(), + id: "Test-PlayerConfig$favorite_item".into(), name: "option".into(), r#type: "Option".into(), key: false, @@ -721,7 +741,7 @@ mod tests { ]; let expected_position = Ty::Struct(Struct { - name: "Position".into(), + name: "Test-Position".into(), children: vec![ dojo_types::schema::Member { name: "name".into(), @@ -756,7 +776,7 @@ mod tests { }); let expected_player_config = Ty::Struct(Struct { - name: "PlayerConfig".into(), + name: "Test-PlayerConfig".into(), children: vec![ dojo_types::schema::Member { name: "favorite_item".into(), @@ -795,14 +815,17 @@ mod tests { ], }); - assert_eq!(parse_sql_model_members("Position", &model_members), expected_position); - assert_eq!(parse_sql_model_members("PlayerConfig", &model_members), expected_player_config); + assert_eq!(parse_sql_model_members("Test", "Position", &model_members), expected_position); + assert_eq!( + parse_sql_model_members("Test", "PlayerConfig", &model_members), + expected_player_config + ); } #[test] fn parse_model_members_with_enum_to_ty() { let model_members = vec![SqlModelMember { - id: "Moves".into(), + id: "Test-Moves".into(), name: "direction".into(), r#type: "Direction".into(), key: false, @@ -813,7 +836,7 @@ mod tests { }]; let expected_ty = Ty::Struct(Struct { - name: "Moves".into(), + name: "Test-Moves".into(), children: vec![dojo_types::schema::Member { name: "direction".into(), key: false, @@ -830,13 +853,13 @@ mod tests { }], }); - assert_eq!(parse_sql_model_members("Moves", &model_members), expected_ty); + assert_eq!(parse_sql_model_members("Test", "Moves", &model_members), expected_ty); } #[test] fn struct_ty_to_query() { let position = Ty::Struct(Struct { - name: "Position".into(), + name: "Test-Position".into(), children: vec![ dojo_types::schema::Member { name: "player".into(), @@ -898,7 +921,7 @@ mod tests { }); let player_config = Ty::Struct(Struct { - name: "PlayerConfig".into(), + name: "Test-PlayerConfig".into(), children: vec![ dojo_types::schema::Member { name: "favorite_item".into(), @@ -942,14 +965,16 @@ mod tests { .unwrap(); let expected_query = - "SELECT entities.id, entities.keys, Position.external_player AS \"Position.player\", \ - Position$vec.external_x AS \"Position$vec.x\", Position$vec.external_y AS \ - \"Position$vec.y\", PlayerConfig$favorite_item.external_Some AS \ - \"PlayerConfig$favorite_item.Some\", PlayerConfig.external_favorite_item AS \ - \"PlayerConfig.favorite_item\" FROM entities JOIN Position$vec ON entities.id = \ - Position$vec.entity_id JOIN Position ON entities.id = Position.entity_id JOIN \ - PlayerConfig$favorite_item ON entities.id = PlayerConfig$favorite_item.entity_id \ - JOIN PlayerConfig ON entities.id = PlayerConfig.entity_id"; + "SELECT entities.id, entities.keys, [Test-Position].external_player AS \ + \"Test-Position.player\", [Test-Position$vec].external_x AS \"Test-Position$vec.x\", \ + [Test-Position$vec].external_y AS \"Test-Position$vec.y\", \ + [Test-PlayerConfig$favorite_item].external_Some AS \ + \"Test-PlayerConfig$favorite_item.Some\", [Test-PlayerConfig].external_favorite_item \ + AS \"Test-PlayerConfig.favorite_item\" FROM entities JOIN [Test-Position$vec] ON \ + entities.id = [Test-Position$vec].entity_id JOIN [Test-Position] ON entities.id = \ + [Test-Position].entity_id JOIN [Test-PlayerConfig$favorite_item] ON entities.id = \ + [Test-PlayerConfig$favorite_item].entity_id JOIN [Test-PlayerConfig] ON entities.id \ + = [Test-PlayerConfig].entity_id"; // todo: completely tests arrays assert_eq!(query.0, expected_query); } diff --git a/crates/torii/core/src/processors/event_message.rs b/crates/torii/core/src/processors/event_message.rs index cb8e7a1971..ff36452817 100644 --- a/crates/torii/core/src/processors/event_message.rs +++ b/crates/torii/core/src/processors/event_message.rs @@ -47,7 +47,7 @@ where event: &Event, ) -> Result<(), Error> { // silently ignore if the model is not found - let model = match db.model(&format!("{:#x}", event.keys[MODEL_INDEX])).await { + let model = match db.model(event.keys[MODEL_INDEX]).await { Ok(model) => model, Err(_) => return Ok(()), }; diff --git a/crates/torii/core/src/processors/register_model.rs b/crates/torii/core/src/processors/register_model.rs index c57896e303..e7d36f494f 100644 --- a/crates/torii/core/src/processors/register_model.rs +++ b/crates/torii/core/src/processors/register_model.rs @@ -48,17 +48,24 @@ where event: &Event, ) -> Result<(), Error> { let name = ByteArray::cairo_deserialize(&event.data, 0)?; + let mut offset = ByteArray::cairo_serialized_size(&name); + let namespace = ByteArray::cairo_deserialize(&event.data, offset)?; + offset += ByteArray::cairo_serialized_size(&namespace); + let name = name.to_string()?; + let namespace = namespace.to_string()?; - let model = world.model_reader(&name).await?; + let model = world.model_reader(&namespace, &name).await?; let schema = model.schema().await?; let layout = model.layout().await?; let unpacked_size: u32 = model.unpacked_size().await?; let packed_size: u32 = model.packed_size().await?; - let class_hash = event.data[1]; - let contract_address = event.data[3]; + let class_hash = event.data[offset]; + // NOTE: offset + 1 is the prev_class_hash, as denoted in + // the ModelRegistered event. so contract address is at offset + 2 + let contract_address = event.data[offset + 2]; info!( target: LOG_TARGET, @@ -78,6 +85,7 @@ where ); db.register_model( + &namespace, schema, layout, class_hash, diff --git a/crates/torii/core/src/processors/store_del_record.rs b/crates/torii/core/src/processors/store_del_record.rs index bc53f5d98f..69439a7754 100644 --- a/crates/torii/core/src/processors/store_del_record.rs +++ b/crates/torii/core/src/processors/store_del_record.rs @@ -49,7 +49,7 @@ where ) -> Result<(), Error> { let selector = event.data[MODEL_INDEX]; - let model = db.model(&format!("{:#x}", selector)).await?; + let model = db.model(selector).await?; info!( target: LOG_TARGET, diff --git a/crates/torii/core/src/processors/store_set_record.rs b/crates/torii/core/src/processors/store_set_record.rs index 548fe49fbe..30b63302d4 100644 --- a/crates/torii/core/src/processors/store_set_record.rs +++ b/crates/torii/core/src/processors/store_set_record.rs @@ -50,7 +50,7 @@ where ) -> Result<(), Error> { let selector = event.data[MODEL_INDEX]; - let model = db.model(&format!("{:#x}", selector)).await?; + let model = db.model(selector).await?; info!( target: LOG_TARGET, diff --git a/crates/torii/core/src/sql.rs b/crates/torii/core/src/sql.rs index eaafa3a92a..6b530e602e 100644 --- a/crates/torii/core/src/sql.rs +++ b/crates/torii/core/src/sql.rs @@ -6,11 +6,11 @@ use chrono::Utc; use dojo_types::primitive::Primitive; use dojo_types::schema::{EnumOption, Member, Ty}; use dojo_world::contracts::abi::model::Layout; +use dojo_world::contracts::naming::compute_model_selector_from_names; use dojo_world::metadata::WorldMetadata; use sqlx::pool::PoolConnection; use sqlx::{Pool, Sqlite}; use starknet::core::types::{Event, Felt, InvokeTransaction, Transaction}; -use starknet::core::utils::get_selector_from_name; use starknet_crypto::poseidon_hash_many; use super::World; @@ -96,6 +96,7 @@ impl Sql { #[allow(clippy::too_many_arguments)] pub async fn register_model( &mut self, + namespace: &str, model: Ty, layout: Layout, class_hash: Felt, @@ -104,16 +105,19 @@ impl Sql { unpacked_size: u32, block_timestamp: u64, ) -> Result<()> { + let selector = compute_model_selector_from_names(namespace, &model.name()); + let insert_models = - "INSERT INTO models (id, name, class_hash, contract_address, layout, packed_size, \ - unpacked_size, executed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO \ - UPDATE SET contract_address=EXCLUDED.contract_address, \ + "INSERT INTO models (id, namespace, name, class_hash, contract_address, layout, \ + packed_size, unpacked_size, executed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON \ + CONFLICT(id) DO UPDATE SET contract_address=EXCLUDED.contract_address, \ class_hash=EXCLUDED.class_hash, layout=EXCLUDED.layout, \ packed_size=EXCLUDED.packed_size, unpacked_size=EXCLUDED.unpacked_size, \ executed_at=EXCLUDED.executed_at RETURNING *"; let model_registered: ModelRegistered = sqlx::query_as(insert_models) // this is temporary until the model hash is precomputed - .bind(&format!("{:#x}", &get_selector_from_name(&model.name())?)) + .bind(&format!("{:#x}", selector)) + .bind(namespace) .bind(model.name()) .bind(format!("{class_hash:#x}")) .bind(format!("{contract_address:#x}")) @@ -126,8 +130,9 @@ impl Sql { let mut model_idx = 0_i64; self.build_register_queries_recursive( + selector, &model, - vec![model.name()], + vec![format!("{}-{}", namespace, model.name())], &mut model_idx, block_timestamp, &mut 0, @@ -156,14 +161,17 @@ impl Sql { return Err(anyhow!("Entity is not a struct")); }; + let namespaced_name = entity.name(); + let (model_namespace, model_name) = namespaced_name.split_once('-').unwrap(); + let entity_id = format!("{:#x}", poseidon_hash_many(&keys)); + let model_id = + format!("{:#x}", compute_model_selector_from_names(model_namespace, model_name)); + self.query_queue.enqueue( "INSERT INTO entity_model (entity_id, model_id) VALUES (?, ?) ON CONFLICT(entity_id, \ model_id) DO NOTHING", - vec![ - Argument::String(entity_id.clone()), - Argument::String(format!("{:#x}", get_selector_from_name(&entity.name())?)), - ], + vec![Argument::String(entity_id.clone()), Argument::String(model_id.clone())], ); let keys_str = felts_sql_string(&keys); @@ -181,7 +189,7 @@ impl Sql { entity_updated.updated_model = Some(entity.clone()); - let path = vec![entity.name()]; + let path = vec![namespaced_name]; self.build_set_entity_queries_recursive( path, event_id, @@ -213,14 +221,17 @@ impl Sql { return Err(anyhow!("Entity is not a struct")); }; + let namespaced_name = entity.name(); + let (model_namespace, model_name) = namespaced_name.split_once('-').unwrap(); + let entity_id = format!("{:#x}", poseidon_hash_many(&keys)); + let model_id = + format!("{:#x}", compute_model_selector_from_names(model_namespace, model_name)); + self.query_queue.enqueue( "INSERT INTO event_model (entity_id, model_id) VALUES (?, ?) ON CONFLICT(entity_id, \ model_id) DO NOTHING", - vec![ - Argument::String(entity_id.clone()), - Argument::String(format!("{:#x}", get_selector_from_name(&entity.name())?)), - ], + vec![Argument::String(entity_id.clone()), Argument::String(model_id.clone())], ); let keys_str = felts_sql_string(&keys); @@ -238,7 +249,7 @@ impl Sql { event_message_updated.updated_model = Some(entity.clone()); - let path = vec![entity.name()]; + let path = vec![namespaced_name]; self.build_set_entity_queries_recursive( path, event_id, @@ -317,10 +328,12 @@ impl Sql { Ok(()) } - pub async fn model(&self, model: &str) -> Result { - match ModelSQLReader::new(model, self.pool.clone()).await { + pub async fn model(&self, selector: Felt) -> Result { + match ModelSQLReader::new(selector, self.pool.clone()).await { Ok(reader) => Ok(reader), - Err(e) => Err(anyhow::anyhow!("Failed to get model from db for selector {model}: {e}")), + Err(e) => { + Err(anyhow::anyhow!("Failed to get model from db for selector {selector:#x}: {e}")) + } } } @@ -423,8 +436,10 @@ impl Sql { }); } + #[allow(clippy::too_many_arguments)] fn build_register_queries_recursive( &mut self, + selector: Felt, model: &Ty, path: Vec, model_idx: &mut i64, @@ -440,6 +455,7 @@ impl Sql { } self.build_model_query( + selector, path.clone(), model, *model_idx, @@ -459,6 +475,7 @@ impl Sql { path_clone.push(pathname.to_string()); self.build_register_queries_recursive( + selector, member, path_clone, &mut (*model_idx + 1), @@ -770,8 +787,10 @@ impl Sql { } } + #[allow(clippy::too_many_arguments)] fn build_model_query( &mut self, + selector: Felt, path: Vec, model: &Ty, model_idx: i64, @@ -802,7 +821,7 @@ impl Sql { create_table_query .push_str(&format!("external_{name} {}, ", cairo_type.to_sql_type())); indices.push(format!( - "CREATE INDEX IF NOT EXISTS idx_{table_id}_{name} ON [{table_id}] \ + "CREATE INDEX IF NOT EXISTS [idx_{table_id}_{name}] ON [{table_id}] \ (external_{name});" )); } else if let Ty::Enum(e) = &ty { @@ -821,7 +840,7 @@ impl Sql { create_table_query.push_str(if array_idx > 0 { ", " } else { "NOT NULL, " }); indices.push(format!( - "CREATE INDEX IF NOT EXISTS idx_{table_id}_{name} ON [{table_id}] \ + "CREATE INDEX IF NOT EXISTS [idx_{table_id}_{name}] ON [{table_id}] \ (external_{name});" )); @@ -836,7 +855,7 @@ impl Sql { } else if let Ty::ByteArray(_) = &ty { create_table_query.push_str(&format!("external_{name} TEXT, ")); indices.push(format!( - "CREATE INDEX IF NOT EXISTS idx_{table_id}_{name} ON [{table_id}] \ + "CREATE INDEX IF NOT EXISTS [idx_{table_id}_{name}] ON [{table_id}] \ (external_{name});" )); } @@ -859,10 +878,7 @@ impl Sql { let arguments = vec![ Argument::String(table_id.clone()), // TEMP: this is temporary until the model hash is precomputed - Argument::String(format!( - "{:#x}", - get_selector_from_name(&path[0].clone()).unwrap() - )), + Argument::String(format!("{:#x}", selector)), Argument::Int(model_idx), Argument::Int(member_idx as i64), Argument::String(name), @@ -888,10 +904,7 @@ impl Sql { let arguments = vec![ Argument::String(table_id.clone()), // TEMP: this is temporary until the model hash is precomputed - Argument::String(format!( - "{:#x}", - get_selector_from_name(&path[0].clone()).unwrap() - )), + Argument::String(format!("{:#x}", selector)), Argument::Int(model_idx), Argument::Int(idx as i64), Argument::String(format!("_{}", idx)), @@ -918,10 +931,7 @@ impl Sql { let arguments = vec![ Argument::String(table_id.clone()), // TEMP: this is temporary until the model hash is precomputed - Argument::String(format!( - "{:#x}", - get_selector_from_name(&path[0].clone()).unwrap() - )), + Argument::String(format!("{:#x}", selector)), Argument::Int(model_idx), Argument::Int(0), Argument::String("data".to_string()), @@ -960,10 +970,7 @@ impl Sql { let arguments = vec![ Argument::String(table_id.clone()), // TEMP: this is temporary until the model hash is precomputed - Argument::String(format!( - "{:#x}", - get_selector_from_name(&path[0].clone()).unwrap() - )), + Argument::String(format!("{:#x}", selector)), Argument::Int(model_idx), Argument::Int(idx as i64), Argument::String(child.name.clone()), diff --git a/crates/torii/core/src/sql_test.rs b/crates/torii/core/src/sql_test.rs index 9edc00a56b..8477470734 100644 --- a/crates/torii/core/src/sql_test.rs +++ b/crates/torii/core/src/sql_test.rs @@ -3,11 +3,13 @@ use std::str::FromStr; use camino::Utf8PathBuf; use dojo_test_utils::compiler; use dojo_test_utils::migration::prepare_migration; +use dojo_world::contracts::naming::compute_model_selector_from_names; use dojo_world::contracts::world::WorldContractReader; -use dojo_world::metadata::dojo_metadata_from_workspace; +use dojo_world::metadata::{dojo_metadata_from_workspace, get_default_namespace_from_ws}; use dojo_world::migration::TxnConfig; use dojo_world::utils::{TransactionExt, TransactionWaiter}; use katana_runner::KatanaRunner; +use scarb::compiler::Profile; use scarb::ops; use sozo_ops::migration::execute_strategy; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; @@ -66,7 +68,7 @@ async fn test_load_from_remote() { let source_project_dir = Utf8PathBuf::from("../../../examples/spawn-and-move/"); let dojo_core_path = Utf8PathBuf::from("../../dojo-core"); - let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path); + let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path, Profile::DEV); let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); let dojo_metadata = dojo_metadata_from_workspace(&ws).expect("No current package with dojo metadata found."); @@ -75,9 +77,15 @@ async fn test_load_from_remote() { let base_dir = manifest_path.parent().unwrap(); let target_dir = format!("{}/target/dev", base_dir); - let mut migration = - prepare_migration(base_dir.into(), target_dir.into(), dojo_metadata.skip_migration) - .unwrap(); + let default_namespace = get_default_namespace_from_ws(&ws); + + let mut migration = prepare_migration( + base_dir.into(), + target_dir.into(), + dojo_metadata.skip_migration, + &default_namespace, + ) + .unwrap(); migration.resolve_variable(migration.world_address().unwrap()).unwrap(); let sequencer = KatanaRunner::new().expect("Failed to start runner."); @@ -125,39 +133,54 @@ async fn test_load_from_remote() { let models = sqlx::query("SELECT * FROM models").fetch_all(&pool).await.unwrap(); assert_eq!(models.len(), 8); - 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'", - ) - .fetch_one(&pool) - .await - .unwrap(); + let (id, name, namespace, packed_size, unpacked_size): (String, String, String, u8, u8) = + sqlx::query_as( + "SELECT id, name, namespace, packed_size, unpacked_size FROM models WHERE name = \ + 'Position'", + ) + .fetch_one(&pool) + .await + .unwrap(); - assert_eq!(id, format!("{:#x}", get_selector_from_name("Position").unwrap())); + assert_eq!( + id, + format!("{:#x}", compute_model_selector_from_names("dojo_examples", "Position")) + ); assert_eq!(name, "Position"); + assert_eq!(namespace, "dojo_examples"); assert_eq!(packed_size, 1); 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 = 'Moves'", - ) - .fetch_one(&pool) - .await - .unwrap(); + let (id, name, namespace, packed_size, unpacked_size): (String, String, String, u8, u8) = + sqlx::query_as( + "SELECT id, name, namespace, packed_size, unpacked_size FROM models WHERE name = \ + 'Moves'", + ) + .fetch_one(&pool) + .await + .unwrap(); - assert_eq!(id, format!("{:#x}", get_selector_from_name("Moves").unwrap())); + assert_eq!(id, format!("{:#x}", compute_model_selector_from_names("dojo_examples", "Moves"))); assert_eq!(name, "Moves"); + assert_eq!(namespace, "dojo_examples"); assert_eq!(packed_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'", - ) - .fetch_one(&pool) - .await - .unwrap(); + let (id, name, namespace, packed_size, unpacked_size): (String, String, String, u8, u8) = + sqlx::query_as( + "SELECT id, name, namespace, packed_size, unpacked_size FROM models WHERE name = \ + 'PlayerConfig'", + ) + .fetch_one(&pool) + .await + .unwrap(); - assert_eq!(id, format!("{:#x}", get_selector_from_name("PlayerConfig").unwrap())); + assert_eq!( + id, + format!("{:#x}", compute_model_selector_from_names("dojo_examples", "PlayerConfig")) + ); assert_eq!(name, "PlayerConfig"); + assert_eq!(namespace, "dojo_examples"); assert_eq!(packed_size, 0); assert_eq!(unpacked_size, 0); @@ -190,7 +213,7 @@ async fn test_load_from_remote_del() { let source_project_dir = Utf8PathBuf::from("../../../examples/spawn-and-move/"); let dojo_core_path = Utf8PathBuf::from("../../dojo-core"); - let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path); + let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path, Profile::DEV); let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); let dojo_metadata = dojo_metadata_from_workspace(&ws).expect("No current package with dojo metadata found."); @@ -199,9 +222,16 @@ async fn test_load_from_remote_del() { let base_dir = manifest_path.parent().unwrap(); let target_dir = format!("{}/target/dev", base_dir); - let mut migration = - prepare_migration(base_dir.into(), target_dir.into(), dojo_metadata.skip_migration) - .unwrap(); + let default_namespace = get_default_namespace_from_ws(&ws); + + let mut migration = prepare_migration( + base_dir.into(), + target_dir.into(), + dojo_metadata.skip_migration, + &default_namespace, + ) + .unwrap(); + migration.resolve_variable(migration.world_address().unwrap()).unwrap(); let sequencer = KatanaRunner::new().expect("Failed to start runner."); @@ -283,9 +313,9 @@ async fn test_load_from_remote_del() { let mut db = Sql::new(pool.clone(), world_address).await.unwrap(); let _ = bootstrap_engine(world, db.clone(), &provider).await; - assert_eq!(count_table("PlayerConfig", &pool).await, 0); - assert_eq!(count_table("PlayerConfig$favorite_item", &pool).await, 0); - assert_eq!(count_table("PlayerConfig$items", &pool).await, 0); + assert_eq!(count_table("dojo_examples-PlayerConfig", &pool).await, 0); + assert_eq!(count_table("dojo_examples-PlayerConfig$favorite_item", &pool).await, 0); + assert_eq!(count_table("dojo_examples-PlayerConfig$items", &pool).await, 0); // TODO: check how we can have a test that is more chronological with Torii re-syncing // to ensure we can test intermediate states. @@ -302,7 +332,7 @@ async fn test_load_from_remote_del() { /// # Returns /// The number of rows in the table. async fn count_table(table_name: &str, pool: &sqlx::Pool) -> i64 { - let count_query = format!("SELECT COUNT(*) FROM {}", table_name); + let count_query = format!("SELECT COUNT(*) FROM [{}]", table_name); let count: (i64,) = sqlx::query_as(&count_query).fetch_one(pool).await.unwrap(); count.0 diff --git a/crates/torii/core/src/types.rs b/crates/torii/core/src/types.rs index b92f9e2364..2eec61cbcb 100644 --- a/crates/torii/core/src/types.rs +++ b/crates/torii/core/src/types.rs @@ -62,6 +62,7 @@ pub struct EventMessage { #[serde(rename_all = "camelCase")] pub struct Model { pub id: String, + pub namespace: String, pub name: String, pub class_hash: String, pub contract_address: String, diff --git a/crates/torii/graphql/Cargo.toml b/crates/torii/graphql/Cargo.toml index ae43a05ba6..192a4cfcb7 100644 --- a/crates/torii/graphql/Cargo.toml +++ b/crates/torii/graphql/Cargo.toml @@ -41,6 +41,7 @@ warp.workspace = true camino.workspace = true dojo-test-utils = { path = "../../dojo-test-utils", features = [ "build-examples" ] } dojo-world = { path = "../../dojo-world" } +katana-runner.workspace = true scarb.workspace = true serial_test = "2.0.0" starknet-crypto.workspace = true diff --git a/crates/torii/graphql/src/object/entity.rs b/crates/torii/graphql/src/object/entity.rs index 75013b4032..f37873c16b 100644 --- a/crates/torii/graphql/src/object/entity.rs +++ b/crates/torii/graphql/src/object/entity.rs @@ -19,7 +19,7 @@ use crate::mapping::ENTITY_TYPE_MAPPING; use crate::object::{resolve_many, resolve_one}; use crate::query::{type_mapping_query, value_mapping_from_row}; use crate::types::TypeData; -use crate::utils::extract; +use crate::utils; pub struct EntityObject; impl BasicObject for EntityObject { @@ -117,11 +117,11 @@ fn model_union_field() -> Field { Value::Object(indexmap) => { let mut conn = ctx.data::>()?.acquire().await?; - let entity_id = extract::(indexmap, "id")?; + let entity_id = utils::extract::(indexmap, "id")?; // fetch name from the models table // using the model id (hashed model name) - let model_ids: Vec<(String, String)> = sqlx::query_as( - "SELECT id, name + let model_ids: Vec<(String, String, String)> = sqlx::query_as( + "SELECT id, namespace, name FROM models WHERE id IN ( SELECT model_id @@ -134,14 +134,14 @@ fn model_union_field() -> Field { .await?; let mut results: Vec> = Vec::new(); - for (id, name) in model_ids { + for (id, namespace, name) in model_ids { // the model id in the model mmeebrs table is the hashed model name (id) let type_mapping = type_mapping_query(&mut conn, &id).await?; // but the table name for the model data is the unhashed model name let data: ValueMapping = match model_data_recursive_query( &mut conn, - vec![name.clone()], + vec![format!("{namespace}-{name}")], &entity_id, &[], &type_mapping, @@ -153,7 +153,10 @@ fn model_union_field() -> Field { _ => unreachable!(), }; - results.push(FieldValue::with_type(FieldValue::owned_any(data), name)); + results.push(FieldValue::with_type( + FieldValue::owned_any(data), + utils::type_name_from_names(&namespace, &name), + )) } Ok(Some(FieldValue::list(results))) @@ -177,7 +180,7 @@ pub async fn model_data_recursive_query( // For nested types, we need to remove prefix in path array let namespace = format!("{}_", path_array[0]); let table_name = &path_array.join("$").replace(&namespace, ""); - let mut query = format!("SELECT * FROM {} WHERE entity_id = '{}' ", table_name, entity_id); + let mut query = format!("SELECT * FROM [{}] WHERE entity_id = '{}' ", table_name, entity_id); for (column_idx, index) in indexes.iter().enumerate() { query.push_str(&format!("AND idx_{} = {} ", column_idx, index)); } diff --git a/crates/torii/graphql/src/object/model.rs b/crates/torii/graphql/src/object/model.rs index a9ecb13607..78428d2f67 100644 --- a/crates/torii/graphql/src/object/model.rs +++ b/crates/torii/graphql/src/object/model.rs @@ -105,6 +105,7 @@ impl ModelObject { IndexMap::from([ (Name::new("id"), Value::from(model.id)), (Name::new("name"), Value::from(model.name)), + (Name::new("namespace"), Value::from(model.namespace)), (Name::new("classHash"), Value::from(model.class_hash)), (Name::new("contractAddress"), Value::from(model.contract_address)), (Name::new("transactionHash"), Value::from(model.transaction_hash)), diff --git a/crates/torii/graphql/src/object/model_data.rs b/crates/torii/graphql/src/object/model_data.rs index 4b221f9146..8a5c555652 100644 --- a/crates/torii/graphql/src/object/model_data.rs +++ b/crates/torii/graphql/src/object/model_data.rs @@ -16,7 +16,7 @@ use crate::mapping::ENTITY_TYPE_MAPPING; use crate::query::data::{count_rows, fetch_multiple_rows, fetch_single_row}; use crate::query::value_mapping_from_row; use crate::types::TypeData; -use crate::utils::extract; +use crate::utils; #[derive(FromRow, Deserialize, PartialEq, Eq, Debug)] pub struct ModelMember { @@ -64,9 +64,13 @@ impl BasicObject for ModelDataObject { } fn objects(&self) -> Vec { + let mut parts = self.type_name().split('_').collect::>(); + let model = parts.pop().unwrap(); + let namespace = parts.join("_"); + let type_name = utils::struct_name_from_names(&namespace, model); let mut objects = data_objects_recursion( &TypeData::Nested((TypeRef::named(self.type_name()), self.type_mapping.clone())), - &vec![self.type_name().to_string()], + &vec![type_name], ); // root object requires entity_field association @@ -96,7 +100,10 @@ impl ResolvableObject for ModelDataObject { let mut field = Field::new(self.name().1, TypeRef::named(field_type), move |ctx| { let type_mapping = type_mapping.clone(); let where_mapping = where_mapping.clone(); - let type_name = type_name.clone(); + let mut parts = type_name.split('_').collect::>(); + let model = parts.pop().unwrap(); + let namespace = parts.join("_"); + let type_name = utils::struct_name_from_names(&namespace, model); FieldFuture::new(async move { let mut conn = ctx.data::>()?.acquire().await?; @@ -188,7 +195,7 @@ pub fn object(type_name: &str, type_mapping: &TypeMapping, path_array: Vec { let mut conn = ctx.data::>()?.acquire().await?; let entity_id = - extract::(indexmap, INTERNAL_ENTITY_ID_KEY)?; + utils::extract::(indexmap, INTERNAL_ENTITY_ID_KEY)?; // if we already fetched our model data, return it if let Some(data) = indexmap.get(&field_name) { @@ -242,7 +249,7 @@ fn entity_field() -> Field { match ctx.parent_value.try_to_value()? { Value::Object(indexmap) => { let mut conn = ctx.data::>()?.acquire().await?; - let entity_id = extract::(indexmap, INTERNAL_ENTITY_ID_KEY)?; + let entity_id = utils::extract::(indexmap, INTERNAL_ENTITY_ID_KEY)?; let data = fetch_single_row(&mut conn, ENTITY_TABLE, ID_COLUMN, &entity_id).await?; let entity = value_mapping_from_row(&data, &ENTITY_TYPE_MAPPING, false)?; diff --git a/crates/torii/graphql/src/query/data.rs b/crates/torii/graphql/src/query/data.rs index b81ae3b8b4..20d68debca 100644 --- a/crates/torii/graphql/src/query/data.rs +++ b/crates/torii/graphql/src/query/data.rs @@ -13,7 +13,7 @@ pub async fn count_rows( keys: &Option>, filters: &Option>, ) -> Result { - let mut query = format!("SELECT COUNT(*) FROM {}", table_name); + let mut query = format!("SELECT COUNT(*) FROM [{}]", table_name); let conditions = build_conditions(keys, filters); if !conditions.is_empty() { @@ -36,7 +36,7 @@ pub async fn fetch_single_row( id_column: &str, id: &str, ) -> sqlx::Result { - let query = format!("SELECT * FROM {} WHERE {} = '{}'", table_name, id_column, id); + let query = format!("SELECT * FROM [{}] WHERE {} = '{}'", table_name, id_column, id); sqlx::query(&query).fetch_one(conn).await } @@ -63,7 +63,7 @@ pub async fn fetch_multiple_rows( conditions.push(handle_cursor(before_cursor, order, CursorDirection::Before, id_column)?); } - let mut query = format!("SELECT * FROM {}", table_name); + let mut query = format!("SELECT * FROM [{}]", table_name); if !conditions.is_empty() { query.push_str(&format!(" WHERE {}", conditions.join(" AND "))); } diff --git a/crates/torii/graphql/src/query/mod.rs b/crates/torii/graphql/src/query/mod.rs index b66c37b8fd..46a1558517 100644 --- a/crates/torii/graphql/src/query/mod.rs +++ b/crates/torii/graphql/src/query/mod.rs @@ -145,9 +145,10 @@ fn parse_nested_type(member: &ModelMember, nested_members: &[&ModelMember]) -> T // sanitizes the member type string // for eg. Position_Array -> Position_ArrayVec2 // Position_(u8, Vec2) -> Position_u8Vec2 - let re = Regex::new(r"[, ()<>]").unwrap(); - let member_type_name = re.replace_all(&member.ty, ""); - let namespaced = format!("{}_{}", model_name, member_type_name); + let re = Regex::new(r"[, ()<>-]").unwrap(); + let sanitized_model_name = model_name.replace('-', "_"); + let sanitized_member_type_name = re.replace_all(&member.ty, ""); + let namespaced = format!("{}_{}", sanitized_model_name, sanitized_member_type_name); TypeData::Nested((TypeRef::named(namespaced), nested_mapping)) } diff --git a/crates/torii/graphql/src/schema.rs b/crates/torii/graphql/src/schema.rs index c2b9dc1787..48a915345b 100644 --- a/crates/torii/graphql/src/schema.rs +++ b/crates/torii/graphql/src/schema.rs @@ -1,6 +1,5 @@ use anyhow::Result; use async_graphql::dynamic::{Object, Scalar, Schema, Subscription, Union}; -use convert_case::{Case, Casing}; use sqlx::SqlitePool; use torii_core::types::Model; @@ -9,6 +8,7 @@ use super::object::entity::EntityObject; use super::object::event::EventObject; use super::object::model_data::ModelDataObject; use super::types::ScalarType; +use super::utils; use crate::constants::{QUERY_TYPE_NAME, SUBSCRIPTION_TYPE_NAME}; use crate::object::event_message::EventMessageObject; use crate::object::metadata::content::ContentObject; @@ -127,8 +127,9 @@ async fn build_objects(pool: &SqlitePool) -> Result<(Vec, Vec Result<(Vec, Vec Result { let source_project_dir = Utf8PathBuf::from("../types-test"); let dojo_core_path = Utf8PathBuf::from("../../dojo-core"); - let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path); + let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path, Profile::DEV); let ws = ops::read_workspace(config.manifest_path(), &config) .unwrap_or_else(|op| panic!("Error building workspace: {op:?}")); @@ -291,33 +290,40 @@ pub async fn spinup_types_test() -> Result { dojo_metadata_from_workspace(&ws).expect("No current package with dojo metadata found."); let target_path = ws.target_dir().path_existent().unwrap().join(config.profile().to_string()); - let migration = - prepare_migration(source_project_dir, target_path, dojo_metadata.skip_migration).unwrap(); - let config = build_test_config("../types-test/Scarb.toml").unwrap(); + + let default_namespace = get_default_namespace_from_ws(&ws); + + let mut migration = prepare_migration( + source_project_dir, + target_path, + dojo_metadata.skip_migration, + &default_namespace, + ) + .unwrap(); + + migration.resolve_variable(migration.world.clone().unwrap().contract_address).unwrap(); + let db = Sql::new(pool.clone(), migration.world_address().unwrap()).await.unwrap(); - let sequencer = - TestSequencer::start(SequencerConfig::default(), get_default_test_starknet_config()).await; + let sequencer = KatanaRunner::new().expect("Failed to start runner."); - let mut account = sequencer.account(); - account.set_block_id(BlockId::Tag(BlockTag::Pending)); + let account = sequencer.account(0); let provider = JsonRpcClient::new(HttpTransport::new(sequencer.url())); - let world = WorldContractReader::new(migration.world_address().unwrap(), &provider); - let ws = ops::read_workspace(config.manifest_path(), &config) - .unwrap_or_else(|op| panic!("Error building workspace: {op:?}")); - execute_strategy(&ws, &migration, &account, TxnConfig::init_wait()).await.unwrap(); + let world = WorldContractReader::new(migration.world_address().unwrap(), &provider); - let manifest = - DeploymentManifest::load_from_remote(&provider, migration.world_address().unwrap()) - .await - .unwrap(); + let output = execute_strategy(&ws, &migration, &account, TxnConfig::init_wait()).await.unwrap(); // Execute `create` and insert 11 records into storage - let records_contract = - manifest.contracts.iter().find(|contract| contract.name.eq("records")).unwrap(); - let record_contract_address = records_contract.inner.address.unwrap(); + let records_contract = output + .contracts + .iter() + .find(|contract| contract.as_ref().unwrap().tag.eq("types_test-records")) + .unwrap(); + + let record_contract_address = records_contract.as_ref().unwrap().contract_address; + let InvokeTransactionResult { transaction_hash } = account .execute_v1(vec![Call { calldata: vec![Felt::from_str("0xa").unwrap()], diff --git a/crates/torii/graphql/src/tests/models_test.rs b/crates/torii/graphql/src/tests/models_test.rs index 99cd71ffcb..66bda6902b 100644 --- a/crates/torii/graphql/src/tests/models_test.rs +++ b/crates/torii/graphql/src/tests/models_test.rs @@ -16,7 +16,7 @@ mod tests { let query = format!( r#" {{ - recordSiblingModels {} {{ + typesTestRecordSiblingModels {} {{ totalCount edges {{ cursor @@ -39,14 +39,18 @@ mod tests { ); let result = run_graphql_query(schema, &query).await; - result.get("recordSiblingModels").ok_or("recordSiblingModels not found").unwrap().clone() + result + .get("typesTestRecordSiblingModels") + .ok_or("typesTestRecordSiblingModels not found") + .unwrap() + .clone() } async fn subrecord_model_query(schema: &Schema, arg: &str) -> Value { let query = format!( r#" {{ - subrecordModels {} {{ + typesTestSubrecordModels {} {{ totalCount edges {{ cursor @@ -71,14 +75,18 @@ mod tests { ); let result = run_graphql_query(schema, &query).await; - result.get("subrecordModels").ok_or("subrecordModels not found").unwrap().clone() + result + .get("typesTestSubrecordModels") + .ok_or("typesTestSubrecordModels not found") + .unwrap() + .clone() } async fn records_model_query(schema: &Schema, arg: &str) -> Value { let query = format!( r#" {{ - recordModels {} {{ + typesTestRecordModels {} {{ totalCount edges {{ cursor @@ -146,7 +154,11 @@ mod tests { ); let result = run_graphql_query(schema, &query).await; - result.get("recordModels").ok_or("recordModels not found").unwrap().clone() + result + .get("typesTestRecordModels") + .ok_or("typesTestRecordModels not found") + .unwrap() + .clone() } // End to end test spins up a test sequencer and deploys types-test project, this takes a while @@ -169,7 +181,7 @@ mod tests { let nested_two = record.node.type_nested_two.as_ref().unwrap(); assert_eq!(connection.total_count, 10); assert_eq!(connection.edges.len(), 10); - assert_eq!(&record.node.__typename, "Record"); + assert_eq!(&record.node.__typename, "types_test_Record"); assert_eq!(entity.keys.clone().unwrap(), vec!["0x0"]); assert_eq!(record.node.depth, "Zero"); assert_eq!(deeply_nested.depth, "One"); diff --git a/crates/torii/graphql/src/tests/subscription_test.rs b/crates/torii/graphql/src/tests/subscription_test.rs index 09b84c40cd..1bc74b6e98 100644 --- a/crates/torii/graphql/src/tests/subscription_test.rs +++ b/crates/torii/graphql/src/tests/subscription_test.rs @@ -7,15 +7,16 @@ mod tests { use dojo_types::primitive::Primitive; use dojo_types::schema::{Enum, EnumOption, Member, Struct, Ty}; use dojo_world::contracts::abi::model::Layout; + use dojo_world::contracts::naming::compute_model_selector_from_names; use serial_test::serial; use sqlx::SqlitePool; use starknet::core::types::{Event, Felt}; - use starknet::core::utils::get_selector_from_name; use starknet_crypto::poseidon_hash_many; use tokio::sync::mpsc; use torii_core::sql::Sql; use crate::tests::{model_fixtures, run_graphql_subscription}; + use crate::utils; #[sqlx::test(migrations = "../migrations")] #[serial] @@ -24,17 +25,20 @@ mod tests { model_fixtures(&mut db).await; // 0. Preprocess expected entity value + let namespace = "types_test".to_string(); let model_name = "Record".to_string(); let key = vec![Felt::ONE]; let entity_id = format!("{:#x}", poseidon_hash_many(&key)); let keys_str = key.iter().map(|k| format!("{:#x}", k)).collect::>().join(","); let block_timestamp = 1710754478_u64; + let type_name = utils::type_name_from_names(&namespace, &model_name); + let expected_value: async_graphql::Value = value!({ "entityUpdated": { "id": entity_id, "keys":vec![keys_str], "models" : [{ - "__typename": model_name, + "__typename": type_name, "depth": "Zero", "record_id": 0, "typeU16": 1, @@ -54,7 +58,7 @@ mod tests { // Set entity with one Record model db.set_entity( Ty::Struct(Struct { - name: model_name, + name: utils::struct_name_from_names(&namespace, &model_name), children: vec![ Member { name: "depth".to_string(), @@ -120,7 +124,7 @@ mod tests { keys models { __typename - ... on Record { + ... on types_test_Record { depth record_id typeU16 @@ -147,17 +151,20 @@ mod tests { model_fixtures(&mut db).await; // 0. Preprocess expected entity value + let namespace = "types_test".to_string(); let model_name = "Record".to_string(); let key = vec![Felt::ONE]; let entity_id = format!("{:#x}", poseidon_hash_many(&key)); let block_timestamp = 1710754478_u64; let keys_str = key.iter().map(|k| format!("{:#x}", k)).collect::>().join(","); + let type_name = utils::type_name_from_names(&namespace, &model_name); + let expected_value: async_graphql::Value = value!({ "entityUpdated": { "id": entity_id, "keys":vec![keys_str], "models" : [{ - "__typename": model_name, + "__typename": type_name, "depth": "Zero", "record_id": 0, "type_felt": format!("{:#x}", Felt::from(1u128)), @@ -174,7 +181,7 @@ mod tests { // Set entity with one Record model db.set_entity( Ty::Struct(Struct { - name: model_name, + name: utils::struct_name_from_names(&namespace, &model_name), children: vec![ Member { name: "depth".to_string(), @@ -225,7 +232,7 @@ mod tests { keys models { __typename - ... on Record { + ... on types_test_Record { depth record_id type_felt @@ -247,14 +254,16 @@ mod tests { async fn test_model_subscription(pool: SqlitePool) { let mut db = Sql::new(pool.clone(), Felt::ZERO).await.unwrap(); // 0. Preprocess model value + let namespace = "types_test".to_string(); let model_name = "Subrecord".to_string(); - let model_id = format!("{:#x}", get_selector_from_name(&model_name).unwrap()); + let model_id = format!("{:#x}", compute_model_selector_from_names(&namespace, &model_name)); let class_hash = Felt::TWO; let contract_address = Felt::THREE; let block_timestamp: u64 = 1710754478_u64; let expected_value: async_graphql::Value = value!({ - "modelRegistered": { "id": model_id, "name":model_name } + "modelRegistered": { "id": model_id, "name": model_name } }); + let (tx, mut rx) = mpsc::channel(7); tokio::spawn(async move { @@ -270,6 +279,7 @@ mod tests { }], }); db.register_model( + &namespace, model, Layout::Fixed(vec![]), class_hash, @@ -308,13 +318,14 @@ mod tests { async fn test_model_subscription_with_id(pool: SqlitePool) { let mut db = Sql::new(pool.clone(), Felt::ZERO).await.unwrap(); // 0. Preprocess model value + let namespace = "types_test".to_string(); let model_name = "Subrecord".to_string(); - let model_id = format!("{:#x}", get_selector_from_name(&model_name).unwrap()); + let model_id = format!("{:#x}", compute_model_selector_from_names(&namespace, &model_name)); let class_hash = Felt::TWO; let contract_address = Felt::THREE; let block_timestamp: u64 = 1710754478_u64; let expected_value: async_graphql::Value = value!({ - "modelRegistered": { "id": model_id, "name":model_name } + "modelRegistered": { "id": model_id, "name": model_name } }); let (tx, mut rx) = mpsc::channel(7); @@ -331,6 +342,7 @@ mod tests { }], }); db.register_model( + &namespace, model, Layout::Fixed(vec![]), class_hash, diff --git a/crates/torii/graphql/src/utils.rs b/crates/torii/graphql/src/utils.rs index a8353e04bb..8f49990d4a 100644 --- a/crates/torii/graphql/src/utils.rs +++ b/crates/torii/graphql/src/utils.rs @@ -1,4 +1,5 @@ use async_graphql::{Result, Value}; +use convert_case::{Case, Casing}; use crate::error::ExtractError; use crate::types::ValueMapping; @@ -45,3 +46,15 @@ pub fn extract( ) -> Result { T::extract(values, key) } + +pub fn field_name_from_names(namespace: &str, model_name: &str) -> String { + format!("{}{}", namespace.to_case(Case::Camel), model_name.to_case(Case::Pascal)) +} + +pub fn type_name_from_names(namespace: &str, model_name: &str) -> String { + format!("{}_{}", namespace, model_name) +} + +pub fn struct_name_from_names(namespace: &str, model_name: &str) -> String { + format!("{}-{}", namespace, model_name) +} diff --git a/crates/torii/grpc/Cargo.toml b/crates/torii/grpc/Cargo.toml index 73ade208de..7582eaf5ee 100644 --- a/crates/torii/grpc/Cargo.toml +++ b/crates/torii/grpc/Cargo.toml @@ -30,13 +30,14 @@ rand.workspace = true serde_json.workspace = true tower.workspace = true tracing.workspace = true +dojo-world = { path = "../../dojo-world", features = [ "contracts" ] } [dev-dependencies] camino.workspace = true dojo-test-utils.workspace = true -dojo-world = { path = "../../dojo-world", features = [ "contracts" ] } scarb.workspace = true sozo-ops.workspace = true +katana-runner.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] tonic-web-wasm-client.workspace = true diff --git a/crates/torii/grpc/proto/types.proto b/crates/torii/grpc/proto/types.proto index 9575820858..fdb49a1f92 100644 --- a/crates/torii/grpc/proto/types.proto +++ b/crates/torii/grpc/proto/types.proto @@ -17,32 +17,29 @@ message WorldMetadata { } message ModelMetadata { + // Model namespace + string namespace = 1; // Model name - string name = 1; + string name = 2; // Model size when it is packed for storage - uint32 packed_size = 2; + uint32 packed_size = 3; // Model size when it is unpacked - uint32 unpacked_size = 3; + uint32 unpacked_size = 4; // hex-encoded class hash of the component - string class_hash = 4; + string class_hash = 5; // The layout of the component in bytes - bytes layout = 5; + bytes layout = 6; // The schema of the component serialized in bytes (for simplicity sake) - bytes schema = 6; + bytes schema = 7; // hex-encoded contract address of the component - string contract_address = 7; -} - -message Model { - string name = 1; - repeated Member members = 2; + string contract_address = 8; } message Entity { // The entity's hashed keys bytes hashed_keys = 1; // Models of the entity - repeated Model models = 2; + repeated Struct models = 2; } message Event { @@ -100,8 +97,8 @@ message Clause { } message ModelKeysClause { - string model = 1; - repeated bytes keys = 2; + string model = 2; + repeated bytes keys = 3; } message EntityKeysClause { @@ -112,9 +109,9 @@ message EntityKeysClause { } message KeysClause { - repeated bytes keys = 1; - PatternMatching pattern_matching = 2; - repeated string models = 3; + repeated bytes keys = 2; + PatternMatching pattern_matching = 3; + repeated string models = 4; } message HashedKeysClause { @@ -122,16 +119,15 @@ message HashedKeysClause { } message MemberClause { - string model = 1; - string member = 2; - ComparisonOperator operator = 3; - Value value = 4; + string model = 2; + string member = 3; + ComparisonOperator operator = 4; + Value value = 5; } message CompositeClause { - string model = 1; - LogicalOperator operator = 2; - repeated Clause clauses = 3; + LogicalOperator operator = 3; + repeated Clause clauses = 4; } enum PatternMatching { diff --git a/crates/torii/grpc/src/server/mod.rs b/crates/torii/grpc/src/server/mod.rs index 9cd741ec89..55d7092865 100644 --- a/crates/torii/grpc/src/server/mod.rs +++ b/crates/torii/grpc/src/server/mod.rs @@ -13,15 +13,16 @@ use std::str::FromStr; use std::sync::Arc; use dojo_types::schema::Ty; +use dojo_world::contracts::naming::compute_model_selector_from_names; use futures::Stream; use proto::world::{ MetadataRequest, MetadataResponse, RetrieveEntitiesRequest, RetrieveEntitiesResponse, RetrieveEventsRequest, RetrieveEventsResponse, SubscribeModelsRequest, SubscribeModelsResponse, }; +use sqlx::prelude::FromRow; use sqlx::sqlite::SqliteRow; use sqlx::{Pool, Row, Sqlite}; use starknet::core::types::Felt; -use starknet::core::utils::{cairo_short_string_to_felt, get_selector_from_name}; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; use subscriptions::event::EventManager; @@ -125,23 +126,39 @@ impl DojoWorld { .fetch_one(&self.pool) .await?; - let models: Vec<(String, String, String, String, u32, u32, String)> = sqlx::query_as( - "SELECT id, name, class_hash, contract_address, packed_size, unpacked_size, layout \ - FROM models", + #[derive(FromRow)] + struct ModelDb { + id: String, + namespace: String, + name: String, + class_hash: String, + contract_address: String, + packed_size: u32, + unpacked_size: u32, + layout: String, + } + + let models: Vec = sqlx::query_as( + "SELECT id, namespace, name, class_hash, contract_address, packed_size, \ + unpacked_size, layout FROM models", ) .fetch_all(&self.pool) .await?; let mut models_metadata = Vec::with_capacity(models.len()); for model in models { - let schema = self.model_cache.schema(&model.0).await?; + let schema = self + .model_cache + .schema(&Felt::from_str(&model.id).map_err(ParseError::FromStr)?) + .await?; models_metadata.push(proto::types::ModelMetadata { - name: model.1, - class_hash: model.2, - contract_address: model.3, - packed_size: model.4, - unpacked_size: model.5, - layout: model.6.as_bytes().to_vec(), + namespace: model.namespace, + name: model.name, + class_hash: model.class_hash, + contract_address: model.contract_address, + packed_size: model.packed_size, + unpacked_size: model.unpacked_size, + layout: model.layout.as_bytes().to_vec(), schema: serde_json::to_vec(&schema).unwrap(), }); } @@ -263,9 +280,13 @@ impl DojoWorld { sqlx::query_as(&query).bind(limit).bind(offset).fetch_all(&self.pool).await?; let mut entities = Vec::with_capacity(db_entities.len()); - for (entity_id, models_str) in &db_entities { - let model_ids: Vec<&str> = models_str.split(',').collect(); - let schemas = self.model_cache.schemas(model_ids).await?; + for (entity_id, models_str) in db_entities { + let model_ids: Vec = models_str + .split(',') + .map(Felt::from_str) + .collect::>() + .map_err(ParseError::FromStr)?; + let schemas = self.model_cache.schemas(&model_ids).await?; let (entity_query, arrays_queries) = build_sql_query( &schemas, @@ -275,14 +296,16 @@ impl DojoWorld { Some(&format!("{table}.id = ?")), )?; - let row = sqlx::query(&entity_query).bind(entity_id).fetch_one(&self.pool).await?; + let row = + sqlx::query(&entity_query).bind(entity_id.clone()).fetch_one(&self.pool).await?; let mut arrays_rows = HashMap::new(); for (name, query) in arrays_queries { - let rows = sqlx::query(&query).bind(entity_id).fetch_all(&self.pool).await?; + let rows = + sqlx::query(&query).bind(entity_id.clone()).fetch_all(&self.pool).await?; arrays_rows.insert(name, rows); } - entities.push(map_row_to_entity(&row, &arrays_rows, &schemas)?); + entities.push(map_row_to_entity(&row, &arrays_rows, schemas.clone())?); } Ok((entities, total_count)) @@ -307,13 +330,24 @@ impl DojoWorld { {} "#, if !keys_clause.models.is_empty() { + // split the model names to namespace and model let model_ids = keys_clause .models .iter() - .map(|model| get_selector_from_name(model).map_err(ParseError::NonAsciiName)) + .map(|model| { + model + .split_once('-') + .ok_or(QueryError::InvalidNamespacedModel(model.clone())) + }) .collect::, _>>()?; - let model_ids_str = - model_ids.iter().map(|id| format!("'{:#x}'", id)).collect::>().join(","); + // get the model selector from namespace and model and format + let model_ids_str = model_ids + .iter() + .map(|(namespace, model)| { + format!("'{:#x}'", compute_model_selector_from_names(namespace, model)) + }) + .collect::>() + .join(","); format!( r#" JOIN {model_relation_table} ON {table}.id = {model_relation_table}.entity_id @@ -356,8 +390,10 @@ impl DojoWorld { .models .iter() .map(|model| { - let model_id = - get_selector_from_name(model).map_err(ParseError::NonAsciiName)?; + let (namespace, name) = model + .split_once('-') + .ok_or(QueryError::InvalidNamespacedModel(model.clone()))?; + let model_id = compute_model_selector_from_names(namespace, name); Ok(format!("INSTR(model_ids, '{:#x}') > 0", model_id)) }) .collect::, Error>>()? @@ -384,8 +420,12 @@ impl DojoWorld { let mut entities = Vec::with_capacity(db_entities.len()); for (entity_id, models_strs) in &db_entities { - let model_ids: Vec<&str> = models_strs.split(',').collect(); - let schemas = self.model_cache.schemas(model_ids).await?; + let model_ids: Vec = models_strs + .split(',') + .map(Felt::from_str) + .collect::>() + .map_err(ParseError::FromStr)?; + let schemas = self.model_cache.schemas(&model_ids).await?; let (entity_query, arrays_queries) = build_sql_query( &schemas, @@ -402,7 +442,7 @@ impl DojoWorld { arrays_rows.insert(name, rows); } - entities.push(map_row_to_entity(&row, &arrays_rows, &schemas)?); + entities.push(map_row_to_entity(&row, &arrays_rows, schemas.clone())?); } Ok((entities, total_count)) @@ -455,6 +495,11 @@ impl DojoWorld { let comparison_value = value_to_string(&value_type)?; + let (namespace, model) = member_clause + .model + .split_once('-') + .ok_or(QueryError::InvalidNamespacedModel(member_clause.model.clone()))?; + let models_query = format!( r#" SELECT group_concat({model_relation_table}.model_id) as model_ids @@ -464,12 +509,16 @@ impl DojoWorld { HAVING INSTR(model_ids, '{:#x}') > 0 LIMIT 1 "#, - get_selector_from_name(&member_clause.model).map_err(ParseError::NonAsciiName)? + compute_model_selector_from_names(namespace, model) ); let (models_str,): (String,) = sqlx::query_as(&models_query).fetch_one(&self.pool).await?; - let model_ids = models_str.split(',').collect::>(); - let schemas = self.model_cache.schemas(model_ids).await?; + let model_ids = models_str + .split(',') + .map(Felt::from_str) + .collect::, _>>() + .map_err(ParseError::FromStr)?; + let schemas = self.model_cache.schemas(&model_ids).await?; let table_name = member_clause.model; let column_name = format!("external_{}", member_clause.member); @@ -499,7 +548,7 @@ impl DojoWorld { let entities_collection = db_entities .iter() - .map(|row| map_row_to_entity(row, &arrays_rows, &schemas)) + .map(|row| map_row_to_entity(row, &arrays_rows, schemas.clone())) .collect::, Error>>()?; // Since there is not limit and offset, total_count is same as number of entities let total_count = entities_collection.len() as u32; @@ -555,8 +604,11 @@ impl DojoWorld { comparison_value, )); - let model_id = - get_selector_from_name(&member.model).map_err(ParseError::NonAsciiName)?; + let (namespace, model) = member + .model + .split_once('-') + .ok_or(QueryError::InvalidNamespacedModel(member.model.clone()))?; + let model_id: Felt = compute_model_selector_from_names(namespace, model); having_clauses.push(format!("INSTR(model_ids, '{:#x}') > 0", model_id)); } _ => return Err(QueryError::UnsupportedQuery.into()), @@ -569,13 +621,13 @@ impl DojoWorld { .into_iter() .map(|(column, op, value)| { bind_values.push(value); - format!("{}.{} {} ?", model, column, op) + format!("[{}].{} {} ?", model, column, op) }) .collect::>() .join(" AND "); join_clauses.push(format!( - "JOIN {} ON {}.id = {}.entity_id AND ({})", + "JOIN [{}] ON [{}].id = [{}].entity_id AND ({})", model, table, model, model_conditions )); } @@ -594,8 +646,8 @@ impl DojoWorld { let count_query = format!( r#" - SELECT COUNT(DISTINCT {table}.id) - FROM {table} + SELECT COUNT(DISTINCT [{table}].id) + FROM [{table}] {join_clause} {where_clause} "# @@ -614,14 +666,14 @@ impl DojoWorld { let query = format!( r#" - SELECT {table}.id, group_concat({model_relation_table}.model_id) as model_ids - FROM {table} - JOIN {model_relation_table} ON {table}.id = {model_relation_table}.entity_id + SELECT [{table}].id, group_concat({model_relation_table}.model_id) as model_ids + FROM [{table}] + JOIN {model_relation_table} ON [{table}].id = {model_relation_table}.entity_id {join_clause} {where_clause} - GROUP BY {table}.id + GROUP BY [{table}].id {having_clause} - ORDER BY {table}.event_id DESC + ORDER BY [{table}].event_id DESC LIMIT ? OFFSET ? "# ); @@ -636,15 +688,19 @@ impl DojoWorld { let mut entities = Vec::with_capacity(db_entities.len()); for (entity_id, models_str) in &db_entities { - let model_ids: Vec<&str> = models_str.split(',').collect(); - let schemas = self.model_cache.schemas(model_ids).await?; + let model_ids: Vec = models_str + .split(',') + .map(Felt::from_str) + .collect::>() + .map_err(ParseError::FromStr)?; + let schemas = self.model_cache.schemas(&model_ids).await?; let (entity_query, arrays_queries) = build_sql_query( &schemas, table, entity_relation_column, - Some(&format!("{table}.id = ?")), - Some(&format!("{table}.id = ?")), + Some(&format!("[{table}].id = ?")), + Some(&format!("[{table}].id = ?")), )?; let row = sqlx::query(&entity_query).bind(entity_id).fetch_one(&self.pool).await?; @@ -654,16 +710,19 @@ impl DojoWorld { arrays_rows.insert(name, rows); } - entities.push(map_row_to_entity(&row, &arrays_rows, &schemas)?); + entities.push(map_row_to_entity(&row, &arrays_rows, schemas.clone())?); } Ok((entities, total_count)) } - pub async fn model_metadata(&self, model: &str) -> Result { + pub async fn model_metadata( + &self, + namespace: &str, + name: &str, + ) -> Result { // selector - let model = - format!("{:#x}", get_selector_from_name(model).map_err(ParseError::NonAsciiName)?); + let model = compute_model_selector_from_names(namespace, name); let (name, class_hash, contract_address, packed_size, unpacked_size, layout): ( String, @@ -673,10 +732,10 @@ impl DojoWorld { u32, String, ) = sqlx::query_as( - "SELECT name, class_hash, contract_address, packed_size, unpacked_size, layout FROM \ - models WHERE id = ?", + "SELECT namespace, name, class_hash, contract_address, packed_size, unpacked_size, \ + layout FROM models WHERE id = ?", ) - .bind(&model) + .bind(format!("{:#x}", model)) .fetch_one(&self.pool) .await?; @@ -684,6 +743,7 @@ impl DojoWorld { let layout = layout.as_bytes().to_vec(); Ok(proto::types::ModelMetadata { + namespace: namespace.to_string(), name, layout, class_hash, @@ -700,16 +760,20 @@ impl DojoWorld { ) -> Result>, Error> { let mut subs = Vec::with_capacity(models_keys.len()); for keys in models_keys { - let model = cairo_short_string_to_felt(&keys.model) - .map_err(ParseError::CairoShortStringToFelt)?; + let (namespace, model) = keys + .model + .split_once('-') + .ok_or(QueryError::InvalidNamespacedModel(keys.model.clone()))?; + + let selector = compute_model_selector_from_names(namespace, model); let proto::types::ModelMetadata { packed_size, .. } = - self.model_metadata(&keys.model).await?; + self.model_metadata(namespace, model).await?; subs.push(ModelDiffRequest { keys, model: subscriptions::model_diff::ModelMetadata { - name: model, + selector, packed_size: packed_size as usize, }, }); @@ -911,15 +975,14 @@ fn map_row_to_event(row: &(String, String, String)) -> Result>, - schemas: &[Ty], + mut schemas: Vec, ) -> Result { let hashed_keys = Felt::from_str(&row.get::("id")).map_err(ParseError::FromStr)?; let models = schemas - .iter() + .iter_mut() .map(|schema| { - let mut schema = schema.to_owned(); - map_row_to_ty("", &schema.name(), &mut schema, row, arrays_rows)?; - Ok(schema.as_struct().expect("schema should be struct").to_owned().try_into().unwrap()) + map_row_to_ty("", &schema.name(), schema, row, arrays_rows)?; + Ok(schema.as_struct().unwrap().clone().into()) }) .collect::, Error>>()?; diff --git a/crates/torii/grpc/src/server/subscriptions/entity.rs b/crates/torii/grpc/src/server/subscriptions/entity.rs index 8f00cbdd5d..79666a170a 100644 --- a/crates/torii/grpc/src/server/subscriptions/entity.rs +++ b/crates/torii/grpc/src/server/subscriptions/entity.rs @@ -117,8 +117,25 @@ impl Service { // if we have a model clause, then we need to check that the entity // has an updated model and that the model name matches the clause if let Some(updated_model) = &entity.updated_model { + let name = updated_model.name(); + let (namespace, name) = name.split_once('-').unwrap(); + if !clause.models.is_empty() - && !clause.models.contains(&updated_model.name()) + && !clause.models.iter().any(|clause_model| { + let (clause_namespace, clause_model) = + clause_model.split_once('-').unwrap(); + // if both namespace and model are empty, we should match all. + // if namespace is specified and model is empty or * we should match + // all models in the namespace + // if namespace and model are specified, we should match the + // specific model + (clause_namespace.is_empty() + || clause_namespace == namespace + || clause_namespace == "*") + && (clause_model.is_empty() + || clause_model == name + || clause_model == "*") + }) { continue; } @@ -179,8 +196,12 @@ impl Service { "#; let (model_ids,): (String,) = sqlx::query_as(models_query).bind(&entity.id).fetch_one(&pool).await?; - let model_ids: Vec<&str> = model_ids.split(',').collect(); - let schemas = cache.schemas(model_ids).await?; + let model_ids: Vec = model_ids + .split(',') + .map(Felt::from_str) + .collect::>() + .map_err(ParseError::FromStr)?; + let schemas = cache.schemas(&model_ids).await?; let (entity_query, arrays_queries) = build_sql_query( &schemas, @@ -198,7 +219,7 @@ impl Service { } let resp = proto::world::SubscribeEntityResponse { - entity: Some(map_row_to_entity(&row, &arrays_rows, &schemas)?), + entity: Some(map_row_to_entity(&row, &arrays_rows, schemas.clone())?), }; if sub.sender.send(Ok(resp)).await.is_err() { diff --git a/crates/torii/grpc/src/server/subscriptions/event_message.rs b/crates/torii/grpc/src/server/subscriptions/event_message.rs index 568ea85e6e..5e6c249129 100644 --- a/crates/torii/grpc/src/server/subscriptions/event_message.rs +++ b/crates/torii/grpc/src/server/subscriptions/event_message.rs @@ -116,8 +116,25 @@ impl Service { // if we have a model clause, then we need to check that the entity // has an updated model and that the model name matches the clause if let Some(updated_model) = &entity.updated_model { + let name = updated_model.name(); + let (namespace, name) = name.split_once('-').unwrap(); + if !clause.models.is_empty() - && !clause.models.contains(&updated_model.name()) + && !clause.models.iter().any(|clause_model| { + let (clause_namespace, clause_model) = + clause_model.split_once('-').unwrap(); + // if both namespace and model are empty, we should match all. + // if namespace is specified and model is empty or * we should match + // all models in the namespace + // if namespace and model are specified, we should match the + // specific model + (clause_namespace.is_empty() + || clause_namespace == namespace + || clause_namespace == "*") + && (clause_model.is_empty() + || clause_model == name + || clause_model == "*") + }) { continue; } @@ -164,8 +181,12 @@ impl Service { "#; let (model_ids,): (String,) = sqlx::query_as(models_query).bind(&entity.id).fetch_one(&pool).await?; - let model_ids: Vec<&str> = model_ids.split(',').collect(); - let schemas = cache.schemas(model_ids).await?; + let model_ids: Vec = model_ids + .split(',') + .map(Felt::from_str) + .collect::>() + .map_err(ParseError::FromStr)?; + let schemas = cache.schemas(&model_ids).await?; let (entity_query, arrays_queries) = build_sql_query( &schemas, @@ -183,7 +204,7 @@ impl Service { } let resp = proto::world::SubscribeEntityResponse { - entity: Some(map_row_to_entity(&row, &arrays_rows, &schemas)?), + entity: Some(map_row_to_entity(&row, &arrays_rows, schemas.clone())?), }; if sub.sender.send(Ok(resp)).await.is_err() { diff --git a/crates/torii/grpc/src/server/subscriptions/model_diff.rs b/crates/torii/grpc/src/server/subscriptions/model_diff.rs index f5088f072d..7438525f72 100644 --- a/crates/torii/grpc/src/server/subscriptions/model_diff.rs +++ b/crates/torii/grpc/src/server/subscriptions/model_diff.rs @@ -27,7 +27,7 @@ use crate::types::ModelKeysClause; pub(crate) const LOG_TARGET: &str = "torii::grpc::server::subscriptions::model_diff"; pub struct ModelMetadata { - pub name: Felt, + pub selector: Felt, pub packed_size: usize, } @@ -67,7 +67,7 @@ impl StateDiffManager { let base = poseidon_hash_many(&[ short_string!("dojo_storage"), - req.model.name, + req.model.selector, poseidon_hash_many(&keys.keys), ]); diff --git a/crates/torii/grpc/src/server/tests/entities_test.rs b/crates/torii/grpc/src/server/tests/entities_test.rs index c32076e496..8aeb9a0756 100644 --- a/crates/torii/grpc/src/server/tests/entities_test.rs +++ b/crates/torii/grpc/src/server/tests/entities_test.rs @@ -2,20 +2,18 @@ use std::str::FromStr; use std::sync::Arc; use camino::Utf8PathBuf; -use dojo_test_utils::compiler::{self, build_test_config}; +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::WorldContractReader; -use dojo_world::metadata::dojo_metadata_from_workspace; +use dojo_world::metadata::{dojo_metadata_from_workspace, get_default_namespace_from_ws}; use dojo_world::migration::TxnConfig; use dojo_world::utils::TransactionWaiter; +use katana_runner::KatanaRunner; +use scarb::compiler::Profile; use scarb::ops; use sozo_ops::migration::execute_strategy; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use starknet::accounts::{Account, Call}; -use starknet::core::types::{BlockId, BlockTag}; use starknet::core::utils::get_selector_from_name; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; @@ -42,7 +40,8 @@ async fn test_entities_queries() { let source_project_dir = Utf8PathBuf::from("../../../examples/spawn-and-move"); let dojo_core_path = Utf8PathBuf::from("../../dojo-core"); - let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path); + let config = compiler::copy_tmp_config(&source_project_dir, &dojo_core_path, Profile::DEV); + println!("config path {:?}", config.manifest_path()); let ws = ops::read_workspace(config.manifest_path(), &config) .unwrap_or_else(|op| panic!("Error building workspace: {op:?}")); @@ -51,26 +50,32 @@ async fn test_entities_queries() { let target_path = ws.target_dir().path_existent().unwrap().join(config.profile().to_string()); - let mut migration = - prepare_migration(source_project_dir, target_path, dojo_metadata.skip_migration).unwrap(); + let default_namespace = get_default_namespace_from_ws(&ws); + + let mut migration = prepare_migration( + config.manifest_path().parent().unwrap().into(), + target_path, + dojo_metadata.skip_migration, + &default_namespace, + ) + .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("Fail to start runner"); + let provider = Arc::new(JsonRpcClient::new(HttpTransport::new(sequencer.url()))); + let world = WorldContractReader::new(migration.world_address().unwrap(), &provider); - let mut account = sequencer.account(); - account.set_block_id(BlockId::Tag(BlockTag::Pending)); + let account = sequencer.account(0); - let config = build_test_config("../../../examples/spawn-and-move/Scarb.toml").unwrap(); - let ws = ops::read_workspace(config.manifest_path(), &config) - .unwrap_or_else(|op| panic!("Error building workspace: {op:?}")); let migration_output = - execute_strategy(&ws, &migration, &account, TxnConfig::default()).await.unwrap(); + execute_strategy(&ws, &migration, &account, TxnConfig::init_wait()).await.unwrap(); let world_address = migration_output.world_address; + println!("output {:?}", migration_output); + // spawn let tx = account .execute_v1(vec![Call { @@ -131,7 +136,7 @@ async fn test_entities_queries() { assert_eq!(entities.len(), 1); let entity: Entity = entities.first().unwrap().clone().try_into().unwrap(); - assert_eq!(entity.models.first().unwrap().name, "Position"); - assert_eq!(entity.models.get(1).unwrap().name, "Moves"); + assert_eq!(entity.models.first().unwrap().name, "dojo_examples-Position"); + assert_eq!(entity.models.get(1).unwrap().name, "dojo_examples-Moves"); assert_eq!(entity.hashed_keys, poseidon_hash_many(&[account.address()])); } diff --git a/crates/torii/grpc/src/types/mod.rs b/crates/torii/grpc/src/types/mod.rs index 0666e382f9..dd576b6ebb 100644 --- a/crates/torii/grpc/src/types/mod.rs +++ b/crates/torii/grpc/src/types/mod.rs @@ -4,6 +4,7 @@ use std::str::FromStr; use dojo_types::primitive::Primitive; use dojo_types::schema::Ty; +use dojo_world::contracts::naming; use serde::{Deserialize, Serialize}; use starknet::core::types::{ ContractStorageDiffItem, Felt, FromStrError, StateDiff, StateUpdate, StorageEntry, @@ -63,7 +64,6 @@ pub struct MemberClause { #[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] pub struct CompositeClause { - pub model: String, pub operator: LogicalOperator, pub clauses: Vec, } @@ -140,6 +140,7 @@ impl TryFrom for dojo_types::schema::ModelMetadata schema, layout, name: value.name, + namespace: value.namespace, packed_size: value.packed_size, unpacked_size: value.unpacked_size, class_hash: Felt::from_str(&value.class_hash)?, @@ -154,7 +155,15 @@ impl TryFrom for dojo_types::WorldMetadata { let models = value .models .into_iter() - .map(|component| Ok((component.name.clone(), component.try_into()?))) + .map(|component| { + Ok(( + naming::compute_model_selector_from_names( + &component.namespace, + &component.name, + ), + component.try_into()?, + )) + }) .collect::, _>>()?; Ok(dojo_types::WorldMetadata { @@ -287,7 +296,6 @@ impl From for proto::types::MemberClause { impl From for proto::types::CompositeClause { fn from(value: CompositeClause) -> Self { Self { - model: value.model, operator: value.operator as i32, clauses: value.clauses.into_iter().map(|clause| clause.into()).collect(), } diff --git a/crates/torii/grpc/src/types/schema.rs b/crates/torii/grpc/src/types/schema.rs index 728a5da5f6..086fbdf530 100644 --- a/crates/torii/grpc/src/types/schema.rs +++ b/crates/torii/grpc/src/types/schema.rs @@ -17,13 +17,7 @@ pub enum SchemaError { #[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] pub struct Entity { pub hashed_keys: Felt, - pub models: Vec, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] -pub struct Model { - pub name: String, - pub members: Vec, + pub models: Vec, } impl TryFrom for Entity { @@ -40,45 +34,22 @@ impl TryFrom for Entity { } } -impl TryFrom for Model { - type Error = SchemaError; - fn try_from(model: proto::types::Model) -> Result { - Ok(Self { - name: model.name, - members: model - .members - .into_iter() - .map(TryInto::try_into) - .collect::, _>>()?, - }) - } -} - -impl TryFrom for proto::types::Ty { - type Error = SchemaError; - fn try_from(ty: Ty) -> Result { +impl From for proto::types::Ty { + fn from(ty: Ty) -> Self { let ty_type = match ty { - Ty::Primitive(primitive) => { - Some(proto::types::ty::TyType::Primitive(primitive.try_into()?)) - } - Ty::Enum(r#enum) => Some(proto::types::ty::TyType::Enum(r#enum.try_into()?)), - Ty::Struct(r#struct) => Some(proto::types::ty::TyType::Struct(r#struct.try_into()?)), + Ty::Primitive(primitive) => Some(proto::types::ty::TyType::Primitive(primitive.into())), + Ty::Enum(r#enum) => Some(proto::types::ty::TyType::Enum(r#enum.into())), + Ty::Struct(r#struct) => Some(proto::types::ty::TyType::Struct(r#struct.into())), Ty::Tuple(tuple) => Some(proto::types::ty::TyType::Tuple(proto::types::Array { - children: tuple - .into_iter() - .map(TryInto::try_into) - .collect::, _>>()?, + children: tuple.into_iter().map(Into::into).collect::>(), })), Ty::Array(array) => Some(proto::types::ty::TyType::Array(proto::types::Array { - children: array - .into_iter() - .map(TryInto::try_into) - .collect::, _>>()?, + children: array.into_iter().map(Into::into).collect::>(), })), Ty::ByteArray(string) => Some(proto::types::ty::TyType::Bytearray(string)), }; - Ok(proto::types::Ty { ty_type }) + proto::types::Ty { ty_type } } } @@ -93,14 +64,9 @@ impl TryFrom for Member { } } -impl TryFrom for proto::types::Member { - type Error = SchemaError; - fn try_from(member: Member) -> Result { - Ok(proto::types::Member { - name: member.name, - ty: Some(member.ty.try_into()?), - key: member.key, - }) +impl From for proto::types::Member { + fn from(member: Member) -> Self { + proto::types::Member { name: member.name, ty: Some(member.ty.into()), key: member.key } } } @@ -114,10 +80,9 @@ impl TryFrom for EnumOption { } } -impl TryFrom for proto::types::EnumOption { - type Error = SchemaError; - fn try_from(option: EnumOption) -> Result { - Ok(proto::types::EnumOption { name: option.name, ty: Some(option.ty.try_into()?) }) +impl From for proto::types::EnumOption { + fn from(option: EnumOption) -> Self { + proto::types::EnumOption { name: option.name, ty: Some(option.ty.into()) } } } @@ -136,18 +101,13 @@ impl TryFrom for Enum { } } -impl TryFrom for proto::types::Enum { - type Error = SchemaError; - fn try_from(r#enum: Enum) -> Result { - Ok(proto::types::Enum { +impl From for proto::types::Enum { + fn from(r#enum: Enum) -> Self { + proto::types::Enum { name: r#enum.name, option: r#enum.option.expect("option value") as u32, - options: r#enum - .options - .into_iter() - .map(TryInto::try_into) - .collect::, _>>()?, - }) + options: r#enum.options.into_iter().map(Into::into).collect::>(), + } } } @@ -165,32 +125,12 @@ impl TryFrom for Struct { } } -impl TryFrom for proto::types::Struct { - type Error = SchemaError; - fn try_from(r#struct: Struct) -> Result { - Ok(proto::types::Struct { +impl From for proto::types::Struct { + fn from(r#struct: Struct) -> Self { + proto::types::Struct { name: r#struct.name, - children: r#struct - .children - .into_iter() - .map(TryInto::try_into) - .collect::, _>>()?, - }) - } -} - -impl TryFrom for proto::types::Model { - type Error = SchemaError; - fn try_from(r#struct: Struct) -> Result { - let r#struct: proto::types::Struct = r#struct.try_into()?; - - Ok(r#struct.into()) - } -} - -impl From for proto::types::Model { - fn from(r#struct: proto::types::Struct) -> Self { - Self { name: r#struct.name, members: r#struct.children } + children: r#struct.children.into_iter().map(Into::into).collect::>(), + } } } @@ -244,9 +184,8 @@ impl TryFrom for Primitive { } } -impl TryFrom for proto::types::Primitive { - type Error = SchemaError; - fn try_from(primitive: Primitive) -> Result { +impl From for proto::types::Primitive { + fn from(primitive: Primitive) -> Self { use proto::types::value::ValueType; let value_type = match primitive { @@ -274,10 +213,10 @@ impl TryFrom for proto::types::Primitive { } .expect("value expected"); - Ok(proto::types::Primitive { + proto::types::Primitive { value: Some(proto::types::Value { value_type: Some(value_type) }), r#type: primitive.to_numeric() as i32, - }) + } } } diff --git a/crates/torii/libp2p/Cargo.toml b/crates/torii/libp2p/Cargo.toml index e6cf3466f9..df2b2eb526 100644 --- a/crates/torii/libp2p/Cargo.toml +++ b/crates/torii/libp2p/Cargo.toml @@ -30,6 +30,7 @@ tracing.workspace = true [dev-dependencies] dojo-test-utils.workspace = true +katana-runner.workspace = true tempfile.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/crates/torii/libp2p/src/server/mod.rs b/crates/torii/libp2p/src/server/mod.rs index 2e4bec0c91..4a64043dab 100644 --- a/crates/torii/libp2p/src/server/mod.rs +++ b/crates/torii/libp2p/src/server/mod.rs @@ -10,6 +10,7 @@ use chrono::Utc; use crypto_bigint::U256; use dojo_types::primitive::Primitive; use dojo_types::schema::Ty; +use dojo_world::contracts::naming::compute_model_selector_from_names; use futures::StreamExt; use indexmap::IndexMap; use libp2p::core::multiaddr::Protocol; @@ -225,8 +226,10 @@ impl Relay

{ }; // select only identity field, if doesn't exist, empty string - let query = - format!("SELECT external_identity FROM {} WHERE id = ?", ty.name()); + let query = format!( + "SELECT external_identity FROM [{}] WHERE id = ?", + ty.name() + ); let entity_identity: Option = match sqlx::query_scalar(&query) .bind(format!("{:#x}", poseidon_hash_many(&keys))) .fetch_optional(&mut *pool) @@ -620,33 +623,33 @@ async fn validate_message( db: &Sql, message: &IndexMap, ) -> Result { - let model_name = if let Some(model_name) = message.get("model") { + let (selector, model) = if let Some(model_name) = message.get("model") { if let PrimitiveType::String(model_name) = model_name { - model_name + let (namespace, name) = model_name.split_once('-').ok_or_else(|| { + Error::InvalidMessageError( + "Model name is not in the format namespace-model".to_string(), + ) + })?; + + (compute_model_selector_from_names(namespace, name), model_name) } else { return Err(Error::InvalidMessageError("Model name is not a string".to_string())); } } else { return Err(Error::InvalidMessageError("Model name is missing".to_string())); }; - let model_selector = get_selector_from_name(model_name).map_err(|e| { - Error::InvalidMessageError(format!("Failed to get selector from model name: {}", e)) - })?; let mut ty = db - .model(&format!("{:#x}", model_selector)) + .model(selector) .await - .map_err(|e| Error::InvalidMessageError(format!("Model {} not found: {}", model_name, e)))? + .map_err(|e| Error::InvalidMessageError(format!("Model {} not found: {}", model, e)))? .schema() .await .map_err(|e| { - Error::InvalidMessageError(format!( - "Failed to get schema for model {}: {}", - model_name, e - )) + Error::InvalidMessageError(format!("Failed to get schema for model {}: {}", model, e)) })?; - if let Some(object) = message.get(model_name) { + if let Some(object) = message.get(model) { parse_value_to_ty(object, &mut ty)?; } else { return Err(Error::InvalidMessageError("Model is missing".to_string())); diff --git a/crates/torii/libp2p/src/tests.rs b/crates/torii/libp2p/src/tests.rs index 9d61f02cc0..2ae78be058 100644 --- a/crates/torii/libp2p/src/tests.rs +++ b/crates/torii/libp2p/src/tests.rs @@ -11,6 +11,7 @@ mod test { use crypto_bigint::U256; use dojo_types::primitive::Primitive; use dojo_types::schema::{Enum, EnumOption, Member, Struct, Ty}; + use katana_runner::KatanaRunner; use serde_json::Number; use starknet::core::types::Felt; #[cfg(target_arch = "wasm32")] @@ -270,9 +271,6 @@ mod test { async fn test_client_messaging() -> Result<(), Box> { use std::time::Duration; - use dojo_test_utils::sequencer::{ - get_default_test_starknet_config, SequencerConfig, TestSequencer, - }; use dojo_types::schema::{Member, Struct, Ty}; use dojo_world::contracts::abi::model::Layout; use indexmap::IndexMap; @@ -300,17 +298,17 @@ mod test { let pool = SqlitePoolOptions::new().max_connections(5).connect_with(options).await.unwrap(); sqlx::migrate!("../migrations").run(&pool).await.unwrap(); - let sequencer = - TestSequencer::start(SequencerConfig::default(), get_default_test_starknet_config()) - .await; + let sequencer = KatanaRunner::new().expect("Failed to create Katana sequencer"); + let provider = JsonRpcClient::new(HttpTransport::new(sequencer.url())); - let account = sequencer.raw_account(); + let account = sequencer.account_data(0); let mut db = Sql::new(pool.clone(), Felt::from_bytes_be(&[0; 32])).await?; // Register the model of our Message db.register_model( + "types_test", Ty::Struct(Struct { name: "Message".to_string(), children: vec![ @@ -358,7 +356,7 @@ mod test { r#type: "shortstring".to_string(), }), Field::SimpleType(SimpleField { - name: "Message".to_string(), + name: "types_test-Message".to_string(), r#type: "Model".to_string(), }), ], @@ -399,23 +397,21 @@ mod test { ), ]), "OffchainMessage", - Domain::new("Message", "1", "0x0", Some("1")), + Domain::new("types_test-Message", "1", "0x0", Some("1")), IndexMap::new(), ); typed_data.message.insert( "model".to_string(), - crate::typed_data::PrimitiveType::String("Message".to_string()), + crate::typed_data::PrimitiveType::String("types_test-Message".to_string()), ); typed_data.message.insert( - "Message".to_string(), + "types_test-Message".to_string(), crate::typed_data::PrimitiveType::Object( vec![ ( "identity".to_string(), - crate::typed_data::PrimitiveType::String( - account.account_address.to_string(), - ), + crate::typed_data::PrimitiveType::String(account.address.to_string()), ), ( "message".to_string(), @@ -427,9 +423,11 @@ mod test { ), ); - let message_hash = typed_data.encode(account.account_address).unwrap(); + let message_hash = typed_data.encode(account.address).unwrap(); let signature = - SigningKey::from_secret_scalar(account.private_key).sign(&message_hash).unwrap(); + SigningKey::from_secret_scalar(account.private_key.clone().unwrap().secret_scalar()) + .sign(&message_hash) + .unwrap(); client .command_sender diff --git a/crates/torii/migrations/20240702140704_model_namespace.sql b/crates/torii/migrations/20240702140704_model_namespace.sql new file mode 100644 index 0000000000..c5800ea162 --- /dev/null +++ b/crates/torii/migrations/20240702140704_model_namespace.sql @@ -0,0 +1,3 @@ +-- Models have now a namespace. +ALTER TABLE models +ADD COLUMN namespace TEXT NOT NULL; diff --git a/crates/torii/types-test/Scarb.toml b/crates/torii/types-test/Scarb.toml index 686b66e17c..2f24b842f6 100644 --- a/crates/torii/types-test/Scarb.toml +++ b/crates/torii/types-test/Scarb.toml @@ -1,5 +1,5 @@ [package] -cairo-version = "2.4.0" +cairo-version = "2.6.3" name = "types_test" version = "0.7.3" @@ -12,9 +12,11 @@ dojo = { path = "../../dojo-core" } [[target.dojo]] build-external-contracts = [ ] -#[tool.dojo.world] -#name = "types-test" -#description = "Graphql types testing" +[tool.dojo.world] +namespace = "types_test" +seed = "types_test" +name = "types test" +description = "Graphql types testing" # icon_uri = "file://assets/icon.png" # cover_uri = "file://assets/cover.png" # website = "https://dojoengine.org" 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 deleted file mode 100644 index 2555ec3eda..0000000000 --- a/crates/torii/types-test/manifests/dev/base/dojo_world_world.toml +++ /dev/null @@ -1,5 +0,0 @@ -kind = "Class" -class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -original_class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -abi = "manifests/dev/abis/base/dojo_world_world.json" -name = "dojo::world::world" diff --git a/examples/spawn-and-move/Scarb.toml b/examples/spawn-and-move/Scarb.toml index 784a0233db..dfcf6791ad 100644 --- a/examples/spawn-and-move/Scarb.toml +++ b/examples/spawn-and-move/Scarb.toml @@ -20,6 +20,8 @@ build-external-contracts = [ ] [tool.dojo.world] description = "example world" name = "example" +seed = "dojo_examples" +namespace = "dojo_examples" [tool.dojo.env] rpc_url = "http://localhost:5050/" @@ -27,7 +29,7 @@ rpc_url = "http://localhost:5050/" # Default account for katana with seed = 0 account_address = "0x6162896d1d7ab204c7ccac6dd5f8e9e7c25ecd5ae4fcb4ad32e57786bb46e03" private_key = "0x1800000000300000180000000000030000000000003006001800006600" -world_address = "0x07efebb0c2d4cc285d48a97a7174def3be7fdd6b7bd29cca758fa2e17e03ef30" +world_address = "0x104dd156d76aeab45146a10869637f161ca6cf9f804704f8bbb12ae5b1b5cfb" # `release` profile # @@ -38,6 +40,8 @@ world_address = "0x07efebb0c2d4cc285d48a97a7174def3be7fdd6b7bd29cca758fa2e17e03e [profile.release.tool.dojo.world] description = "example world" name = "example" +seed = "dojo_examples" +namespace = "dojo_examples" [profile.release.tool.dojo.env] rpc_url = "http://localhost:5050/" 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-40b6994c.json similarity index 85% rename from examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_actions_actions.json rename to examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples-actions-40b6994c.json index 574aa89668..310e06be9e 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-40b6994c.json @@ -1,16 +1,67 @@ [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -18,6 +69,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, @@ -155,24 +217,6 @@ } ] }, - { - "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": "interface", "name": "dojo_examples::actions::IActions", 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/base/contracts/dojo_examples-mock_token-31599eb2.json similarity index 61% rename from examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_others_others.json rename to examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples-mock_token-31599eb2.json index 36d8c3ef78..17da9b2135 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_others_others.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples-mock_token-31599eb2.json @@ -1,16 +1,67 @@ [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -18,6 +69,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, @@ -56,29 +118,16 @@ { "type": "impl", "name": "IDojoInitImpl", - "interface_name": "dojo_examples::others::others::IDojoInit" + "interface_name": "dojo_examples::mock_token::mock_token::IDojoInit" }, { "type": "interface", - "name": "dojo_examples::others::others::IDojoInit", + "name": "dojo_examples::mock_token::mock_token::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" - } - ], + "inputs": [], "outputs": [], "state_mutability": "view" } @@ -133,7 +182,7 @@ }, { "type": "event", - "name": "dojo_examples::others::others::Event", + "name": "dojo_examples::mock_token::mock_token::Event", "kind": "enum", "variants": [ { diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/contracts/dojo_examples_others_others.json b/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples-others-61de2c18.json similarity index 67% rename from examples/spawn-and-move/manifests/release/abis/deployments/contracts/dojo_examples_others_others.json rename to examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples-others-61de2c18.json index 36d8c3ef78..2e0c485d8b 100644 --- a/examples/spawn-and-move/manifests/release/abis/deployments/contracts/dojo_examples_others_others.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples-others-61de2c18.json @@ -1,16 +1,67 @@ [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -18,6 +69,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, diff --git a/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples_mock_token_mock_token.json b/examples/spawn-and-move/manifests/dev/abis/base/dojo-base.json similarity index 67% rename from examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples_mock_token_mock_token.json rename to examples/spawn-and-move/manifests/dev/abis/base/dojo-base.json index f4bde33e6d..ee9ceaac66 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples_mock_token_mock_token.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/dojo-base.json @@ -1,26 +1,4 @@ [ - { - "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", @@ -53,24 +31,6 @@ } ] }, - { - "type": "impl", - "name": "IDojoInitImpl", - "interface_name": "dojo_examples::mock_token::mock_token::IDojoInit" - }, - { - "type": "interface", - "name": "dojo_examples::mock_token::mock_token::IDojoInit", - "items": [ - { - "type": "function", - "name": "dojo_init", - "inputs": [], - "outputs": [], - "state_mutability": "view" - } - ] - }, { "type": "impl", "name": "UpgradableImpl", @@ -94,6 +54,11 @@ } ] }, + { + "type": "constructor", + "name": "constructor", + "inputs": [] + }, { "type": "event", "name": "dojo::components::upgradeable::upgradeable::Upgraded", @@ -120,13 +85,13 @@ }, { "type": "event", - "name": "dojo_examples::mock_token::mock_token::Event", + "name": "dojo::base::base::Event", "kind": "enum", "variants": [ { "name": "UpgradeableEvent", "type": "dojo::components::upgradeable::upgradeable::Event", - "kind": "nested" + "kind": "flat" } ] } 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.json similarity index 89% rename from examples/spawn-and-move/manifests/dev/abis/base/dojo_world_world.json rename to examples/spawn-and-move/manifests/dev/abis/base/dojo-world.json index c1a2447839..0e5e96210e 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.json @@ -194,6 +194,18 @@ "outputs": [], "state_mutability": "external" }, + { + "type": "function", + "name": "register_namespace", + "inputs": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray" + } + ], + "outputs": [], + "state_mutability": "external" + }, { "type": "function", "name": "deploy_contract", @@ -401,7 +413,7 @@ "name": "is_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -421,7 +433,7 @@ "name": "grant_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -437,7 +449,7 @@ "name": "revoke_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -447,6 +459,66 @@ ], "outputs": [], "state_mutability": "external" + }, + { + "type": "function", + "name": "can_write_resource", + "inputs": [ + { + "name": "resource_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_model", + "inputs": [ + { + "name": "model_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_namespace", + "inputs": [ + { + "name": "namespace_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" } ] }, @@ -692,6 +764,16 @@ "name": "address", "type": "core::starknet::contract_address::ContractAddress", "kind": "data" + }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "name", + "type": "core::byte_array::ByteArray", + "kind": "data" } ] }, @@ -741,6 +823,23 @@ } ] }, + { + "type": "event", + "name": "dojo::world::world::NamespaceRegistered", + "kind": "struct", + "members": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "hash", + "type": "core::felt252", + "kind": "data" + } + ] + }, { "type": "event", "name": "dojo::world::world::ModelRegistered", @@ -751,6 +850,11 @@ "type": "core::byte_array::ByteArray", "kind": "data" }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash", @@ -818,7 +922,7 @@ "kind": "struct", "members": [ { - "name": "model", + "name": "resource", "type": "core::felt252", "kind": "data" }, @@ -956,6 +1060,11 @@ "type": "dojo::world::world::MetadataUpdate", "kind": "nested" }, + { + "name": "NamespaceRegistered", + "type": "dojo::world::world::NamespaceRegistered", + "kind": "nested" + }, { "name": "ModelRegistered", "type": "dojo::world::world::ModelRegistered", diff --git a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_others_others_contract_initialized.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json similarity index 92% rename from examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_others_others_contract_initialized.json rename to examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json index 1dbbd313d8..a46919adf4 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_others_others_contract_initialized.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_message.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Message-1bb1d226.json similarity index 91% rename from examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_message.json rename to examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Message-1bb1d226.json index ec760c99fb..8e8af178cf 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_message.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Message-1bb1d226.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_mock_token.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-MockToken-38903c7c.json similarity index 91% rename from examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_mock_token.json rename to examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-MockToken-38903c7c.json index b2c1c340fa..b02c79f885 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_mock_token.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-MockToken-38903c7c.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_actions_actions_moved.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Moved-318ae40d.json similarity index 92% rename from examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_actions_actions_moved.json rename to examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Moved-318ae40d.json index 89abdcfec5..5138063ef3 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_actions_actions_moved.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Moved-318ae40d.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_moves.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Moves-2e2accba.json similarity index 92% rename from examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_moves.json rename to examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Moves-2e2accba.json index ee2cf17b26..c5404bf877 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_moves.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Moves-2e2accba.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_player_config.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-PlayerConfig-3adad785.json similarity index 92% rename from examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_player_config.json rename to examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-PlayerConfig-3adad785.json index 8c33ebabc4..a70cb0881d 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_player_config.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-PlayerConfig-3adad785.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_position.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Position-1e145e26.json similarity index 92% rename from examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_position.json rename to examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Position-1e145e26.json index bceec42587..cb815337ec 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_position.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-Position-1e145e26.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_server_profile.json b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json similarity index 91% rename from examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_server_profile.json rename to examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json index 9b523d5d2b..31f5457868 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_server_profile.json +++ b/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", 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-40b6994c.json similarity index 85% rename from examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_actions_actions.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples-actions-40b6994c.json index 574aa89668..310e06be9e 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-40b6994c.json @@ -1,16 +1,67 @@ [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -18,6 +69,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, @@ -155,24 +217,6 @@ } ] }, - { - "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": "interface", "name": "dojo_examples::actions::IActions", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples-mock_token-31599eb2.json b/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples-mock_token-31599eb2.json new file mode 100644 index 0000000000..17da9b2135 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples-mock_token-31599eb2.json @@ -0,0 +1,195 @@ +[ + { + "type": "impl", + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", + "name": "dojo::contract::IContract", + "items": [ + { + "type": "function", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "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::mock_token::mock_token::IDojoInit" + }, + { + "type": "interface", + "name": "dojo_examples::mock_token::mock_token::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [], + "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::mock_token::mock_token::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/release/abis/base/contracts/dojo_examples_others_others.json b/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples-others-61de2c18.json similarity index 67% rename from examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples_others_others.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples-others-61de2c18.json index 36d8c3ef78..2e0c485d8b 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples_others_others.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples-others-61de2c18.json @@ -1,16 +1,67 @@ [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -18,6 +69,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, 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.json similarity index 89% rename from examples/spawn-and-move/manifests/dev/abis/deployments/dojo_world_world.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/dojo-world.json index c1a2447839..0e5e96210e 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.json @@ -194,6 +194,18 @@ "outputs": [], "state_mutability": "external" }, + { + "type": "function", + "name": "register_namespace", + "inputs": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray" + } + ], + "outputs": [], + "state_mutability": "external" + }, { "type": "function", "name": "deploy_contract", @@ -401,7 +413,7 @@ "name": "is_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -421,7 +433,7 @@ "name": "grant_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -437,7 +449,7 @@ "name": "revoke_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -447,6 +459,66 @@ ], "outputs": [], "state_mutability": "external" + }, + { + "type": "function", + "name": "can_write_resource", + "inputs": [ + { + "name": "resource_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_model", + "inputs": [ + { + "name": "model_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_namespace", + "inputs": [ + { + "name": "namespace_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" } ] }, @@ -692,6 +764,16 @@ "name": "address", "type": "core::starknet::contract_address::ContractAddress", "kind": "data" + }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "name", + "type": "core::byte_array::ByteArray", + "kind": "data" } ] }, @@ -741,6 +823,23 @@ } ] }, + { + "type": "event", + "name": "dojo::world::world::NamespaceRegistered", + "kind": "struct", + "members": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "hash", + "type": "core::felt252", + "kind": "data" + } + ] + }, { "type": "event", "name": "dojo::world::world::ModelRegistered", @@ -751,6 +850,11 @@ "type": "core::byte_array::ByteArray", "kind": "data" }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash", @@ -818,7 +922,7 @@ "kind": "struct", "members": [ { - "name": "model", + "name": "resource", "type": "core::felt252", "kind": "data" }, @@ -956,6 +1060,11 @@ "type": "dojo::world::world::MetadataUpdate", "kind": "nested" }, + { + "name": "NamespaceRegistered", + "type": "dojo::world::world::NamespaceRegistered", + "kind": "nested" + }, { "name": "ModelRegistered", "type": "dojo::world::world::ModelRegistered", diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_others_others_contract_initialized.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-ContractInitialized-376b7bd6.json similarity index 92% rename from examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_others_others_contract_initialized.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-ContractInitialized-376b7bd6.json index 1dbbd313d8..a46919adf4 100644 --- a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_others_others_contract_initialized.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-ContractInitialized-376b7bd6.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_message.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Message-1bb1d226.json similarity index 91% rename from examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_message.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Message-1bb1d226.json index ec760c99fb..8e8af178cf 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_message.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Message-1bb1d226.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_mock_token.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-MockToken-38903c7c.json similarity index 91% rename from examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_mock_token.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-MockToken-38903c7c.json index b2c1c340fa..b02c79f885 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_mock_token.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-MockToken-38903c7c.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_actions_actions_moved.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Moved-318ae40d.json similarity index 92% rename from examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_actions_actions_moved.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Moved-318ae40d.json index 89abdcfec5..5138063ef3 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_actions_actions_moved.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Moved-318ae40d.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_moves.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Moves-2e2accba.json similarity index 92% rename from examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_moves.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Moves-2e2accba.json index ee2cf17b26..c5404bf877 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_moves.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Moves-2e2accba.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_player_config.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-PlayerConfig-3adad785.json similarity index 92% rename from examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_player_config.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-PlayerConfig-3adad785.json index 8c33ebabc4..a70cb0881d 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples_models_player_config.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-PlayerConfig-3adad785.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_position.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Position-1e145e26.json similarity index 92% rename from examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_position.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Position-1e145e26.json index bceec42587..cb815337ec 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_position.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-Position-1e145e26.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_server_profile.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-ServerProfile-4caad1e6.json similarity index 91% rename from examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_server_profile.json rename to examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-ServerProfile-4caad1e6.json index 9b523d5d2b..31f5457868 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_server_profile.json +++ b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples-ServerProfile-4caad1e6.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_moves.json b/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_moves.json deleted file mode 100644 index ee2cf17b26..0000000000 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_models_moves.json +++ /dev/null @@ -1,393 +0,0 @@ -[ - { - "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": "movesImpl", - "interface_name": "dojo_examples::models::Imoves" - }, - { - "type": "enum", - "name": "dojo_examples::models::Direction", - "variants": [ - { - "name": "None", - "type": "()" - }, - { - "name": "Left", - "type": "()" - }, - { - "name": "Right", - "type": "()" - }, - { - "name": "Up", - "type": "()" - }, - { - "name": "Down", - "type": "()" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::Moves", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "remaining", - "type": "core::integer::u8" - }, - { - "name": "last_direction", - "type": "dojo_examples::models::Direction" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::models::Imoves", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::models::Moves" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::models::moves::Event", - "kind": "enum", - "variants": [] - } -] \ No newline at end of file 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 deleted file mode 100644 index 1dbbd313d8..0000000000 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_others_others_contract_initialized.json +++ /dev/null @@ -1,367 +0,0 @@ -[ - { - "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-40b6994c.toml b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-actions-40b6994c.toml new file mode 100644 index 0000000000..34d9543e28 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-actions-40b6994c.toml @@ -0,0 +1,11 @@ +kind = "DojoContract" +class_hash = "0x7b394d087b5cf4f3b740253c591138bf98d177ef0d9b5c00b0477a145f3fb75" +original_class_hash = "0x7b394d087b5cf4f3b740253c591138bf98d177ef0d9b5c00b0477a145f3fb75" +base_class_hash = "0x0" +abi = "manifests/dev/abis/base/contracts/dojo_examples-actions-40b6994c.json" +reads = [] +writes = [] +computed = [] +init_calldata = [] +tag = "dojo_examples-actions" +manifest_name = "dojo_examples-actions-40b6994c" diff --git a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-mock_token-31599eb2.toml b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-mock_token-31599eb2.toml new file mode 100644 index 0000000000..1a2a64dea4 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-mock_token-31599eb2.toml @@ -0,0 +1,11 @@ +kind = "DojoContract" +class_hash = "0x6a55b3f612b0bc5e55603d805c188c0220aa53017fd2f690abe8bad50867ef2" +original_class_hash = "0x6a55b3f612b0bc5e55603d805c188c0220aa53017fd2f690abe8bad50867ef2" +base_class_hash = "0x0" +abi = "manifests/dev/abis/base/contracts/dojo_examples-mock_token-31599eb2.json" +reads = [] +writes = [] +computed = [] +init_calldata = [] +tag = "dojo_examples-mock_token" +manifest_name = "dojo_examples-mock_token-31599eb2" diff --git a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-others-61de2c18.toml b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-others-61de2c18.toml new file mode 100644 index 0000000000..31438c1f0f --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-others-61de2c18.toml @@ -0,0 +1,11 @@ +kind = "DojoContract" +class_hash = "0x3a61e2fafaee0ca4ed5166fbb417270563b4d8518cd1e086733cc346e8ea6b9" +original_class_hash = "0x3a61e2fafaee0ca4ed5166fbb417270563b4d8518cd1e086733cc346e8ea6b9" +base_class_hash = "0x0" +abi = "manifests/dev/abis/base/contracts/dojo_examples-others-61de2c18.json" +reads = [] +writes = [] +computed = [] +init_calldata = [] +tag = "dojo_examples-others" +manifest_name = "dojo_examples-others-61de2c18" 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 deleted file mode 100644 index 3e9c232447..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_actions_actions.toml +++ /dev/null @@ -1,10 +0,0 @@ -kind = "DojoContract" -class_hash = "0x7d70dda5cb8dcb697ccc2c129254c554e8994f1b231527f7641a14706af016f" -original_class_hash = "0x7d70dda5cb8dcb697ccc2c129254c554e8994f1b231527f7641a14706af016f" -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_mock_token_mock_token.toml b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_mock_token_mock_token.toml deleted file mode 100644 index 572af4068e..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_mock_token_mock_token.toml +++ /dev/null @@ -1,10 +0,0 @@ -kind = "DojoContract" -class_hash = "0x5c32bc974f96f82ded691bb95bf26b2783f874fffb50fe84a331acb75ecf5b2" -original_class_hash = "0x5c32bc974f96f82ded691bb95bf26b2783f874fffb50fe84a331acb75ecf5b2" -base_class_hash = "0x0" -abi = "manifests/dev/abis/base/contracts/dojo_examples_mock_token_mock_token.json" -reads = [] -writes = [] -computed = [] -init_calldata = [] -name = "dojo_examples::mock_token::mock_token" 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 deleted file mode 100644 index 0526bc5473..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples_others_others.toml +++ /dev/null @@ -1,10 +0,0 @@ -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_base_base.toml b/examples/spawn-and-move/manifests/dev/base/dojo-base.toml similarity index 66% rename from examples/spawn-and-move/manifests/dev/base/dojo_base_base.toml rename to examples/spawn-and-move/manifests/dev/base/dojo-base.toml index 6c4b5de67e..4e2fc2e0bb 100644 --- a/examples/spawn-and-move/manifests/dev/base/dojo_base_base.toml +++ b/examples/spawn-and-move/manifests/dev/base/dojo-base.toml @@ -1,4 +1,6 @@ kind = "Class" class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" original_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -name = "dojo::base::base" +abi = "manifests/dev/abis/base/dojo-base.json" +tag = "dojo-base" +manifest_name = "dojo-base" diff --git a/examples/spawn-and-move/manifests/dev/base/dojo-world.toml b/examples/spawn-and-move/manifests/dev/base/dojo-world.toml new file mode 100644 index 0000000000..94821ba3f7 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/dojo-world.toml @@ -0,0 +1,6 @@ +kind = "Class" +class_hash = "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2" +original_class_hash = "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2" +abi = "manifests/dev/abis/base/dojo-world.json" +tag = "dojo-world" +manifest_name = "dojo-world" 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 deleted file mode 100644 index 2555ec3eda..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/dojo_world_world.toml +++ /dev/null @@ -1,5 +0,0 @@ -kind = "Class" -class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -original_class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -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-ContractInitialized-376b7bd6.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-ContractInitialized-376b7bd6.toml new file mode 100644 index 0000000000..0aacc170a4 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-ContractInitialized-376b7bd6.toml @@ -0,0 +1,21 @@ +kind = "DojoModel" +class_hash = "0x1ba185f6f09a7a73f32e6e1d6767c182abe5635d3107c79b64dfdb9f25b3c3a" +original_class_hash = "0x1ba185f6f09a7a73f32e6e1d6767c182abe5635d3107c79b64dfdb9f25b3c3a" +abi = "manifests/dev/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json" +tag = "dojo_examples-ContractInitialized" +manifest_name = "dojo_examples-ContractInitialized-376b7bd6" + +[[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/base/models/dojo_examples-Message-1bb1d226.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Message-1bb1d226.toml new file mode 100644 index 0000000000..698d0a8f0e --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Message-1bb1d226.toml @@ -0,0 +1,26 @@ +kind = "DojoModel" +class_hash = "0x6d9703c6df6af6f097cfdd2e7f838f4fd2b2f2ae27bf27e76065ffb903f9c9b" +original_class_hash = "0x6d9703c6df6af6f097cfdd2e7f838f4fd2b2f2ae27bf27e76065ffb903f9c9b" +abi = "manifests/dev/abis/base/models/dojo_examples-Message-1bb1d226.json" +tag = "dojo_examples-Message" +manifest_name = "dojo_examples-Message-1bb1d226" + +[[members]] +name = "identity" +type = "ContractAddress" +key = true + +[[members]] +name = "channel" +type = "felt252" +key = true + +[[members]] +name = "message" +type = "ByteArray" +key = false + +[[members]] +name = "salt" +type = "felt252" +key = true diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-MockToken-38903c7c.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-MockToken-38903c7c.toml new file mode 100644 index 0000000000..b607d23d57 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-MockToken-38903c7c.toml @@ -0,0 +1,16 @@ +kind = "DojoModel" +class_hash = "0x637d1d7966a14967047e31f3512c694e32b4d6a6dda4afe4b225a8b6f8f3519" +original_class_hash = "0x637d1d7966a14967047e31f3512c694e32b4d6a6dda4afe4b225a8b6f8f3519" +abi = "manifests/dev/abis/base/models/dojo_examples-MockToken-38903c7c.json" +tag = "dojo_examples-MockToken" +manifest_name = "dojo_examples-MockToken-38903c7c" + +[[members]] +name = "account" +type = "ContractAddress" +key = true + +[[members]] +name = "amount" +type = "u128" +key = false diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Moved-318ae40d.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Moved-318ae40d.toml new file mode 100644 index 0000000000..c8e044bd5a --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Moved-318ae40d.toml @@ -0,0 +1,16 @@ +kind = "DojoModel" +class_hash = "0x838672e7f67cd6e67336283805b0ef168c814e65b6ba1d3a7a5f31c10559c4" +original_class_hash = "0x838672e7f67cd6e67336283805b0ef168c814e65b6ba1d3a7a5f31c10559c4" +abi = "manifests/dev/abis/base/models/dojo_examples-Moved-318ae40d.json" +tag = "dojo_examples-Moved" +manifest_name = "dojo_examples-Moved-318ae40d" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "direction" +type = "Direction" +key = false diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Moves-2e2accba.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Moves-2e2accba.toml new file mode 100644 index 0000000000..0a661e61c7 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Moves-2e2accba.toml @@ -0,0 +1,21 @@ +kind = "DojoModel" +class_hash = "0x4e9b10687f487bc0fe159affa3963cf0f7548136a2b614914e11af4497d609" +original_class_hash = "0x4e9b10687f487bc0fe159affa3963cf0f7548136a2b614914e11af4497d609" +abi = "manifests/dev/abis/base/models/dojo_examples-Moves-2e2accba.json" +tag = "dojo_examples-Moves" +manifest_name = "dojo_examples-Moves-2e2accba" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "remaining" +type = "u8" +key = false + +[[members]] +name = "last_direction" +type = "Direction" +key = false diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-PlayerConfig-3adad785.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-PlayerConfig-3adad785.toml new file mode 100644 index 0000000000..a83db11151 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-PlayerConfig-3adad785.toml @@ -0,0 +1,26 @@ +kind = "DojoModel" +class_hash = "0x3b94d89f5b3589438b8ffcf2cbc8061670deee1d0f81247ab2346d8991b4eca" +original_class_hash = "0x3b94d89f5b3589438b8ffcf2cbc8061670deee1d0f81247ab2346d8991b4eca" +abi = "manifests/dev/abis/base/models/dojo_examples-PlayerConfig-3adad785.json" +tag = "dojo_examples-PlayerConfig" +manifest_name = "dojo_examples-PlayerConfig-3adad785" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "name" +type = "ByteArray" +key = false + +[[members]] +name = "items" +type = "Array" +key = false + +[[members]] +name = "favorite_item" +type = "Option" +key = false diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Position-1e145e26.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Position-1e145e26.toml new file mode 100644 index 0000000000..6eea086fef --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-Position-1e145e26.toml @@ -0,0 +1,16 @@ +kind = "DojoModel" +class_hash = "0x59e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34" +original_class_hash = "0x59e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34" +abi = "manifests/dev/abis/base/models/dojo_examples-Position-1e145e26.json" +tag = "dojo_examples-Position" +manifest_name = "dojo_examples-Position-1e145e26" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "vec" +type = "Vec2" +key = false diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-ServerProfile-4caad1e6.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-ServerProfile-4caad1e6.toml new file mode 100644 index 0000000000..4b77f78c88 --- /dev/null +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-ServerProfile-4caad1e6.toml @@ -0,0 +1,21 @@ +kind = "DojoModel" +class_hash = "0x38986e1b875d48b2931932879412cf0b6c52d0e04aaac6e2780995b3f47b91b" +original_class_hash = "0x38986e1b875d48b2931932879412cf0b6c52d0e04aaac6e2780995b3f47b91b" +abi = "manifests/dev/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json" +tag = "dojo_examples-ServerProfile" +manifest_name = "dojo_examples-ServerProfile-4caad1e6" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "server_id" +type = "u32" +key = true + +[[members]] +name = "name" +type = "ByteArray" +key = false 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 deleted file mode 100644 index 8b79f5c317..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_actions_actions_moved.toml +++ /dev/null @@ -1,15 +0,0 @@ -kind = "DojoModel" -class_hash = "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c" -original_class_hash = "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c" -abi = "manifests/dev/abis/base/models/dojo_examples_actions_actions_moved.json" -name = "dojo_examples::actions::actions::moved" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "direction" -type = "Direction" -key = false diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_message.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_message.toml deleted file mode 100644 index 15ad89aace..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_message.toml +++ /dev/null @@ -1,25 +0,0 @@ -kind = "DojoModel" -class_hash = "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5" -original_class_hash = "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5" -abi = "manifests/dev/abis/base/models/dojo_examples_models_message.json" -name = "dojo_examples::models::message" - -[[members]] -name = "identity" -type = "ContractAddress" -key = true - -[[members]] -name = "channel" -type = "felt252" -key = true - -[[members]] -name = "message" -type = "ByteArray" -key = false - -[[members]] -name = "salt" -type = "felt252" -key = true diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_mock_token.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_mock_token.toml deleted file mode 100644 index 1bf036168c..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_mock_token.toml +++ /dev/null @@ -1,15 +0,0 @@ -kind = "DojoModel" -class_hash = "0x6a21c56878ba470ac7a51f336ca6a59781de38e1810d2d20866ab2b52138a6d" -original_class_hash = "0x6a21c56878ba470ac7a51f336ca6a59781de38e1810d2d20866ab2b52138a6d" -abi = "manifests/dev/abis/base/models/dojo_examples_models_mock_token.json" -name = "dojo_examples::models::mock_token" - -[[members]] -name = "account" -type = "ContractAddress" -key = true - -[[members]] -name = "amount" -type = "u128" -key = false 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 deleted file mode 100644 index 6241643aa4..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_moves.toml +++ /dev/null @@ -1,20 +0,0 @@ -kind = "DojoModel" -class_hash = "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8" -original_class_hash = "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8" -abi = "manifests/dev/abis/base/models/dojo_examples_models_moves.json" -name = "dojo_examples::models::moves" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "remaining" -type = "u8" -key = false - -[[members]] -name = "last_direction" -type = "Direction" -key = false 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 deleted file mode 100644 index 459eb9ebdb..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_player_config.toml +++ /dev/null @@ -1,25 +0,0 @@ -kind = "DojoModel" -class_hash = "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55" -original_class_hash = "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55" -abi = "manifests/dev/abis/base/models/dojo_examples_models_player_config.json" -name = "dojo_examples::models::player_config" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "name" -type = "ByteArray" -key = false - -[[members]] -name = "items" -type = "Array" -key = false - -[[members]] -name = "favorite_item" -type = "Option" -key = false 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 deleted file mode 100644 index d29abaa4e0..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_position.toml +++ /dev/null @@ -1,15 +0,0 @@ -kind = "DojoModel" -class_hash = "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff" -original_class_hash = "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff" -abi = "manifests/dev/abis/base/models/dojo_examples_models_position.json" -name = "dojo_examples::models::position" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "vec" -type = "Vec2" -key = false diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_server_profile.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_server_profile.toml deleted file mode 100644 index e2774133c8..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_models_server_profile.toml +++ /dev/null @@ -1,20 +0,0 @@ -kind = "DojoModel" -class_hash = "0x6dc51a232ffcfaa02646636daf1b8acab8121fa69458258da8df1620aff07ab" -original_class_hash = "0x6dc51a232ffcfaa02646636daf1b8acab8121fa69458258da8df1620aff07ab" -abi = "manifests/dev/abis/base/models/dojo_examples_models_server_profile.json" -name = "dojo_examples::models::server_profile" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "server_id" -type = "u32" -key = true - -[[members]] -name = "name" -type = "ByteArray" -key = false 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 deleted file mode 100644 index e4b4f0fdc3..0000000000 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples_others_others_contract_initialized.toml +++ /dev/null @@ -1,20 +0,0 @@ -kind = "DojoModel" -class_hash = "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c" -original_class_hash = "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c" -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 928d7596e1..162816500f 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": "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9", - "original_class_hash": "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9", + "class_hash": "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2", + "original_class_hash": "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2", "abi": [ { "type": "impl", @@ -199,6 +199,18 @@ "outputs": [], "state_mutability": "external" }, + { + "type": "function", + "name": "register_namespace", + "inputs": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray" + } + ], + "outputs": [], + "state_mutability": "external" + }, { "type": "function", "name": "deploy_contract", @@ -406,7 +418,7 @@ "name": "is_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -426,7 +438,7 @@ "name": "grant_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -442,7 +454,7 @@ "name": "revoke_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -452,6 +464,66 @@ ], "outputs": [], "state_mutability": "external" + }, + { + "type": "function", + "name": "can_write_resource", + "inputs": [ + { + "name": "resource_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_model", + "inputs": [ + { + "name": "model_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_namespace", + "inputs": [ + { + "name": "namespace_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" } ] }, @@ -697,6 +769,16 @@ "name": "address", "type": "core::starknet::contract_address::ContractAddress", "kind": "data" + }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "name", + "type": "core::byte_array::ByteArray", + "kind": "data" } ] }, @@ -746,6 +828,23 @@ } ] }, + { + "type": "event", + "name": "dojo::world::world::NamespaceRegistered", + "kind": "struct", + "members": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "hash", + "type": "core::felt252", + "kind": "data" + } + ] + }, { "type": "event", "name": "dojo::world::world::ModelRegistered", @@ -756,6 +855,11 @@ "type": "core::byte_array::ByteArray", "kind": "data" }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash", @@ -823,7 +927,7 @@ "kind": "struct", "members": [ { - "name": "model", + "name": "resource", "type": "core::felt252", "kind": "data" }, @@ -961,6 +1065,11 @@ "type": "dojo::world::world::MetadataUpdate", "kind": "nested" }, + { + "name": "NamespaceRegistered", + "type": "dojo::world::world::NamespaceRegistered", + "kind": "nested" + }, { "name": "ModelRegistered", "type": "dojo::world::world::ModelRegistered", @@ -999,43 +1108,95 @@ ] } ], - "address": "0x7efebb0c2d4cc285d48a97a7174def3be7fdd6b7bd29cca758fa2e17e03ef30", - "transaction_hash": "0x60316eb232789f4d8352c6afdc36b76d33362d72b43bf78183b43f196779a9d", + "address": "0x104dd156d76aeab45146a10869637f161ca6cf9f804704f8bbb12ae5b1b5cfb", + "transaction_hash": "0x280e50610d4467bfe1be1adaae7f77642adbe2ad4106cb861e28441e94ff287", "block_number": 3, "seed": "dojo_examples", "metadata": { "profile_name": "dev", "rpc_url": "http://localhost:5050/" }, - "name": "dojo::world::world" + "manifest_name": "dojo-world" }, "base": { "kind": "Class", "class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", "original_class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", - "abi": null, - "name": "dojo::base::base" + "abi": "manifests/dev/abis/base/dojo-base.json", + "tag": "dojo-base", + "manifest_name": "dojo-base" }, "contracts": [ { "kind": "DojoContract", - "address": "0x5c70a663d6b48d8e4c6aaa9572e3735a732ac3765700d470463e670587852af", - "class_hash": "0x7d70dda5cb8dcb697ccc2c129254c554e8994f1b231527f7641a14706af016f", - "original_class_hash": "0x7d70dda5cb8dcb697ccc2c129254c554e8994f1b231527f7641a14706af016f", + "address": "0x2a570e12405096e725508ba1f4ade127edd42e0fcb5890b8f12f76ef043623", + "class_hash": "0x7b394d087b5cf4f3b740253c591138bf98d177ef0d9b5c00b0477a145f3fb75", + "original_class_hash": "0x7b394d087b5cf4f3b740253c591138bf98d177ef0d9b5c00b0477a145f3fb75", "base_class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", "abi": [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -1043,6 +1204,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, @@ -1180,24 +1352,6 @@ } ] }, - { - "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": "interface", "name": "dojo_examples::actions::IActions", @@ -1349,32 +1503,84 @@ ], "reads": [], "writes": [ - "Moves", - "Position" + "dojo_examples-Moves", + "dojo_examples-Position" ], "computed": [], "init_calldata": [], - "name": "dojo_examples::actions::actions" + "tag": "dojo_examples-actions", + "manifest_name": "dojo_examples-actions-40b6994c" }, { "kind": "DojoContract", - "address": "0x75961b2027c52948ecebfd347aa427436ea308d41997fa9b3c98380f7011d53", - "class_hash": "0x5c32bc974f96f82ded691bb95bf26b2783f874fffb50fe84a331acb75ecf5b2", - "original_class_hash": "0x5c32bc974f96f82ded691bb95bf26b2783f874fffb50fe84a331acb75ecf5b2", + "address": "0x26f33e8d81dad06c79c2d944ea519a850e007eb8432ae20d38db82caea590b2", + "class_hash": "0x6a55b3f612b0bc5e55603d805c188c0220aa53017fd2f690abe8bad50867ef2", + "original_class_hash": "0x6a55b3f612b0bc5e55603d805c188c0220aa53017fd2f690abe8bad50867ef2", "base_class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", "abi": [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -1382,6 +1588,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, @@ -1499,27 +1716,79 @@ "writes": [], "computed": [], "init_calldata": [], - "name": "dojo_examples::mock_token::mock_token" + "tag": "dojo_examples-mock_token", + "manifest_name": "dojo_examples-mock_token-31599eb2" }, { "kind": "DojoContract", - "address": "0x3f51cd82daaf5907d2fd082ae3f45ae2ef96ab61677f46abc16e0a54d3392d1", - "class_hash": "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a", - "original_class_hash": "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a", + "address": "0x3304896afaa421f362b3b8e8586773f9a7fcaca050b3fd6885400908803f344", + "class_hash": "0x3a61e2fafaee0ca4ed5166fbb417270563b4d8518cd1e086733cc346e8ea6b9", + "original_class_hash": "0x3a61e2fafaee0ca4ed5166fbb417270563b4d8518cd1e086733cc346e8ea6b9", "base_class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", "abi": [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -1527,6 +1796,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, @@ -1657,11 +1937,12 @@ "writes": [], "computed": [], "init_calldata": [ - "$contract_address:dojo_examples::actions::actions", - "$class_hash:dojo_examples::actions::actions", + "$contract_address:dojo_examples-actions", + "$class_hash:dojo_examples-actions", "10" ], - "name": "dojo_examples::others::others" + "tag": "dojo_examples-others", + "manifest_name": "dojo_examples-others-61de2c18" } ], "models": [ @@ -1669,18 +1950,23 @@ "kind": "DojoModel", "members": [ { - "name": "player", + "name": "contract_address", "type": "ContractAddress", "key": true }, { - "name": "direction", - "type": "Direction", + "name": "contract_class", + "type": "ClassHash", + "key": false + }, + { + "name": "value", + "type": "u8", "key": false } ], - "class_hash": "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c", - "original_class_hash": "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c", + "class_hash": "0x1ba185f6f09a7a73f32e6e1d6767c182abe5635d3107c79b64dfdb9f25b3c3a", + "original_class_hash": "0x1ba185f6f09a7a73f32e6e1d6767c182abe5635d3107c79b64dfdb9f25b3c3a", "abi": [ { "type": "impl", @@ -1954,6 +2240,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -2002,52 +2321,30 @@ }, { "type": "impl", - "name": "movedImpl", - "interface_name": "dojo_examples::actions::actions::Imoved" - }, - { - "type": "enum", - "name": "dojo_examples::models::Direction", - "variants": [ - { - "name": "None", - "type": "()" - }, - { - "name": "Left", - "type": "()" - }, - { - "name": "Right", - "type": "()" - }, - { - "name": "Up", - "type": "()" - }, - { - "name": "Down", - "type": "()" - } - ] + "name": "contract_initializedImpl", + "interface_name": "dojo_examples::others::others::Icontract_initialized" }, { "type": "struct", - "name": "dojo_examples::actions::actions::Moved", + "name": "dojo_examples::others::others::ContractInitialized", "members": [ { - "name": "player", + "name": "contract_address", "type": "core::starknet::contract_address::ContractAddress" }, { - "name": "direction", - "type": "dojo_examples::models::Direction" + "name": "contract_class", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "value", + "type": "core::integer::u8" } ] }, { "type": "interface", - "name": "dojo_examples::actions::actions::Imoved", + "name": "dojo_examples::others::others::Icontract_initialized", "items": [ { "type": "function", @@ -2055,7 +2352,7 @@ "inputs": [ { "name": "model", - "type": "dojo_examples::actions::actions::Moved" + "type": "dojo_examples::others::others::ContractInitialized" } ], "outputs": [], @@ -2065,12 +2362,13 @@ }, { "type": "event", - "name": "dojo_examples::actions::actions::moved::Event", + "name": "dojo_examples::others::others::contract_initialized::Event", "kind": "enum", "variants": [] } ], - "name": "dojo_examples::actions::actions::moved" + "tag": "dojo_examples-ContractInitialized", + "manifest_name": "dojo_examples-ContractInitialized-376b7bd6" }, { "kind": "DojoModel", @@ -2096,8 +2394,8 @@ "key": true } ], - "class_hash": "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5", - "original_class_hash": "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5", + "class_hash": "0x6d9703c6df6af6f097cfdd2e7f838f4fd2b2f2ae27bf27e76065ffb903f9c9b", + "original_class_hash": "0x6d9703c6df6af6f097cfdd2e7f838f4fd2b2f2ae27bf27e76065ffb903f9c9b", "abi": [ { "type": "impl", @@ -2371,6 +2669,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -2469,7 +2800,8 @@ "variants": [] } ], - "name": "dojo_examples::models::message" + "tag": "dojo_examples-Message", + "manifest_name": "dojo_examples-Message-1bb1d226" }, { "kind": "DojoModel", @@ -2485,8 +2817,8 @@ "key": false } ], - "class_hash": "0x6a21c56878ba470ac7a51f336ca6a59781de38e1810d2d20866ab2b52138a6d", - "original_class_hash": "0x6a21c56878ba470ac7a51f336ca6a59781de38e1810d2d20866ab2b52138a6d", + "class_hash": "0x637d1d7966a14967047e31f3512c694e32b4d6a6dda4afe4b225a8b6f8f3519", + "original_class_hash": "0x637d1d7966a14967047e31f3512c694e32b4d6a6dda4afe4b225a8b6f8f3519", "abi": [ { "type": "impl", @@ -2760,6 +3092,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -2850,7 +3215,8 @@ "variants": [] } ], - "name": "dojo_examples::models::mock_token" + "tag": "dojo_examples-MockToken", + "manifest_name": "dojo_examples-MockToken-38903c7c" }, { "kind": "DojoModel", @@ -2861,18 +3227,13 @@ "key": true }, { - "name": "remaining", - "type": "u8", - "key": false - }, - { - "name": "last_direction", + "name": "direction", "type": "Direction", "key": false } ], - "class_hash": "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8", - "original_class_hash": "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8", + "class_hash": "0x838672e7f67cd6e67336283805b0ef168c814e65b6ba1d3a7a5f31c10559c4", + "original_class_hash": "0x838672e7f67cd6e67336283805b0ef168c814e65b6ba1d3a7a5f31c10559c4", "abi": [ { "type": "impl", @@ -3146,6 +3507,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -3194,8 +3588,8 @@ }, { "type": "impl", - "name": "movesImpl", - "interface_name": "dojo_examples::models::Imoves" + "name": "movedImpl", + "interface_name": "dojo_examples::actions::actions::Imoved" }, { "type": "enum", @@ -3225,25 +3619,21 @@ }, { "type": "struct", - "name": "dojo_examples::models::Moves", + "name": "dojo_examples::actions::actions::Moved", "members": [ { "name": "player", "type": "core::starknet::contract_address::ContractAddress" }, { - "name": "remaining", - "type": "core::integer::u8" - }, - { - "name": "last_direction", + "name": "direction", "type": "dojo_examples::models::Direction" } ] }, { "type": "interface", - "name": "dojo_examples::models::Imoves", + "name": "dojo_examples::actions::actions::Imoved", "items": [ { "type": "function", @@ -3251,7 +3641,7 @@ "inputs": [ { "name": "model", - "type": "dojo_examples::models::Moves" + "type": "dojo_examples::actions::actions::Moved" } ], "outputs": [], @@ -3261,12 +3651,13 @@ }, { "type": "event", - "name": "dojo_examples::models::moves::Event", + "name": "dojo_examples::actions::actions::moved::Event", "kind": "enum", "variants": [] } ], - "name": "dojo_examples::models::moves" + "tag": "dojo_examples-Moved", + "manifest_name": "dojo_examples-Moved-318ae40d" }, { "kind": "DojoModel", @@ -3277,23 +3668,18 @@ "key": true }, { - "name": "name", - "type": "ByteArray", + "name": "remaining", + "type": "u8", "key": false }, { - "name": "items", - "type": "Array", - "key": false - }, - { - "name": "favorite_item", - "type": "Option", + "name": "last_direction", + "type": "Direction", "key": false } ], - "class_hash": "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55", - "original_class_hash": "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55", + "class_hash": "0x4e9b10687f487bc0fe159affa3963cf0f7548136a2b614914e11af4497d609", + "original_class_hash": "0x4e9b10687f487bc0fe159affa3963cf0f7548136a2b614914e11af4497d609", "abi": [ { "type": "impl", @@ -3567,6 +3953,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -3615,48 +4034,56 @@ }, { "type": "impl", - "name": "player_configImpl", - "interface_name": "dojo_examples::models::Iplayer_config" + "name": "movesImpl", + "interface_name": "dojo_examples::models::Imoves" }, { - "type": "struct", - "name": "dojo_examples::models::PlayerItem", - "members": [ + "type": "enum", + "name": "dojo_examples::models::Direction", + "variants": [ { - "name": "item_id", - "type": "core::integer::u32" + "name": "None", + "type": "()" }, { - "name": "quantity", - "type": "core::integer::u32" + "name": "Left", + "type": "()" + }, + { + "name": "Right", + "type": "()" + }, + { + "name": "Up", + "type": "()" + }, + { + "name": "Down", + "type": "()" } ] }, { "type": "struct", - "name": "dojo_examples::models::PlayerConfig", + "name": "dojo_examples::models::Moves", "members": [ { "name": "player", "type": "core::starknet::contract_address::ContractAddress" }, { - "name": "name", - "type": "core::byte_array::ByteArray" - }, - { - "name": "items", - "type": "core::array::Array::" + "name": "remaining", + "type": "core::integer::u8" }, { - "name": "favorite_item", - "type": "core::option::Option::" + "name": "last_direction", + "type": "dojo_examples::models::Direction" } ] }, { "type": "interface", - "name": "dojo_examples::models::Iplayer_config", + "name": "dojo_examples::models::Imoves", "items": [ { "type": "function", @@ -3664,7 +4091,7 @@ "inputs": [ { "name": "model", - "type": "dojo_examples::models::PlayerConfig" + "type": "dojo_examples::models::Moves" } ], "outputs": [], @@ -3674,12 +4101,13 @@ }, { "type": "event", - "name": "dojo_examples::models::player_config::Event", + "name": "dojo_examples::models::moves::Event", "kind": "enum", "variants": [] } ], - "name": "dojo_examples::models::player_config" + "tag": "dojo_examples-Moves", + "manifest_name": "dojo_examples-Moves-2e2accba" }, { "kind": "DojoModel", @@ -3690,13 +4118,23 @@ "key": true }, { - "name": "vec", - "type": "Vec2", + "name": "name", + "type": "ByteArray", + "key": false + }, + { + "name": "items", + "type": "Array", + "key": false + }, + { + "name": "favorite_item", + "type": "Option", "key": false } ], - "class_hash": "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff", - "original_class_hash": "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff", + "class_hash": "0x3b94d89f5b3589438b8ffcf2cbc8061670deee1d0f81247ab2346d8991b4eca", + "original_class_hash": "0x3b94d89f5b3589438b8ffcf2cbc8061670deee1d0f81247ab2346d8991b4eca", "abi": [ { "type": "impl", @@ -3970,6 +4408,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -4018,40 +4489,48 @@ }, { "type": "impl", - "name": "positionImpl", - "interface_name": "dojo_examples::models::Iposition" + "name": "player_configImpl", + "interface_name": "dojo_examples::models::Iplayer_config" }, { "type": "struct", - "name": "dojo_examples::models::Vec2", + "name": "dojo_examples::models::PlayerItem", "members": [ { - "name": "x", + "name": "item_id", "type": "core::integer::u32" }, { - "name": "y", + "name": "quantity", "type": "core::integer::u32" } ] }, { "type": "struct", - "name": "dojo_examples::models::Position", + "name": "dojo_examples::models::PlayerConfig", "members": [ { "name": "player", "type": "core::starknet::contract_address::ContractAddress" }, { - "name": "vec", - "type": "dojo_examples::models::Vec2" + "name": "name", + "type": "core::byte_array::ByteArray" + }, + { + "name": "items", + "type": "core::array::Array::" + }, + { + "name": "favorite_item", + "type": "core::option::Option::" } ] }, { "type": "interface", - "name": "dojo_examples::models::Iposition", + "name": "dojo_examples::models::Iplayer_config", "items": [ { "type": "function", @@ -4059,7 +4538,7 @@ "inputs": [ { "name": "model", - "type": "dojo_examples::models::Position" + "type": "dojo_examples::models::PlayerConfig" } ], "outputs": [], @@ -4069,12 +4548,13 @@ }, { "type": "event", - "name": "dojo_examples::models::position::Event", + "name": "dojo_examples::models::player_config::Event", "kind": "enum", "variants": [] } ], - "name": "dojo_examples::models::position" + "tag": "dojo_examples-PlayerConfig", + "manifest_name": "dojo_examples-PlayerConfig-3adad785" }, { "kind": "DojoModel", @@ -4085,18 +4565,13 @@ "key": true }, { - "name": "server_id", - "type": "u32", - "key": true - }, - { - "name": "name", - "type": "ByteArray", + "name": "vec", + "type": "Vec2", "key": false } ], - "class_hash": "0x6dc51a232ffcfaa02646636daf1b8acab8121fa69458258da8df1620aff07ab", - "original_class_hash": "0x6dc51a232ffcfaa02646636daf1b8acab8121fa69458258da8df1620aff07ab", + "class_hash": "0x59e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34", + "original_class_hash": "0x59e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34", "abi": [ { "type": "impl", @@ -4370,6 +4845,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -4418,30 +4926,40 @@ }, { "type": "impl", - "name": "server_profileImpl", - "interface_name": "dojo_examples::models::Iserver_profile" + "name": "positionImpl", + "interface_name": "dojo_examples::models::Iposition" }, { "type": "struct", - "name": "dojo_examples::models::ServerProfile", + "name": "dojo_examples::models::Vec2", "members": [ { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" + "name": "x", + "type": "core::integer::u32" }, { - "name": "server_id", + "name": "y", "type": "core::integer::u32" + } + ] + }, + { + "type": "struct", + "name": "dojo_examples::models::Position", + "members": [ + { + "name": "player", + "type": "core::starknet::contract_address::ContractAddress" }, { - "name": "name", - "type": "core::byte_array::ByteArray" + "name": "vec", + "type": "dojo_examples::models::Vec2" } ] }, { "type": "interface", - "name": "dojo_examples::models::Iserver_profile", + "name": "dojo_examples::models::Iposition", "items": [ { "type": "function", @@ -4449,7 +4967,7 @@ "inputs": [ { "name": "model", - "type": "dojo_examples::models::ServerProfile" + "type": "dojo_examples::models::Position" } ], "outputs": [], @@ -4459,34 +4977,35 @@ }, { "type": "event", - "name": "dojo_examples::models::server_profile::Event", + "name": "dojo_examples::models::position::Event", "kind": "enum", "variants": [] } ], - "name": "dojo_examples::models::server_profile" + "tag": "dojo_examples-Position", + "manifest_name": "dojo_examples-Position-1e145e26" }, { "kind": "DojoModel", "members": [ { - "name": "contract_address", + "name": "player", "type": "ContractAddress", "key": true }, { - "name": "contract_class", - "type": "ClassHash", - "key": false + "name": "server_id", + "type": "u32", + "key": true }, { - "name": "value", - "type": "u8", + "name": "name", + "type": "ByteArray", "key": false } ], - "class_hash": "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c", - "original_class_hash": "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c", + "class_hash": "0x38986e1b875d48b2931932879412cf0b6c52d0e04aaac6e2780995b3f47b91b", + "original_class_hash": "0x38986e1b875d48b2931932879412cf0b6c52d0e04aaac6e2780995b3f47b91b", "abi": [ { "type": "impl", @@ -4760,6 +5279,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -4808,30 +5360,30 @@ }, { "type": "impl", - "name": "contract_initializedImpl", - "interface_name": "dojo_examples::others::others::Icontract_initialized" + "name": "server_profileImpl", + "interface_name": "dojo_examples::models::Iserver_profile" }, { "type": "struct", - "name": "dojo_examples::others::others::ContractInitialized", + "name": "dojo_examples::models::ServerProfile", "members": [ { - "name": "contract_address", + "name": "player", "type": "core::starknet::contract_address::ContractAddress" }, { - "name": "contract_class", - "type": "core::starknet::class_hash::ClassHash" + "name": "server_id", + "type": "core::integer::u32" }, { - "name": "value", - "type": "core::integer::u8" + "name": "name", + "type": "core::byte_array::ByteArray" } ] }, { "type": "interface", - "name": "dojo_examples::others::others::Icontract_initialized", + "name": "dojo_examples::models::Iserver_profile", "items": [ { "type": "function", @@ -4839,7 +5391,7 @@ "inputs": [ { "name": "model", - "type": "dojo_examples::others::others::ContractInitialized" + "type": "dojo_examples::models::ServerProfile" } ], "outputs": [], @@ -4849,12 +5401,13 @@ }, { "type": "event", - "name": "dojo_examples::others::others::contract_initialized::Event", + "name": "dojo_examples::models::server_profile::Event", "kind": "enum", "variants": [] } ], - "name": "dojo_examples::others::others::contract_initialized" + "tag": "dojo_examples-ServerProfile", + "manifest_name": "dojo_examples-ServerProfile-4caad1e6" } ] } \ 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 fd58cefd55..ad80716ce8 100644 --- a/examples/spawn-and-move/manifests/dev/manifest.toml +++ b/examples/spawn-and-move/manifests/dev/manifest.toml @@ -1,13 +1,13 @@ [world] kind = "WorldContract" -class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -original_class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -abi = "manifests/dev/abis/deployments/dojo_world_world.json" -address = "0x7efebb0c2d4cc285d48a97a7174def3be7fdd6b7bd29cca758fa2e17e03ef30" -transaction_hash = "0x60316eb232789f4d8352c6afdc36b76d33362d72b43bf78183b43f196779a9d" +class_hash = "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2" +original_class_hash = "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2" +abi = "abis/deployments/dojo-world.json" +address = "0x104dd156d76aeab45146a10869637f161ca6cf9f804704f8bbb12ae5b1b5cfb" +transaction_hash = "0x280e50610d4467bfe1be1adaae7f77642adbe2ad4106cb861e28441e94ff287" block_number = 3 seed = "dojo_examples" -name = "dojo::world::world" +manifest_name = "dojo-world" [world.metadata] profile_name = "dev" @@ -17,77 +17,89 @@ rpc_url = "http://localhost:5050/" kind = "Class" class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" original_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -name = "dojo::base::base" +abi = "manifests/dev/abis/base/dojo-base.json" +tag = "dojo-base" +manifest_name = "dojo-base" [[contracts]] kind = "DojoContract" -address = "0x5c70a663d6b48d8e4c6aaa9572e3735a732ac3765700d470463e670587852af" -class_hash = "0x7d70dda5cb8dcb697ccc2c129254c554e8994f1b231527f7641a14706af016f" -original_class_hash = "0x7d70dda5cb8dcb697ccc2c129254c554e8994f1b231527f7641a14706af016f" +address = "0x2a570e12405096e725508ba1f4ade127edd42e0fcb5890b8f12f76ef043623" +class_hash = "0x7b394d087b5cf4f3b740253c591138bf98d177ef0d9b5c00b0477a145f3fb75" +original_class_hash = "0x7b394d087b5cf4f3b740253c591138bf98d177ef0d9b5c00b0477a145f3fb75" base_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -abi = "manifests/dev/abis/deployments/contracts/dojo_examples_actions_actions.json" +abi = "abis/deployments/contracts/dojo_examples-actions-40b6994c.json" reads = [] writes = [ - "Moves", - "Position", + "dojo_examples-Moves", + "dojo_examples-Position", ] computed = [] init_calldata = [] -name = "dojo_examples::actions::actions" +tag = "dojo_examples-actions" +manifest_name = "dojo_examples-actions-40b6994c" [[contracts]] kind = "DojoContract" -address = "0x75961b2027c52948ecebfd347aa427436ea308d41997fa9b3c98380f7011d53" -class_hash = "0x5c32bc974f96f82ded691bb95bf26b2783f874fffb50fe84a331acb75ecf5b2" -original_class_hash = "0x5c32bc974f96f82ded691bb95bf26b2783f874fffb50fe84a331acb75ecf5b2" +address = "0x26f33e8d81dad06c79c2d944ea519a850e007eb8432ae20d38db82caea590b2" +class_hash = "0x6a55b3f612b0bc5e55603d805c188c0220aa53017fd2f690abe8bad50867ef2" +original_class_hash = "0x6a55b3f612b0bc5e55603d805c188c0220aa53017fd2f690abe8bad50867ef2" base_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -abi = "manifests/dev/abis/deployments/contracts/dojo_examples_mock_token_mock_token.json" +abi = "abis/deployments/contracts/dojo_examples-mock_token-31599eb2.json" reads = [] writes = [] computed = [] init_calldata = [] -name = "dojo_examples::mock_token::mock_token" +tag = "dojo_examples-mock_token" +manifest_name = "dojo_examples-mock_token-31599eb2" [[contracts]] kind = "DojoContract" -address = "0x3f51cd82daaf5907d2fd082ae3f45ae2ef96ab61677f46abc16e0a54d3392d1" -class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" -original_class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" +address = "0x3304896afaa421f362b3b8e8586773f9a7fcaca050b3fd6885400908803f344" +class_hash = "0x3a61e2fafaee0ca4ed5166fbb417270563b4d8518cd1e086733cc346e8ea6b9" +original_class_hash = "0x3a61e2fafaee0ca4ed5166fbb417270563b4d8518cd1e086733cc346e8ea6b9" base_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -abi = "manifests/dev/abis/deployments/contracts/dojo_examples_others_others.json" +abi = "abis/deployments/contracts/dojo_examples-others-61de2c18.json" reads = [] writes = [] computed = [] init_calldata = [ - "$contract_address:dojo_examples::actions::actions", - "$class_hash:dojo_examples::actions::actions", + "$contract_address:dojo_examples-actions", + "$class_hash:dojo_examples-actions", "10", ] -name = "dojo_examples::others::others" +tag = "dojo_examples-others" +manifest_name = "dojo_examples-others-61de2c18" [[models]] kind = "DojoModel" -class_hash = "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c" -original_class_hash = "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c" -abi = "manifests/dev/abis/deployments/models/dojo_examples_actions_actions_moved.json" -name = "dojo_examples::actions::actions::moved" +class_hash = "0x1ba185f6f09a7a73f32e6e1d6767c182abe5635d3107c79b64dfdb9f25b3c3a" +original_class_hash = "0x1ba185f6f09a7a73f32e6e1d6767c182abe5635d3107c79b64dfdb9f25b3c3a" +abi = "abis/deployments/models/dojo_examples-ContractInitialized-376b7bd6.json" +tag = "dojo_examples-ContractInitialized" +manifest_name = "dojo_examples-ContractInitialized-376b7bd6" [[models.members]] -name = "player" +name = "contract_address" type = "ContractAddress" key = true [[models.members]] -name = "direction" -type = "Direction" +name = "contract_class" +type = "ClassHash" +key = false + +[[models.members]] +name = "value" +type = "u8" key = false [[models]] kind = "DojoModel" -class_hash = "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5" -original_class_hash = "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5" -abi = "manifests/dev/abis/deployments/models/dojo_examples_models_message.json" -name = "dojo_examples::models::message" +class_hash = "0x6d9703c6df6af6f097cfdd2e7f838f4fd2b2f2ae27bf27e76065ffb903f9c9b" +original_class_hash = "0x6d9703c6df6af6f097cfdd2e7f838f4fd2b2f2ae27bf27e76065ffb903f9c9b" +abi = "abis/deployments/models/dojo_examples-Message-1bb1d226.json" +tag = "dojo_examples-Message" +manifest_name = "dojo_examples-Message-1bb1d226" [[models.members]] name = "identity" @@ -111,10 +123,11 @@ key = true [[models]] kind = "DojoModel" -class_hash = "0x6a21c56878ba470ac7a51f336ca6a59781de38e1810d2d20866ab2b52138a6d" -original_class_hash = "0x6a21c56878ba470ac7a51f336ca6a59781de38e1810d2d20866ab2b52138a6d" -abi = "manifests/dev/abis/deployments/models/dojo_examples_models_mock_token.json" -name = "dojo_examples::models::mock_token" +class_hash = "0x637d1d7966a14967047e31f3512c694e32b4d6a6dda4afe4b225a8b6f8f3519" +original_class_hash = "0x637d1d7966a14967047e31f3512c694e32b4d6a6dda4afe4b225a8b6f8f3519" +abi = "abis/deployments/models/dojo_examples-MockToken-38903c7c.json" +tag = "dojo_examples-MockToken" +manifest_name = "dojo_examples-MockToken-38903c7c" [[models.members]] name = "account" @@ -128,10 +141,29 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8" -original_class_hash = "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8" -abi = "manifests/dev/abis/deployments/models/dojo_examples_models_moves.json" -name = "dojo_examples::models::moves" +class_hash = "0x838672e7f67cd6e67336283805b0ef168c814e65b6ba1d3a7a5f31c10559c4" +original_class_hash = "0x838672e7f67cd6e67336283805b0ef168c814e65b6ba1d3a7a5f31c10559c4" +abi = "abis/deployments/models/dojo_examples-Moved-318ae40d.json" +tag = "dojo_examples-Moved" +manifest_name = "dojo_examples-Moved-318ae40d" + +[[models.members]] +name = "player" +type = "ContractAddress" +key = true + +[[models.members]] +name = "direction" +type = "Direction" +key = false + +[[models]] +kind = "DojoModel" +class_hash = "0x4e9b10687f487bc0fe159affa3963cf0f7548136a2b614914e11af4497d609" +original_class_hash = "0x4e9b10687f487bc0fe159affa3963cf0f7548136a2b614914e11af4497d609" +abi = "abis/deployments/models/dojo_examples-Moves-2e2accba.json" +tag = "dojo_examples-Moves" +manifest_name = "dojo_examples-Moves-2e2accba" [[models.members]] name = "player" @@ -150,10 +182,11 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55" -original_class_hash = "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55" -abi = "manifests/dev/abis/deployments/models/dojo_examples_models_player_config.json" -name = "dojo_examples::models::player_config" +class_hash = "0x3b94d89f5b3589438b8ffcf2cbc8061670deee1d0f81247ab2346d8991b4eca" +original_class_hash = "0x3b94d89f5b3589438b8ffcf2cbc8061670deee1d0f81247ab2346d8991b4eca" +abi = "abis/deployments/models/dojo_examples-PlayerConfig-3adad785.json" +tag = "dojo_examples-PlayerConfig" +manifest_name = "dojo_examples-PlayerConfig-3adad785" [[models.members]] name = "player" @@ -177,10 +210,11 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff" -original_class_hash = "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff" -abi = "manifests/dev/abis/deployments/models/dojo_examples_models_position.json" -name = "dojo_examples::models::position" +class_hash = "0x59e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34" +original_class_hash = "0x59e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34" +abi = "abis/deployments/models/dojo_examples-Position-1e145e26.json" +tag = "dojo_examples-Position" +manifest_name = "dojo_examples-Position-1e145e26" [[models.members]] name = "player" @@ -194,10 +228,11 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x6dc51a232ffcfaa02646636daf1b8acab8121fa69458258da8df1620aff07ab" -original_class_hash = "0x6dc51a232ffcfaa02646636daf1b8acab8121fa69458258da8df1620aff07ab" -abi = "manifests/dev/abis/deployments/models/dojo_examples_models_server_profile.json" -name = "dojo_examples::models::server_profile" +class_hash = "0x38986e1b875d48b2931932879412cf0b6c52d0e04aaac6e2780995b3f47b91b" +original_class_hash = "0x38986e1b875d48b2931932879412cf0b6c52d0e04aaac6e2780995b3f47b91b" +abi = "abis/deployments/models/dojo_examples-ServerProfile-4caad1e6.json" +tag = "dojo_examples-ServerProfile" +manifest_name = "dojo_examples-ServerProfile-4caad1e6" [[models.members]] name = "player" @@ -213,25 +248,3 @@ key = true name = "name" type = "ByteArray" key = false - -[[models]] -kind = "DojoModel" -class_hash = "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c" -original_class_hash = "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c" -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 deleted file mode 100644 index b21fc1adec..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_actions_actions.toml +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 129f942bd8..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/contracts/dojo_examples_others_others.toml +++ /dev/null @@ -1,4 +0,0 @@ -name = "dojo_examples::others::others" -reads = [] -writes = [] -init_calldata = ["$contract_address:dojo_examples::actions::actions", "$class_hash:dojo_examples::actions::actions", "10"] diff --git a/examples/spawn-and-move/manifests/dev/overlays/dojo_base_base.toml b/examples/spawn-and-move/manifests/dev/overlays/dojo_base_base.toml deleted file mode 100644 index f706470d45..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/dojo_base_base.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo::base::base" diff --git a/examples/spawn-and-move/manifests/dev/overlays/dojo_world_world.toml b/examples/spawn-and-move/manifests/dev/overlays/dojo_world_world.toml deleted file mode 100644 index a3e686e3ef..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/dojo_world_world.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo::world::world" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_actions_actions_moved.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_actions_actions_moved.toml deleted file mode 100644 index 4958a7a15c..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_actions_actions_moved.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::actions::actions::moved" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_emote_message.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_emote_message.toml deleted file mode 100644 index d60162cc72..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_emote_message.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::emote_message" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_moves.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_moves.toml deleted file mode 100644 index dc8784e746..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_moves.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::moves" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_player_config.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_player_config.toml deleted file mode 100644 index 6af8240b36..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_player_config.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::player_config" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_position.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_position.toml deleted file mode 100644 index df38e71c32..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_models_position.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::position" diff --git a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_others_others_contract_initialized.toml b/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_others_others_contract_initialized.toml deleted file mode 100644 index f8f3053fe5..0000000000 --- a/examples/spawn-and-move/manifests/dev/overlays/models/dojo_examples_others_others_contract_initialized.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::others::others::contract_initialized" diff --git a/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples_actions_actions.json b/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-actions-40b6994c.json similarity index 75% rename from examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples_actions_actions.json rename to examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-actions-40b6994c.json index 21aed968a7..310e06be9e 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples_actions_actions.json +++ b/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-actions-40b6994c.json @@ -1,16 +1,45 @@ [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", "inputs": [], "outputs": [ { @@ -18,6 +47,39 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, @@ -155,24 +217,6 @@ } ] }, - { - "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": "interface", "name": "dojo_examples::actions::IActions", @@ -182,7 +226,7 @@ "name": "spawn", "inputs": [], "outputs": [], - "state_mutability": "view" + "state_mutability": "external" }, { "type": "function", @@ -194,7 +238,7 @@ } ], "outputs": [], - "state_mutability": "view" + "state_mutability": "external" }, { "type": "function", @@ -206,7 +250,41 @@ } ], "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "get_player_position", + "inputs": [], + "outputs": [ + { + "type": "dojo_examples::models::Position" + } + ], "state_mutability": "view" + }, + { + "type": "function", + "name": "reset_player_config", + "inputs": [], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "set_player_server_profile", + "inputs": [ + { + "name": "server_id", + "type": "core::integer::u32" + }, + { + "name": "name", + "type": "core::byte_array::ByteArray" + } + ], + "outputs": [], + "state_mutability": "external" } ] }, diff --git a/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-mock_token-31599eb2.json b/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-mock_token-31599eb2.json new file mode 100644 index 0000000000..17da9b2135 --- /dev/null +++ b/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-mock_token-31599eb2.json @@ -0,0 +1,195 @@ +[ + { + "type": "impl", + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", + "name": "dojo::contract::IContract", + "items": [ + { + "type": "function", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "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::mock_token::mock_token::IDojoInit" + }, + { + "type": "interface", + "name": "dojo_examples::mock_token::mock_token::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [], + "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::mock_token::mock_token::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/contracts/dojo_examples_others_others.json b/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-others-61de2c18.json similarity index 67% rename from examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_others_others.json rename to examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-others-61de2c18.json index 36d8c3ef78..2e0c485d8b 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/contracts/dojo_examples_others_others.json +++ b/examples/spawn-and-move/manifests/release/abis/base/contracts/dojo_examples-others-61de2c18.json @@ -1,16 +1,67 @@ [ { "type": "impl", - "name": "DojoResourceProviderImpl", - "interface_name": "dojo::world::IDojoResourceProvider" + "name": "ContractImpl", + "interface_name": "dojo::contract::IContract" + }, + { + "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": "interface", - "name": "dojo::world::IDojoResourceProvider", + "name": "dojo::contract::IContract", "items": [ { "type": "function", - "name": "dojo_resource", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", "inputs": [], "outputs": [ { @@ -18,6 +69,17 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" } ] }, diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_mock_token_mock_token.json b/examples/spawn-and-move/manifests/release/abis/base/dojo-base.json similarity index 67% rename from examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_mock_token_mock_token.json rename to examples/spawn-and-move/manifests/release/abis/base/dojo-base.json index f4bde33e6d..ee9ceaac66 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/contracts/dojo_examples_mock_token_mock_token.json +++ b/examples/spawn-and-move/manifests/release/abis/base/dojo-base.json @@ -1,26 +1,4 @@ [ - { - "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", @@ -53,24 +31,6 @@ } ] }, - { - "type": "impl", - "name": "IDojoInitImpl", - "interface_name": "dojo_examples::mock_token::mock_token::IDojoInit" - }, - { - "type": "interface", - "name": "dojo_examples::mock_token::mock_token::IDojoInit", - "items": [ - { - "type": "function", - "name": "dojo_init", - "inputs": [], - "outputs": [], - "state_mutability": "view" - } - ] - }, { "type": "impl", "name": "UpgradableImpl", @@ -94,6 +54,11 @@ } ] }, + { + "type": "constructor", + "name": "constructor", + "inputs": [] + }, { "type": "event", "name": "dojo::components::upgradeable::upgradeable::Upgraded", @@ -120,13 +85,13 @@ }, { "type": "event", - "name": "dojo_examples::mock_token::mock_token::Event", + "name": "dojo::base::base::Event", "kind": "enum", "variants": [ { "name": "UpgradeableEvent", "type": "dojo::components::upgradeable::upgradeable::Event", - "kind": "nested" + "kind": "flat" } ] } diff --git a/examples/spawn-and-move/manifests/release/abis/base/dojo_world_world.json b/examples/spawn-and-move/manifests/release/abis/base/dojo-world.json similarity index 89% rename from examples/spawn-and-move/manifests/release/abis/base/dojo_world_world.json rename to examples/spawn-and-move/manifests/release/abis/base/dojo-world.json index c1a2447839..0e5e96210e 100644 --- a/examples/spawn-and-move/manifests/release/abis/base/dojo_world_world.json +++ b/examples/spawn-and-move/manifests/release/abis/base/dojo-world.json @@ -194,6 +194,18 @@ "outputs": [], "state_mutability": "external" }, + { + "type": "function", + "name": "register_namespace", + "inputs": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray" + } + ], + "outputs": [], + "state_mutability": "external" + }, { "type": "function", "name": "deploy_contract", @@ -401,7 +413,7 @@ "name": "is_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -421,7 +433,7 @@ "name": "grant_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -437,7 +449,7 @@ "name": "revoke_writer", "inputs": [ { - "name": "model", + "name": "resource", "type": "core::felt252" }, { @@ -447,6 +459,66 @@ ], "outputs": [], "state_mutability": "external" + }, + { + "type": "function", + "name": "can_write_resource", + "inputs": [ + { + "name": "resource_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_model", + "inputs": [ + { + "name": "model_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "can_write_namespace", + "inputs": [ + { + "name": "namespace_id", + "type": "core::felt252" + }, + { + "name": "contract", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" } ] }, @@ -692,6 +764,16 @@ "name": "address", "type": "core::starknet::contract_address::ContractAddress", "kind": "data" + }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "name", + "type": "core::byte_array::ByteArray", + "kind": "data" } ] }, @@ -741,6 +823,23 @@ } ] }, + { + "type": "event", + "name": "dojo::world::world::NamespaceRegistered", + "kind": "struct", + "members": [ + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, + { + "name": "hash", + "type": "core::felt252", + "kind": "data" + } + ] + }, { "type": "event", "name": "dojo::world::world::ModelRegistered", @@ -751,6 +850,11 @@ "type": "core::byte_array::ByteArray", "kind": "data" }, + { + "name": "namespace", + "type": "core::byte_array::ByteArray", + "kind": "data" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash", @@ -818,7 +922,7 @@ "kind": "struct", "members": [ { - "name": "model", + "name": "resource", "type": "core::felt252", "kind": "data" }, @@ -956,6 +1060,11 @@ "type": "dojo::world::world::MetadataUpdate", "kind": "nested" }, + { + "name": "NamespaceRegistered", + "type": "dojo::world::world::NamespaceRegistered", + "kind": "nested" + }, { "name": "ModelRegistered", "type": "dojo::world::world::ModelRegistered", 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/release/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json similarity index 92% rename from examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_others_others_contract_initialized.json rename to examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json index 1dbbd313d8..a46919adf4 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_others_others_contract_initialized.json +++ b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_message.json b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Message-1bb1d226.json similarity index 91% rename from examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_message.json rename to examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Message-1bb1d226.json index ec760c99fb..8e8af178cf 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_message.json +++ b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Message-1bb1d226.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_mock_token.json b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-MockToken-38903c7c.json similarity index 91% rename from examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_mock_token.json rename to examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-MockToken-38903c7c.json index b2c1c340fa..b02c79f885 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_mock_token.json +++ b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-MockToken-38903c7c.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_actions_actions_moved.json b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Moved-318ae40d.json similarity index 92% rename from examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_actions_actions_moved.json rename to examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Moved-318ae40d.json index 89abdcfec5..5138063ef3 100644 --- a/examples/spawn-and-move/manifests/dev/abis/deployments/models/dojo_examples_actions_actions_moved.json +++ b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Moved-318ae40d.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_moves.json b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Moves-2e2accba.json similarity index 92% rename from examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_moves.json rename to examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Moves-2e2accba.json index ee2cf17b26..c5404bf877 100644 --- a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_moves.json +++ b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Moves-2e2accba.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_player_config.json b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-PlayerConfig-3adad785.json similarity index 92% rename from examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_player_config.json rename to examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-PlayerConfig-3adad785.json index 8c33ebabc4..a70cb0881d 100644 --- a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_player_config.json +++ b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-PlayerConfig-3adad785.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_position.json b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Position-1e145e26.json similarity index 92% rename from examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_position.json rename to examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Position-1e145e26.json index bceec42587..cb815337ec 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_position.json +++ b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-Position-1e145e26.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", diff --git a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_player_config.json b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json similarity index 88% rename from examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_player_config.json rename to examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json index 8c33ebabc4..31f5457868 100644 --- a/examples/spawn-and-move/manifests/dev/abis/base/models/dojo_examples_models_player_config.json +++ b/examples/spawn-and-move/manifests/release/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json @@ -271,6 +271,39 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "unpacked_size", @@ -319,48 +352,30 @@ }, { "type": "impl", - "name": "player_configImpl", - "interface_name": "dojo_examples::models::Iplayer_config" + "name": "server_profileImpl", + "interface_name": "dojo_examples::models::Iserver_profile" }, { "type": "struct", - "name": "dojo_examples::models::PlayerItem", + "name": "dojo_examples::models::ServerProfile", "members": [ { - "name": "item_id", - "type": "core::integer::u32" + "name": "player", + "type": "core::starknet::contract_address::ContractAddress" }, { - "name": "quantity", + "name": "server_id", "type": "core::integer::u32" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::PlayerConfig", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" }, { "name": "name", "type": "core::byte_array::ByteArray" - }, - { - "name": "items", - "type": "core::array::Array::" - }, - { - "name": "favorite_item", - "type": "core::option::Option::" } ] }, { "type": "interface", - "name": "dojo_examples::models::Iplayer_config", + "name": "dojo_examples::models::Iserver_profile", "items": [ { "type": "function", @@ -368,7 +383,7 @@ "inputs": [ { "name": "model", - "type": "dojo_examples::models::PlayerConfig" + "type": "dojo_examples::models::ServerProfile" } ], "outputs": [], @@ -378,7 +393,7 @@ }, { "type": "event", - "name": "dojo_examples::models::player_config::Event", + "name": "dojo_examples::models::server_profile::Event", "kind": "enum", "variants": [] } diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/contracts/dojo_examples_actions_actions.json b/examples/spawn-and-move/manifests/release/abis/deployments/contracts/dojo_examples_actions_actions.json deleted file mode 100644 index 21aed968a7..0000000000 --- a/examples/spawn-and-move/manifests/release/abis/deployments/contracts/dojo_examples_actions_actions.json +++ /dev/null @@ -1,290 +0,0 @@ -[ - { - "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": "ActionsComputedImpl", - "interface_name": "dojo_examples::actions::IActionsComputed" - }, - { - "type": "struct", - "name": "dojo_examples::models::Vec2", - "members": [ - { - "name": "x", - "type": "core::integer::u32" - }, - { - "name": "y", - "type": "core::integer::u32" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::Position", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "vec", - "type": "dojo_examples::models::Vec2" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::actions::IActionsComputed", - "items": [ - { - "type": "function", - "name": "tile_terrain", - "inputs": [ - { - "name": "vec", - "type": "dojo_examples::models::Vec2" - } - ], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "quadrant", - "inputs": [ - { - "name": "pos", - "type": "dojo_examples::models::Position" - } - ], - "outputs": [ - { - "type": "core::integer::u8" - } - ], - "state_mutability": "view" - } - ] - }, - { - "type": "impl", - "name": "ActionsImpl", - "interface_name": "dojo_examples::actions::IActions" - }, - { - "type": "enum", - "name": "dojo_examples::models::Direction", - "variants": [ - { - "name": "None", - "type": "()" - }, - { - "name": "Left", - "type": "()" - }, - { - "name": "Right", - "type": "()" - }, - { - "name": "Up", - "type": "()" - }, - { - "name": "Down", - "type": "()" - } - ] - }, - { - "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": "interface", - "name": "dojo_examples::actions::IActions", - "items": [ - { - "type": "function", - "name": "spawn", - "inputs": [], - "outputs": [], - "state_mutability": "view" - }, - { - "type": "function", - "name": "move", - "inputs": [ - { - "name": "direction", - "type": "dojo_examples::models::Direction" - } - ], - "outputs": [], - "state_mutability": "view" - }, - { - "type": "function", - "name": "set_player_config", - "inputs": [ - { - "name": "name", - "type": "core::byte_array::ByteArray" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "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", - "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::actions::actions::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/release/abis/deployments/dojo_world_world.json b/examples/spawn-and-move/manifests/release/abis/deployments/dojo_world_world.json deleted file mode 100644 index c1a2447839..0000000000 --- a/examples/spawn-and-move/manifests/release/abis/deployments/dojo_world_world.json +++ /dev/null @@ -1,996 +0,0 @@ -[ - { - "type": "impl", - "name": "World", - "interface_name": "dojo::world::IWorld" - }, - { - "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": "struct", - "name": "dojo::resource_metadata::ResourceMetadata", - "members": [ - { - "name": "resource_id", - "type": "core::felt252" - }, - { - "name": "metadata_uri", - "type": "core::byte_array::ByteArray" - } - ] - }, - { - "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": "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": "enum", - "name": "core::bool", - "variants": [ - { - "name": "False", - "type": "()" - }, - { - "name": "True", - "type": "()" - } - ] - }, - { - "type": "interface", - "name": "dojo::world::IWorld", - "items": [ - { - "type": "function", - "name": "metadata", - "inputs": [ - { - "name": "resource_id", - "type": "core::felt252" - } - ], - "outputs": [ - { - "type": "dojo::resource_metadata::ResourceMetadata" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "set_metadata", - "inputs": [ - { - "name": "metadata", - "type": "dojo::resource_metadata::ResourceMetadata" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "model", - "inputs": [ - { - "name": "selector", - "type": "core::felt252" - } - ], - "outputs": [ - { - "type": "(core::starknet::class_hash::ClassHash, core::starknet::contract_address::ContractAddress)" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "register_model", - "inputs": [ - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "deploy_contract", - "inputs": [ - { - "name": "salt", - "type": "core::felt252" - }, - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash" - }, - { - "name": "init_calldata", - "type": "core::array::Span::" - } - ], - "outputs": [ - { - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "upgrade_contract", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash" - } - ], - "outputs": [ - { - "type": "core::starknet::class_hash::ClassHash" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "uuid", - "inputs": [], - "outputs": [ - { - "type": "core::integer::u32" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "emit", - "inputs": [ - { - "name": "keys", - "type": "core::array::Array::" - }, - { - "name": "values", - "type": "core::array::Span::" - } - ], - "outputs": [], - "state_mutability": "view" - }, - { - "type": "function", - "name": "entity", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "keys", - "type": "core::array::Span::" - }, - { - "name": "layout", - "type": "dojo::database::introspect::Layout" - } - ], - "outputs": [ - { - "type": "core::array::Span::" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "set_entity", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "keys", - "type": "core::array::Span::" - }, - { - "name": "values", - "type": "core::array::Span::" - }, - { - "name": "layout", - "type": "dojo::database::introspect::Layout" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "delete_entity", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "keys", - "type": "core::array::Span::" - }, - { - "name": "layout", - "type": "dojo::database::introspect::Layout" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "base", - "inputs": [], - "outputs": [ - { - "type": "core::starknet::class_hash::ClassHash" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "is_owner", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "resource", - "type": "core::felt252" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "grant_owner", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "resource", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "revoke_owner", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "resource", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "is_writer", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "contract", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "grant_writer", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "contract", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "revoke_writer", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "contract", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "UpgradeableWorld", - "interface_name": "dojo::world::IUpgradeableWorld" - }, - { - "type": "interface", - "name": "dojo::world::IUpgradeableWorld", - "items": [ - { - "type": "function", - "name": "upgrade", - "inputs": [ - { - "name": "new_class_hash", - "type": "core::starknet::class_hash::ClassHash" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "UpgradeableState", - "interface_name": "dojo::interfaces::IUpgradeableState" - }, - { - "type": "struct", - "name": "dojo::interfaces::StorageUpdate", - "members": [ - { - "name": "key", - "type": "core::felt252" - }, - { - "name": "value", - "type": "core::felt252" - } - ] - }, - { - "type": "struct", - "name": "core::array::Span::", - "members": [ - { - "name": "snapshot", - "type": "@core::array::Array::" - } - ] - }, - { - "type": "struct", - "name": "dojo::interfaces::ProgramOutput", - "members": [ - { - "name": "prev_state_root", - "type": "core::felt252" - }, - { - "name": "new_state_root", - "type": "core::felt252" - }, - { - "name": "block_number", - "type": "core::felt252" - }, - { - "name": "block_hash", - "type": "core::felt252" - }, - { - "name": "config_hash", - "type": "core::felt252" - }, - { - "name": "world_da_hash", - "type": "core::felt252" - }, - { - "name": "message_to_starknet_segment", - "type": "core::array::Span::" - }, - { - "name": "message_to_appchain_segment", - "type": "core::array::Span::" - } - ] - }, - { - "type": "interface", - "name": "dojo::interfaces::IUpgradeableState", - "items": [ - { - "type": "function", - "name": "upgrade_state", - "inputs": [ - { - "name": "new_state", - "type": "core::array::Span::" - }, - { - "name": "program_output", - "type": "dojo::interfaces::ProgramOutput" - }, - { - "name": "program_hash", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "ConfigImpl", - "interface_name": "dojo::config::interface::IConfig" - }, - { - "type": "interface", - "name": "dojo::config::interface::IConfig", - "items": [ - { - "type": "function", - "name": "set_differ_program_hash", - "inputs": [ - { - "name": "program_hash", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "set_merger_program_hash", - "inputs": [ - { - "name": "program_hash", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "get_differ_program_hash", - "inputs": [], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "get_merger_program_hash", - "inputs": [], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "set_facts_registry", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "get_facts_registry", - "inputs": [], - "outputs": [ - { - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "state_mutability": "view" - } - ] - }, - { - "type": "constructor", - "name": "constructor", - "inputs": [ - { - "name": "contract_base", - "type": "core::starknet::class_hash::ClassHash" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::WorldSpawned", - "kind": "struct", - "members": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "creator", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::ContractDeployed", - "kind": "struct", - "members": [ - { - "name": "salt", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - }, - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::ContractUpgraded", - "kind": "struct", - "members": [ - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - }, - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::WorldUpgraded", - "kind": "struct", - "members": [ - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::MetadataUpdate", - "kind": "struct", - "members": [ - { - "name": "resource", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "uri", - "type": "core::byte_array::ByteArray", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::ModelRegistered", - "kind": "struct", - "members": [ - { - "name": "name", - "type": "core::byte_array::ByteArray", - "kind": "data" - }, - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - }, - { - "name": "prev_class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - }, - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "prev_address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::StoreSetRecord", - "kind": "struct", - "members": [ - { - "name": "table", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "keys", - "type": "core::array::Span::", - "kind": "data" - }, - { - "name": "values", - "type": "core::array::Span::", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::StoreDelRecord", - "kind": "struct", - "members": [ - { - "name": "table", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "keys", - "type": "core::array::Span::", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::WriterUpdated", - "kind": "struct", - "members": [ - { - "name": "model", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "contract", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "value", - "type": "core::bool", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::OwnerUpdated", - "kind": "struct", - "members": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "resource", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "value", - "type": "core::bool", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::config::component::Config::DifferProgramHashUpdate", - "kind": "struct", - "members": [ - { - "name": "program_hash", - "type": "core::felt252", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::config::component::Config::MergerProgramHashUpdate", - "kind": "struct", - "members": [ - { - "name": "program_hash", - "type": "core::felt252", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::config::component::Config::FactsRegistryUpdate", - "kind": "struct", - "members": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::config::component::Config::Event", - "kind": "enum", - "variants": [ - { - "name": "DifferProgramHashUpdate", - "type": "dojo::config::component::Config::DifferProgramHashUpdate", - "kind": "nested" - }, - { - "name": "MergerProgramHashUpdate", - "type": "dojo::config::component::Config::MergerProgramHashUpdate", - "kind": "nested" - }, - { - "name": "FactsRegistryUpdate", - "type": "dojo::config::component::Config::FactsRegistryUpdate", - "kind": "nested" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::StateUpdated", - "kind": "struct", - "members": [ - { - "name": "da_hash", - "type": "core::felt252", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::Event", - "kind": "enum", - "variants": [ - { - "name": "WorldSpawned", - "type": "dojo::world::world::WorldSpawned", - "kind": "nested" - }, - { - "name": "ContractDeployed", - "type": "dojo::world::world::ContractDeployed", - "kind": "nested" - }, - { - "name": "ContractUpgraded", - "type": "dojo::world::world::ContractUpgraded", - "kind": "nested" - }, - { - "name": "WorldUpgraded", - "type": "dojo::world::world::WorldUpgraded", - "kind": "nested" - }, - { - "name": "MetadataUpdate", - "type": "dojo::world::world::MetadataUpdate", - "kind": "nested" - }, - { - "name": "ModelRegistered", - "type": "dojo::world::world::ModelRegistered", - "kind": "nested" - }, - { - "name": "StoreSetRecord", - "type": "dojo::world::world::StoreSetRecord", - "kind": "nested" - }, - { - "name": "StoreDelRecord", - "type": "dojo::world::world::StoreDelRecord", - "kind": "nested" - }, - { - "name": "WriterUpdated", - "type": "dojo::world::world::WriterUpdated", - "kind": "nested" - }, - { - "name": "OwnerUpdated", - "type": "dojo::world::world::OwnerUpdated", - "kind": "nested" - }, - { - "name": "ConfigEvent", - "type": "dojo::config::component::Config::Event", - "kind": "nested" - }, - { - "name": "StateUpdated", - "type": "dojo::world::world::StateUpdated", - "kind": "nested" - } - ] - } -] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_actions_actions_moved.json b/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_actions_actions_moved.json deleted file mode 100644 index 89abdcfec5..0000000000 --- a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_actions_actions_moved.json +++ /dev/null @@ -1,389 +0,0 @@ -[ - { - "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": "movedImpl", - "interface_name": "dojo_examples::actions::actions::Imoved" - }, - { - "type": "enum", - "name": "dojo_examples::models::Direction", - "variants": [ - { - "name": "None", - "type": "()" - }, - { - "name": "Left", - "type": "()" - }, - { - "name": "Right", - "type": "()" - }, - { - "name": "Up", - "type": "()" - }, - { - "name": "Down", - "type": "()" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::actions::actions::Moved", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "direction", - "type": "dojo_examples::models::Direction" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::actions::actions::Imoved", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::actions::actions::Moved" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::actions::actions::moved::Event", - "kind": "enum", - "variants": [] - } -] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_emote_message.json b/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_emote_message.json deleted file mode 100644 index 9c63d0fc5d..0000000000 --- a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_emote_message.json +++ /dev/null @@ -1,389 +0,0 @@ -[ - { - "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": "emote_messageImpl", - "interface_name": "dojo_examples::models::Iemote_message" - }, - { - "type": "enum", - "name": "dojo_examples::models::Emote", - "variants": [ - { - "name": "None", - "type": "()" - }, - { - "name": "Happy", - "type": "()" - }, - { - "name": "Sad", - "type": "()" - }, - { - "name": "Angry", - "type": "()" - }, - { - "name": "Love", - "type": "()" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::EmoteMessage", - "members": [ - { - "name": "identity", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "emote", - "type": "dojo_examples::models::Emote" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::models::Iemote_message", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::models::EmoteMessage" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::models::emote_message::Event", - "kind": "enum", - "variants": [] - } -] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_message.json b/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_message.json deleted file mode 100644 index ec760c99fb..0000000000 --- a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_message.json +++ /dev/null @@ -1,371 +0,0 @@ -[ - { - "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": "messageImpl", - "interface_name": "dojo_examples::models::Imessage" - }, - { - "type": "struct", - "name": "dojo_examples::models::Message", - "members": [ - { - "name": "identity", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "channel", - "type": "core::felt252" - }, - { - "name": "message", - "type": "core::byte_array::ByteArray" - }, - { - "name": "salt", - "type": "core::felt252" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::models::Imessage", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::models::Message" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::models::message::Event", - "kind": "enum", - "variants": [] - } -] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_position.json b/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_position.json deleted file mode 100644 index bceec42587..0000000000 --- a/examples/spawn-and-move/manifests/release/abis/deployments/models/dojo_examples_models_position.json +++ /dev/null @@ -1,377 +0,0 @@ -[ - { - "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": "positionImpl", - "interface_name": "dojo_examples::models::Iposition" - }, - { - "type": "struct", - "name": "dojo_examples::models::Vec2", - "members": [ - { - "name": "x", - "type": "core::integer::u32" - }, - { - "name": "y", - "type": "core::integer::u32" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::Position", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "vec", - "type": "dojo_examples::models::Vec2" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::models::Iposition", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::models::Position" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::models::position::Event", - "kind": "enum", - "variants": [] - } -] \ No newline at end of file diff --git a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-actions-40b6994c.toml b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-actions-40b6994c.toml new file mode 100644 index 0000000000..d50705247c --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-actions-40b6994c.toml @@ -0,0 +1,11 @@ +kind = "DojoContract" +class_hash = "0x7b394d087b5cf4f3b740253c591138bf98d177ef0d9b5c00b0477a145f3fb75" +original_class_hash = "0x7b394d087b5cf4f3b740253c591138bf98d177ef0d9b5c00b0477a145f3fb75" +base_class_hash = "0x0" +abi = "manifests/release/abis/base/contracts/dojo_examples-actions-40b6994c.json" +reads = [] +writes = [] +computed = [] +init_calldata = [] +tag = "dojo_examples-actions" +manifest_name = "dojo_examples-actions-40b6994c" diff --git a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-mock_token-31599eb2.toml b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-mock_token-31599eb2.toml new file mode 100644 index 0000000000..b9109222f9 --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-mock_token-31599eb2.toml @@ -0,0 +1,11 @@ +kind = "DojoContract" +class_hash = "0x6a55b3f612b0bc5e55603d805c188c0220aa53017fd2f690abe8bad50867ef2" +original_class_hash = "0x6a55b3f612b0bc5e55603d805c188c0220aa53017fd2f690abe8bad50867ef2" +base_class_hash = "0x0" +abi = "manifests/release/abis/base/contracts/dojo_examples-mock_token-31599eb2.json" +reads = [] +writes = [] +computed = [] +init_calldata = [] +tag = "dojo_examples-mock_token" +manifest_name = "dojo_examples-mock_token-31599eb2" diff --git a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-others-61de2c18.toml b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-others-61de2c18.toml new file mode 100644 index 0000000000..2b791ea2d5 --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-others-61de2c18.toml @@ -0,0 +1,11 @@ +kind = "DojoContract" +class_hash = "0x3a61e2fafaee0ca4ed5166fbb417270563b4d8518cd1e086733cc346e8ea6b9" +original_class_hash = "0x3a61e2fafaee0ca4ed5166fbb417270563b4d8518cd1e086733cc346e8ea6b9" +base_class_hash = "0x0" +abi = "manifests/release/abis/base/contracts/dojo_examples-others-61de2c18.json" +reads = [] +writes = [] +computed = [] +init_calldata = [] +tag = "dojo_examples-others" +manifest_name = "dojo_examples-others-61de2c18" diff --git a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_actions_actions.toml b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_actions_actions.toml deleted file mode 100644 index bf0f86489b..0000000000 --- a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_actions_actions.toml +++ /dev/null @@ -1,10 +0,0 @@ -kind = "DojoContract" -class_hash = "0x6d905953360cf18e3393d128c6ced40b38fc83b033412c8541fd4aba59d2767" -original_class_hash = "0x6d905953360cf18e3393d128c6ced40b38fc83b033412c8541fd4aba59d2767" -base_class_hash = "0x0" -abi = "manifests/release/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/release/base/contracts/dojo_examples_mock_token_mock_token.toml b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_mock_token_mock_token.toml deleted file mode 100644 index 65fbfcc8ad..0000000000 --- a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_mock_token_mock_token.toml +++ /dev/null @@ -1,10 +0,0 @@ -kind = "DojoContract" -class_hash = "0x5c32bc974f96f82ded691bb95bf26b2783f874fffb50fe84a331acb75ecf5b2" -original_class_hash = "0x5c32bc974f96f82ded691bb95bf26b2783f874fffb50fe84a331acb75ecf5b2" -base_class_hash = "0x0" -abi = "manifests/release/abis/base/contracts/dojo_examples_mock_token_mock_token.json" -reads = [] -writes = [] -computed = [] -init_calldata = [] -name = "dojo_examples::mock_token::mock_token" diff --git a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_others_others.toml b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_others_others.toml deleted file mode 100644 index 66cb83c9d6..0000000000 --- a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples_others_others.toml +++ /dev/null @@ -1,10 +0,0 @@ -kind = "DojoContract" -class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" -original_class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" -base_class_hash = "0x0" -abi = "manifests/release/abis/base/contracts/dojo_examples_others_others.json" -reads = [] -writes = [] -computed = [] -init_calldata = [] -name = "dojo_examples::others::others" diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_base_base.toml b/examples/spawn-and-move/manifests/release/base/dojo-base.toml similarity index 65% rename from crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_base_base.toml rename to examples/spawn-and-move/manifests/release/base/dojo-base.toml index 6c4b5de67e..feb0efd085 100644 --- a/crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests/dev/base/dojo_base_base.toml +++ b/examples/spawn-and-move/manifests/release/base/dojo-base.toml @@ -1,4 +1,6 @@ kind = "Class" class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" original_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -name = "dojo::base::base" +abi = "manifests/release/abis/base/dojo-base.json" +tag = "dojo-base" +manifest_name = "dojo-base" diff --git a/examples/spawn-and-move/manifests/release/base/dojo-world.toml b/examples/spawn-and-move/manifests/release/base/dojo-world.toml new file mode 100644 index 0000000000..cad6228974 --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/dojo-world.toml @@ -0,0 +1,6 @@ +kind = "Class" +class_hash = "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2" +original_class_hash = "0x1498dd1197805ec05d37da956d0fc568023a4c25578b0523b4f4f0d0e4f16c2" +abi = "manifests/release/abis/base/dojo-world.json" +tag = "dojo-world" +manifest_name = "dojo-world" diff --git a/examples/spawn-and-move/manifests/release/base/dojo_world_world.toml b/examples/spawn-and-move/manifests/release/base/dojo_world_world.toml deleted file mode 100644 index e9badcfe4c..0000000000 --- a/examples/spawn-and-move/manifests/release/base/dojo_world_world.toml +++ /dev/null @@ -1,5 +0,0 @@ -kind = "Class" -class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -original_class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -abi = "manifests/release/abis/base/dojo_world_world.json" -name = "dojo::world::world" diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-ContractInitialized-376b7bd6.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-ContractInitialized-376b7bd6.toml new file mode 100644 index 0000000000..9e7ac1b5fa --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-ContractInitialized-376b7bd6.toml @@ -0,0 +1,21 @@ +kind = "DojoModel" +class_hash = "0x1ba185f6f09a7a73f32e6e1d6767c182abe5635d3107c79b64dfdb9f25b3c3a" +original_class_hash = "0x1ba185f6f09a7a73f32e6e1d6767c182abe5635d3107c79b64dfdb9f25b3c3a" +abi = "manifests/release/abis/base/models/dojo_examples-ContractInitialized-376b7bd6.json" +tag = "dojo_examples-ContractInitialized" +manifest_name = "dojo_examples-ContractInitialized-376b7bd6" + +[[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/release/base/models/dojo_examples-Message-1bb1d226.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Message-1bb1d226.toml new file mode 100644 index 0000000000..415899e171 --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Message-1bb1d226.toml @@ -0,0 +1,26 @@ +kind = "DojoModel" +class_hash = "0x6d9703c6df6af6f097cfdd2e7f838f4fd2b2f2ae27bf27e76065ffb903f9c9b" +original_class_hash = "0x6d9703c6df6af6f097cfdd2e7f838f4fd2b2f2ae27bf27e76065ffb903f9c9b" +abi = "manifests/release/abis/base/models/dojo_examples-Message-1bb1d226.json" +tag = "dojo_examples-Message" +manifest_name = "dojo_examples-Message-1bb1d226" + +[[members]] +name = "identity" +type = "ContractAddress" +key = true + +[[members]] +name = "channel" +type = "felt252" +key = true + +[[members]] +name = "message" +type = "ByteArray" +key = false + +[[members]] +name = "salt" +type = "felt252" +key = true diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-MockToken-38903c7c.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-MockToken-38903c7c.toml new file mode 100644 index 0000000000..90d11271ae --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-MockToken-38903c7c.toml @@ -0,0 +1,16 @@ +kind = "DojoModel" +class_hash = "0x637d1d7966a14967047e31f3512c694e32b4d6a6dda4afe4b225a8b6f8f3519" +original_class_hash = "0x637d1d7966a14967047e31f3512c694e32b4d6a6dda4afe4b225a8b6f8f3519" +abi = "manifests/release/abis/base/models/dojo_examples-MockToken-38903c7c.json" +tag = "dojo_examples-MockToken" +manifest_name = "dojo_examples-MockToken-38903c7c" + +[[members]] +name = "account" +type = "ContractAddress" +key = true + +[[members]] +name = "amount" +type = "u128" +key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Moved-318ae40d.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Moved-318ae40d.toml new file mode 100644 index 0000000000..3dc784d978 --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Moved-318ae40d.toml @@ -0,0 +1,16 @@ +kind = "DojoModel" +class_hash = "0x838672e7f67cd6e67336283805b0ef168c814e65b6ba1d3a7a5f31c10559c4" +original_class_hash = "0x838672e7f67cd6e67336283805b0ef168c814e65b6ba1d3a7a5f31c10559c4" +abi = "manifests/release/abis/base/models/dojo_examples-Moved-318ae40d.json" +tag = "dojo_examples-Moved" +manifest_name = "dojo_examples-Moved-318ae40d" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "direction" +type = "Direction" +key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Moves-2e2accba.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Moves-2e2accba.toml new file mode 100644 index 0000000000..50f4c1774f --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Moves-2e2accba.toml @@ -0,0 +1,21 @@ +kind = "DojoModel" +class_hash = "0x4e9b10687f487bc0fe159affa3963cf0f7548136a2b614914e11af4497d609" +original_class_hash = "0x4e9b10687f487bc0fe159affa3963cf0f7548136a2b614914e11af4497d609" +abi = "manifests/release/abis/base/models/dojo_examples-Moves-2e2accba.json" +tag = "dojo_examples-Moves" +manifest_name = "dojo_examples-Moves-2e2accba" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "remaining" +type = "u8" +key = false + +[[members]] +name = "last_direction" +type = "Direction" +key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-PlayerConfig-3adad785.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-PlayerConfig-3adad785.toml new file mode 100644 index 0000000000..762114119b --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-PlayerConfig-3adad785.toml @@ -0,0 +1,26 @@ +kind = "DojoModel" +class_hash = "0x3b94d89f5b3589438b8ffcf2cbc8061670deee1d0f81247ab2346d8991b4eca" +original_class_hash = "0x3b94d89f5b3589438b8ffcf2cbc8061670deee1d0f81247ab2346d8991b4eca" +abi = "manifests/release/abis/base/models/dojo_examples-PlayerConfig-3adad785.json" +tag = "dojo_examples-PlayerConfig" +manifest_name = "dojo_examples-PlayerConfig-3adad785" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "name" +type = "ByteArray" +key = false + +[[members]] +name = "items" +type = "Array" +key = false + +[[members]] +name = "favorite_item" +type = "Option" +key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Position-1e145e26.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Position-1e145e26.toml new file mode 100644 index 0000000000..be845b84a8 --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-Position-1e145e26.toml @@ -0,0 +1,16 @@ +kind = "DojoModel" +class_hash = "0x59e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34" +original_class_hash = "0x59e57c16c3bc8c59a768a342496837275e399509366640620a0682826275a34" +abi = "manifests/release/abis/base/models/dojo_examples-Position-1e145e26.json" +tag = "dojo_examples-Position" +manifest_name = "dojo_examples-Position-1e145e26" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "vec" +type = "Vec2" +key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-ServerProfile-4caad1e6.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-ServerProfile-4caad1e6.toml new file mode 100644 index 0000000000..b41cbad8c7 --- /dev/null +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-ServerProfile-4caad1e6.toml @@ -0,0 +1,21 @@ +kind = "DojoModel" +class_hash = "0x38986e1b875d48b2931932879412cf0b6c52d0e04aaac6e2780995b3f47b91b" +original_class_hash = "0x38986e1b875d48b2931932879412cf0b6c52d0e04aaac6e2780995b3f47b91b" +abi = "manifests/release/abis/base/models/dojo_examples-ServerProfile-4caad1e6.json" +tag = "dojo_examples-ServerProfile" +manifest_name = "dojo_examples-ServerProfile-4caad1e6" + +[[members]] +name = "player" +type = "ContractAddress" +key = true + +[[members]] +name = "server_id" +type = "u32" +key = true + +[[members]] +name = "name" +type = "ByteArray" +key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_actions_actions_moved.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples_actions_actions_moved.toml deleted file mode 100644 index 74c1e890a1..0000000000 --- a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_actions_actions_moved.toml +++ /dev/null @@ -1,15 +0,0 @@ -kind = "DojoModel" -class_hash = "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c" -original_class_hash = "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c" -abi = "manifests/release/abis/base/models/dojo_examples_actions_actions_moved.json" -name = "dojo_examples::actions::actions::moved" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "direction" -type = "Direction" -key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_message.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_message.toml deleted file mode 100644 index fdc87b27cb..0000000000 --- a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_message.toml +++ /dev/null @@ -1,25 +0,0 @@ -kind = "DojoModel" -class_hash = "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5" -original_class_hash = "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5" -abi = "manifests/release/abis/base/models/dojo_examples_models_message.json" -name = "dojo_examples::models::message" - -[[members]] -name = "identity" -type = "ContractAddress" -key = true - -[[members]] -name = "channel" -type = "felt252" -key = true - -[[members]] -name = "message" -type = "ByteArray" -key = false - -[[members]] -name = "salt" -type = "felt252" -key = true diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_mock_token.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_mock_token.toml deleted file mode 100644 index 3d0147bc11..0000000000 --- a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_mock_token.toml +++ /dev/null @@ -1,15 +0,0 @@ -kind = "DojoModel" -class_hash = "0x6a21c56878ba470ac7a51f336ca6a59781de38e1810d2d20866ab2b52138a6d" -original_class_hash = "0x6a21c56878ba470ac7a51f336ca6a59781de38e1810d2d20866ab2b52138a6d" -abi = "manifests/release/abis/base/models/dojo_examples_models_mock_token.json" -name = "dojo_examples::models::mock_token" - -[[members]] -name = "account" -type = "ContractAddress" -key = true - -[[members]] -name = "amount" -type = "u128" -key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_moves.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_moves.toml deleted file mode 100644 index 8954550f5e..0000000000 --- a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_moves.toml +++ /dev/null @@ -1,20 +0,0 @@ -kind = "DojoModel" -class_hash = "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8" -original_class_hash = "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8" -abi = "manifests/release/abis/base/models/dojo_examples_models_moves.json" -name = "dojo_examples::models::moves" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "remaining" -type = "u8" -key = false - -[[members]] -name = "last_direction" -type = "Direction" -key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_player_config.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_player_config.toml deleted file mode 100644 index bbeae83193..0000000000 --- a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_player_config.toml +++ /dev/null @@ -1,25 +0,0 @@ -kind = "DojoModel" -class_hash = "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55" -original_class_hash = "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55" -abi = "manifests/release/abis/base/models/dojo_examples_models_player_config.json" -name = "dojo_examples::models::player_config" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "name" -type = "ByteArray" -key = false - -[[members]] -name = "items" -type = "Array" -key = false - -[[members]] -name = "favorite_item" -type = "Option" -key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_position.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_position.toml deleted file mode 100644 index 562adaba78..0000000000 --- a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_models_position.toml +++ /dev/null @@ -1,15 +0,0 @@ -kind = "DojoModel" -class_hash = "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff" -original_class_hash = "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff" -abi = "manifests/release/abis/base/models/dojo_examples_models_position.json" -name = "dojo_examples::models::position" - -[[members]] -name = "player" -type = "ContractAddress" -key = true - -[[members]] -name = "vec" -type = "Vec2" -key = false diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_others_others_contract_initialized.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples_others_others_contract_initialized.toml deleted file mode 100644 index 51efb699ba..0000000000 --- a/examples/spawn-and-move/manifests/release/base/models/dojo_examples_others_others_contract_initialized.toml +++ /dev/null @@ -1,20 +0,0 @@ -kind = "DojoModel" -class_hash = "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c" -original_class_hash = "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c" -abi = "manifests/release/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/release/manifest.json b/examples/spawn-and-move/manifests/release/manifest.json deleted file mode 100644 index 8ae464adc9..0000000000 --- a/examples/spawn-and-move/manifests/release/manifest.json +++ /dev/null @@ -1,3910 +0,0 @@ -{ - "world": { - "kind": "WorldContract", - "class_hash": "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9", - "original_class_hash": "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9", - "abi": [ - { - "type": "impl", - "name": "World", - "interface_name": "dojo::world::IWorld" - }, - { - "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": "struct", - "name": "dojo::resource_metadata::ResourceMetadata", - "members": [ - { - "name": "resource_id", - "type": "core::felt252" - }, - { - "name": "metadata_uri", - "type": "core::byte_array::ByteArray" - } - ] - }, - { - "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": "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": "enum", - "name": "core::bool", - "variants": [ - { - "name": "False", - "type": "()" - }, - { - "name": "True", - "type": "()" - } - ] - }, - { - "type": "interface", - "name": "dojo::world::IWorld", - "items": [ - { - "type": "function", - "name": "metadata", - "inputs": [ - { - "name": "resource_id", - "type": "core::felt252" - } - ], - "outputs": [ - { - "type": "dojo::resource_metadata::ResourceMetadata" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "set_metadata", - "inputs": [ - { - "name": "metadata", - "type": "dojo::resource_metadata::ResourceMetadata" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "model", - "inputs": [ - { - "name": "selector", - "type": "core::felt252" - } - ], - "outputs": [ - { - "type": "(core::starknet::class_hash::ClassHash, core::starknet::contract_address::ContractAddress)" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "register_model", - "inputs": [ - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "deploy_contract", - "inputs": [ - { - "name": "salt", - "type": "core::felt252" - }, - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash" - }, - { - "name": "init_calldata", - "type": "core::array::Span::" - } - ], - "outputs": [ - { - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "upgrade_contract", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash" - } - ], - "outputs": [ - { - "type": "core::starknet::class_hash::ClassHash" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "uuid", - "inputs": [], - "outputs": [ - { - "type": "core::integer::u32" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "emit", - "inputs": [ - { - "name": "keys", - "type": "core::array::Array::" - }, - { - "name": "values", - "type": "core::array::Span::" - } - ], - "outputs": [], - "state_mutability": "view" - }, - { - "type": "function", - "name": "entity", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "keys", - "type": "core::array::Span::" - }, - { - "name": "layout", - "type": "dojo::database::introspect::Layout" - } - ], - "outputs": [ - { - "type": "core::array::Span::" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "set_entity", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "keys", - "type": "core::array::Span::" - }, - { - "name": "values", - "type": "core::array::Span::" - }, - { - "name": "layout", - "type": "dojo::database::introspect::Layout" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "delete_entity", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "keys", - "type": "core::array::Span::" - }, - { - "name": "layout", - "type": "dojo::database::introspect::Layout" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "base", - "inputs": [], - "outputs": [ - { - "type": "core::starknet::class_hash::ClassHash" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "is_owner", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "resource", - "type": "core::felt252" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "grant_owner", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "resource", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "revoke_owner", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "resource", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "is_writer", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "contract", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "grant_writer", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "contract", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "revoke_writer", - "inputs": [ - { - "name": "model", - "type": "core::felt252" - }, - { - "name": "contract", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "UpgradeableWorld", - "interface_name": "dojo::world::IUpgradeableWorld" - }, - { - "type": "interface", - "name": "dojo::world::IUpgradeableWorld", - "items": [ - { - "type": "function", - "name": "upgrade", - "inputs": [ - { - "name": "new_class_hash", - "type": "core::starknet::class_hash::ClassHash" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "UpgradeableState", - "interface_name": "dojo::interfaces::IUpgradeableState" - }, - { - "type": "struct", - "name": "dojo::interfaces::StorageUpdate", - "members": [ - { - "name": "key", - "type": "core::felt252" - }, - { - "name": "value", - "type": "core::felt252" - } - ] - }, - { - "type": "struct", - "name": "core::array::Span::", - "members": [ - { - "name": "snapshot", - "type": "@core::array::Array::" - } - ] - }, - { - "type": "struct", - "name": "dojo::interfaces::ProgramOutput", - "members": [ - { - "name": "prev_state_root", - "type": "core::felt252" - }, - { - "name": "new_state_root", - "type": "core::felt252" - }, - { - "name": "block_number", - "type": "core::felt252" - }, - { - "name": "block_hash", - "type": "core::felt252" - }, - { - "name": "config_hash", - "type": "core::felt252" - }, - { - "name": "world_da_hash", - "type": "core::felt252" - }, - { - "name": "message_to_starknet_segment", - "type": "core::array::Span::" - }, - { - "name": "message_to_appchain_segment", - "type": "core::array::Span::" - } - ] - }, - { - "type": "interface", - "name": "dojo::interfaces::IUpgradeableState", - "items": [ - { - "type": "function", - "name": "upgrade_state", - "inputs": [ - { - "name": "new_state", - "type": "core::array::Span::" - }, - { - "name": "program_output", - "type": "dojo::interfaces::ProgramOutput" - }, - { - "name": "program_hash", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "ConfigImpl", - "interface_name": "dojo::config::interface::IConfig" - }, - { - "type": "interface", - "name": "dojo::config::interface::IConfig", - "items": [ - { - "type": "function", - "name": "set_differ_program_hash", - "inputs": [ - { - "name": "program_hash", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "set_merger_program_hash", - "inputs": [ - { - "name": "program_hash", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "get_differ_program_hash", - "inputs": [], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "get_merger_program_hash", - "inputs": [], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "set_facts_registry", - "inputs": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "get_facts_registry", - "inputs": [], - "outputs": [ - { - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "state_mutability": "view" - } - ] - }, - { - "type": "constructor", - "name": "constructor", - "inputs": [ - { - "name": "contract_base", - "type": "core::starknet::class_hash::ClassHash" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::WorldSpawned", - "kind": "struct", - "members": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "creator", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::ContractDeployed", - "kind": "struct", - "members": [ - { - "name": "salt", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - }, - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::ContractUpgraded", - "kind": "struct", - "members": [ - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - }, - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::WorldUpgraded", - "kind": "struct", - "members": [ - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::MetadataUpdate", - "kind": "struct", - "members": [ - { - "name": "resource", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "uri", - "type": "core::byte_array::ByteArray", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::ModelRegistered", - "kind": "struct", - "members": [ - { - "name": "name", - "type": "core::byte_array::ByteArray", - "kind": "data" - }, - { - "name": "class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - }, - { - "name": "prev_class_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - }, - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "prev_address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::StoreSetRecord", - "kind": "struct", - "members": [ - { - "name": "table", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "keys", - "type": "core::array::Span::", - "kind": "data" - }, - { - "name": "values", - "type": "core::array::Span::", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::StoreDelRecord", - "kind": "struct", - "members": [ - { - "name": "table", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "keys", - "type": "core::array::Span::", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::WriterUpdated", - "kind": "struct", - "members": [ - { - "name": "model", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "contract", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "value", - "type": "core::bool", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::OwnerUpdated", - "kind": "struct", - "members": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "resource", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "value", - "type": "core::bool", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::config::component::Config::DifferProgramHashUpdate", - "kind": "struct", - "members": [ - { - "name": "program_hash", - "type": "core::felt252", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::config::component::Config::MergerProgramHashUpdate", - "kind": "struct", - "members": [ - { - "name": "program_hash", - "type": "core::felt252", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::config::component::Config::FactsRegistryUpdate", - "kind": "struct", - "members": [ - { - "name": "address", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::config::component::Config::Event", - "kind": "enum", - "variants": [ - { - "name": "DifferProgramHashUpdate", - "type": "dojo::config::component::Config::DifferProgramHashUpdate", - "kind": "nested" - }, - { - "name": "MergerProgramHashUpdate", - "type": "dojo::config::component::Config::MergerProgramHashUpdate", - "kind": "nested" - }, - { - "name": "FactsRegistryUpdate", - "type": "dojo::config::component::Config::FactsRegistryUpdate", - "kind": "nested" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::StateUpdated", - "kind": "struct", - "members": [ - { - "name": "da_hash", - "type": "core::felt252", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "dojo::world::world::Event", - "kind": "enum", - "variants": [ - { - "name": "WorldSpawned", - "type": "dojo::world::world::WorldSpawned", - "kind": "nested" - }, - { - "name": "ContractDeployed", - "type": "dojo::world::world::ContractDeployed", - "kind": "nested" - }, - { - "name": "ContractUpgraded", - "type": "dojo::world::world::ContractUpgraded", - "kind": "nested" - }, - { - "name": "WorldUpgraded", - "type": "dojo::world::world::WorldUpgraded", - "kind": "nested" - }, - { - "name": "MetadataUpdate", - "type": "dojo::world::world::MetadataUpdate", - "kind": "nested" - }, - { - "name": "ModelRegistered", - "type": "dojo::world::world::ModelRegistered", - "kind": "nested" - }, - { - "name": "StoreSetRecord", - "type": "dojo::world::world::StoreSetRecord", - "kind": "nested" - }, - { - "name": "StoreDelRecord", - "type": "dojo::world::world::StoreDelRecord", - "kind": "nested" - }, - { - "name": "WriterUpdated", - "type": "dojo::world::world::WriterUpdated", - "kind": "nested" - }, - { - "name": "OwnerUpdated", - "type": "dojo::world::world::OwnerUpdated", - "kind": "nested" - }, - { - "name": "ConfigEvent", - "type": "dojo::config::component::Config::Event", - "kind": "nested" - }, - { - "name": "StateUpdated", - "type": "dojo::world::world::StateUpdated", - "kind": "nested" - } - ] - } - ], - "address": "0x7efebb0c2d4cc285d48a97a7174def3be7fdd6b7bd29cca758fa2e17e03ef30", - "transaction_hash": "0x60316eb232789f4d8352c6afdc36b76d33362d72b43bf78183b43f196779a9d", - "block_number": 3, - "seed": "dojo_examples", - "metadata": { - "profile_name": "release", - "rpc_url": "http://localhost:5050/" - }, - "name": "dojo::world::world" - }, - "base": { - "kind": "Class", - "class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", - "original_class_hash": "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46", - "abi": null, - "name": "dojo::base::base" - }, - "contracts": [ - { - "kind": "DojoContract", - "address": "0x5c70a663d6b48d8e4c6aaa9572e3735a732ac3765700d470463e670587852af", - "class_hash": "0x6d905953360cf18e3393d128c6ced40b38fc83b033412c8541fd4aba59d2767", - "original_class_hash": "0x6d905953360cf18e3393d128c6ced40b38fc83b033412c8541fd4aba59d2767", - "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": "ActionsComputedImpl", - "interface_name": "dojo_examples::actions::IActionsComputed" - }, - { - "type": "struct", - "name": "dojo_examples::models::Vec2", - "members": [ - { - "name": "x", - "type": "core::integer::u32" - }, - { - "name": "y", - "type": "core::integer::u32" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::Position", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "vec", - "type": "dojo_examples::models::Vec2" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::actions::IActionsComputed", - "items": [ - { - "type": "function", - "name": "tile_terrain", - "inputs": [ - { - "name": "vec", - "type": "dojo_examples::models::Vec2" - } - ], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "quadrant", - "inputs": [ - { - "name": "pos", - "type": "dojo_examples::models::Position" - } - ], - "outputs": [ - { - "type": "core::integer::u8" - } - ], - "state_mutability": "view" - } - ] - }, - { - "type": "impl", - "name": "ActionsImpl", - "interface_name": "dojo_examples::actions::IActions" - }, - { - "type": "enum", - "name": "dojo_examples::models::Direction", - "variants": [ - { - "name": "None", - "type": "()" - }, - { - "name": "Left", - "type": "()" - }, - { - "name": "Right", - "type": "()" - }, - { - "name": "Up", - "type": "()" - }, - { - "name": "Down", - "type": "()" - } - ] - }, - { - "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": "interface", - "name": "dojo_examples::actions::IActions", - "items": [ - { - "type": "function", - "name": "spawn", - "inputs": [], - "outputs": [], - "state_mutability": "view" - }, - { - "type": "function", - "name": "move", - "inputs": [ - { - "name": "direction", - "type": "dojo_examples::models::Direction" - } - ], - "outputs": [], - "state_mutability": "view" - }, - { - "type": "function", - "name": "set_player_config", - "inputs": [ - { - "name": "name", - "type": "core::byte_array::ByteArray" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "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", - "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::actions::actions::Event", - "kind": "enum", - "variants": [ - { - "name": "UpgradeableEvent", - "type": "dojo::components::upgradeable::upgradeable::Event", - "kind": "nested" - } - ] - } - ], - "reads": [], - "writes": [ - "Moves", - "Position" - ], - "computed": [], - "init_calldata": [], - "name": "dojo_examples::actions::actions" - }, - { - "kind": "DojoContract", - "address": "0x3f51cd82daaf5907d2fd082ae3f45ae2ef96ab61677f46abc16e0a54d3392d1", - "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": [ - "$class_hash:dojo_examples::actions::actions", - "$contract_address:dojo_examples::actions::actions", - "10" - ], - "name": "dojo_examples::others::others" - } - ], - "models": [ - { - "kind": "DojoModel", - "members": [ - { - "name": "player", - "type": "ContractAddress", - "key": true - }, - { - "name": "direction", - "type": "Direction", - "key": false - } - ], - "class_hash": "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c", - "original_class_hash": "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c", - "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": "movedImpl", - "interface_name": "dojo_examples::actions::actions::Imoved" - }, - { - "type": "enum", - "name": "dojo_examples::models::Direction", - "variants": [ - { - "name": "None", - "type": "()" - }, - { - "name": "Left", - "type": "()" - }, - { - "name": "Right", - "type": "()" - }, - { - "name": "Up", - "type": "()" - }, - { - "name": "Down", - "type": "()" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::actions::actions::Moved", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "direction", - "type": "dojo_examples::models::Direction" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::actions::actions::Imoved", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::actions::actions::Moved" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::actions::actions::moved::Event", - "kind": "enum", - "variants": [] - } - ], - "name": "dojo_examples::actions::actions::moved" - }, - { - "kind": "DojoModel", - "members": [ - { - "name": "identity", - "type": "ContractAddress", - "key": true - }, - { - "name": "channel", - "type": "felt252", - "key": true - }, - { - "name": "message", - "type": "ByteArray", - "key": false - }, - { - "name": "salt", - "type": "felt252", - "key": true - } - ], - "class_hash": "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5", - "original_class_hash": "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5", - "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": "messageImpl", - "interface_name": "dojo_examples::models::Imessage" - }, - { - "type": "struct", - "name": "dojo_examples::models::Message", - "members": [ - { - "name": "identity", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "channel", - "type": "core::felt252" - }, - { - "name": "message", - "type": "core::byte_array::ByteArray" - }, - { - "name": "salt", - "type": "core::felt252" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::models::Imessage", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::models::Message" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::models::message::Event", - "kind": "enum", - "variants": [] - } - ], - "name": "dojo_examples::models::message" - }, - { - "kind": "DojoModel", - "members": [ - { - "name": "player", - "type": "ContractAddress", - "key": true - }, - { - "name": "remaining", - "type": "u8", - "key": false - }, - { - "name": "last_direction", - "type": "Direction", - "key": false - } - ], - "class_hash": "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8", - "original_class_hash": "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8", - "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": "movesImpl", - "interface_name": "dojo_examples::models::Imoves" - }, - { - "type": "enum", - "name": "dojo_examples::models::Direction", - "variants": [ - { - "name": "None", - "type": "()" - }, - { - "name": "Left", - "type": "()" - }, - { - "name": "Right", - "type": "()" - }, - { - "name": "Up", - "type": "()" - }, - { - "name": "Down", - "type": "()" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::Moves", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "remaining", - "type": "core::integer::u8" - }, - { - "name": "last_direction", - "type": "dojo_examples::models::Direction" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::models::Imoves", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::models::Moves" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::models::moves::Event", - "kind": "enum", - "variants": [] - } - ], - "name": "dojo_examples::models::moves" - }, - { - "kind": "DojoModel", - "members": [ - { - "name": "player", - "type": "ContractAddress", - "key": true - }, - { - "name": "name", - "type": "ByteArray", - "key": false - }, - { - "name": "items", - "type": "Array", - "key": false - }, - { - "name": "favorite_item", - "type": "Option", - "key": false - } - ], - "class_hash": "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55", - "original_class_hash": "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55", - "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": "player_configImpl", - "interface_name": "dojo_examples::models::Iplayer_config" - }, - { - "type": "struct", - "name": "dojo_examples::models::PlayerItem", - "members": [ - { - "name": "item_id", - "type": "core::integer::u32" - }, - { - "name": "quantity", - "type": "core::integer::u32" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::PlayerConfig", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "name", - "type": "core::byte_array::ByteArray" - }, - { - "name": "items", - "type": "core::array::Array::" - }, - { - "name": "favorite_item", - "type": "core::option::Option::" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::models::Iplayer_config", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::models::PlayerConfig" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::models::player_config::Event", - "kind": "enum", - "variants": [] - } - ], - "name": "dojo_examples::models::player_config" - }, - { - "kind": "DojoModel", - "members": [ - { - "name": "player", - "type": "ContractAddress", - "key": true - }, - { - "name": "vec", - "type": "Vec2", - "key": false - } - ], - "class_hash": "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff", - "original_class_hash": "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff", - "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": "positionImpl", - "interface_name": "dojo_examples::models::Iposition" - }, - { - "type": "struct", - "name": "dojo_examples::models::Vec2", - "members": [ - { - "name": "x", - "type": "core::integer::u32" - }, - { - "name": "y", - "type": "core::integer::u32" - } - ] - }, - { - "type": "struct", - "name": "dojo_examples::models::Position", - "members": [ - { - "name": "player", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "vec", - "type": "dojo_examples::models::Vec2" - } - ] - }, - { - "type": "interface", - "name": "dojo_examples::models::Iposition", - "items": [ - { - "type": "function", - "name": "ensure_abi", - "inputs": [ - { - "name": "model", - "type": "dojo_examples::models::Position" - } - ], - "outputs": [], - "state_mutability": "view" - } - ] - }, - { - "type": "event", - "name": "dojo_examples::models::position::Event", - "kind": "enum", - "variants": [] - } - ], - "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": "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c", - "original_class_hash": "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c", - "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/release/manifest.toml b/examples/spawn-and-move/manifests/release/manifest.toml deleted file mode 100644 index 71ebf9944e..0000000000 --- a/examples/spawn-and-move/manifests/release/manifest.toml +++ /dev/null @@ -1,185 +0,0 @@ -[world] -kind = "WorldContract" -class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -original_class_hash = "0x3f63cecdc4964acafb921ba2934c6507d1b3c344edb64c2762cf08053169ab9" -abi = "manifests/release/abis/deployments/dojo_world_world.json" -address = "0x7efebb0c2d4cc285d48a97a7174def3be7fdd6b7bd29cca758fa2e17e03ef30" -transaction_hash = "0x60316eb232789f4d8352c6afdc36b76d33362d72b43bf78183b43f196779a9d" -block_number = 3 -seed = "dojo_examples" -name = "dojo::world::world" - -[world.metadata] -profile_name = "release" -rpc_url = "http://localhost:5050/" - -[base] -kind = "Class" -class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -original_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -name = "dojo::base::base" - -[[contracts]] -kind = "DojoContract" -address = "0x5c70a663d6b48d8e4c6aaa9572e3735a732ac3765700d470463e670587852af" -class_hash = "0x6d905953360cf18e3393d128c6ced40b38fc83b033412c8541fd4aba59d2767" -original_class_hash = "0x6d905953360cf18e3393d128c6ced40b38fc83b033412c8541fd4aba59d2767" -base_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -abi = "manifests/release/abis/deployments/contracts/dojo_examples_actions_actions.json" -reads = [] -writes = [ - "Moves", - "Position", -] -computed = [] -init_calldata = [] -name = "dojo_examples::actions::actions" - -[[contracts]] -kind = "DojoContract" -address = "0x3f51cd82daaf5907d2fd082ae3f45ae2ef96ab61677f46abc16e0a54d3392d1" -class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" -original_class_hash = "0x3ee016157303a7ce35a9fecdb5b8519159df620c5780527d61443636aaa3c3a" -base_class_hash = "0x22f3e55b61d86c2ac5239fa3b3b8761f26b9a5c0b5f61ddbd5d756ced498b46" -abi = "manifests/release/abis/deployments/contracts/dojo_examples_others_others.json" -reads = [] -writes = [] -computed = [] -init_calldata = [ - "$class_hash:dojo_examples::actions::actions", - "$contract_address:dojo_examples::actions::actions", - "10", -] -name = "dojo_examples::others::others" - -[[models]] -kind = "DojoModel" -class_hash = "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c" -original_class_hash = "0x4ef89963afe500337aaf757ad52394ea8e311077a825b3de5a5f32d3457997c" -abi = "manifests/release/abis/deployments/models/dojo_examples_actions_actions_moved.json" -name = "dojo_examples::actions::actions::moved" - -[[models.members]] -name = "player" -type = "ContractAddress" -key = true - -[[models.members]] -name = "direction" -type = "Direction" -key = false - -[[models]] -kind = "DojoModel" -class_hash = "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5" -original_class_hash = "0x33bdb1a470ea0ee7d77faa3c75151934b3b129c0bad7333ec6c2dad1b98ec5" -abi = "manifests/release/abis/deployments/models/dojo_examples_models_message.json" -name = "dojo_examples::models::message" - -[[models.members]] -name = "identity" -type = "ContractAddress" -key = true - -[[models.members]] -name = "channel" -type = "felt252" -key = true - -[[models.members]] -name = "message" -type = "ByteArray" -key = false - -[[models.members]] -name = "salt" -type = "felt252" -key = true - -[[models]] -kind = "DojoModel" -class_hash = "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8" -original_class_hash = "0x456d85286b34249fffa0a13d1ac490cc1f5b02eb1a1c92d820c59d6b0f2eaa8" -abi = "manifests/release/abis/deployments/models/dojo_examples_models_moves.json" -name = "dojo_examples::models::moves" - -[[models.members]] -name = "player" -type = "ContractAddress" -key = true - -[[models.members]] -name = "remaining" -type = "u8" -key = false - -[[models.members]] -name = "last_direction" -type = "Direction" -key = false - -[[models]] -kind = "DojoModel" -class_hash = "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55" -original_class_hash = "0x584d016a91d077c86ff1c014e0c4d735946d1084dd0d4c0a80cee6d04629e55" -abi = "manifests/release/abis/deployments/models/dojo_examples_models_player_config.json" -name = "dojo_examples::models::player_config" - -[[models.members]] -name = "player" -type = "ContractAddress" -key = true - -[[models.members]] -name = "name" -type = "ByteArray" -key = false - -[[models.members]] -name = "items" -type = "Array" -key = false - -[[models.members]] -name = "favorite_item" -type = "Option" -key = false - -[[models]] -kind = "DojoModel" -class_hash = "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff" -original_class_hash = "0x27942375b09862291ece780c573e8c625df4ba41fd7524e0658ca75fff014ff" -abi = "manifests/release/abis/deployments/models/dojo_examples_models_position.json" -name = "dojo_examples::models::position" - -[[models.members]] -name = "player" -type = "ContractAddress" -key = true - -[[models.members]] -name = "vec" -type = "Vec2" -key = false - -[[models]] -kind = "DojoModel" -class_hash = "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c" -original_class_hash = "0x4b29afc6db744bd87f7276869620348557c11b984e9f3fcb27c4d55efb0ab6c" -abi = "manifests/release/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/release/overlays/contracts/dojo_examples_actions_actions.toml b/examples/spawn-and-move/manifests/release/overlays/contracts/dojo_examples_actions_actions.toml deleted file mode 100644 index 188130a013..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/contracts/dojo_examples_actions_actions.toml +++ /dev/null @@ -1,4 +0,0 @@ -init_calldata = [ ] -name = "dojo_examples::actions::actions" -reads = [ ] -writes = [ "Moves", "Position" ] diff --git a/examples/spawn-and-move/manifests/release/overlays/contracts/dojo_examples_others_others.toml b/examples/spawn-and-move/manifests/release/overlays/contracts/dojo_examples_others_others.toml deleted file mode 100644 index 82545bca81..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/contracts/dojo_examples_others_others.toml +++ /dev/null @@ -1,8 +0,0 @@ -init_calldata = [ - "$class_hash:dojo_examples::actions::actions", - "$contract_address:dojo_examples::actions::actions", - "10", -] -name = "dojo_examples::others::others" -reads = [ ] -writes = [ ] diff --git a/examples/spawn-and-move/manifests/release/overlays/dojo_base_base.toml b/examples/spawn-and-move/manifests/release/overlays/dojo_base_base.toml deleted file mode 100644 index f706470d45..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/dojo_base_base.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo::base::base" diff --git a/examples/spawn-and-move/manifests/release/overlays/dojo_world_world.toml b/examples/spawn-and-move/manifests/release/overlays/dojo_world_world.toml deleted file mode 100644 index a3e686e3ef..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/dojo_world_world.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo::world::world" diff --git a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_actions_actions_moved.toml b/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_actions_actions_moved.toml deleted file mode 100644 index 4958a7a15c..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_actions_actions_moved.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::actions::actions::moved" diff --git a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_message.toml b/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_message.toml deleted file mode 100644 index 5d9412cae2..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_message.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::message" diff --git a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_mock_token.toml b/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_mock_token.toml deleted file mode 100644 index a5ba5684a6..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_mock_token.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::mock_token" diff --git a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_moves.toml b/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_moves.toml deleted file mode 100644 index dc8784e746..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_moves.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::moves" diff --git a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_player_config.toml b/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_player_config.toml deleted file mode 100644 index 6af8240b36..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_player_config.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::player_config" diff --git a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_position.toml b/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_position.toml deleted file mode 100644 index df38e71c32..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_models_position.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::models::position" diff --git a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_others_others_contract_initialized.toml b/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_others_others_contract_initialized.toml deleted file mode 100644 index f8f3053fe5..0000000000 --- a/examples/spawn-and-move/manifests/release/overlays/models/dojo_examples_others_others_contract_initialized.toml +++ /dev/null @@ -1 +0,0 @@ -name = "dojo_examples::others::others::contract_initialized" diff --git a/examples/spawn-and-move/overlays/dev/actions.toml b/examples/spawn-and-move/overlays/dev/actions.toml new file mode 100644 index 0000000000..31849b7894 --- /dev/null +++ b/examples/spawn-and-move/overlays/dev/actions.toml @@ -0,0 +1,2 @@ +tag = "dojo_examples-actions" +writes = ["dojo_examples-Moves", "dojo_examples-Position"] diff --git a/examples/spawn-and-move/overlays/dev/others.toml b/examples/spawn-and-move/overlays/dev/others.toml new file mode 100644 index 0000000000..effbcca1da --- /dev/null +++ b/examples/spawn-and-move/overlays/dev/others.toml @@ -0,0 +1,2 @@ +tag = "dojo_examples-others" +init_calldata = ["$contract_address:dojo_examples-actions", "$class_hash:dojo_examples-actions", "10"] diff --git a/examples/spawn-and-move/src/actions.cairo b/examples/spawn-and-move/src/actions.cairo index 241fe54ea3..a0d7753e0f 100644 --- a/examples/spawn-and-move/src/actions.cairo +++ b/examples/spawn-and-move/src/actions.cairo @@ -165,7 +165,7 @@ mod tests { // models let mut models = array![position::TEST_CLASS_HASH, moves::TEST_CLASS_HASH,]; // deploy world with models - let world = spawn_test_world(models); + let world = spawn_test_world("dojo_examples", models); // deploy systems contract let contract_address = world diff --git a/scripts/rebuild_test_artifacts.sh b/scripts/rebuild_test_artifacts.sh index 4546401044..addbdd1b72 100755 --- a/scripts/rebuild_test_artifacts.sh +++ b/scripts/rebuild_test_artifacts.sh @@ -14,9 +14,17 @@ rm -rf examples/spawn-and-move/manifests/dev/base rm -rf examples/spawn-and-move/manifests/dev/manifest.json rm -rf examples/spawn-and-move/manifests/dev/manifest.toml +rm -rf examples/spawn-and-move/manifests/release/abis +rm -rf examples/spawn-and-move/manifests/release/base +rm -rf examples/spawn-and-move/manifests/release/manifest.json +rm -rf examples/spawn-and-move/manifests/release/manifest.toml + rm -rf crates/torii/types-test/target rm -rf crates/torii/types-test/manifests +rm -rf crates/dojo-lang/src/manifest_test_data/compiler_cairo/target +rm -rf crates/dojo-lang/src/manifest_test_data/compiler_cairo/manifests + cargo run --bin dojo-world-abigen # Fix the cairo test to re-generate the code that is expected to be tested. @@ -25,7 +33,9 @@ CAIRO_FIX_TESTS=1 cargo test --package dojo-lang semantics # Re-run the minimal tests, this will re-build the projects + generate the build artifacts. cargo run -r --bin sozo -- build --manifest-path examples/spawn-and-move/Scarb.toml +cargo run -r --bin sozo -- build --manifest-path examples/spawn-and-move/Scarb.toml -P release cargo run -r --bin sozo -- build --manifest-path crates/torii/types-test/Scarb.toml +cargo run -r --bin sozo -- build --manifest-path crates/dojo-lang/src/manifest_test_data/compiler_cairo/Scarb.toml # Finally, to include all the examples manifest, you should re-deploy the examples. cargo run -r --bin sozo -- --offline migrate apply --manifest-path examples/spawn-and-move/Scarb.toml