diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ea5a14368..e7738a5379 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ env: jobs: build: runs-on: ubuntu-latest-4-cores + needs: [fmt, cairofmt] steps: - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v2 @@ -55,6 +56,7 @@ jobs: ensure-wasm: runs-on: ubuntu-latest + needs: [fmt, cairofmt] container: image: ghcr.io/dojoengine/dojo-dev:v1.0.9 steps: @@ -153,6 +155,7 @@ jobs: clippy: runs-on: ubuntu-latest-4-cores + needs: [fmt, cairofmt] container: image: ghcr.io/dojoengine/dojo-dev:v1.0.9 steps: @@ -171,6 +174,7 @@ jobs: docs: runs-on: ubuntu-latest + needs: [fmt, cairofmt] container: image: ghcr.io/dojoengine/dojo-dev:v1.0.9 steps: diff --git a/Cargo.lock b/Cargo.lock index 35a24d284c..429762001f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2497,16 +2497,16 @@ dependencies = [ [[package]] name = "cainome" -version = "0.4.8" -source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +version = "0.4.11" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.11#355b88b7b808656d729e9dfd16f81d80c5c30fbf" dependencies = [ "anyhow", "async-trait", - "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", - "cainome-cairo-serde-derive 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", - "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", - "cainome-rs 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", - "cainome-rs-macro 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", + "cainome-cairo-serde-derive 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", + "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", + "cainome-rs 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", + "cainome-rs-macro 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", "camino", "clap", "clap_complete", @@ -2524,7 +2524,7 @@ dependencies = [ [[package]] name = "cainome-cairo-serde" version = "0.1.0" -source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.11#355b88b7b808656d729e9dfd16f81d80c5c30fbf" dependencies = [ "num-bigint", "serde", @@ -2556,7 +2556,7 @@ dependencies = [ [[package]] name = "cainome-cairo-serde-derive" version = "0.1.0" -source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.11#355b88b7b808656d729e9dfd16f81d80c5c30fbf" dependencies = [ "proc-macro2", "quote", @@ -2578,7 +2578,7 @@ dependencies = [ [[package]] name = "cainome-parser" version = "0.1.0" -source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.11#355b88b7b808656d729e9dfd16f81d80c5c30fbf" dependencies = [ "convert_case 0.6.0", "quote", @@ -2617,11 +2617,11 @@ dependencies = [ [[package]] name = "cainome-rs" version = "0.1.0" -source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.11#355b88b7b808656d729e9dfd16f81d80c5c30fbf" dependencies = [ "anyhow", - "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", - "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", + "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", "camino", "prettyplease", "proc-macro2", @@ -2671,12 +2671,12 @@ dependencies = [ [[package]] name = "cainome-rs-macro" version = "0.1.0" -source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c77d8834d18a2ae3c59d3a5e8dd5" +source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.11#355b88b7b808656d729e9dfd16f81d80c5c30fbf" dependencies = [ "anyhow", - "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", - "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", - "cainome-rs 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", + "cainome-parser 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", + "cainome-rs 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", "proc-macro-error", "proc-macro2", "quote", @@ -4858,7 +4858,7 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "cainome 0.4.8", + "cainome 0.4.11", "camino", "chrono", "convert_case 0.6.0", @@ -4974,6 +4974,7 @@ dependencies = [ "assert_fs", "async-trait", "camino", + "katana-chain-spec", "katana-core", "katana-executor", "katana-node", @@ -5015,7 +5016,7 @@ name = "dojo-types" version = "1.0.9" dependencies = [ "anyhow", - "cainome 0.4.8", + "cainome 0.4.11", "crypto-bigint", "hex", "indexmap 2.5.0", @@ -5055,7 +5056,7 @@ version = "1.0.9" dependencies = [ "anyhow", "async-trait", - "cainome 0.4.8", + "cainome 0.4.11", "cairo-lang-starknet-classes", "dojo-types 1.0.9", "futures", @@ -5081,7 +5082,7 @@ name = "dojo-world-abigen" version = "1.0.9" dependencies = [ "anyhow", - "cainome 0.4.8", + "cainome 0.4.11", "cairo-lang-starknet", "cairo-lang-starknet-classes", "camino", @@ -8443,7 +8444,7 @@ dependencies = [ "anyhow", "assert_matches", "byte-unit", - "cainome 0.4.8", + "cainome 0.4.11", "clap", "clap_complete", "comfy-table", @@ -8451,17 +8452,17 @@ dependencies = [ "dojo-utils", "inquire", "katana-cairo", + "katana-chain-spec", "katana-cli", "katana-db", "katana-node", "katana-primitives", - "serde", + "lazy_static", "serde_json", "shellexpand", "spinoff", "starknet 0.12.0", "tokio", - "toml 0.8.19", ] [[package]] @@ -8479,6 +8480,20 @@ dependencies = [ "starknet_api", ] +[[package]] +name = "katana-chain-spec" +version = "1.0.9" +dependencies = [ + "alloy-primitives", + "anyhow", + "katana-primitives", + "lazy_static", + "serde", + "serde_json", + "starknet 0.12.0", + "url", +] + [[package]] name = "katana-cli" version = "1.0.9" @@ -8486,10 +8501,11 @@ dependencies = [ "alloy-primitives", "anyhow", "assert_matches", - "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.10)", + "cainome-cairo-serde 0.1.0 (git+https://github.com/cartridge-gg/cainome?tag=v0.4.11)", "clap", "console", "dojo-utils", + "katana-chain-spec", "katana-core", "katana-node", "katana-primitives", @@ -8543,6 +8559,7 @@ dependencies = [ "dojo-metrics", "futures", "hex", + "katana-chain-spec", "katana-db", "katana-executor", "katana-pool", @@ -8602,6 +8619,7 @@ dependencies = [ "blockifier", "criterion", "katana-cairo", + "katana-chain-spec", "katana-primitives", "katana-provider", "katana-rpc-types", @@ -8657,6 +8675,7 @@ dependencies = [ "futures", "hyper 0.14.30", "jsonrpsee 0.16.3", + "katana-chain-spec", "katana-core", "katana-db", "katana-executor", @@ -8676,6 +8695,7 @@ dependencies = [ "strum_macros 0.25.3", "thiserror 1.0.63", "tokio", + "toml 0.8.19", "tower 0.4.13", "tower-http 0.4.4", "tracing", @@ -8771,6 +8791,7 @@ dependencies = [ "auto_impl", "bitvec", "futures", + "katana-chain-spec", "katana-db", "katana-primitives", "katana-runner", @@ -8798,7 +8819,7 @@ dependencies = [ "alloy-primitives", "anyhow", "assert_matches", - "cainome 0.4.8", + "cainome 0.4.11", "dojo-metrics", "dojo-test-utils", "dojo-utils", @@ -13963,7 +13984,7 @@ version = "1.0.9" dependencies = [ "anyhow", "async-trait", - "cainome 0.4.8", + "cainome 0.4.11", "cairo-lang-compiler", "cairo-lang-filesystem", "cairo-lang-project", @@ -14015,7 +14036,7 @@ dependencies = [ "anyhow", "assert_fs", "async-trait", - "cainome 0.4.8", + "cainome 0.4.11", "colored", "colored_json", "dojo-test-utils", @@ -15748,7 +15769,7 @@ dependencies = [ "async-trait", "base64 0.21.7", "bitflags 2.6.0", - "cainome 0.4.8", + "cainome 0.4.11", "chrono", "crypto-bigint", "data-url", @@ -15761,7 +15782,6 @@ dependencies = [ "hashlink", "ipfs-api-backend-hyper", "katana-runner", - "num-traits 0.2.19", "once_cell", "reqwest 0.11.27", "scarb", @@ -15823,7 +15843,7 @@ dependencies = [ name = "torii-grpc" version = "1.0.9" dependencies = [ - "cainome 0.4.8", + "cainome 0.4.11", "camino", "crypto-bigint", "dojo-test-utils", @@ -15871,7 +15891,7 @@ name = "torii-relay" version = "1.0.9" dependencies = [ "anyhow", - "cainome 0.4.8", + "cainome 0.4.11", "chrono", "crypto-bigint", "dojo-types 1.0.9", diff --git a/Cargo.toml b/Cargo.toml index b6c0ae31f0..a59e580384 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ members = [ "crates/dojo/utils", "crates/dojo/world", "crates/dojo/world/abigen", + "crates/katana/chain-spec", "crates/katana/cli", "crates/katana/controller", "crates/katana/core", @@ -73,8 +74,8 @@ debug = true inherits = "release" [workspace.dependencies] -cainome = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.4.10", features = [ "abigen-rs" ] } -cainome-cairo-serde = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.4.10" } +cainome = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.4.11", features = [ "abigen-rs" ] } +cainome-cairo-serde = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.4.11" } dojo-utils = { path = "crates/dojo/utils" } # metrics @@ -93,6 +94,7 @@ topological-sort = "0.2" # katana katana-cairo = { path = "crates/katana/cairo" } +katana-chain-spec = { path = "crates/katana/chain-spec" } katana-cli = { path = "crates/katana/cli" } katana-codecs = { path = "crates/katana/storage/codecs" } katana-codecs-derive = { path = "crates/katana/storage/codecs/derive" } diff --git a/README.md b/README.md index 8ebe1c5cf5..89c4e5d5f6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ ![Dojo Feature Matrix](.github/feature_matrix.png) -# Dojo: Provable Games and Applications [![discord](https://img.shields.io/badge/join-dojo-green?logo=discord&logoColor=white)](https://discord.com/invite/dojoengine) [![Telegram Chat][tg-badge]][tg-url] ![Github Actions][gha-badge] +# Dojo: Provable Games and Applications [![discord](https://img.shields.io/badge/join-dojo-green?logo=discord&logoColor=white)](https://discord.com/invite/dojoengine) [![Telegram Chat][tg-badge]][tg-url] [![Github Actions][gha-badge]][gha-url] [gha-badge]: https://img.shields.io/github/actions/workflow/status/dojoengine/dojo/ci.yml?branch=main +[gha-url]: https://github.com/dojoengine/dojo/actions/workflows/ci.yml?query=branch%3Amain [tg-badge]: https://img.shields.io/endpoint?color=neon&logo=telegram&label=chat&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fdojoengine [tg-url]: https://t.me/dojoengine diff --git a/bin/katana/Cargo.toml b/bin/katana/Cargo.toml index 04f8c76f57..ff178296fb 100644 --- a/bin/katana/Cargo.toml +++ b/bin/katana/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true [dependencies] katana-cairo.workspace = true +katana-chain-spec.workspace = true katana-cli.workspace = true katana-db.workspace = true katana-node.workspace = true @@ -22,13 +23,12 @@ comfy-table = "7.1.1" dirs = "5.0.1" dojo-utils.workspace = true inquire = "0.7.5" -serde.workspace = true +lazy_static.workspace = true serde_json.workspace = true shellexpand = "3.1.0" spinoff.workspace = true starknet.workspace = true tokio.workspace = true -toml.workspace = true [dev-dependencies] assert_matches.workspace = true diff --git a/bin/katana/src/cli/init/mod.rs b/bin/katana/src/cli/init/mod.rs index 51f27e5016..4532de754e 100644 --- a/bin/katana/src/cli/init/mod.rs +++ b/bin/katana/src/cli/init/mod.rs @@ -8,11 +8,15 @@ use anyhow::{anyhow, Context, Result}; use cainome::rs::abigen; use clap::Args; use dojo_utils::TransactionWaiter; -use inquire::{Confirm, CustomType, Text}; +use inquire::{Confirm, CustomType}; use katana_cairo::lang::starknet_classes::casm_contract_class::CasmContractClass; use katana_cairo::lang::starknet_classes::contract_class::ContractClass; +use katana_chain_spec::{SettlementLayer, DEV_UNALLOCATED}; +use katana_primitives::chain::ChainId; +use katana_primitives::genesis::allocation::DevAllocationsGenerator; +use katana_primitives::genesis::Genesis; use katana_primitives::{felt, ContractAddress, Felt}; -use serde::{Deserialize, Serialize}; +use lazy_static::lazy_static; use starknet::accounts::{Account, ConnectedAccount, ExecutionEncoding, SingleOwnerAccount}; use starknet::contract::ContractFactory; use starknet::core::types::contract::{CompiledClass, SierraClass}; @@ -38,55 +42,15 @@ struct InitInput { // the rpc url for the settlement layer. rpc_url: Url, - fee_token: ContractAddress, - settlement_contract: ContractAddress, // path at which the config file will be written at. output_path: PathBuf, } -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct SettlementLayer { - // the account address that was used to initialized the l1 deployments - pub account: ContractAddress, - - // The id of the settlement chain. - pub id: String, - - pub rpc_url: Url, - - // - The token that will be used to pay for tx fee in the appchain. - // - For now, this must be the native token that is used to pay for tx fee in the settlement - // chain. - pub fee_token: ContractAddress, - - // - The bridge contract for bridging the fee token from L1 to the appchain - // - This will be part of the initialization process. - pub bridge_contract: ContractAddress, - - // - The core appchain contract used to settlement - // - This is deployed on the L1 - pub settlement_contract: ContractAddress, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct InitConfiguration { - // the initialized chain id - pub id: String, - - // the fee token contract - // - // this corresponds to the l1 token contract - pub fee_token: ContractAddress, - - pub settlement: SettlementLayer, -} - #[derive(Debug, Args)] pub struct InitArgs { + /// The path to where the config file will be written at. #[arg(value_name = "PATH")] pub output_path: Option, } @@ -99,27 +63,34 @@ impl InitArgs { let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?; let input = self.prompt(&rt)?; - let output = InitConfiguration { - id: input.id, - fee_token: ContractAddress::default(), - settlement: SettlementLayer { - account: input.account, - id: input.settlement_id, - rpc_url: input.rpc_url, - fee_token: input.fee_token, - bridge_contract: ContractAddress::default(), - settlement_contract: input.settlement_contract, - }, + let settlement = SettlementLayer::Starknet { + account: input.account, + rpc_url: input.rpc_url, + id: ChainId::parse(&input.settlement_id)?, + core_contract: input.settlement_contract, }; - let content = toml::to_string_pretty(&output)?; - fs::write(input.output_path, content)?; + let mut chain_spec = DEV_UNALLOCATED.clone(); + chain_spec.genesis = GENESIS.clone(); + chain_spec.id = ChainId::parse(&input.id)?; + chain_spec.settlement = Some(settlement); - Ok(()) + chain_spec.store(input.output_path) } fn prompt(&self, rt: &Runtime) -> Result { - let chain_id = Text::new("Id").prompt()?; + let chain_id = CustomType::::new("Id") + .with_help_message("This will be the id of your rollup chain.") + // checks that the input is a valid ascii string. + .with_parser(&|input| { + if input.is_ascii() { + Ok(input.to_string()) + } else { + Err(()) + } + }) + .with_error_message("Must be valid ASCII characters") + .prompt()?; let url = CustomType::::new("Settlement RPC URL") .with_default(Url::parse("http://localhost:5050")?) @@ -157,11 +128,12 @@ impl InitArgs { ExecutionEncoding::New, ); - // The L1 fee token. Must be an existing token. - let fee_token = CustomType::::new("Fee token") - .with_parser(contract_exist_parser) - .with_error_message("Please enter a valid fee token (the token must exist on L1)") - .prompt()?; + // TODO: uncomment once we actually using the fee token. + // // The L1 fee token. Must be an existing token. + // let fee_token = CustomType::::new("Fee token") + // .with_parser(contract_exist_parser) + // .with_error_message("Please enter a valid fee token (the token must exist on L1)") + // .prompt()?; // The core settlement contract on L1 let settlement_contract = @@ -192,7 +164,6 @@ impl InitArgs { settlement_contract, settlement_id: parse_cairo_short_string(&l1_chain_id)?, id: chain_id, - fee_token, rpc_url: url, output_path, }) @@ -232,7 +203,7 @@ where Err(ProviderError::StarknetError(StarknetError::ClassHashNotFound)) => { sp.update_text("Declaring contract..."); - let res = account.declare_v2(contract.into(), compiled_class_hash).send().await?; + let res = account.declare_v3(contract.into(), compiled_class_hash).send().await?; let _ = TransactionWaiter::new(res.transaction_hash, account.provider()).await?; } @@ -300,9 +271,9 @@ fn get_compiled_class_hash(artifact: &str) -> Result { Ok(compiled_class.class_hash()?) } -// > CONFIG_DIR/$chain_id/config.toml +// > CONFIG_DIR/$chain_id/config.json fn config_path(id: &str) -> Result { - Ok(config_dir(id)?.join("config").with_extension("toml")) + Ok(config_dir(id)?.join("config").with_extension("json")) } fn config_dir(id: &str) -> Result { @@ -333,3 +304,13 @@ impl Display for Path { write!(f, "{}", self.0.display()) } } + +lazy_static! { + static ref GENESIS: Genesis = { + // master account + let accounts = DevAllocationsGenerator::new(1).generate(); + let mut genesis = Genesis::default(); + genesis.extend_allocations(accounts.into_iter().map(|(k, v)| (k, v.into()))); + genesis + }; +} diff --git a/bin/sozo/tests/test_data/policies.json b/bin/sozo/tests/test_data/policies.json index 26ec67d783..2094e3d30f 100644 --- a/bin/sozo/tests/test_data/policies.json +++ b/bin/sozo/tests/test_data/policies.json @@ -1,48 +1,4 @@ [ - { - "target": "0x72a9f501c260b2d13f8988ea172680c5c1fdc085c5b44bdcac8477362ed5290", - "method": "upgrade" - }, - { - "target": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "method": "spawn" - }, - { - "target": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "method": "move" - }, - { - "target": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "method": "set_player_config" - }, - { - "target": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "method": "reset_player_config" - }, - { - "target": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "method": "set_player_server_profile" - }, - { - "target": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "method": "set_models" - }, - { - "target": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "method": "enter_dungeon" - }, - { - "target": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "method": "upgrade" - }, - { - "target": "0x50b1497d463d52cbeb5919a35a82360ea6702db2b9c62c2d69c167995f34c08", - "method": "upgrade" - }, - { - "target": "0x4b41a2abaeff170f3a04acb0144790a5a812e25e7a735dfef959247cfeb527", - "method": "upgrade" - }, { "target": "0x52ee4d3cba58d1a0462bbfb6813bf5aa1b35078c3b859cded2b727c1d9469ea", "method": "uuid" @@ -127,6 +83,54 @@ "target": "0x52ee4d3cba58d1a0462bbfb6813bf5aa1b35078c3b859cded2b727c1d9469ea", "method": "upgrade" }, + { + "target": "0x50b1497d463d52cbeb5919a35a82360ea6702db2b9c62c2d69c167995f34c08", + "method": "upgrade" + }, + { + "target": "0x72a9f501c260b2d13f8988ea172680c5c1fdc085c5b44bdcac8477362ed5290", + "method": "upgrade" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "spawn" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "move" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "set_player_config" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "update_player_config_name" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "reset_player_config" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "set_player_server_profile" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "set_models" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "enter_dungeon" + }, + { + "target": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "method": "upgrade" + }, + { + "target": "0x4b41a2abaeff170f3a04acb0144790a5a812e25e7a735dfef959247cfeb527", + "method": "upgrade" + }, { "target": "0x2af9427c5a277474c079a1283c880ee8a6f0f8fbf73ce969c08d88befec1bba", "method": "__declare_transaction__" diff --git a/crates/dojo/test-utils/Cargo.toml b/crates/dojo/test-utils/Cargo.toml index f2e47f07e5..95e70c4ce2 100644 --- a/crates/dojo/test-utils/Cargo.toml +++ b/crates/dojo/test-utils/Cargo.toml @@ -6,15 +6,17 @@ repository.workspace = true version.workspace = true [dependencies] -anyhow.workspace = true -assert_fs.workspace = true -async-trait.workspace = true -camino.workspace = true +katana-chain-spec.workspace = true katana-core = { workspace = true } katana-executor = { workspace = true, features = [ "blockifier" ] } katana-node.workspace = true katana-primitives = { workspace = true } katana-rpc.workspace = true + +anyhow.workspace = true +assert_fs.workspace = true +async-trait.workspace = true +camino.workspace = true scarb.workspace = true scarb-ui.workspace = true serde.workspace = true diff --git a/crates/dojo/test-utils/src/sequencer.rs b/crates/dojo/test-utils/src/sequencer.rs index ef1b023eec..b7ef3cd54c 100644 --- a/crates/dojo/test-utils/src/sequencer.rs +++ b/crates/dojo/test-utils/src/sequencer.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use katana_chain_spec::ChainSpec; use katana_core::backend::Backend; use katana_core::constants::DEFAULT_SEQUENCER_ADDRESS; use katana_executor::implementation::blockifier::BlockifierFactory; @@ -8,7 +9,6 @@ use katana_node::config::rpc::{RpcConfig, DEFAULT_RPC_ADDR, DEFAULT_RPC_MAX_CONN pub use katana_node::config::*; use katana_node::LaunchedNode; use katana_primitives::chain::ChainId; -use katana_primitives::chain_spec::ChainSpec; use katana_rpc::Error; use rpc::RpcModulesList; use starknet::accounts::{ExecutionEncoding, SingleOwnerAccount}; diff --git a/crates/katana/chain-spec/Cargo.toml b/crates/katana/chain-spec/Cargo.toml new file mode 100644 index 0000000000..0e7636c9ec --- /dev/null +++ b/crates/katana/chain-spec/Cargo.toml @@ -0,0 +1,20 @@ +[package] +edition.workspace = true +license.workspace = true +name = "katana-chain-spec" +repository.workspace = true +version.workspace = true + +[dependencies] +katana-primitives.workspace = true + +alloy-primitives.workspace = true +anyhow.workspace = true +lazy_static.workspace = true +serde.workspace = true +serde_json.workspace = true +starknet.workspace = true +url.workspace = true + +[features] +controller = [ ] diff --git a/crates/katana/primitives/src/chain_spec.rs b/crates/katana/chain-spec/src/lib.rs similarity index 84% rename from crates/katana/primitives/src/chain_spec.rs rename to crates/katana/chain-spec/src/lib.rs index ceaef74660..2bb3667fab 100644 --- a/crates/katana/primitives/src/chain_spec.rs +++ b/crates/katana/chain-spec/src/lib.rs @@ -1,17 +1,18 @@ use std::collections::BTreeMap; +use std::fs::File; +use std::io::BufReader; +use std::path::{Path, PathBuf}; use alloy_primitives::U256; -use lazy_static::lazy_static; -use starknet::core::utils::cairo_short_string_to_felt; -use starknet_crypto::Felt; - -use crate::block::{Block, Header}; -use crate::chain::ChainId; -use crate::class::ClassHash; -use crate::contract::ContractAddress; -use crate::da::L1DataAvailabilityMode; -use crate::genesis::allocation::{DevAllocationsGenerator, GenesisAllocation}; -use crate::genesis::constant::{ +use anyhow::{Context, Result}; +use katana_primitives::block::{Block, Header}; +use katana_primitives::chain::ChainId; +use katana_primitives::class::ClassHash; +use katana_primitives::contract::ContractAddress; +use katana_primitives::da::L1DataAvailabilityMode; +use katana_primitives::eth::{self, Address as EthAddress}; +use katana_primitives::genesis::allocation::{DevAllocationsGenerator, GenesisAllocation}; +use katana_primitives::genesis::constant::{ get_fee_token_balance_base_storage_address, DEFAULT_ACCOUNT_CLASS_PUBKEY_STORAGE_SLOT, DEFAULT_ETH_FEE_TOKEN_ADDRESS, DEFAULT_LEGACY_ERC20_CLASS, DEFAULT_LEGACY_ERC20_CLASS_HASH, DEFAULT_LEGACY_UDC_CLASS, DEFAULT_LEGACY_UDC_CLASS_HASH, @@ -19,30 +20,41 @@ use crate::genesis::constant::{ DEFAULT_STRK_FEE_TOKEN_ADDRESS, DEFAULT_UDC_ADDRESS, ERC20_DECIMAL_STORAGE_SLOT, ERC20_NAME_STORAGE_SLOT, ERC20_SYMBOL_STORAGE_SLOT, ERC20_TOTAL_SUPPLY_STORAGE_SLOT, }; -use crate::genesis::Genesis; -use crate::state::StateUpdatesWithClasses; -use crate::utils::split_u256; -use crate::version::{ProtocolVersion, CURRENT_STARKNET_VERSION}; - -/// A chain specification. -// TODO: include l1 core contract -// TODO: create a chain spec and genesis builder to abstract inserting aux classes +use katana_primitives::genesis::Genesis; +use katana_primitives::state::StateUpdatesWithClasses; +use katana_primitives::utils::split_u256; +use katana_primitives::version::{ProtocolVersion, CURRENT_STARKNET_VERSION}; +use katana_primitives::Felt; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use starknet::core::utils::cairo_short_string_to_felt; +use url::Url; + +/// The rollup chain specification. #[derive(Debug, Clone)] pub struct ChainSpec { - /// The network chain id. + /// The rollup network chain id. pub id: ChainId, - /// The genesis block. + + /// The chain's genesis states. pub genesis: Genesis, + /// The chain fee token contract. pub fee_contracts: FeeContracts, - /// The protocol version. + + // TODO: Maybe remove? Doesn't seem like this should belong here. pub version: ProtocolVersion, + + /// The chain's settlement layer configurations. + /// + /// This is optional if the chain is on development mode. + pub settlement: Option, } /// Tokens that can be used for transaction fee payments in the chain. As /// supported on Starknet. // TODO: include both l1 and l2 addresses -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct FeeContracts { /// L2 ETH fee token address. Used for paying pre-V3 transactions. pub eth: ContractAddress, @@ -50,7 +62,79 @@ pub struct FeeContracts { pub strk: ContractAddress, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum SettlementLayer { + Ethereum { + // The id of the settlement chain. + id: eth::ChainId, + + // url for ethereum rpc provider + rpc_url: Url, + + account: EthAddress, + + // - The core appchain contract used to settlement + // - This is deployed on the L1 + core_contract: EthAddress, + }, + + Starknet { + // The id of the settlement chain. + id: ChainId, + + // url for starknet rpc provider + rpc_url: Url, + + // the account address that was used to initialized the l1 deployments + account: ContractAddress, + + // - The core appchain contract used to settlement + // - This is deployed on the L1 + core_contract: ContractAddress, + }, +} + +////////////////////////////////////////////////////////////// +// ChainSpec implementations +////////////////////////////////////////////////////////////// + impl ChainSpec { + pub fn load>(path: P) -> Result { + let content = std::fs::read_to_string(&path)?; + let cs = serde_json::from_str::(&content)?; + + let file = File::open(&cs.genesis).context("failed to open genesis file")?; + let genesis: Genesis = serde_json::from_reader(BufReader::new(file))?; + + Ok(Self { + genesis, + id: cs.id, + version: cs.version, + settlement: cs.settlement, + fee_contracts: cs.fee_contracts, + }) + } + + pub fn store>(self, path: P) -> anyhow::Result<()> { + let cfg_path = path.as_ref(); + let mut genesis_path = cfg_path.to_path_buf(); + genesis_path.set_file_name("genesis.json"); + + let stored = ChainSpecFile { + id: self.id, + version: self.version, + genesis: genesis_path, + settlement: self.settlement, + fee_contracts: self.fee_contracts, + }; + + serde_json::to_writer_pretty(File::create(cfg_path)?, &stored)?; + serde_json::to_writer_pretty(File::create(stored.genesis)?, &self.genesis)?; + + Ok(()) + } + pub fn block(&self) -> Block { let header = Header { state_diff_length: 0, @@ -118,6 +202,16 @@ impl Default for ChainSpec { } } +#[derive(Debug, Serialize, Deserialize)] +struct ChainSpecFile { + id: ChainId, + fee_contracts: FeeContracts, + version: ProtocolVersion, + #[serde(skip_serializing_if = "Option::is_none")] + settlement: Option, + genesis: PathBuf, +} + lazy_static! { /// The default chain specification in dev mode. pub static ref DEV: ChainSpec = { @@ -138,7 +232,14 @@ lazy_static! { let id = ChainId::parse("KATANA").unwrap(); let genesis = Genesis::default(); let fee_contracts = FeeContracts { eth: DEFAULT_ETH_FEE_TOKEN_ADDRESS, strk: DEFAULT_STRK_FEE_TOKEN_ADDRESS }; - ChainSpec { id, genesis, fee_contracts, version: CURRENT_STARKNET_VERSION } + + ChainSpec { + id, + genesis, + fee_contracts, + settlement: None, + version: CURRENT_STARKNET_VERSION, + } }; } @@ -248,23 +349,25 @@ mod tests { use std::str::FromStr; use alloy_primitives::U256; - use starknet::macros::felt; - - use super::*; - use crate::address; - use crate::block::{Block, GasPrices, Header}; - use crate::da::L1DataAvailabilityMode; - use crate::genesis::allocation::{GenesisAccount, GenesisAccountAlloc, GenesisContractAlloc}; - #[cfg(feature = "slot")] - use crate::genesis::constant::{CONTROLLER_ACCOUNT_CLASS, CONTROLLER_CLASS_HASH}; - use crate::genesis::constant::{ + use katana_primitives::address; + use katana_primitives::block::{Block, GasPrices, Header}; + use katana_primitives::da::L1DataAvailabilityMode; + use katana_primitives::genesis::allocation::{ + GenesisAccount, GenesisAccountAlloc, GenesisContractAlloc, + }; + #[cfg(feature = "controller")] + use katana_primitives::genesis::constant::{CONTROLLER_ACCOUNT_CLASS, CONTROLLER_CLASS_HASH}; + use katana_primitives::genesis::constant::{ DEFAULT_ACCOUNT_CLASS, DEFAULT_ACCOUNT_CLASS_HASH, DEFAULT_ACCOUNT_CLASS_PUBKEY_STORAGE_SLOT, DEFAULT_ACCOUNT_COMPILED_CLASS_HASH, DEFAULT_LEGACY_ERC20_CLASS, DEFAULT_LEGACY_ERC20_COMPILED_CLASS_HASH, DEFAULT_LEGACY_UDC_CLASS, DEFAULT_LEGACY_UDC_COMPILED_CLASS_HASH, }; - use crate::genesis::GenesisClass; - use crate::version::CURRENT_STARKNET_VERSION; + use katana_primitives::genesis::GenesisClass; + use katana_primitives::version::CURRENT_STARKNET_VERSION; + use starknet::macros::felt; + + use super::*; #[test] fn genesis_block_and_state_updates() { @@ -292,7 +395,7 @@ mod tests { class: DEFAULT_ACCOUNT_CLASS.clone().into(), }, ), - #[cfg(feature = "slot")] + #[cfg(feature = "controller")] ( CONTROLLER_CLASS_HASH, GenesisClass { @@ -358,6 +461,7 @@ mod tests { eth: DEFAULT_ETH_FEE_TOKEN_ADDRESS, strk: DEFAULT_STRK_FEE_TOKEN_ADDRESS, }, + settlement: None, }; // setup expected storage values @@ -403,7 +507,7 @@ mod tests { assert_eq!(actual_block.header.events_count, expected_block.header.events_count); assert_eq!(actual_block.body, expected_block.body); - if cfg!(feature = "slot") { + if cfg!(feature = "controller") { assert!(actual_state_updates.classes.len() == 4); } else { assert!(actual_state_updates.classes.len() == 3); @@ -477,7 +581,7 @@ mod tests { "The default oz account contract sierra class should be declared" ); - #[cfg(feature = "slot")] + #[cfg(feature = "controller")] { assert_eq!( actual_state_updates.state_updates.declared_classes.get(&CONTROLLER_CLASS_HASH), diff --git a/crates/katana/cli/Cargo.toml b/crates/katana/cli/Cargo.toml index be65483377..a3caabf554 100644 --- a/crates/katana/cli/Cargo.toml +++ b/crates/katana/cli/Cargo.toml @@ -6,7 +6,7 @@ repository.workspace = true version.workspace = true [dependencies] -dojo-utils.workspace = true +katana-chain-spec.workspace = true katana-core.workspace = true katana-node.workspace = true katana-primitives.workspace = true @@ -18,6 +18,7 @@ anyhow.workspace = true cainome-cairo-serde.workspace = true clap.workspace = true console.workspace = true +dojo-utils.workspace = true serde.workspace = true serde_json = "1.0.132" shellexpand = "3.1.0" diff --git a/crates/katana/cli/src/args.rs b/crates/katana/cli/src/args.rs index cd57acb1b2..a4d798ff56 100644 --- a/crates/katana/cli/src/args.rs +++ b/crates/katana/cli/src/args.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use alloy_primitives::U256; use anyhow::{bail, Context, Result}; use clap::Parser; +use katana_chain_spec::ChainSpec; use katana_core::constants::DEFAULT_SEQUENCER_ADDRESS; use katana_core::service::messaging::MessagingConfig; use katana_node::config::db::DbConfig; @@ -15,7 +16,6 @@ use katana_node::config::fork::ForkingConfig; use katana_node::config::metrics::MetricsConfig; use katana_node::config::rpc::{RpcConfig, RpcModuleKind, RpcModulesList}; use katana_node::config::{Config, SequencingConfig}; -use katana_primitives::chain_spec::{self, ChainSpec}; use katana_primitives::genesis::allocation::DevAllocationsGenerator; use katana_primitives::genesis::constant::DEFAULT_PREFUNDED_ACCOUNT_BALANCE; use serde::{Deserialize, Serialize}; @@ -38,6 +38,10 @@ pub struct NodeArgs { #[arg(long)] pub silent: bool, + /// Path to the chain configuration file. + #[arg(long, hide = true)] + pub chain: Option, + /// Disable auto and interval mining, and mine on demand instead via an endpoint. #[arg(long)] #[arg(conflicts_with = "block_time")] @@ -176,20 +180,8 @@ impl NodeArgs { let execution = self.execution_config(); let sequencing = self.sequencer_config(); let messaging = self.messaging.clone(); - let l1_provider_url = self.gpo_config(); - - Ok(Config { - metrics, - db, - dev, - rpc, - chain, - execution, - sequencing, - messaging, - forking, - l1_provider_url, - }) + + Ok(Config { metrics, db, dev, rpc, chain, execution, sequencing, messaging, forking }) } fn sequencer_config(&self) -> SequencingConfig { @@ -233,32 +225,35 @@ impl NodeArgs { } fn chain_spec(&self) -> Result> { - let mut chain_spec = chain_spec::DEV_UNALLOCATED.clone(); - - if let Some(id) = self.starknet.environment.chain_id { - chain_spec.id = id; - } - - if let Some(genesis) = self.starknet.genesis.clone() { - chain_spec.genesis = genesis; - } else { + if let Some(path) = &self.chain { + let mut chain_spec = ChainSpec::load(path).context("failed to load chain spec")?; chain_spec.genesis.sequencer_address = *DEFAULT_SEQUENCER_ADDRESS; + Ok(Arc::new(chain_spec)) } + // exclusively for development mode + else { + let mut chain_spec = katana_chain_spec::DEV_UNALLOCATED.clone(); + chain_spec.genesis.sequencer_address = *DEFAULT_SEQUENCER_ADDRESS; - // generate dev accounts - let accounts = DevAllocationsGenerator::new(self.development.total_accounts) - .with_seed(parse_seed(&self.development.seed)) - .with_balance(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE)) - .generate(); + if let Some(id) = self.starknet.environment.chain_id { + chain_spec.id = id; + } - chain_spec.genesis.extend_allocations(accounts.into_iter().map(|(k, v)| (k, v.into()))); + // generate dev accounts + let accounts = DevAllocationsGenerator::new(self.development.total_accounts) + .with_seed(parse_seed(&self.development.seed)) + .with_balance(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE)) + .generate(); - #[cfg(feature = "slot")] - if self.slot.controller { - katana_slot_controller::add_controller_account(&mut chain_spec.genesis)?; - } + chain_spec.genesis.extend_allocations(accounts.into_iter().map(|(k, v)| (k, v.into()))); + + #[cfg(feature = "slot")] + if self.slot.controller { + katana_slot_controller::add_controller_account(&mut chain_spec.genesis)?; + } - Ok(Arc::new(chain_spec)) + Ok(Arc::new(chain_spec)) + } } fn dev_config(&self) -> DevConfig { @@ -324,10 +319,6 @@ impl NodeArgs { None } - fn gpo_config(&self) -> Option { - self.l1_provider_url.clone() - } - /// Parse the node config from the command line arguments and the config file, /// and merge them together prioritizing the command line arguments. pub fn with_config_file(mut self) -> Result { diff --git a/crates/katana/cli/src/options.rs b/crates/katana/cli/src/options.rs index 88adabbd9b..83d5ef1dd5 100644 --- a/crates/katana/cli/src/options.rs +++ b/crates/katana/cli/src/options.rs @@ -170,7 +170,7 @@ pub struct EnvironmentOptions { /// The chain ID. If a raw hex string (`0x` prefix) is provided, then it'd /// used as the actual chain ID. Otherwise, it's represented as the raw /// ASCII values. It must be a valid Cairo short string. - #[arg(long)] + #[arg(long, conflicts_with = "chain")] #[arg(value_parser = ChainId::parse)] #[serde(default)] pub chain_id: Option, @@ -201,10 +201,6 @@ impl Default for EnvironmentOptions { impl EnvironmentOptions { pub fn merge(&mut self, other: Option<&Self>) { if let Some(other) = other { - if self.chain_id.is_none() { - self.chain_id = other.chain_id; - } - if self.validate_max_steps == DEFAULT_VALIDATION_MAX_STEPS { self.validate_max_steps = other.validate_max_steps; } diff --git a/crates/katana/cli/src/utils.rs b/crates/katana/cli/src/utils.rs index eb8457166c..675589b652 100644 --- a/crates/katana/cli/src/utils.rs +++ b/crates/katana/cli/src/utils.rs @@ -5,8 +5,8 @@ use anyhow::{Context, Result}; use clap::builder::PossibleValue; use clap::ValueEnum; use console::Style; +use katana_chain_spec::ChainSpec; use katana_primitives::block::{BlockHash, BlockHashOrNumber, BlockNumber}; -use katana_primitives::chain_spec::ChainSpec; use katana_primitives::class::ClassHash; use katana_primitives::contract::ContractAddress; use katana_primitives::genesis::allocation::GenesisAccountAlloc; diff --git a/crates/katana/core/Cargo.toml b/crates/katana/core/Cargo.toml index 896f619a48..748be4d631 100644 --- a/crates/katana/core/Cargo.toml +++ b/crates/katana/core/Cargo.toml @@ -7,6 +7,7 @@ repository.workspace = true version.workspace = true [dependencies] +katana-chain-spec.workspace = true katana-db.workspace = true katana-executor = { workspace = true, features = [ "blockifier" ] } katana-pool.workspace = true diff --git a/crates/katana/core/src/backend/gas_oracle.rs b/crates/katana/core/src/backend/gas_oracle.rs index 381b110655..e0eb648116 100644 --- a/crates/katana/core/src/backend/gas_oracle.rs +++ b/crates/katana/core/src/backend/gas_oracle.rs @@ -83,7 +83,7 @@ impl L1GasOracle { let prices = oracle.prices.clone(); let l1_provider = oracle.l1_provider.clone(); - task_spawner.build_task().critical().name("L1 Gas Oracle worker").spawn( + task_spawner.build_task().critical().name("L1 Gas Oracle Worker").spawn( async move { let mut worker = GasOracleWorker::new(prices, l1_provider); worker diff --git a/crates/katana/core/src/backend/mod.rs b/crates/katana/core/src/backend/mod.rs index a50cf6e384..12ab721309 100644 --- a/crates/katana/core/src/backend/mod.rs +++ b/crates/katana/core/src/backend/mod.rs @@ -1,11 +1,11 @@ use std::sync::Arc; use gas_oracle::L1GasOracle; +use katana_chain_spec::ChainSpec; use katana_executor::{ExecutionOutput, ExecutionResult, ExecutorFactory}; use katana_primitives::block::{ FinalityStatus, Header, PartialHeader, SealedBlock, SealedBlockWithStatus, }; -use katana_primitives::chain_spec::ChainSpec; use katana_primitives::da::L1DataAvailabilityMode; use katana_primitives::env::BlockEnv; use katana_primitives::receipt::{Event, ReceiptWithTxHash}; diff --git a/crates/katana/core/src/backend/storage.rs b/crates/katana/core/src/backend/storage.rs index ceca70d17d..0f10d60b14 100644 --- a/crates/katana/core/src/backend/storage.rs +++ b/crates/katana/core/src/backend/storage.rs @@ -1,11 +1,11 @@ use std::sync::Arc; use anyhow::{anyhow, bail, Context, Result}; +use katana_chain_spec::ChainSpec; use katana_db::mdbx::DbEnv; use katana_primitives::block::{ BlockHashOrNumber, BlockIdOrTag, BlockNumber, FinalityStatus, SealedBlockWithStatus, }; -use katana_primitives::chain_spec::ChainSpec; use katana_primitives::da::L1DataAvailabilityMode; use katana_primitives::hash::{self, StarkHash}; use katana_primitives::state::StateUpdatesWithClasses; @@ -257,7 +257,7 @@ mod tests { use katana_primitives::state::StateUpdatesWithClasses; use katana_primitives::trace::TxExecInfo; use katana_primitives::transaction::{InvokeTx, Tx, TxWithHash}; - use katana_primitives::{chain_spec, Felt}; + use katana_primitives::Felt; use katana_provider::providers::db::DbProvider; use katana_provider::traits::block::{ BlockHashProvider, BlockNumberProvider, BlockProvider, BlockWriter, @@ -272,7 +272,7 @@ mod tests { fn blockchain_from_genesis_states() { let provider = DbProvider::new_ephemeral(); - let blockchain = Blockchain::new_with_chain(provider, &chain_spec::DEV) + let blockchain = Blockchain::new_with_chain(provider, &katana_chain_spec::DEV) .expect("failed to create blockchain from genesis block"); let state = blockchain.provider().latest().expect("failed to get latest state"); @@ -314,7 +314,7 @@ mod tests { { let db = katana_db::init_db(&db_path).expect("Failed to init database"); - let blockchain = Blockchain::new_with_db(db, &chain_spec::DEV) + let blockchain = Blockchain::new_with_db(db, &katana_chain_spec::DEV) .expect("Failed to create db-backed blockchain storage"); blockchain @@ -361,7 +361,7 @@ mod tests { { let db = katana_db::init_db(db_path).expect("Failed to init database"); - let blockchain = Blockchain::new_with_db(db, &chain_spec::DEV) + let blockchain = Blockchain::new_with_db(db, &katana_chain_spec::DEV) .expect("Failed to create db-backed blockchain storage"); // assert genesis state is correct diff --git a/crates/katana/executor/Cargo.toml b/crates/katana/executor/Cargo.toml index 785d18d3d4..60679b3046 100644 --- a/crates/katana/executor/Cargo.toml +++ b/crates/katana/executor/Cargo.toml @@ -20,11 +20,13 @@ blockifier = { git = "https://github.com/dojoengine/sequencer", tag = "v0.8.0-rc katana-cairo = { workspace = true, optional = true } [dev-dependencies] -alloy-primitives.workspace = true -anyhow.workspace = true katana-cairo.workspace = true +katana-chain-spec.workspace = true katana-provider = { workspace = true, features = [ "test-utils" ] } katana-rpc-types.workspace = true + +alloy-primitives.workspace = true +anyhow.workspace = true num-traits.workspace = true rstest.workspace = true rstest_reuse.workspace = true diff --git a/crates/katana/executor/tests/fixtures/mod.rs b/crates/katana/executor/tests/fixtures/mod.rs index 2442f961e1..30ea870bf8 100644 --- a/crates/katana/executor/tests/fixtures/mod.rs +++ b/crates/katana/executor/tests/fixtures/mod.rs @@ -1,13 +1,13 @@ pub mod transaction; use alloy_primitives::U256; +use katana_chain_spec::ChainSpec; use katana_executor::implementation::noop::NoopExecutorFactory; use katana_executor::{ExecutionFlags, ExecutorFactory}; use katana_primitives::block::{ Block, ExecutableBlock, FinalityStatus, GasPrices, PartialHeader, SealedBlockWithStatus, }; use katana_primitives::chain::ChainId; -use katana_primitives::chain_spec::{self, ChainSpec}; use katana_primitives::class::{CompiledClass, ContractClass}; use katana_primitives::contract::ContractAddress; use katana_primitives::da::L1DataAvailabilityMode; @@ -50,7 +50,7 @@ pub fn contract_class() -> (CompiledClass, ContractClass) { #[rstest::fixture] #[once] pub fn chain() -> ChainSpec { - let mut chain = chain_spec::DEV_UNALLOCATED.clone(); + let mut chain = katana_chain_spec::DEV_UNALLOCATED.clone(); // to generate the exact list of accounts as you would when you just run `katana` w/o // any additional flags diff --git a/crates/katana/executor/tests/fixtures/transaction.rs b/crates/katana/executor/tests/fixtures/transaction.rs index 6896239f96..f990af588f 100644 --- a/crates/katana/executor/tests/fixtures/transaction.rs +++ b/crates/katana/executor/tests/fixtures/transaction.rs @@ -1,5 +1,5 @@ +use katana_chain_spec::ChainSpec; use katana_primitives::chain::ChainId; -use katana_primitives::chain_spec::ChainSpec; use katana_primitives::contract::{ContractAddress, Nonce}; use katana_primitives::env::CfgEnv; use katana_primitives::genesis::allocation::GenesisAllocation; diff --git a/crates/katana/node/Cargo.toml b/crates/katana/node/Cargo.toml index aa47c61777..53963fa56f 100644 --- a/crates/katana/node/Cargo.toml +++ b/crates/katana/node/Cargo.toml @@ -6,6 +6,7 @@ repository.workspace = true version.workspace = true [dependencies] +katana-chain-spec.workspace = true katana-core.workspace = true katana-db.workspace = true katana-executor.workspace = true @@ -26,6 +27,7 @@ serde.workspace = true serde_json.workspace = true starknet.workspace = true thiserror.workspace = true +toml.workspace = true tower = { workspace = true, features = [ "full" ] } tower-http = { workspace = true, features = [ "full" ] } tracing.workspace = true diff --git a/crates/katana/node/src/config/mod.rs b/crates/katana/node/src/config/mod.rs index 045e30bdee..e2d32e51c7 100644 --- a/crates/katana/node/src/config/mod.rs +++ b/crates/katana/node/src/config/mod.rs @@ -11,11 +11,10 @@ use db::DbConfig; use dev::DevConfig; use execution::ExecutionConfig; use fork::ForkingConfig; +use katana_chain_spec::ChainSpec; use katana_core::service::messaging::MessagingConfig; -use katana_primitives::chain_spec::ChainSpec; use metrics::MetricsConfig; use rpc::RpcConfig; -use url::Url; /// Node configurations. /// @@ -48,9 +47,6 @@ pub struct Config { /// Development options. pub dev: DevConfig, - - /// Provider url for gas price oracle - pub l1_provider_url: Option, } /// Configurations related to block production. diff --git a/crates/katana/node/src/lib.rs b/crates/katana/node/src/lib.rs index bebddacae0..02df9cd816 100644 --- a/crates/katana/node/src/lib.rs +++ b/crates/katana/node/src/lib.rs @@ -17,16 +17,17 @@ use dojo_metrics::exporters::prometheus::PrometheusRecorder; use dojo_metrics::{Report, Server as MetricsServer}; use hyper::Method; use jsonrpsee::RpcModule; +use katana_chain_spec::SettlementLayer; use katana_core::backend::gas_oracle::L1GasOracle; use katana_core::backend::storage::Blockchain; use katana_core::backend::Backend; -use katana_core::constants::{ - DEFAULT_ETH_L1_DATA_GAS_PRICE, DEFAULT_ETH_L1_GAS_PRICE, DEFAULT_STRK_L1_DATA_GAS_PRICE, - DEFAULT_STRK_L1_GAS_PRICE, -}; use katana_core::env::BlockContextGenerator; use katana_core::service::block_producer::BlockProducer; use katana_db::mdbx::DbEnv; +use katana_executor::implementation::blockifier::blockifier::test_utils::{ + DEFAULT_ETH_L1_DATA_GAS_PRICE, DEFAULT_ETH_L1_GAS_PRICE, DEFAULT_STRK_L1_DATA_GAS_PRICE, + DEFAULT_STRK_L1_GAS_PRICE, +}; use katana_executor::implementation::blockifier::BlockifierFactory; use katana_executor::ExecutionFlags; use katana_pool::ordering::FiFo; @@ -140,11 +141,8 @@ impl Node { let rpc_handle = self.rpc_server.start(self.config.rpc.socket_addr()).await?; // --- start the gas oracle worker task - - if let Some(ref url) = self.config.l1_provider_url { - self.backend.gas_oracle.run_worker(self.task_manager.task_spawner()); - info!(%url, "Gas Price Oracle started."); - }; + self.backend.gas_oracle.run_worker(self.task_manager.task_spawner()); + info!(target: "node", "Gas price oracle worker started."); Ok(LaunchedNode { node: self, rpc: rpc_handle }) } @@ -205,9 +203,16 @@ pub async fn build(mut config: Config) -> Result { let gas_oracle = if let Some(fixed_prices) = &config.dev.fixed_gas_prices { // Use fixed gas prices if provided in the configuration L1GasOracle::fixed(fixed_prices.gas_price.clone(), fixed_prices.data_gas_price.clone()) - } else if let Some(url) = &config.l1_provider_url { - // Default to a sampled gas oracle using the given provider - L1GasOracle::sampled(url.clone()) + } else if let Some(settlement) = &config.chain.settlement { + match settlement { + SettlementLayer::Ethereum { rpc_url, .. } => { + // Default to a sampled gas oracle using the given provider + L1GasOracle::sampled(rpc_url.clone()) + } + SettlementLayer::Starknet { .. } => { + todo!("starknet gas oracle") + } + } } else { // Use default fixed gas prices if no url and if no fixed prices are provided L1GasOracle::fixed( diff --git a/crates/katana/primitives/src/contract.rs b/crates/katana/primitives/src/contract.rs index 71cdabc5de..da1aa89d77 100644 --- a/crates/katana/primitives/src/contract.rs +++ b/crates/katana/primitives/src/contract.rs @@ -22,6 +22,11 @@ pub type Nonce = Felt; pub struct ContractAddress(pub Felt); impl ContractAddress { + pub const ZERO: Self = Self(Felt::ZERO); + pub const ONE: Self = Self(Felt::ONE); + pub const TWO: Self = Self(Felt::TWO); + pub const THREE: Self = Self(Felt::THREE); + pub fn new(address: Felt) -> Self { ContractAddress(normalize_address(address)) } diff --git a/crates/katana/primitives/src/eth.rs b/crates/katana/primitives/src/eth.rs new file mode 100644 index 0000000000..3542fb34ec --- /dev/null +++ b/crates/katana/primitives/src/eth.rs @@ -0,0 +1 @@ +pub use alloy_primitives::{Address, ChainId}; diff --git a/crates/katana/primitives/src/lib.rs b/crates/katana/primitives/src/lib.rs index 23fddf3707..25662a8093 100644 --- a/crates/katana/primitives/src/lib.rs +++ b/crates/katana/primitives/src/lib.rs @@ -2,11 +2,11 @@ pub mod block; pub mod chain; -pub mod chain_spec; pub mod class; pub mod contract; pub mod da; pub mod env; +pub mod eth; pub mod event; pub mod fee; pub mod genesis; diff --git a/crates/katana/storage/provider/Cargo.toml b/crates/katana/storage/provider/Cargo.toml index b7a268b486..2903191d85 100644 --- a/crates/katana/storage/provider/Cargo.toml +++ b/crates/katana/storage/provider/Cargo.toml @@ -7,6 +7,7 @@ version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +katana-chain-spec.workspace = true katana-db = { workspace = true, features = [ "test-utils" ] } katana-primitives.workspace = true katana-trie.workspace = true @@ -32,7 +33,7 @@ serde_json = { version = "1.0.133" } default = [ "fork", "in-memory" ] fork = [ "dep:futures", "dep:starknet", "dep:tokio", "in-memory" ] in-memory = [ ] -test-utils = [ "dep:alloy-primitives"] +test-utils = [ "dep:alloy-primitives" ] [dev-dependencies] alloy-primitives.workspace = true diff --git a/crates/katana/storage/provider/src/test_utils.rs b/crates/katana/storage/provider/src/test_utils.rs index bf3fdeb113..7bd0f338c7 100644 --- a/crates/katana/storage/provider/src/test_utils.rs +++ b/crates/katana/storage/provider/src/test_utils.rs @@ -1,16 +1,16 @@ use std::sync::Arc; use alloy_primitives::U256; +use katana_chain_spec::ChainSpec; use katana_db::mdbx::test_utils; +use katana_primitives::address; use katana_primitives::block::{BlockHash, FinalityStatus}; -use katana_primitives::chain_spec::ChainSpec; use katana_primitives::contract::ContractAddress; use katana_primitives::genesis::allocation::{ DevGenesisAccount, GenesisAccountAlloc, GenesisAllocation, }; use katana_primitives::genesis::{Genesis, GenesisClass}; use katana_primitives::utils::class::parse_sierra_class; -use katana_primitives::{address, chain_spec}; use starknet::macros::felt; use crate::providers::db::DbProvider; @@ -41,7 +41,7 @@ fn initialize_test_provider(provider: &P) { /// This includes: /// - An account with simple `__execute__` function, deployed at address `0x1`. pub fn create_chain_for_testing() -> ChainSpec { - let mut chain = chain_spec::DEV_UNALLOCATED.clone(); + let mut chain = katana_chain_spec::DEV_UNALLOCATED.clone(); let class_hash = felt!("0x111"); let address = address!("0x1"); diff --git a/crates/torii/core/Cargo.toml b/crates/torii/core/Cargo.toml index b9c90fa17e..bec2fe9a90 100644 --- a/crates/torii/core/Cargo.toml +++ b/crates/torii/core/Cargo.toml @@ -23,7 +23,6 @@ dojo-world.workspace = true futures-channel = "0.3.0" futures-util.workspace = true hashlink.workspace = true -num-traits.workspace = true once_cell.workspace = true reqwest.workspace = true serde.workspace = true diff --git a/crates/torii/core/src/engine.rs b/crates/torii/core/src/engine.rs index f4279e6a5d..46d84cc57f 100644 --- a/crates/torii/core/src/engine.rs +++ b/crates/torii/core/src/engine.rs @@ -859,8 +859,10 @@ impl Engine

{ let task_identifier = match processor.event_key().as_str() { "StoreSetRecord" | "StoreUpdateRecord" | "StoreUpdateMember" | "StoreDelRecord" => { let mut hasher = DefaultHasher::new(); - event.keys[0].hash(&mut hasher); + // model selector event.keys[1].hash(&mut hasher); + // entity id + event.keys[2].hash(&mut hasher); hasher.finish() } _ => 0, diff --git a/crates/torii/core/src/processors/mod.rs b/crates/torii/core/src/processors/mod.rs index 4d4c5c637d..edbde5d3c3 100644 --- a/crates/torii/core/src/processors/mod.rs +++ b/crates/torii/core/src/processors/mod.rs @@ -25,9 +25,6 @@ pub mod store_update_record; pub mod upgrade_event; pub mod upgrade_model; -const MODEL_INDEX: usize = 0; -const ENTITY_ID_INDEX: usize = 1; - #[derive(Clone, Debug, Default)] pub struct EventProcessorConfig { pub historical_events: HashSet, diff --git a/crates/torii/core/src/processors/store_update_member.rs b/crates/torii/core/src/processors/store_update_member.rs index 2dd28735ff..cac9a2af8b 100644 --- a/crates/torii/core/src/processors/store_update_member.rs +++ b/crates/torii/core/src/processors/store_update_member.rs @@ -1,22 +1,18 @@ use anyhow::{Context, Error, Result}; use async_trait::async_trait; use dojo_types::schema::{Struct, Ty}; -use dojo_world::contracts::naming; +use dojo_world::contracts::abigen::world::Event as WorldEvent; use dojo_world::contracts::world::WorldContractReader; -use num_traits::ToPrimitive; use starknet::core::types::Event; use starknet::core::utils::get_selector_from_name; use starknet::providers::Provider; -use tracing::{info, warn}; +use tracing::info; use super::{EventProcessor, EventProcessorConfig}; -use crate::processors::{ENTITY_ID_INDEX, MODEL_INDEX}; use crate::sql::Sql; pub(crate) const LOG_TARGET: &str = "torii_core::processors::store_update_member"; -const MEMBER_INDEX: usize = 2; - #[derive(Default, Debug)] pub struct StoreUpdateMemberProcessor; @@ -29,16 +25,7 @@ where "StoreUpdateMember".to_string() } - fn validate(&self, event: &Event) -> bool { - if event.keys.len() > 1 { - info!( - target: LOG_TARGET, - event_key = %>::event_key(self), - invalid_keys = %>::event_keys_as_string(self, event), - "Invalid event keys." - ); - return false; - } + fn validate(&self, _event: &Event) -> bool { true } @@ -52,13 +39,27 @@ where event: &Event, _config: &EventProcessorConfig, ) -> Result<(), Error> { - let model_id = event.data[MODEL_INDEX]; - let entity_id = event.data[ENTITY_ID_INDEX]; - let member_selector = event.data[MEMBER_INDEX]; + // Torii version is coupled to the world version, so we can expect the event to be well + // formed. + let event = match WorldEvent::try_from(event).unwrap_or_else(|_| { + panic!( + "Expected {} event to be well formed.", + >::event_key(self) + ) + }) { + WorldEvent::StoreUpdateMember(e) => e, + _ => { + unreachable!() + } + }; + + let model_selector = event.selector; + let entity_id = event.entity_id; + let member_selector = event.member_selector; // If the model does not exist, silently ignore it. // This can happen if only specific namespaces are indexed. - let model = match db.model(model_id).await { + let model = match db.model(model_selector).await { Ok(m) => m, Err(e) => { if e.to_string().contains("no rows") { @@ -90,30 +91,12 @@ where "Store update member.", ); - let values_start = MEMBER_INDEX + 1; - let values_end: usize = - values_start + event.data[values_start].to_usize().context("invalid usize")?; - - // Skip the length to only get the values as they will be deserialized. - let mut values = event.data[values_start + 1..=values_end].to_vec(); - - let tag = naming::get_tag(&model.namespace, &model.name); - - if !db.does_entity_exist(tag.clone(), entity_id).await? { - warn!( - target: LOG_TARGET, - tag, - entity_id = format!("{:#x}", entity_id), - "Entity not found, must be set before updating a member.", - ); - - return Ok(()); - } - + let mut values = event.values.to_vec(); member.ty.deserialize(&mut values)?; - let wrapped_ty = Ty::Struct(Struct { name: schema.name(), children: vec![member] }); - db.set_entity(wrapped_ty, event_id, block_timestamp, entity_id, model_id, None).await?; + let wrapped_ty = Ty::Struct(Struct { name: schema.name(), children: vec![member] }); + db.set_entity(wrapped_ty, event_id, block_timestamp, entity_id, model_selector, None) + .await?; Ok(()) } } diff --git a/crates/torii/core/src/sql/mod.rs b/crates/torii/core/src/sql/mod.rs index 783fd246b4..cbcd9c3638 100644 --- a/crates/torii/core/src/sql/mod.rs +++ b/crates/torii/core/src/sql/mod.rs @@ -511,15 +511,6 @@ impl Sql { self.model_cache.model(&selector).await.map_err(|e| e.into()) } - pub async fn does_entity_exist(&self, model: String, key: Felt) -> Result { - let sql = format!("SELECT COUNT(*) FROM [{model}] WHERE id = ?"); - - let count: i64 = - sqlx::query_scalar(&sql).bind(format!("{:#x}", key)).fetch_one(&self.pool).await?; - - Ok(count > 0) - } - pub async fn entities(&self, model: String) -> Result>> { let query = sqlx::query_as::<_, (i32, String, String)>("SELECT * FROM ?").bind(model); let mut conn: PoolConnection = self.pool.acquire().await?; diff --git a/crates/torii/core/src/sql/test.rs b/crates/torii/core/src/sql/test.rs index 7d79f3ba4a..42a603c539 100644 --- a/crates/torii/core/src/sql/test.rs +++ b/crates/torii/core/src/sql/test.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::str::FromStr; use std::sync::Arc; -use cainome::cairo_serde::ContractAddress; +use cainome::cairo_serde::{ByteArray, CairoSerde, ContractAddress}; use dojo_test_utils::compiler::CompilerTestSetup; use dojo_test_utils::migration::copy_spawn_and_move_db; use dojo_utils::{TransactionExt, TransactionWaiter, TxnConfig}; @@ -205,6 +205,115 @@ async fn test_load_from_remote(sequencer: &RunnerCtx) { assert_eq!(keys, format!("{:#x}/", account.address())); } +#[ignore = "This test is being flaky and need to find why. Sometimes it fails, sometimes it passes."] +#[tokio::test(flavor = "multi_thread")] +#[katana_runner::test(accounts = 10, db_dir = copy_spawn_and_move_db().as_str())] +async fn test_load_from_remote_update(sequencer: &RunnerCtx) { + let setup = CompilerTestSetup::from_examples("../../dojo/core", "../../../examples/"); + let config = setup.build_test_config("spawn-and-move", Profile::DEV); + + let ws = scarb::ops::read_workspace(config.manifest_path(), &config).unwrap(); + + let account = sequencer.account(0); + let provider = Arc::new(JsonRpcClient::new(HttpTransport::new(sequencer.url()))); + + let world_local = ws.load_world_local().unwrap(); + let world_address = world_local.deterministic_world_address().unwrap(); + let actions_address = world_local + .get_contract_address_local(compute_selector_from_names("ns", "actions")) + .unwrap(); + + let world = WorldContract::new(world_address, &account); + + let res = world + .grant_writer(&compute_bytearray_hash("ns"), &ContractAddress(actions_address)) + .send_with_cfg(&TxnConfig::init_wait()) + .await + .unwrap(); + + TransactionWaiter::new(res.transaction_hash, &provider).await.unwrap(); + + // spawn + let res = account + .execute_v1(vec![Call { + to: actions_address, + selector: get_selector_from_name("spawn").unwrap(), + calldata: vec![], + }]) + .send_with_cfg(&TxnConfig::init_wait()) + .await + .unwrap(); + + TransactionWaiter::new(res.transaction_hash, &provider).await.unwrap(); + + // Set player config. + let res = account + .execute_v1(vec![Call { + to: actions_address, + selector: get_selector_from_name("set_player_config").unwrap(), + // Empty ByteArray. + calldata: vec![Felt::ZERO, Felt::ZERO, Felt::ZERO], + }]) + .send_with_cfg(&TxnConfig::init_wait()) + .await + .unwrap(); + + TransactionWaiter::new(res.transaction_hash, &provider).await.unwrap(); + + let name = ByteArray::from_string("mimi").unwrap(); + let res = account + .execute_v1(vec![Call { + to: actions_address, + selector: get_selector_from_name("update_player_config_name").unwrap(), + calldata: ByteArray::cairo_serialize(&name), + }]) + .send_with_cfg(&TxnConfig::init_wait()) + .await + .unwrap(); + + TransactionWaiter::new(res.transaction_hash, &provider).await.unwrap(); + + let world_reader = WorldContractReader::new(world_address, Arc::clone(&provider)); + + let tempfile = NamedTempFile::new().unwrap(); + let path = tempfile.path().to_string_lossy(); + let options = SqliteConnectOptions::from_str(&path).unwrap().create_if_missing(true); + let pool = SqlitePoolOptions::new().connect_with(options).await.unwrap(); + sqlx::migrate!("../migrations").run(&pool).await.unwrap(); + + let (shutdown_tx, _) = broadcast::channel(1); + let (mut executor, sender) = + Executor::new(pool.clone(), shutdown_tx.clone(), Arc::clone(&provider), 100).await.unwrap(); + tokio::spawn(async move { + executor.run().await.unwrap(); + }); + + let model_cache = Arc::new(ModelCache::new(pool.clone())); + let db = Sql::new( + pool.clone(), + sender.clone(), + &[Contract { address: world_reader.address, r#type: ContractType::WORLD }], + model_cache.clone(), + ) + .await + .unwrap(); + + let _ = bootstrap_engine(world_reader, db.clone(), Arc::clone(&provider)).await.unwrap(); + + let name: String = sqlx::query_scalar( + format!( + "SELECT name FROM [ns-PlayerConfig] WHERE internal_id = '{:#x}'", + poseidon_hash_many(&[account.address()]) + ) + .as_str(), + ) + .fetch_one(&pool) + .await + .unwrap(); + + assert_eq!(name, "mimi"); +} + #[ignore = "This test is being flaky and need to find why. Sometimes it fails, sometimes it passes."] #[tokio::test(flavor = "multi_thread")] #[katana_runner::test(accounts = 10, db_dir = copy_spawn_and_move_db().as_str())] diff --git a/crates/torii/types-test/Scarb.lock b/crates/torii/types-test/Scarb.lock index 2197d440fb..b298ae6561 100644 --- a/crates/torii/types-test/Scarb.lock +++ b/crates/torii/types-test/Scarb.lock @@ -14,7 +14,7 @@ version = "2.8.4" [[package]] name = "types_test" -version = "1.0.5" +version = "1.0.9" dependencies = [ "dojo", ] diff --git a/examples/simple/Scarb.lock b/examples/simple/Scarb.lock index d5dfe49119..09def408e1 100644 --- a/examples/simple/Scarb.lock +++ b/examples/simple/Scarb.lock @@ -3,7 +3,7 @@ version = 1 [[package]] name = "dojo" -version = "1.0.5" +version = "1.0.9" dependencies = [ "dojo_plugin", ] diff --git a/examples/spawn-and-move/Scarb.lock b/examples/spawn-and-move/Scarb.lock index 446c733bdf..aadeffbf77 100644 --- a/examples/spawn-and-move/Scarb.lock +++ b/examples/spawn-and-move/Scarb.lock @@ -31,7 +31,7 @@ dependencies = [ [[package]] name = "dojo_examples" -version = "1.0.8" +version = "1.0.9" dependencies = [ "armory", "bestiary", diff --git a/examples/spawn-and-move/manifest_dev.json b/examples/spawn-and-move/manifest_dev.json index 057ee7d64d..fec6bf6d76 100644 --- a/examples/spawn-and-move/manifest_dev.json +++ b/examples/spawn-and-move/manifest_dev.json @@ -1252,8 +1252,8 @@ }, "contracts": [ { - "address": "0x7e8a52c68b243d3a86a55c04ccec2edc760252d5952566d3af4001bd6cf38f3", - "class_hash": "0x636c2cf31b094097625cb5ada96f54ee9a3f7bc6d8cde00cc85e5ef0c622c8b", + "address": "0x6bfba78b8f4f42da469860a95291c2fad959c91ea747e2062de763ff4e62c4a", + "class_hash": "0x3e65d56b082c47e9e0952503424bcf6956ddd6d5faff40dd9900ea787d89c6", "abi": [ { "type": "impl", @@ -1399,6 +1399,18 @@ "outputs": [], "state_mutability": "external" }, + { + "type": "function", + "name": "update_player_config_name", + "inputs": [ + { + "name": "name", + "type": "core::byte_array::ByteArray" + } + ], + "outputs": [], + "state_mutability": "external" + }, { "type": "function", "name": "get_player_position", @@ -1585,6 +1597,7 @@ "spawn", "move", "set_player_config", + "update_player_config_name", "reset_player_config", "set_player_server_profile", "set_models", diff --git a/examples/spawn-and-move/src/actions.cairo b/examples/spawn-and-move/src/actions.cairo index 95c8d4a4d5..cd116a2255 100644 --- a/examples/spawn-and-move/src/actions.cairo +++ b/examples/spawn-and-move/src/actions.cairo @@ -5,6 +5,7 @@ pub trait IActions { fn spawn(ref self: T); fn move(ref self: T, direction: Direction); fn set_player_config(ref self: T, name: ByteArray); + fn update_player_config_name(ref self: T, name: ByteArray); fn get_player_position(self: @T) -> Position; fn reset_player_config(ref self: T); fn set_player_server_profile(ref self: T, server_id: u32, name: ByteArray); @@ -22,7 +23,7 @@ pub mod actions { Position, Moves, MovesValue, Direction, Vec2, PlayerConfig, PlayerItem, ServerProfile, }; use dojo_examples::utils::next_position; - use dojo::model::{ModelStorage, ModelValueStorage}; + use dojo::model::{ModelStorage, ModelValueStorage, Model}; use dojo::event::EventStorage; // Features can be used on modules, structs, trait and `use`. Not inside @@ -131,6 +132,17 @@ pub mod actions { world.write_model(@config); } + fn update_player_config_name(ref self: ContractState, name: ByteArray) { + let mut world = self.world_default(); + let player = get_caller_address(); + + // Don't need to read the model here, we directly overwrite the member "name". + world + .write_member( + Model::::ptr_from_keys(player), selector!("name"), name + ); + } + fn reset_player_config(ref self: ContractState) { let player = get_caller_address(); let mut world = self.world_default(); diff --git a/spawn-and-move-db.tar.gz b/spawn-and-move-db.tar.gz index 8f02dc10d4..db935cfd44 100644 Binary files a/spawn-and-move-db.tar.gz and b/spawn-and-move-db.tar.gz differ diff --git a/types-test-db.tar.gz b/types-test-db.tar.gz index a6b2f3cd82..9fb1a5c0d2 100644 Binary files a/types-test-db.tar.gz and b/types-test-db.tar.gz differ