From 3a2ed4bc511b416584b1930573886e21ac9f38fb Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Fri, 6 Oct 2023 17:18:54 +0530 Subject: [PATCH] split nft-utils and move module specific code into module and remaining into demo-rollup (#992) --- Cargo.lock | 14 +- Cargo.toml | 2 - examples/demo-rollup/Cargo.toml | 10 +- .../demo-rollup/src/bin/sov_nft_script.rs | 102 ++++++++- .../sov-nft-module/offchain_readme.md | 4 +- .../sov-nft-module/src/utils.rs | 91 +++++++- utils/nft-utils/Cargo.toml | 25 --- utils/nft-utils/src/lib.rs | 201 ------------------ 8 files changed, 199 insertions(+), 250 deletions(-) rename utils/nft-utils/src/main.rs => examples/demo-rollup/src/bin/sov_nft_script.rs (52%) delete mode 100644 utils/nft-utils/Cargo.toml delete mode 100644 utils/nft-utils/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a2ee93ed1..8e2b584dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5031,19 +5031,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" -[[package]] -name = "nft-utils" -version = "0.2.0" -dependencies = [ - "borsh", - "demo-stf", - "sov-modules-api", - "sov-nft-module", - "sov-rollup-interface", - "sov-sequencer", - "tokio", -] - [[package]] name = "nibble_vec" version = "0.1.0" @@ -8406,6 +8393,7 @@ dependencies = [ "sov-evm", "sov-modules-api", "sov-modules-stf-template", + "sov-nft-module", "sov-risc0-adapter", "sov-rollup-interface", "sov-sequencer", diff --git a/Cargo.toml b/Cargo.toml index 69a42aa1e..3347c1dfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,7 @@ members = [ "utils/zk-cycle-macros", "utils/zk-cycle-utils", - "utils/nft-utils", "utils/bashtestmd", - "utils/nft-utils", "module-system/sov-cli", "module-system/sov-modules-stf-template", diff --git a/examples/demo-rollup/Cargo.toml b/examples/demo-rollup/Cargo.toml index 2814df35e..a26449958 100644 --- a/examples/demo-rollup/Cargo.toml +++ b/examples/demo-rollup/Cargo.toml @@ -19,11 +19,12 @@ sov-stf-runner = { path = "../../full-node/sov-stf-runner" } sov-rollup-interface = { path = "../../rollup-interface" } sov-modules-stf-template = { path = "../../module-system/sov-modules-stf-template" } sov-modules-api = { path = "../../module-system/sov-modules-api" } +sov-nft-module = { path = "../../module-system/module-implementations/sov-nft-module" } demo-stf = { path = "../demo-stf" } methods = { path = "../demo-prover/methods" } +borsh = { workspace = true, features = ["bytes"]} async-trait = { workspace = true, optional = true } anyhow = { workspace = true, optional = true } -borsh = { workspace = true, features = ["bytes"], optional = true } jsonrpsee = { workspace = true, features = ["http-client", "server"], optional = true } serde = { workspace = true, features = ["derive"], optional = true } serde_json = { workspace = true, optional = true } @@ -78,7 +79,7 @@ native = ["anyhow", "jsonrpsee", "serde", "serde_json", "tracing", "tokio", "tra "demo-stf/native", "sov-modules-stf-template/native", "sov-risc0-adapter/native", "sov-modules-api/native", "sov-state/native", "sov-cli", "clap", "sov-celestia-adapter/native", "sov-db", "sov-sequencer", "sov-stf-runner/native", "sov-modules-api/native", "sov-rollup-interface/native"] -bench = ["native", "async-trait", "borsh", "hex"] +bench = ["native", "async-trait", "hex"] offchain = ["demo-stf/offchain"] [[bench]] @@ -100,6 +101,11 @@ name = "sov-cli" path = "src/sov-cli/main.rs" required-features = ["native"] +[[bin]] +name = "sov-nft-script" +path = "src/bin/sov_nft_script.rs" +required-features = ["native"] + [[bin]] name = "sov-demo-rollup" path = "src/main.rs" diff --git a/utils/nft-utils/src/main.rs b/examples/demo-rollup/src/bin/sov_nft_script.rs similarity index 52% rename from utils/nft-utils/src/main.rs rename to examples/demo-rollup/src/bin/sov_nft_script.rs index 15d01ae71..87037d22a 100644 --- a/utils/nft-utils/src/main.rs +++ b/examples/demo-rollup/src/bin/sov_nft_script.rs @@ -1,12 +1,18 @@ use std::thread; use std::time::Duration; -use nft_utils::{ - build_create_collection_transactions, build_mint_transactions, build_transfer_transactions, -}; +use borsh::ser::BorshSerialize; +use demo_stf::runtime::RuntimeCall; use sov_modules_api::default_context::DefaultContext; use sov_modules_api::default_signature::private_key::DefaultPrivateKey; -use sov_nft_module::utils::get_collection_address; +use sov_modules_api::transaction::Transaction; +use sov_modules_api::PrivateKey; +use sov_nft_module::utils::{ + get_collection_address, get_create_collection_message, get_mint_nft_message, + get_transfer_nft_message, +}; +use sov_nft_module::{CallMessage, CollectionAddress}; +use sov_rollup_interface::mocks::MockDaSpec; use sov_sequencer::utils::SimpleClient; const COLLECTION_1: &str = "Sovereign Squirrel Syndicate"; @@ -28,6 +34,94 @@ const PK3: [u8; 32] = [ 161, 208, 245, 116, 93, 84, 37, 87, 171, 44, 30, 239, ]; +pub fn build_transaction( + signer: &DefaultPrivateKey, + message: CallMessage, + nonce: u64, +) -> Transaction { + let runtime_encoded_message = RuntimeCall::::nft(message); + Transaction::::new_signed_tx( + signer, + runtime_encoded_message.try_to_vec().unwrap(), + nonce, + ) +} + +pub fn build_create_collection_transactions( + creator_pk: &DefaultPrivateKey, + start_nonce: &mut u64, + base_uri: &str, + collections: &[&str], +) -> Vec> { + collections + .iter() + .map(|&collection_name| { + let tx = build_transaction( + creator_pk, + get_create_collection_message( + &creator_pk.default_address(), + collection_name, + base_uri, + ), + *start_nonce, + ); + *start_nonce += 1; + tx + }) + .collect() +} + +/// Convenience and readability wrapper for build_mint_nft_transaction +pub fn build_mint_transactions( + creator_pk: &DefaultPrivateKey, + start_nonce: &mut u64, + collection: &str, + start_nft_id: &mut u64, + num: usize, + base_uri: &str, + owner_pk: &DefaultPrivateKey, +) -> Vec> { + (0..num) + .map(|_| { + let tx = build_transaction( + creator_pk, + get_mint_nft_message( + &creator_pk.default_address(), + collection, + *start_nft_id, + base_uri, + &owner_pk.default_address(), + ), + *start_nonce, + ); + *start_nft_id += 1; + *start_nonce += 1; + tx + }) + .collect() +} + +pub fn build_transfer_transactions( + signer: &DefaultPrivateKey, + start_nonce: &mut u64, + collection_address: &CollectionAddress, + nft_ids: Vec, +) -> Vec> { + nft_ids + .into_iter() + .map(|nft_id| { + let new_owner = DefaultPrivateKey::generate().default_address(); + let tx = build_transaction( + signer, + get_transfer_nft_message(collection_address, nft_id, &new_owner), + *start_nonce, + ); + *start_nonce += 1; + tx + }) + .collect() +} + #[tokio::main] async fn main() { let creator_pk = DefaultPrivateKey::try_from(&PK1[..]).unwrap(); diff --git a/module-system/module-implementations/sov-nft-module/offchain_readme.md b/module-system/module-implementations/sov-nft-module/offchain_readme.md index 3a4345ddf..63e091ef6 100644 --- a/module-system/module-implementations/sov-nft-module/offchain_readme.md +++ b/module-system/module-implementations/sov-nft-module/offchain_readme.md @@ -53,8 +53,8 @@ rm -rf demo_data; POSTGRES_CONNECTION_STRING="postgresql://username:password@loc * `--da-layer mock` is used to run an in-memory local DA layer * Run the NFT minting script ```bash -$ cd sovereign/utils/nft-utils -$ cargo run +$ cd sovereign/examples/demo-rollup +$ cargo run --bin sov-nft-script ``` * The above script creates 3 NFT collections, mints some NFTs to each collection * The tables can be explored by connecting to postgres and running sample queries from above \ No newline at end of file diff --git a/module-system/module-implementations/sov-nft-module/src/utils.rs b/module-system/module-implementations/sov-nft-module/src/utils.rs index 584772fab..bf174ee69 100644 --- a/module-system/module-implementations/sov-nft-module/src/utils.rs +++ b/module-system/module-implementations/sov-nft-module/src/utils.rs @@ -1,6 +1,6 @@ use sov_modules_api::digest::Digest; -use crate::CollectionAddress; +use crate::{CallMessage, CollectionAddress, UserAddress}; /// Derives token address from `collection_name`, `sender` pub fn get_collection_address( @@ -14,3 +14,92 @@ pub fn get_collection_address( let hash: [u8; 32] = hasher.finalize().into(); CollectionAddress::new(&C::Address::from(hash)) } + +fn get_collection_metadata_url(base_url: &str, collection_address: &str) -> String { + format!("{}/collection/{}", base_url, collection_address) +} + +fn get_nft_metadata_url(base_url: &str, collection_address: &str, nft_id: u64) -> String { + format!("{}/nft/{}/{}", base_url, collection_address, nft_id) +} + +/// Constructs a CallMessage to create a new NFT collection. +/// +/// # Arguments +/// +/// * `sender_address`: The address of the sender who will sign the transaction. +/// * `collection_name`: Name of the collection to be created. +/// * `base_uri`: Base URI to be used with the collection name +/// +/// # Returns +/// +/// Returns a CallMessage Variant which can then be serialized into a transaction +pub fn get_create_collection_message( + sender_address: &C::Address, + collection_name: &str, + base_uri: &str, +) -> CallMessage { + let collection_address = get_collection_address::(collection_name, sender_address.as_ref()); + + let collection_uri = get_collection_metadata_url(base_uri, &collection_address.to_string()); + CallMessage::::CreateCollection { + name: collection_name.to_string(), + collection_uri, + } +} + +/// Constructs a CallMessage to mint a new NFT. +/// +/// # Arguments +/// +/// * `signer`: The private key used for signing the transaction. +/// * `nonce`: The nonce to be used for the transaction. +/// * `collection_name`: The name of the collection to which the NFT belongs. +/// * `token_id`: The unique identifier for the new NFT. +/// * `owner`: The address of the user to whom the NFT will be minted. +/// +/// # Returns +/// +/// Returns a signed transaction for minting a new NFT to a specified user. +pub fn get_mint_nft_message( + sender_address: &C::Address, + collection_name: &str, + token_id: u64, + base_uri: &str, + owner: &C::Address, +) -> CallMessage { + let collection_address = get_collection_address::(collection_name, sender_address.as_ref()); + let token_uri = get_nft_metadata_url(base_uri, &collection_address.to_string(), token_id); + CallMessage::::MintNft { + collection_name: collection_name.to_string(), + token_uri, + token_id, + owner: UserAddress::new(owner), + frozen: false, + } +} + +/// Constructs a CallMessage to transfer an NFT to another user. +/// +/// # Arguments +/// +/// * `signer`: The private key used for signing the transaction. +/// * `nonce`: The nonce to be used for the transaction. +/// * `collection_address`: The address of the collection to which the NFT belongs. +/// * `token_id`: The unique identifier for the NFT being transferred. +/// * `to`: The address of the user to whom the NFT will be transferred. +/// +/// # Returns +/// +/// Returns a signed transaction for transferring an NFT to a specified user. +pub fn get_transfer_nft_message( + collection_address: &CollectionAddress, + token_id: u64, + to: &C::Address, +) -> CallMessage { + CallMessage::::TransferNft { + collection_address: collection_address.clone(), + token_id, + to: UserAddress::new(to), + } +} diff --git a/utils/nft-utils/Cargo.toml b/utils/nft-utils/Cargo.toml deleted file mode 100644 index 6375f1ee6..000000000 --- a/utils/nft-utils/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "nft-utils" -authors = { workspace = true } -description = "Utils for NFTs" -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } -version = { workspace = true } -readme = "README.md" -resolver = "2" -autotests = false -publish = false - -[dependencies] -borsh = { workspace = true } -tokio = { workspace = true } - -sov-nft-module = { path = "../../module-system/module-implementations/sov-nft-module", features = ["native"]} -sov-sequencer = { path = "../../full-node/sov-sequencer"} -demo-stf = {path = "../../examples/demo-stf"} -sov-rollup-interface = {path = "../../rollup-interface"} - -sov-modules-api = {path = "../../module-system/sov-modules-api", features = ["native"]} diff --git a/utils/nft-utils/src/lib.rs b/utils/nft-utils/src/lib.rs deleted file mode 100644 index bb280d598..000000000 --- a/utils/nft-utils/src/lib.rs +++ /dev/null @@ -1,201 +0,0 @@ -use borsh::ser::BorshSerialize; -use demo_stf::runtime::RuntimeCall; -use sov_modules_api::default_context::DefaultContext; -use sov_modules_api::default_signature::private_key::DefaultPrivateKey; -use sov_modules_api::transaction::Transaction; -use sov_modules_api::{Address, PrivateKey}; -use sov_nft_module::utils::get_collection_address; -use sov_nft_module::{CallMessage, CollectionAddress, UserAddress}; -use sov_rollup_interface::mocks::MockDaSpec; - -fn get_collection_metadata_url(base_url: &str, collection_address: &str) -> String { - format!("{}/collection/{}", base_url, collection_address) -} - -fn get_nft_metadata_url(base_url: &str, collection_address: &str, nft_id: u64) -> String { - format!("{}/nft/{}/{}", base_url, collection_address, nft_id) -} - -/// Convenience and readability wrapper for build_create_collection_transaction -pub fn build_create_collection_transactions( - creator_pk: &DefaultPrivateKey, - start_nonce: &mut u64, - base_uri: &str, - collections: &[&str], -) -> Vec> { - collections - .iter() - .map(|&collection_name| { - let tx = build_create_collection_transaction( - creator_pk, - *start_nonce, - collection_name, - base_uri, - ); - *start_nonce += 1; - tx - }) - .collect() -} - -/// Constructs a transaction to create a new NFT collection. -/// -/// # Arguments -/// -/// * `signer`: The private key used for signing the transaction. -/// * `nonce`: The nonce to be used for the transaction. -/// * `collection_name`: The name of the collection to be created. -/// -/// # Returns -/// -/// Returns a signed transaction for creating a new NFT collection. -pub fn build_create_collection_transaction( - signer: &DefaultPrivateKey, - nonce: u64, - collection_name: &str, - base_uri: &str, -) -> Transaction { - let collection_address = get_collection_address::( - collection_name, - signer.default_address().as_ref(), - ); - - let collection_uri = get_collection_metadata_url(base_uri, &collection_address.to_string()); - let create_collection_message = RuntimeCall::::nft( - CallMessage::::CreateCollection { - name: collection_name.to_string(), - collection_uri, - }, - ); - Transaction::::new_signed_tx( - signer, - create_collection_message.try_to_vec().unwrap(), - nonce, - ) -} - -/// Convenience and readability wrapper for build_mint_nft_transaction -pub fn build_mint_transactions( - creator_pk: &DefaultPrivateKey, - start_nonce: &mut u64, - collection: &str, - start_nft_id: &mut u64, - num: usize, - base_uri: &str, - owner_pk: &DefaultPrivateKey, -) -> Vec> { - (0..num) - .map(|_| { - let tx = build_mint_nft_transaction( - creator_pk, - *start_nonce, - collection, - *start_nft_id, - base_uri, - &owner_pk.default_address(), - ); - *start_nft_id += 1; - *start_nonce += 1; - tx - }) - .collect() -} - -/// Constructs a transaction to mint a new NFT. -/// -/// # Arguments -/// -/// * `signer`: The private key used for signing the transaction. -/// * `nonce`: The nonce to be used for the transaction. -/// * `collection_name`: The name of the collection to which the NFT belongs. -/// * `token_id`: The unique identifier for the new NFT. -/// * `owner`: The address of the user to whom the NFT will be minted. -/// -/// # Returns -/// -/// Returns a signed transaction for minting a new NFT to a specified user. -pub fn build_mint_nft_transaction( - signer: &DefaultPrivateKey, - nonce: u64, - collection_name: &str, - token_id: u64, - base_uri: &str, - owner: &Address, -) -> Transaction { - let collection_address = get_collection_address::( - collection_name, - signer.default_address().as_ref(), - ); - let token_uri = get_nft_metadata_url(base_uri, &collection_address.to_string(), token_id); - let mint_nft_message = - RuntimeCall::::nft(CallMessage::::MintNft { - collection_name: collection_name.to_string(), - token_uri, - token_id, - owner: UserAddress::new(owner), - frozen: false, - }); - Transaction::::new_signed_tx( - signer, - mint_nft_message.try_to_vec().unwrap(), - nonce, - ) -} - -/// Convenience and readability wrapper for build_transfer_nft_transaction -pub fn build_transfer_transactions( - signer: &DefaultPrivateKey, - start_nonce: &mut u64, - collection_address: &CollectionAddress, - nft_ids: Vec, -) -> Vec> { - nft_ids - .into_iter() - .map(|nft_id| { - let new_owner = DefaultPrivateKey::generate().default_address(); - let tx = build_transfer_nft_transaction( - signer, - *start_nonce, - collection_address, - nft_id, - &new_owner, - ); - *start_nonce += 1; - tx - }) - .collect() -} - -/// Constructs a transaction to transfer an NFT to another user. -/// -/// # Arguments -/// -/// * `signer`: The private key used for signing the transaction. -/// * `nonce`: The nonce to be used for the transaction. -/// * `collection_address`: The address of the collection to which the NFT belongs. -/// * `token_id`: The unique identifier for the NFT being transferred. -/// * `to`: The address of the user to whom the NFT will be transferred. -/// -/// # Returns -/// -/// Returns a signed transaction for transferring an NFT to a specified user. -pub fn build_transfer_nft_transaction( - signer: &DefaultPrivateKey, - nonce: u64, - collection_address: &CollectionAddress, - token_id: u64, - to: &Address, -) -> Transaction { - let transfer_message = RuntimeCall::::nft(CallMessage::< - DefaultContext, - >::TransferNft { - collection_address: collection_address.clone(), - token_id, - to: UserAddress::new(to), - }); - Transaction::::new_signed_tx( - signer, - transfer_message.try_to_vec().unwrap(), - nonce, - ) -}