From 792194c1333226bbd1cec2d0aec7012d6f3a81fd Mon Sep 17 00:00:00 2001 From: glihm Date: Wed, 20 Dec 2023 10:27:32 -0600 Subject: [PATCH] feat: use of `WorldContractReader` from `abigen!` (#1010) * feat: add cainome abigen to sozo * feat: add cainome abigen to torii * fix: adjust test and fmt * fix: remove additional calldata input * feat(katana-rpc): add ContractErrorData when it applies * fix: fix tests * fix: adjust indexes for executor call results * fix: restore Scarb.lock and add .tool-versions * chore: bump cainome to 0.1.5 * fix: embed world and executor ABI in .git * chore: bump cainome to 0.1.7 * chore: update Scarb.lock * docs: update README for contract ABI * fix: fmt clippy from merge * fix: fix PR comments * fix: fix typo after test being run * fix: fix Scarb.lock version --- Cargo.lock | 48 ++ crates/dojo-core/.tool-versions | 1 + crates/dojo-world/Cargo.toml | 1 + crates/dojo-world/src/contracts/abi/README.md | 13 + .../src/contracts/abi/executor.json | 53 ++ .../dojo-world/src/contracts/abi/world.json | 775 ++++++++++++++++++ .../dojo-world/src/contracts/cairo_utils.rs | 26 + crates/dojo-world/src/contracts/mod.rs | 3 +- crates/dojo-world/src/contracts/model.rs | 72 +- crates/dojo-world/src/contracts/model_test.rs | 4 +- crates/dojo-world/src/contracts/world.rs | 334 +------- crates/dojo-world/src/contracts/world_test.rs | 18 +- crates/dojo-world/src/manifest.rs | 22 +- crates/sozo/src/ops/auth.rs | 4 +- crates/sozo/src/ops/migration/mod.rs | 20 +- crates/sozo/src/ops/model.rs | 6 +- crates/sozo/src/ops/register.rs | 13 +- crates/torii/client/src/client/mod.rs | 6 +- crates/torii/core/src/engine.rs | 2 +- crates/torii/core/src/processors/mod.rs | 2 +- .../core/src/processors/register_model.rs | 2 +- examples/spawn-and-move/Scarb.lock | 2 +- 22 files changed, 1046 insertions(+), 381 deletions(-) create mode 100644 crates/dojo-core/.tool-versions create mode 100644 crates/dojo-world/src/contracts/abi/README.md create mode 100644 crates/dojo-world/src/contracts/abi/executor.json create mode 100644 crates/dojo-world/src/contracts/abi/world.json create mode 100644 crates/dojo-world/src/contracts/cairo_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 67bb5e3c7b..d1ecb5a4b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1117,6 +1117,53 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" +[[package]] +name = "cainome" +version = "0.1.5" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.1.7#2d4b52b2e79796f76fba9e3a1b1027e8e63292b8" +dependencies = [ + "cainome-cairo-serde", + "cainome-parser", + "cainome-rs", +] + +[[package]] +name = "cainome-cairo-serde" +version = "0.1.0" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.1.7#2d4b52b2e79796f76fba9e3a1b1027e8e63292b8" +dependencies = [ + "starknet", + "thiserror", +] + +[[package]] +name = "cainome-parser" +version = "0.1.0" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.1.7#2d4b52b2e79796f76fba9e3a1b1027e8e63292b8" +dependencies = [ + "quote", + "serde_json", + "starknet", + "syn 2.0.41", + "thiserror", +] + +[[package]] +name = "cainome-rs" +version = "0.1.0" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.1.7#2d4b52b2e79796f76fba9e3a1b1027e8e63292b8" +dependencies = [ + "anyhow", + "cainome-cairo-serde", + "cainome-parser", + "proc-macro2", + "quote", + "serde_json", + "starknet", + "syn 2.0.41", + "thiserror", +] + [[package]] name = "cairo-felt" version = "0.8.2" @@ -2761,6 +2808,7 @@ dependencies = [ "assert_fs", "assert_matches", "async-trait", + "cainome", "cairo-lang-filesystem", "cairo-lang-project", "cairo-lang-starknet", diff --git a/crates/dojo-core/.tool-versions b/crates/dojo-core/.tool-versions new file mode 100644 index 0000000000..21cfc80772 --- /dev/null +++ b/crates/dojo-core/.tool-versions @@ -0,0 +1 @@ +scarb 2.4.0 diff --git a/crates/dojo-world/Cargo.toml b/crates/dojo-world/Cargo.toml index d6da3ff3b0..cbedf2fa5e 100644 --- a/crates/dojo-world/Cargo.toml +++ b/crates/dojo-world/Cargo.toml @@ -24,6 +24,7 @@ starknet.workspace = true thiserror.workspace = true tracing.workspace = true +cainome = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.1.7", features = ["abigen-rs"] } dojo-types = { path = "../dojo-types", optional = true } http = { version = "0.2.9", optional = true } ipfs-api-backend-hyper = { git = "https://github.com/ferristseng/rust-ipfs-api", rev = "af2c17f7b19ef5b9898f458d97a90055c3605633", features = [ "with-hyper-rustls" ], optional = true } diff --git a/crates/dojo-world/src/contracts/abi/README.md b/crates/dojo-world/src/contracts/abi/README.md new file mode 100644 index 0000000000..3985ecc602 --- /dev/null +++ b/crates/dojo-world/src/contracts/abi/README.md @@ -0,0 +1,13 @@ +# Embedded ABI for contracts + +Currently, the ABIs for `world` and `executor` are embedded in the repo. +To build them, consider the following: + +1. Change directory into `examples/spawn-and-move` at the root of the workspace. +2. Build the example with `sozo`. +3. Extract the ABI key only for `world` and `executor`: +``` +sozo build +jq .abi ./target/dev/dojo\:\:world\:\:world.json > ../../crates/dojo-world/src/contracts/abi/world.json +jq .abi ./target/dev/dojo\:\:executor\:\:executor.json > ../../crates/dojo-world/src/contracts/abi/executor.json +``` diff --git a/crates/dojo-world/src/contracts/abi/executor.json b/crates/dojo-world/src/contracts/abi/executor.json new file mode 100644 index 0000000000..ad21e66317 --- /dev/null +++ b/crates/dojo-world/src/contracts/abi/executor.json @@ -0,0 +1,53 @@ +[ + { + "type": "impl", + "name": "Executor", + "interface_name": "dojo::executor::IExecutor" + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "interface", + "name": "dojo::executor::IExecutor", + "items": [ + { + "type": "function", + "name": "call", + "inputs": [ + { + "name": "class_hash", + "type": "core::starknet::class_hash::ClassHash" + }, + { + "name": "entrypoint", + "type": "core::felt252" + }, + { + "name": "calldata", + "type": "core::array::Span::" + } + ], + "outputs": [ + { + "type": "core::array::Span::" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "dojo::executor::executor::Event", + "kind": "enum", + "variants": [] + } +] diff --git a/crates/dojo-world/src/contracts/abi/world.json b/crates/dojo-world/src/contracts/abi/world.json new file mode 100644 index 0000000000..f1f0fa062b --- /dev/null +++ b/crates/dojo-world/src/contracts/abi/world.json @@ -0,0 +1,775 @@ +[ + { + "type": "impl", + "name": "World", + "interface_name": "dojo::world::IWorld" + }, + { + "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": "core::option::Option::", + "variants": [ + { + "name": "Some", + "type": "core::felt252" + }, + { + "name": "None", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::>" + } + ] + }, + { + "type": "enum", + "name": "core::bool", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, + { + "type": "interface", + "name": "dojo::world::IWorld", + "items": [ + { + "type": "function", + "name": "metadata_uri", + "inputs": [ + { + "name": "resource", + "type": "core::felt252" + } + ], + "outputs": [ + { + "type": "core::array::Span::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "set_metadata_uri", + "inputs": [ + { + "name": "resource", + "type": "core::felt252" + }, + { + "name": "uri", + "type": "core::array::Span::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "model", + "inputs": [ + { + "name": "name", + "type": "core::felt252" + } + ], + "outputs": [ + { + "type": "core::starknet::class_hash::ClassHash" + } + ], + "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" + } + ], + "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": "offset", + "type": "core::integer::u8" + }, + { + "name": "length", + "type": "core::integer::u32" + }, + { + "name": "layout", + "type": "core::array::Span::" + } + ], + "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": "offset", + "type": "core::integer::u8" + }, + { + "name": "values", + "type": "core::array::Span::" + }, + { + "name": "layout", + "type": "core::array::Span::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "entities", + "inputs": [ + { + "name": "model", + "type": "core::felt252" + }, + { + "name": "index", + "type": "core::option::Option::" + }, + { + "name": "values", + "type": "core::array::Span::" + }, + { + "name": "values_length", + "type": "core::integer::u32" + }, + { + "name": "values_layout", + "type": "core::array::Span::" + } + ], + "outputs": [ + { + "type": "(core::array::Span::, core::array::Span::>)" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "entity_ids", + "inputs": [ + { + "name": "model", + "type": "core::felt252" + } + ], + "outputs": [ + { + "type": "core::array::Span::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "set_executor", + "inputs": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "executor", + "inputs": [], + "outputs": [ + { + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "base", + "inputs": [], + "outputs": [ + { + "type": "core::starknet::class_hash::ClassHash" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "delete_entity", + "inputs": [ + { + "name": "model", + "type": "core::felt252" + }, + { + "name": "keys", + "type": "core::array::Span::" + }, + { + "name": "layout", + "type": "core::array::Span::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "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": "system", + "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": "system", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "revoke_writer", + "inputs": [ + { + "name": "model", + "type": "core::felt252" + }, + { + "name": "system", + "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": "constructor", + "name": "constructor", + "inputs": [ + { + "name": "executor", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "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::array::Span::", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "dojo::world::world::ModelRegistered", + "kind": "struct", + "members": [ + { + "name": "name", + "type": "core::felt252", + "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" + } + ] + }, + { + "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": "offset", + "type": "core::integer::u8", + "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": "system", + "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::world::world::ExecutorUpdated", + "kind": "struct", + "members": [ + { + "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::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": "ExecutorUpdated", + "type": "dojo::world::world::ExecutorUpdated", + "kind": "nested" + } + ] + } +] diff --git a/crates/dojo-world/src/contracts/cairo_utils.rs b/crates/dojo-world/src/contracts/cairo_utils.rs new file mode 100644 index 0000000000..b43ff5be7d --- /dev/null +++ b/crates/dojo-world/src/contracts/cairo_utils.rs @@ -0,0 +1,26 @@ +use anyhow::{anyhow, Result}; +use http::uri::Uri; +use starknet::core::types::FieldElement; +use starknet::core::utils::cairo_short_string_to_felt; + +pub fn str_to_felt(string: &str) -> Result { + cairo_short_string_to_felt(string).map_err(|e| { + anyhow!(format!("Failed to convert string `{}` to cairo short string: {}", string, e)) + }) +} + +pub fn encode_uri(uri: &str) -> Result> { + let parsed: Uri = + uri.try_into().map_err(|e| anyhow!("Failed to encode URI `{}`: {}", uri, e))?; + + Ok(parsed + .to_string() + .chars() + .collect::>() + .chunks(31) + .map(|chunk| { + let s: String = chunk.iter().collect(); + cairo_short_string_to_felt(&s) + }) + .collect::, _>>()?) +} diff --git a/crates/dojo-world/src/contracts/mod.rs b/crates/dojo-world/src/contracts/mod.rs index 8d122a0dd3..3c09618fc4 100644 --- a/crates/dojo-world/src/contracts/mod.rs +++ b/crates/dojo-world/src/contracts/mod.rs @@ -1,4 +1,5 @@ +pub mod cairo_utils; pub mod model; pub mod world; -pub use world::{WorldContract, WorldContractError, WorldContractReader}; +pub use world::{WorldContract, WorldContractReader}; diff --git a/crates/dojo-world/src/contracts/model.rs b/crates/dojo-world/src/contracts/model.rs index 433f3b8cd4..b0ac6e37ed 100644 --- a/crates/dojo-world/src/contracts/model.rs +++ b/crates/dojo-world/src/contracts/model.rs @@ -1,10 +1,11 @@ use std::vec; use async_trait::async_trait; +use cainome::cairo_serde::Error as CainomeError; use dojo_types::packing::{parse_ty, unpack, PackingError, ParseError}; use dojo_types::primitive::PrimitiveError; use dojo_types::schema::Ty; -use starknet::core::types::{FieldElement, FunctionCall, StarknetError}; +use starknet::core::types::{FieldElement, StarknetError}; use starknet::core::utils::{ cairo_short_string_to_felt, get_selector_from_name, CairoShortStringToFeltError, ParseCairoShortStringError, @@ -13,9 +14,8 @@ use starknet::macros::short_string; use starknet::providers::{Provider, ProviderError}; use starknet_crypto::poseidon_hash_many; -use crate::contracts::world::{ContractReaderError, WorldContractReader}; +use crate::contracts::WorldContractReader; -const WORLD_MODEL_SELECTOR_STR: &str = "model"; const SCHEMA_SELECTOR_STR: &str = "schema"; const LAYOUT_SELECTOR_STR: &str = "layout"; const PACKED_SIZE_SELECTOR_STR: &str = "packed_size"; @@ -36,13 +36,13 @@ pub enum ModelError { #[error(transparent)] CairoShortStringToFeltError(#[from] CairoShortStringToFeltError), #[error(transparent)] - ContractReaderError(#[from] ContractReaderError), - #[error(transparent)] CairoTypeError(#[from] PrimitiveError), #[error(transparent)] Parse(#[from] ParseError), #[error(transparent)] Packing(#[from] PackingError), + #[error(transparent)] + Cainome(#[from] CainomeError), } #[cfg_attr(not(target_arch = "wasm32"), async_trait)] @@ -55,7 +55,7 @@ pub trait ModelReader { async fn layout(&self) -> Result, E>; } -pub struct ModelRPCReader<'a, P: Sync + Send> { +pub struct ModelRPCReader<'a, P: Provider + Sync + Send> { /// The name of the model name: FieldElement, /// The class hash of the model @@ -74,26 +74,15 @@ where ) -> Result, ModelError> { let name = cairo_short_string_to_felt(name)?; - let class_hash = world - .provider() - .call( - FunctionCall { - calldata: vec![name], - contract_address: world.address(), - entry_point_selector: get_selector_from_name(WORLD_MODEL_SELECTOR_STR).unwrap(), - }, - world.block_id(), - ) - .await - .map(|res| res[0]) - .map_err(|err| match err { - ProviderError::StarknetError(StarknetError::ContractNotFound) => { - ModelError::ModelNotFound - } + let class_hash = + world.model(&name).block_id(world.block_id).call().await.map_err(|err| match err { + CainomeError::Provider(ProviderError::StarknetError( + StarknetError::ContractNotFound, + )) => ModelError::ModelNotFound, err => err.into(), })?; - Ok(Self { world_reader: world, class_hash, name }) + Ok(Self { world_reader: world, class_hash: class_hash.into(), name }) } pub async fn entity_storage( @@ -112,9 +101,9 @@ where .world_reader .provider() .get_storage_at( - self.world_reader.address(), + self.world_reader.address, key + slot.into(), - self.world_reader.block_id(), + self.world_reader.block_id, ) .await?; @@ -152,44 +141,37 @@ where async fn schema(&self) -> Result { let entrypoint = get_selector_from_name(SCHEMA_SELECTOR_STR).unwrap(); - let res = self - .world_reader - .executor_call(self.class_hash, vec![entrypoint, FieldElement::ZERO]) - .await?; + let res = self.world_reader.executor_call(self.class_hash, entrypoint, vec![]).await?; - Ok(parse_ty(&res[1..])?) + Ok(parse_ty(&res)?) } async fn packed_size(&self) -> Result { let entrypoint = get_selector_from_name(PACKED_SIZE_SELECTOR_STR).unwrap(); - let res = self - .world_reader - .executor_call(self.class_hash, vec![entrypoint, FieldElement::ZERO]) - .await?; + let res = self.world_reader.executor_call(self.class_hash, entrypoint, vec![]).await?; - Ok(res[1]) + Ok(res[0]) } async fn unpacked_size(&self) -> Result { let entrypoint = get_selector_from_name(UNPACKED_SIZE_SELECTOR_STR).unwrap(); - let res = self - .world_reader - .executor_call(self.class_hash, vec![entrypoint, FieldElement::ZERO]) - .await?; + let res = self.world_reader.executor_call(self.class_hash, entrypoint, vec![]).await?; - Ok(res[1]) + Ok(res[0]) } async fn layout(&self) -> Result, ModelError> { let entrypoint = get_selector_from_name(LAYOUT_SELECTOR_STR).unwrap(); - let res = self - .world_reader - .executor_call(self.class_hash, vec![entrypoint, FieldElement::ZERO]) - .await?; + let res = self.world_reader.executor_call(self.class_hash, entrypoint, vec![]).await?; - Ok(res[2..].into()) + // Layout entrypoint expanded by the #[model] attribute returns a + // `Span`. So cainome generated code will deserialize the result + // of `executor.call()` which is a Vec. + // So inside the vec, we skip the first element, which is the length + // of the span returned by `layout` entrypoint of the model code. + Ok(res[1..].into()) } } diff --git a/crates/dojo-world/src/contracts/model_test.rs b/crates/dojo-world/src/contracts/model_test.rs index cd235080da..efab444182 100644 --- a/crates/dojo-world/src/contracts/model_test.rs +++ b/crates/dojo-world/src/contracts/model_test.rs @@ -24,7 +24,7 @@ async fn test_model() { .await; let world = WorldContractReader::new(world_address, provider); - let position = world.model("Position").await.unwrap(); + let position = world.model_reader("Position").await.unwrap(); let schema = position.schema().await.unwrap(); assert_eq!( @@ -68,7 +68,7 @@ async fn test_model() { .unwrap() ); - let moves = world.model("Moves").await.unwrap(); + let moves = world.model_reader("Moves").await.unwrap(); let schema = moves.schema().await.unwrap(); assert_eq!( diff --git a/crates/dojo-world/src/contracts/world.rs b/crates/dojo-world/src/contracts/world.rs index 93bd56d8c7..b8bcfe9d2d 100644 --- a/crates/dojo-world/src/contracts/world.rs +++ b/crates/dojo-world/src/contracts/world.rs @@ -1,15 +1,9 @@ use std::result::Result; -use http::uri::{InvalidUri, Uri}; -use starknet::accounts::{AccountError, Call, ConnectedAccount}; -use starknet::core::types::{ - BlockId, BlockTag, FieldElement, FunctionCall, InvokeTransactionResult, -}; -use starknet::core::utils::{ - cairo_short_string_to_felt, get_selector_from_name, CairoShortStringToFeltError, -}; -use starknet::macros::selector; -use starknet::providers::{Provider, ProviderError}; +pub use abigen::world::{WorldContract, WorldContractReader}; +use cainome::cairo_serde::Result as CainomeResult; +use starknet::core::types::FieldElement; +use starknet::providers::Provider; use super::model::{ModelError, ModelRPCReader}; @@ -17,318 +11,62 @@ use super::model::{ModelError, ModelRPCReader}; #[path = "world_test.rs"] pub(crate) mod test; -#[derive(Debug, thiserror::Error)] -pub enum WorldContractError { - #[error(transparent)] - ProviderError(#[from] ProviderError), - #[error(transparent)] - AccountError(#[from] AccountError), - #[error(transparent)] - CairoShortStringToFeltError(#[from] CairoShortStringToFeltError), - #[error(transparent)] - ContractReaderError(#[from] ContractReaderError), - #[error("Invalid metadata uri")] - InvalidMetadataUri(#[from] InvalidUri), -} - -pub struct WorldContract<'a, A> -where - A: ConnectedAccount, -{ - account: &'a A, - reader: WorldContractReader<&'a ::Provider>, -} - -impl<'a, A> WorldContract<'a, A> -where - A: ConnectedAccount, -{ - pub fn new(address: FieldElement, account: &'a A) -> Self { - Self { account, reader: WorldContractReader::new(address, account.provider()) } +#[cfg(not(doctest))] +pub mod abigen { + pub mod world { + use cainome::rs::abigen; + abigen!(WorldContract, "crates/dojo-world/src/contracts/abi/world.json"); } - pub fn account(&self) -> &A { - self.account + pub mod executor { + use cainome::rs::abigen; + abigen!(ExecutorContract, "crates/dojo-world/src/contracts/abi/executor.json"); } } -impl<'a, A> WorldContract<'a, A> -where - A: ConnectedAccount + Sync, -{ - pub async fn set_executor( - &self, - executor: FieldElement, - ) -> Result> { - self.account - .execute(vec![Call { - to: self.reader.address, - calldata: vec![executor], - selector: selector!("set_executor"), - }]) - .send() - .await - } - - pub async fn set_metadata_uri( - &self, - resource: FieldElement, - metadata_uri: String, - ) -> Result> { - let parsed: Uri = - metadata_uri.try_into().map_err(WorldContractError::InvalidMetadataUri)?; - - let mut encoded = parsed - .to_string() - .chars() - .collect::>() - .chunks(31) - .map(|chunk| { - let s: String = chunk.iter().collect(); - cairo_short_string_to_felt(&s).unwrap() - }) - .collect::>(); - - encoded.insert(0, encoded.len().into()); - encoded.insert(0, resource); - - self.account - .execute(vec![Call { - calldata: encoded, - to: self.reader.address, - selector: get_selector_from_name("set_metadata_uri").unwrap(), - }]) - .send() - .await - .map_err(WorldContractError::AccountError) - } - - pub async fn grant_writer( - &self, - model: &str, - contract: FieldElement, - ) -> Result> { - let model = cairo_short_string_to_felt(model) - .map_err(WorldContractError::CairoShortStringToFeltError)?; - - self.account - .execute(vec![Call { - calldata: vec![model, contract], - to: self.reader.address, - selector: get_selector_from_name("grant_writer").unwrap(), - }]) - .send() - .await - .map_err(WorldContractError::AccountError) - } - - pub async fn register_models( - &self, - models: &[FieldElement], - ) -> Result> { - let calls = models - .iter() - .map(|c| Call { - to: self.reader.address, - selector: selector!("register_model"), - calldata: vec![*c], - }) - .collect::>(); - - self.account.execute(calls).send().await +#[cfg(doctest)] +pub mod abigen { + pub mod world { + use cainome::rs::abigen; + abigen!(WorldContract, "src/contracts/abi/world.json"); } - pub async fn deploy_contract( - &self, - salt: &FieldElement, - class_hash: &FieldElement, - ) -> Result> { - self.account - .execute(vec![Call { - to: self.reader.address, - selector: selector!("deploy_contract"), - calldata: vec![*salt, *class_hash], - }]) - .send() - .await + pub mod executor { + use cainome::rs::abigen; + abigen!(ExecutorContract, "src/contracts/abi/executor.json"); } - - pub async fn executor(&self) -> Result { - self.reader.executor().await - } - - pub async fn base(&self) -> Result { - self.reader.base().await - } - - pub async fn model( - &'a self, - name: &str, - ) -> Result, ModelError> { - self.reader.model(name).await - } -} - -#[derive(Debug, thiserror::Error)] -pub enum ContractReaderError { - #[error(transparent)] - ProviderError(#[from] ProviderError), - #[error(transparent)] - CairoShortStringToFeltError(#[from] CairoShortStringToFeltError), -} - -pub struct WorldContractReader

{ - provider: P, - block_id: BlockId, - address: FieldElement, } impl

WorldContractReader

where - P: Provider, + P: Provider + Sync + Send, { - pub fn new(address: FieldElement, provider: P) -> Self { - Self { address, provider, block_id: BlockId::Tag(BlockTag::Latest) } - } - - pub fn with_block(self, block: BlockId) -> Self { - Self { block_id: block, ..self } - } - - pub fn address(&self) -> FieldElement { - self.address - } - - pub fn provider(&self) -> &P { - &self.provider - } - - pub fn block_id(&self) -> BlockId { - self.block_id + pub async fn model_reader(&self, name: &str) -> Result, ModelError> { + ModelRPCReader::new(name, self).await } } impl

WorldContractReader

where - P: Provider, + P: Provider + Sync + Send, { - pub async fn is_authorized( - &self, - system: &str, - model: &str, - execution_role: &str, - ) -> Result { - let res = self - .provider - .call( - FunctionCall { - calldata: vec![ - cairo_short_string_to_felt(system)?, - cairo_short_string_to_felt(model)?, - cairo_short_string_to_felt(execution_role)?, - ], - contract_address: self.address, - entry_point_selector: selector!("is_authorized"), - }, - self.block_id, - ) - .await?; - - Ok(res[0] == FieldElement::ONE) - } - - pub async fn is_account_admin(&self) -> Result { - let res = self - .provider - .call( - FunctionCall { - calldata: vec![], - contract_address: self.address, - entry_point_selector: selector!("is_account_admin"), - }, - self.block_id, - ) - .await?; - - Ok(res[0] == FieldElement::ONE) - } - - pub async fn executor(&self) -> Result { - let res = self - .provider - .call( - FunctionCall { - calldata: vec![], - contract_address: self.address, - entry_point_selector: selector!("executor"), - }, - self.block_id, - ) - .await?; - - Ok(res[0]) - } - - pub async fn metadata_uri(&self) -> Result { - let res = self - .provider - .call( - FunctionCall { - calldata: vec![], - contract_address: self.address, - entry_point_selector: selector!("metadata_uri"), - }, - self.block_id, - ) - .await?; - - Ok(res[0]) - } - - pub async fn base(&self) -> Result { - let res = self - .provider - .call( - FunctionCall { - calldata: vec![], - contract_address: self.address, - entry_point_selector: selector!("base"), - }, - self.block_id, - ) - .await?; - - Ok(res[0]) - } - pub async fn executor_call( &self, class_hash: FieldElement, - mut calldata: Vec, - ) -> Result, ContractReaderError> { - calldata.insert(0, class_hash); - - let res = self - .provider - .call( - FunctionCall { - calldata, - contract_address: self.executor().await?, - entry_point_selector: selector!("call"), - }, - self.block_id, - ) + entry_point: FieldElement, + calldata: Vec, + ) -> CainomeResult> { + let executor_address = self.executor().block_id(self.block_id).call().await?; + + let executor = + abigen::executor::ExecutorContractReader::new(executor_address.into(), &self.provider); + + let res = executor + .call(&class_hash.into(), &entry_point, &calldata) + .block_id(self.block_id) + .call() .await?; Ok(res) } } - -impl<'a, P> WorldContractReader

-where - P: Provider + Sync + Send, -{ - pub async fn model(&'a self, name: &str) -> Result, ModelError> { - ModelRPCReader::new(name, self).await - } -} diff --git a/crates/dojo-world/src/contracts/world_test.rs b/crates/dojo-world/src/contracts/world_test.rs index 2e41ef6e2e..2028fad8b8 100644 --- a/crates/dojo-world/src/contracts/world_test.rs +++ b/crates/dojo-world/src/contracts/world_test.rs @@ -4,7 +4,7 @@ use camino::Utf8PathBuf; use dojo_test_utils::sequencer::{ get_default_test_starknet_config, SequencerConfig, TestSequencer, }; -use starknet::accounts::ConnectedAccount; +use starknet::accounts::{Account, ConnectedAccount}; use starknet::core::types::FieldElement; use super::{WorldContract, WorldContractReader}; @@ -26,9 +26,9 @@ async fn test_world_contract_reader() { .await; let world = WorldContractReader::new(world_address, provider); - let executor = world.executor().await.unwrap(); + let executor = world.executor().call().await.unwrap(); - assert_eq!(executor, executor_address); + assert_eq!(FieldElement::from(executor), executor_address); } pub async fn deploy_world( @@ -82,10 +82,14 @@ pub async fn deploy_world( // wait for the tx to be mined tokio::time::sleep(Duration::from_millis(250)).await; - let _ = WorldContract::new(world_address, &account) - .register_models(&declare_output.iter().map(|o| o.class_hash).collect::>()) - .await - .unwrap(); + let world = WorldContract::new(world_address, &account); + + let calls = declare_output + .iter() + .map(|o| world.register_model_getcall(&o.class_hash.into())) + .collect::>(); + + let _ = account.execute(calls).send().await.unwrap(); // wait for the tx to be mined tokio::time::sleep(Duration::from_millis(250)).await; diff --git a/crates/dojo-world/src/manifest.rs b/crates/dojo-world/src/manifest.rs index 5854b7110f..75dfac5135 100644 --- a/crates/dojo-world/src/manifest.rs +++ b/crates/dojo-world/src/manifest.rs @@ -3,6 +3,7 @@ use std::fs; use std::path::Path; use ::serde::{Deserialize, Serialize}; +use cainome::cairo_serde::Error as CainomeError; use cairo_lang_starknet::abi; use serde_with::serde_as; use smol_str::SmolStr; @@ -19,7 +20,6 @@ use starknet::providers::{Provider, ProviderError}; use thiserror::Error; use crate::contracts::model::ModelError; -use crate::contracts::world::ContractReaderError; use crate::contracts::WorldContractReader; #[cfg(test)] @@ -45,7 +45,7 @@ pub enum ManifestError { #[error(transparent)] Provider(#[from] ProviderError), #[error(transparent)] - ContractRead(#[from] ContractReaderError), + ContractRead(#[from] CainomeError), #[error(transparent)] Model(#[from] ModelError), } @@ -172,13 +172,14 @@ impl Manifest { err => err.into(), })?; - let world = WorldContractReader::new(world_address, &provider).with_block(BLOCK_ID); + let world = WorldContractReader::new(world_address, provider); - let executor_address = world.executor().await?; - let base_class_hash = world.base().await?; + let executor_address = world.executor().block_id(BLOCK_ID).call().await?; + let base_class_hash = world.base().block_id(BLOCK_ID).call().await?; - let executor_class_hash = provider - .get_class_hash_at(BLOCK_ID, executor_address) + let executor_class_hash = world + .provider() + .get_class_hash_at(BLOCK_ID, FieldElement::from(executor_address)) .await .map_err(|err| match err { ProviderError::StarknetError(StarknetError::ContractNotFound) => { @@ -187,7 +188,8 @@ impl Manifest { err => err.into(), })?; - let (models, contracts) = get_remote_models_and_contracts(world_address, provider).await?; + let (models, contracts) = + get_remote_models_and_contracts(world_address, &world.provider()).await?; Ok(Manifest { models, @@ -200,13 +202,13 @@ impl Manifest { }, executor: Contract { name: EXECUTOR_CONTRACT_NAME.into(), - address: Some(executor_address), + address: Some(executor_address.into()), class_hash: executor_class_hash, ..Default::default() }, base: Class { name: BASE_CONTRACT_NAME.into(), - class_hash: base_class_hash, + class_hash: base_class_hash.into(), ..Default::default() }, }) diff --git a/crates/sozo/src/ops/auth.rs b/crates/sozo/src/ops/auth.rs index 27125e9d6b..e2757f59e6 100644 --- a/crates/sozo/src/ops/auth.rs +++ b/crates/sozo/src/ops/auth.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use dojo_world::contracts::cairo_utils; use dojo_world::contracts::world::WorldContract; use dojo_world::metadata::Environment; @@ -14,7 +15,8 @@ pub async fn execute(command: AuthCommand, env_metadata: Option) -> let world = WorldContract::new(world_address, &account); let res = world - .grant_writer(&model, contract) + .grant_writer(&cairo_utils::str_to_felt(&model)?, &contract.into()) + .send() .await .with_context(|| "Failed to send transaction")?; diff --git a/crates/sozo/src/ops/migration/mod.rs b/crates/sozo/src/ops/migration/mod.rs index 4b8fc8b851..1fbfb96b29 100644 --- a/crates/sozo/src/ops/migration/mod.rs +++ b/crates/sozo/src/ops/migration/mod.rs @@ -1,6 +1,7 @@ use std::path::Path; use anyhow::{anyhow, bail, Context, Result}; +use dojo_world::contracts::cairo_utils; use dojo_world::contracts::world::WorldContract; use dojo_world::manifest::{Manifest, ManifestError}; use dojo_world::metadata::dojo_metadata_from_workspace; @@ -292,7 +293,8 @@ where let addr = strategy.world_address()?; let InvokeTransactionResult { transaction_hash } = WorldContract::new(addr, &migrator) - .set_executor(executor.contract_address) + .set_executor(&executor.contract_address.into()) + .send() .await?; TransactionWaiter::new(transaction_hash, migrator.provider()).await?; @@ -343,9 +345,12 @@ where if let Some(meta) = metadata.as_ref().and_then(|inner| inner.world()) { match meta.upload().await { Ok(hash) => { + let encoded_uri = cairo_utils::encode_uri(&format!("ipfs://{hash}"))?; + let InvokeTransactionResult { transaction_hash } = WorldContract::new(world.contract_address, migrator) - .set_metadata_uri(FieldElement::ZERO, format!("ipfs://{hash}")) + .set_metadata_uri(&FieldElement::ZERO, &encoded_uri) + .send() .await .map_err(|e| anyhow!("Failed to set World metadata: {e}"))?; @@ -459,9 +464,16 @@ where } let world_address = strategy.world_address()?; + let world = WorldContract::new(world_address, migrator); + + let calls = models + .iter() + .map(|c| world.register_model_getcall(&c.diff.local.into())) + .collect::>(); - let InvokeTransactionResult { transaction_hash } = WorldContract::new(world_address, migrator) - .register_models(&models.iter().map(|c| c.diff.local).collect::>()) + let InvokeTransactionResult { transaction_hash } = migrator + .execute(calls) + .send() .await .map_err(|e| anyhow!("Failed to register models to World: {e}"))?; diff --git a/crates/sozo/src/ops/model.rs b/crates/sozo/src/ops/model.rs index c22404dcec..a6cce2444d 100644 --- a/crates/sozo/src/ops/model.rs +++ b/crates/sozo/src/ops/model.rs @@ -15,7 +15,7 @@ pub async fn execute(command: ModelCommands, env_metadata: Option) let world = WorldContractReader::new(world_address, &provider) .with_block(BlockId::Tag(BlockTag::Pending)); - let model = world.model(&name).await?; + let model = world.model_reader(&name).await?; println!("{:#x}", model.class_hash()); } @@ -27,7 +27,7 @@ pub async fn execute(command: ModelCommands, env_metadata: Option) let world = WorldContractReader::new(world_address, &provider) .with_block(BlockId::Tag(BlockTag::Pending)); - let model = world.model(&name).await?; + let model = world.model_reader(&name).await?; let schema = model.schema().await?; if to_json { @@ -44,7 +44,7 @@ pub async fn execute(command: ModelCommands, env_metadata: Option) let world = WorldContractReader::new(world_address, &provider) .with_block(BlockId::Tag(BlockTag::Pending)); - let model = world.model(&name).await?; + let model = world.model_reader(&name).await?; let entity = model.entity(&keys).await?; println!("{entity}") diff --git a/crates/sozo/src/ops/register.rs b/crates/sozo/src/ops/register.rs index e91a97a7f3..5906a90fc6 100644 --- a/crates/sozo/src/ops/register.rs +++ b/crates/sozo/src/ops/register.rs @@ -1,6 +1,7 @@ use anyhow::{Context, Result}; -use dojo_world::contracts::world::WorldContract; +use dojo_world::contracts::WorldContract; use dojo_world::metadata::Environment; +use starknet::accounts::Account; use crate::commands::register::RegisterCommand; @@ -13,8 +14,14 @@ pub async fn execute(command: RegisterCommand, env_metadata: Option let account = account.account(provider, env_metadata.as_ref()).await?; let world = WorldContract::new(world_address, &account); - let res = world - .register_models(&models) + let calls = models + .iter() + .map(|c| world.register_model_getcall(&(*c).into())) + .collect::>(); + + let res = account + .execute(calls) + .send() .await .with_context(|| "Failed to send transaction")?; diff --git a/crates/torii/client/src/client/mod.rs b/crates/torii/client/src/client/mod.rs index a79ba11ac5..6f231a447a 100644 --- a/crates/torii/client/src/client/mod.rs +++ b/crates/torii/client/src/client/mod.rs @@ -71,7 +71,7 @@ impl Client { // TODO: change this to querying the gRPC url instead let subbed_models = subbed_models.models_keys.read().clone(); for keys in subbed_models { - let model_reader = world_reader.model(&keys.model).await?; + let model_reader = world_reader.model_reader(&keys.model).await?; let values = model_reader.entity_storage(&keys.keys).await?; client_storage.set_model_storage( @@ -137,7 +137,7 @@ impl Client { }; if !self.subscribed_models.is_synced(keys) { - let model = self.world_reader.model(&keys.model).await?; + let model = self.world_reader.model_reader(&keys.model).await?; return Ok(Some(model.entity(&keys.keys).await?)); } @@ -232,7 +232,7 @@ impl Client { } async fn initiate_model(&self, model: &str, keys: Vec) -> Result<(), Error> { - let model_reader = self.world_reader.model(model).await?; + let model_reader = self.world_reader.model_reader(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)?, diff --git a/crates/torii/core/src/engine.rs b/crates/torii/core/src/engine.rs index 6f4724e7c5..55e6a57114 100644 --- a/crates/torii/core/src/engine.rs +++ b/crates/torii/core/src/engine.rs @@ -178,7 +178,7 @@ impl<'db, P: Provider + Sync> Engine<'db, P> { let mut world_event = false; for (event_idx, event) in invoke_receipt.events.iter().enumerate() { - if event.from_address != self.world.address() { + if event.from_address != self.world.address { continue; } diff --git a/crates/torii/core/src/processors/mod.rs b/crates/torii/core/src/processors/mod.rs index e8cb64da42..d503671b7d 100644 --- a/crates/torii/core/src/processors/mod.rs +++ b/crates/torii/core/src/processors/mod.rs @@ -14,7 +14,7 @@ pub mod store_transaction; #[async_trait] pub trait EventProcessor

where - P: Provider, + P: Provider + Sync, { fn event_key(&self) -> String; diff --git a/crates/torii/core/src/processors/register_model.rs b/crates/torii/core/src/processors/register_model.rs index 54b01f946d..1771605f6c 100644 --- a/crates/torii/core/src/processors/register_model.rs +++ b/crates/torii/core/src/processors/register_model.rs @@ -45,7 +45,7 @@ where ) -> Result<(), Error> { let name = parse_cairo_short_string(&event.data[0])?; - let model = world.model(&name).await?; + let model = world.model_reader(&name).await?; let schema = model.schema().await?; let layout = model.layout().await?; diff --git a/examples/spawn-and-move/Scarb.lock b/examples/spawn-and-move/Scarb.lock index 9474cc125c..883898be30 100644 --- a/examples/spawn-and-move/Scarb.lock +++ b/examples/spawn-and-move/Scarb.lock @@ -10,7 +10,7 @@ dependencies = [ [[package]] name = "dojo_examples" -version = "0.4.0-rc0" +version = "0.4.1" dependencies = [ "dojo", ]