From 09a12ccc38b3905b74c0aea573220dce4ae7be74 Mon Sep 17 00:00:00 2001 From: "remy.baranx@gmail.com" Date: Wed, 13 Mar 2024 10:37:43 +0700 Subject: [PATCH 1/4] feat: add transaction_hash and block_number in the manifest file Store the `transaction_hash` and the `block_number` of the World deployment in the manifest file. --- bin/sozo/src/commands/dev.rs | 11 ++-- bin/sozo/src/ops/migration/mod.rs | 64 +++++++++++++-------- crates/dojo-world/src/manifest.rs | 19 +++++- crates/dojo-world/src/migration/mod.rs | 24 ++++++-- crates/dojo-world/src/migration/strategy.rs | 9 +-- 5 files changed, 83 insertions(+), 44 deletions(-) diff --git a/bin/sozo/src/commands/dev.rs b/bin/sozo/src/commands/dev.rs index 5f873abdc7..0cce9b04ec 100644 --- a/bin/sozo/src/commands/dev.rs +++ b/bin/sozo/src/commands/dev.rs @@ -143,11 +143,12 @@ where match migration::apply_diff(ws, &target_dir, diff, name.clone(), world_address, account, None) .await { - Ok(address) => { - config - .ui() - .print(format!("🎉 World at address {} updated!", format_args!("{:#x}", address))); - world_address = Some(address); + Ok(migration_output) => { + config.ui().print(format!( + "🎉 World at address {} updated!", + format_args!("{:#x}", migration_output.world_address) + )); + world_address = Some(migration_output.world_address); } Err(err) => { config.ui().error(err.to_string()); diff --git a/bin/sozo/src/ops/migration/mod.rs b/bin/sozo/src/ops/migration/mod.rs index 4149675aae..85270be078 100644 --- a/bin/sozo/src/ops/migration/mod.rs +++ b/bin/sozo/src/ops/migration/mod.rs @@ -44,6 +44,12 @@ use crate::commands::options::starknet::StarknetOptions; use crate::commands::options::transaction::TransactionOptions; use crate::commands::options::world::WorldOptions; +pub struct MigrationOutput { + pub world_address: FieldElement, + pub world_tx_hash: Option, + pub world_block_number: Option +} + pub async fn execute( ws: &Workspace<'_>, args: MigrateArgs, @@ -79,8 +85,8 @@ pub async fn execute( if total_diffs == 0 { ui.print("\n✨ No changes to be made. Remote World is already up to date!") } else { - // Mirate according to the diff. - let world_address = apply_diff( + // Migrate according to the diff. + let migration_output = apply_diff( ws, &target_dir, diff, @@ -96,7 +102,7 @@ pub async fn execute( local_manifest, remote_manifest, &manifest_dir, - world_address, + migration_output, &chain_id, ) .await?; @@ -110,14 +116,16 @@ async fn update_manifests_and_abis( local_manifest: BaseManifest, remote_manifest: Option, manifest_dir: &Utf8PathBuf, - world_address: FieldElement, + migration_output: MigrationOutput, chain_id: &str, ) -> Result<()> { let ui = ws.config().ui(); ui.print("\n✨ Updating manifests..."); let mut local_manifest: DeployedManifest = local_manifest.into(); - local_manifest.world.inner.address = Some(world_address); + local_manifest.world.inner.address = Some(migration_output.world_address); + local_manifest.world.inner.transaction_hash = migration_output.world_tx_hash; + local_manifest.world.inner.block_number = migration_output.world_block_number; let base_class_hash = match remote_manifest { Some(manifest) => *manifest.base.inner.class_hash(), @@ -126,7 +134,7 @@ async fn update_manifests_and_abis( local_manifest.contracts.iter_mut().for_each(|c| { let salt = generate_salt(&c.name); - c.inner.address = Some(get_contract_address(salt, base_class_hash, &[], world_address)); + c.inner.address = Some(get_contract_address(salt, base_class_hash, &[], migration_output.world_address)); }); // copy abi files from `abi/base` to `abi/deployments/{chain_id}` and update abi path in @@ -191,7 +199,7 @@ pub(crate) async fn apply_diff( world_address: Option, account: &SingleOwnerAccount, txn_config: Option, -) -> Result +) -> Result where P: Provider + Sync + Send + 'static, S: Signer + Sync + Send + 'static, @@ -201,15 +209,15 @@ where println!(" "); - let block_height = execute_strategy(ws, &strategy, account, txn_config) + let migration_output = execute_strategy(ws, &strategy, account, txn_config) .await .map_err(|e| anyhow!(e)) .with_context(|| "Problem trying to migrate.")?; - if let Some(block_height) = block_height { + if let Some(block_number) = migration_output.world_block_number { ui.print(format!( "\n🎉 Successfully migrated World on block #{} at address {}", - block_height, + block_number, bold_message(format!( "{:#x}", strategy.world_address().expect("world address must exist") @@ -225,7 +233,7 @@ where )); } - strategy.world_address() + Ok(migration_output) } pub(crate) async fn setup_env( @@ -357,19 +365,19 @@ fn prepare_migration( Ok(migration) } -// returns the Some(block number) at which migration world is deployed, returns none if world was -// not redeployed pub async fn execute_strategy( ws: &Workspace<'_>, strategy: &MigrationStrategy, migrator: &SingleOwnerAccount, txn_config: Option, -) -> Result> +) -> Result where P: Provider + Sync + Send + 'static, S: Signer + Sync + Send + 'static, { let ui = ws.config().ui(); + let mut world_tx_hash: Option = None; + let mut world_block_number: Option = None; match &strategy.base { Some(base) => { @@ -399,12 +407,19 @@ where ui.print_header("# World"); let calldata = vec![strategy.base.as_ref().unwrap().diff.local]; - deploy_contract(world, "world", calldata.clone(), migrator, &ui, &txn_config) - .await - .map_err(|e| { - ui.verbose(format!("{e:?}")); - anyhow!("Failed to deploy world: {e}") - })?; + let deploy_result = + deploy_contract(world, "world", calldata.clone(), migrator, &ui, &txn_config) + .await + .map_err(|e| { + ui.verbose(format!("{e:?}")); + anyhow!("Failed to deploy world: {e}") + })?; + + (world_tx_hash, world_block_number) = if let ContractDeploymentOutput::Output(deploy_result) = deploy_result { + (Some(deploy_result.transaction_hash), deploy_result.block_number) + } else { + (None, None) + }; ui.print_sub(format!("Contract address: {:#x}", world.contract_address)); @@ -454,10 +469,11 @@ where register_models(strategy, migrator, &ui, txn_config.clone()).await?; deploy_contracts(strategy, migrator, &ui, txn_config).await?; - // This gets current block numder if helpful - // let block_height = migrator.provider().block_number().await.ok(); - - Ok(None) + Ok(MigrationOutput { + world_address: strategy.world_address()?, + world_tx_hash, + world_block_number, + }) } enum ContractDeploymentOutput { diff --git a/crates/dojo-world/src/manifest.rs b/crates/dojo-world/src/manifest.rs index 2b9386a91f..8f43bfb816 100644 --- a/crates/dojo-world/src/manifest.rs +++ b/crates/dojo-world/src/manifest.rs @@ -161,6 +161,9 @@ pub struct Contract { pub abi: Option, #[serde_as(as = "Option")] pub address: Option, + #[serde_as(as = "Option")] + pub transaction_hash: Option, + pub block_number: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -174,7 +177,13 @@ pub struct BaseManifest { impl From> for Manifest { fn from(value: Manifest) -> Self { Manifest::new( - Contract { class_hash: value.inner.class_hash, abi: value.inner.abi, address: None }, + Contract { + class_hash: value.inner.class_hash, + abi: value.inner.abi, + address: None, + transaction_hash: None, + block_number: None, + }, value.name, ) } @@ -328,7 +337,13 @@ impl DeployedManifest { models, contracts, world: Manifest::new( - Contract { address: Some(world_address), class_hash: world_class_hash, abi: None }, + Contract { + address: Some(world_address), + class_hash: world_class_hash, + abi: None, + transaction_hash: None, + block_number: None, + }, WORLD_CONTRACT_NAME.into(), ), base: Manifest::new( diff --git a/crates/dojo-world/src/migration/mod.rs b/crates/dojo-world/src/migration/mod.rs index 38f4fb6953..feeca7372d 100644 --- a/crates/dojo-world/src/migration/mod.rs +++ b/crates/dojo-world/src/migration/mod.rs @@ -10,7 +10,7 @@ use starknet::accounts::{Account, AccountError, Call, ConnectedAccount, SingleOw use starknet::core::types::contract::{CompiledClass, SierraClass}; use starknet::core::types::{ BlockId, BlockTag, DeclareTransactionResult, FieldElement, FlattenedSierraClass, FunctionCall, - InvokeTransactionResult, StarknetError, + InvokeTransactionResult, MaybePendingTransactionReceipt, StarknetError, TransactionReceipt, }; use starknet::core::utils::{ get_contract_address, get_selector_from_name, CairoShortStringToFeltError, @@ -32,6 +32,7 @@ pub type DeclareOutput = DeclareTransactionResult; #[derive(Clone, Debug)] pub struct DeployOutput { pub transaction_hash: FieldElement, + pub block_number: Option, pub contract_address: FieldElement, pub declare: Option, } @@ -200,9 +201,10 @@ pub trait Deployable: Declarable + Sync { let InvokeTransactionResult { transaction_hash } = txn.send().await.map_err(MigrationError::Migrator)?; - TransactionWaiter::new(transaction_hash, account.provider()).await?; + let receipt = TransactionWaiter::new(transaction_hash, account.provider()).await?; + let block_number = get_block_number_from_receipt(receipt); - Ok(DeployOutput { transaction_hash, contract_address, declare }) + Ok(DeployOutput { transaction_hash, block_number, contract_address, declare }) } async fn deploy( @@ -264,9 +266,10 @@ pub trait Deployable: Declarable + Sync { let InvokeTransactionResult { transaction_hash } = txn.send().await.map_err(MigrationError::Migrator)?; - TransactionWaiter::new(transaction_hash, account.provider()).await?; + let receipt = TransactionWaiter::new(transaction_hash, account.provider()).await?; + let block_number = get_block_number_from_receipt(receipt); - Ok(DeployOutput { transaction_hash, contract_address, declare }) + Ok(DeployOutput { transaction_hash, block_number, contract_address, declare }) } fn salt(&self) -> FieldElement; @@ -298,3 +301,14 @@ fn get_compiled_class_hash(artifact_path: &PathBuf) -> Result { let compiled_class: CompiledClass = serde_json::from_str(&res)?; Ok(compiled_class.class_hash()?) } + +fn get_block_number_from_receipt(receipt: MaybePendingTransactionReceipt) -> Option { + match receipt { + MaybePendingTransactionReceipt::Receipt(receipt) => match receipt { + TransactionReceipt::Deploy(r) => Some(r.block_number), + TransactionReceipt::Invoke(r) => Some(r.block_number), + _ => None, + }, + MaybePendingTransactionReceipt::PendingReceipt(_receipt) => None, + } +} diff --git a/crates/dojo-world/src/migration/strategy.rs b/crates/dojo-world/src/migration/strategy.rs index 9ce9cf05dd..0873e962de 100644 --- a/crates/dojo-world/src/migration/strategy.rs +++ b/crates/dojo-world/src/migration/strategy.rs @@ -11,14 +11,7 @@ use starknet_crypto::{poseidon_hash_many, poseidon_hash_single}; use super::class::{ClassDiff, ClassMigration}; use super::contract::{ContractDiff, ContractMigration}; use super::world::WorldDiff; -use super::{DeployOutput, MigrationType, RegisterOutput}; - -#[derive(Debug)] -pub struct MigrationOutput { - pub world: Option, - pub contracts: Vec, - pub models: Option, -} +use super::MigrationType; #[derive(Debug)] pub struct MigrationStrategy { From 0e8f0dd96afb3ae9f7d39d3fd07cfb52a1e99727 Mon Sep 17 00:00:00 2001 From: "remy.baranx@gmail.com" Date: Wed, 13 Mar 2024 11:24:42 +0700 Subject: [PATCH 2/4] merge transaction_hash and block_number from previous deployed manifest if exists --- bin/sozo/src/ops/migration/mod.rs | 29 ++++++++++++++++++++--------- crates/dojo-world/src/manifest.rs | 5 +++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/bin/sozo/src/ops/migration/mod.rs b/bin/sozo/src/ops/migration/mod.rs index 85270be078..6221c38367 100644 --- a/bin/sozo/src/ops/migration/mod.rs +++ b/bin/sozo/src/ops/migration/mod.rs @@ -122,10 +122,27 @@ async fn update_manifests_and_abis( let ui = ws.config().ui(); ui.print("\n✨ Updating manifests..."); + let deployed_path = manifest_dir + .join(MANIFESTS_DIR) + .join(DEPLOYMENTS_DIR) + .join(chain_id) + .with_extension("toml"); + let mut local_manifest: DeployedManifest = local_manifest.into(); + + if deployed_path.exists() { + let previous_manifest = DeployedManifest::load_from_path(&deployed_path)?; + local_manifest.merge_from_previous(previous_manifest); + }; + local_manifest.world.inner.address = Some(migration_output.world_address); - local_manifest.world.inner.transaction_hash = migration_output.world_tx_hash; - local_manifest.world.inner.block_number = migration_output.world_block_number; + + if migration_output.world_tx_hash.is_some() { + local_manifest.world.inner.transaction_hash = migration_output.world_tx_hash; + } + if migration_output.world_block_number.is_some() { + local_manifest.world.inner.block_number = migration_output.world_block_number; + } let base_class_hash = match remote_manifest { Some(manifest) => *manifest.base.inner.class_hash(), @@ -141,13 +158,7 @@ async fn update_manifests_and_abis( // local_manifest update_manifest_abis(&mut local_manifest, manifest_dir, chain_id).await; - local_manifest.write_to_path( - &manifest_dir - .join(MANIFESTS_DIR) - .join(DEPLOYMENTS_DIR) - .join(chain_id) - .with_extension("toml"), - )?; + local_manifest.write_to_path(&deployed_path)?; ui.print("\n✨ Done."); Ok(()) diff --git a/crates/dojo-world/src/manifest.rs b/crates/dojo-world/src/manifest.rs index 8f43bfb816..d0ef450876 100644 --- a/crates/dojo-world/src/manifest.rs +++ b/crates/dojo-world/src/manifest.rs @@ -295,6 +295,11 @@ impl DeployedManifest { Ok(manifest) } + pub fn merge_from_previous(&mut self, previous: DeployedManifest) { + self.world.inner.transaction_hash = previous.world.inner.transaction_hash; + self.world.inner.block_number = previous.world.inner.block_number; + } + pub fn write_to_path(&self, path: &Utf8PathBuf) -> Result<()> { fs::create_dir_all(path.parent().unwrap())?; From 0b510c077b3537f5bdb3ed75172982c60507849c Mon Sep 17 00:00:00 2001 From: "remy.baranx@gmail.com" Date: Wed, 13 Mar 2024 12:01:15 +0700 Subject: [PATCH 3/4] update spawn-and-move example --- examples/spawn-and-move/manifests/deployments/KATANA.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/spawn-and-move/manifests/deployments/KATANA.toml b/examples/spawn-and-move/manifests/deployments/KATANA.toml index 635293b441..60bbdf7004 100644 --- a/examples/spawn-and-move/manifests/deployments/KATANA.toml +++ b/examples/spawn-and-move/manifests/deployments/KATANA.toml @@ -2,6 +2,8 @@ kind = "Contract" class_hash = "0x799bc4e9da10bfb3dd88e6f223c9cfbf7745435cd14f5d69675ea448e578cd" address = "0x1385f25d20a724edc9c7b3bd9636c59af64cbaf9fcd12f33b3af96b2452f295" +transaction_hash = "0x6afefdcc49b3563a4f3657900ba71e9f9356861b15b942a73f2018f046a1048" +block_number = 3 name = "dojo::world::world" [base] From 5ab63005684caa3967c73ef07cc29cb6316fd817 Mon Sep 17 00:00:00 2001 From: "remy.baranx@gmail.com" Date: Wed, 13 Mar 2024 12:14:33 +0700 Subject: [PATCH 4/4] fix fmt issues --- bin/sozo/src/ops/migration/mod.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/bin/sozo/src/ops/migration/mod.rs b/bin/sozo/src/ops/migration/mod.rs index 6221c38367..4c2c3b7cdd 100644 --- a/bin/sozo/src/ops/migration/mod.rs +++ b/bin/sozo/src/ops/migration/mod.rs @@ -47,7 +47,7 @@ use crate::commands::options::world::WorldOptions; pub struct MigrationOutput { pub world_address: FieldElement, pub world_tx_hash: Option, - pub world_block_number: Option + pub world_block_number: Option, } pub async fn execute( @@ -123,10 +123,10 @@ async fn update_manifests_and_abis( ui.print("\n✨ Updating manifests..."); let deployed_path = manifest_dir - .join(MANIFESTS_DIR) - .join(DEPLOYMENTS_DIR) - .join(chain_id) - .with_extension("toml"); + .join(MANIFESTS_DIR) + .join(DEPLOYMENTS_DIR) + .join(chain_id) + .with_extension("toml"); let mut local_manifest: DeployedManifest = local_manifest.into(); @@ -151,7 +151,8 @@ async fn update_manifests_and_abis( local_manifest.contracts.iter_mut().for_each(|c| { let salt = generate_salt(&c.name); - c.inner.address = Some(get_contract_address(salt, base_class_hash, &[], migration_output.world_address)); + c.inner.address = + Some(get_contract_address(salt, base_class_hash, &[], migration_output.world_address)); }); // copy abi files from `abi/base` to `abi/deployments/{chain_id}` and update abi path in @@ -426,11 +427,12 @@ where anyhow!("Failed to deploy world: {e}") })?; - (world_tx_hash, world_block_number) = if let ContractDeploymentOutput::Output(deploy_result) = deploy_result { - (Some(deploy_result.transaction_hash), deploy_result.block_number) - } else { - (None, None) - }; + (world_tx_hash, world_block_number) = + if let ContractDeploymentOutput::Output(deploy_result) = deploy_result { + (Some(deploy_result.transaction_hash), deploy_result.block_number) + } else { + (None, None) + }; ui.print_sub(format!("Contract address: {:#x}", world.contract_address));