diff --git a/crates/dojo-world/src/metadata.rs b/crates/dojo-world/src/metadata.rs index 2d50f5fa70..c432d09186 100644 --- a/crates/dojo-world/src/metadata.rs +++ b/crates/dojo-world/src/metadata.rs @@ -109,17 +109,31 @@ pub fn dojo_metadata_from_workspace(ws: &Workspace<'_>) -> DojoMetadata { if let Ok(manifest) = BaseManifest::load_from_path(&manifest_dir.join(BASE_DIR)) { for model in manifest.models { let name = model.name.to_string(); - dojo_metadata.artifacts.insert( + dojo_metadata.resources_artifacts.insert( name.clone(), - build_artifact_from_name(&sources_dir, &abis_dir.join("models"), &name), + ResourceMetadata { + name: name.clone(), + artifacts: build_artifact_from_name( + &sources_dir, + &abis_dir.join("models"), + &name, + ), + }, ); } for contract in manifest.contracts { let name = contract.name.to_string(); - dojo_metadata.artifacts.insert( + dojo_metadata.resources_artifacts.insert( name.clone(), - build_artifact_from_name(&sources_dir, &abis_dir.join("contracts"), &name), + ResourceMetadata { + name: name.clone(), + artifacts: build_artifact_from_name( + &sources_dir, + &abis_dir.join("contracts"), + &name, + ), + }, ); } } @@ -135,12 +149,19 @@ pub struct ProjectMetadata { pub env: Option, } +/// Metadata for a user defined resource (models, contracts). +#[derive(Default, Serialize, Deserialize, Debug, Clone)] +pub struct ResourceMetadata { + pub name: String, + pub artifacts: ArtifactMetadata, +} + /// Metadata collected from the project configuration and the Dojo workspace #[derive(Default, Deserialize, Debug, Clone)] pub struct DojoMetadata { pub world: WorldMetadata, pub env: Option, - pub artifacts: HashMap, + pub resources_artifacts: HashMap, } #[derive(Debug)] @@ -342,6 +363,34 @@ impl ArtifactMetadata { } } +impl ResourceMetadata { + pub async fn upload(&self) -> Result { + let mut meta = self.clone(); + let client = + IpfsClient::from_str(IPFS_CLIENT_URL)?.with_credentials(IPFS_USERNAME, IPFS_PASSWORD); + + if let Some(Uri::File(abi)) = &self.artifacts.abi { + let abi_data = std::fs::read(abi)?; + let reader = Cursor::new(abi_data); + let response = client.add(reader).await?; + meta.artifacts.abi = Some(Uri::Ipfs(format!("ipfs://{}", response.hash))) + }; + + if let Some(Uri::File(source)) = &self.artifacts.source { + let source_data = std::fs::read(source)?; + let reader = Cursor::new(source_data); + let response = client.add(reader).await?; + meta.artifacts.source = Some(Uri::Ipfs(format!("ipfs://{}", response.hash))) + }; + + let serialized = json!(meta).to_string(); + let reader = Cursor::new(serialized); + let response = client.add(reader).await?; + + Ok(response.hash) + } +} + impl DojoMetadata { pub fn env(&self) -> Option<&Environment> { self.env.as_ref() diff --git a/crates/dojo-world/src/metadata_test.rs b/crates/dojo-world/src/metadata_test.rs index 64d93723c4..412230164c 100644 --- a/crates/dojo-world/src/metadata_test.rs +++ b/crates/dojo-world/src/metadata_test.rs @@ -174,13 +174,18 @@ async fn get_full_dojo_metadata_from_workspace() { dbg!(&artifacts); for (abi_subdir, name) in artifacts { - let artifact = dojo_metadata.artifacts.get(&name); - assert!(artifact.is_some(), "bad artifact for {}", name); - let artifact = artifact.unwrap(); + let resource = dojo_metadata.resources_artifacts.get(&name); + assert!(resource.is_some(), "bad resource metadata for {}", name); + let resource = resource.unwrap(); let sanitized_name = name.replace("::", "_"); - check_artifact(artifact.clone(), sanitized_name, &abis_dir.join(abi_subdir), &sources_dir); + check_artifact( + resource.artifacts.clone(), + sanitized_name, + &abis_dir.join(abi_subdir), + &sources_dir, + ); } } diff --git a/crates/sozo/ops/src/migration/migrate.rs b/crates/sozo/ops/src/migration/migrate.rs index 044479a5d6..831fd22c48 100644 --- a/crates/sozo/ops/src/migration/migrate.rs +++ b/crates/sozo/ops/src/migration/migrate.rs @@ -9,7 +9,7 @@ use dojo_world::manifest::{ AbiFormat, BaseManifest, DeploymentManifest, DojoContract, DojoModel, Manifest, ManifestMethods, WorldContract as ManifestWorldContract, WorldMetadata, }; -use dojo_world::metadata::{dojo_metadata_from_workspace, ArtifactMetadata}; +use dojo_world::metadata::{dojo_metadata_from_workspace, ResourceMetadata}; use dojo_world::migration::class::ClassMigration; use dojo_world::migration::contract::ContractMigration; use dojo_world::migration::strategy::{generate_salt, prepare_for_migration, MigrationStrategy}; @@ -255,13 +255,12 @@ where /// on success. async fn upload_on_ipfs_and_create_resource( ui: &Ui, - element_name: String, resource_id: FieldElement, - artifact: ArtifactMetadata, + metadata: ResourceMetadata, ) -> Result { - match artifact.upload().await { + match metadata.upload().await { Ok(hash) => { - ui.print_sub(format!("{}: ipfs://{}", element_name, hash)); + ui.print_sub(format!("{}: ipfs://{}", metadata.name, hash)); create_resource_metadata(resource_id, hash) } Err(_) => Err(anyhow!("Failed to upload IPFS resource.")), @@ -330,10 +329,9 @@ where // models if !migration_output.models.is_empty() { for model_name in migration_output.models { - if let Some(m) = dojo_metadata.artifacts.get(&model_name) { + if let Some(m) = dojo_metadata.resources_artifacts.get(&model_name) { ipfs.push(upload_on_ipfs_and_create_resource( &ui, - model_name.clone(), get_selector_from_name(&model_name).expect("ASCII model name"), m.clone(), )); @@ -346,10 +344,9 @@ where if !migrated_contracts.is_empty() { for contract in migrated_contracts { - if let Some(m) = dojo_metadata.artifacts.get(&contract.name) { + if let Some(m) = dojo_metadata.resources_artifacts.get(&contract.name) { ipfs.push(upload_on_ipfs_and_create_resource( &ui, - contract.name.clone(), contract.contract_address, m.clone(), )); diff --git a/crates/sozo/ops/src/tests/migration.rs b/crates/sozo/ops/src/tests/migration.rs index 708724a4c8..1f82982bf7 100644 --- a/crates/sozo/ops/src/tests/migration.rs +++ b/crates/sozo/ops/src/tests/migration.rs @@ -484,19 +484,19 @@ async fn check_artifact_metadata( ) { let resource = world_reader.metadata(&resource_id).call().await.unwrap(); - let expected_artifact = dojo_metadata.artifacts.get(element_name); + let expected_resource = dojo_metadata.resources_artifacts.get(element_name); assert!( - expected_artifact.is_some(), + expected_resource.is_some(), "Unable to find local artifact metadata for {}", element_name ); - let expected_artifact = expected_artifact.unwrap(); + let expected_resource = expected_resource.unwrap(); check_ipfs_metadata( client, element_name, &resource.metadata_uri.to_string().unwrap(), - expected_artifact, + &expected_resource.artifacts, ) .await; } diff --git a/crates/torii/core/src/processors/metadata_update.rs b/crates/torii/core/src/processors/metadata_update.rs index 07859743b2..ed2432f712 100644 --- a/crates/torii/core/src/processors/metadata_update.rs +++ b/crates/torii/core/src/processors/metadata_update.rs @@ -4,11 +4,11 @@ use anyhow::{Error, Result}; use async_trait::async_trait; use base64::engine::general_purpose; use base64::Engine as _; +use cainome::cairo_serde::{ByteArray, CairoSerde}; use dojo_world::contracts::world::WorldContractReader; use dojo_world::metadata::{Uri, WorldMetadata}; use reqwest::Client; use starknet::core::types::{Event, MaybePendingTransactionReceipt}; -use starknet::core::utils::parse_cairo_short_string; use starknet::providers::Provider; use starknet_crypto::FieldElement; use tokio_util::bytes::Bytes; @@ -58,18 +58,7 @@ where event: &Event, ) -> Result<(), Error> { let resource = &event.data[0]; - let uri_len: u8 = event.data[1].try_into().unwrap(); - - let uri_str = if uri_len > 0 { - event.data[2..=uri_len as usize + 1] - .iter() - .map(parse_cairo_short_string) - .collect::, _>>()? - .concat() - } else { - "".to_string() - }; - + let uri_str = ByteArray::cairo_deserialize(&event.data, 1)?.to_string()?; info!( target: LOG_TARGET, resource = %format!("{:#x}", resource),