Skip to content

Commit

Permalink
split nft-utils and move module specific code into module and remaini…
Browse files Browse the repository at this point in the history
…ng into demo-rollup (#992)
  • Loading branch information
dubbelosix authored Oct 6, 2023
1 parent 1a73dae commit 3a2ed4b
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 250 deletions.
14 changes: 1 addition & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 8 additions & 2 deletions examples/demo-rollup/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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]]
Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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<DefaultContext>,
nonce: u64,
) -> Transaction<DefaultContext> {
let runtime_encoded_message = RuntimeCall::<DefaultContext, MockDaSpec>::nft(message);
Transaction::<DefaultContext>::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<Transaction<DefaultContext>> {
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<Transaction<DefaultContext>> {
(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<DefaultContext>,
nft_ids: Vec<u64>,
) -> Vec<Transaction<DefaultContext>> {
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
91 changes: 90 additions & 1 deletion module-system/module-implementations/sov-nft-module/src/utils.rs
Original file line number Diff line number Diff line change
@@ -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<C: sov_modules_api::Context>(
Expand All @@ -14,3 +14,92 @@ pub fn get_collection_address<C: sov_modules_api::Context>(
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<C: sov_modules_api::Context>(
sender_address: &C::Address,
collection_name: &str,
base_uri: &str,
) -> CallMessage<C> {
let collection_address = get_collection_address::<C>(collection_name, sender_address.as_ref());

let collection_uri = get_collection_metadata_url(base_uri, &collection_address.to_string());
CallMessage::<C>::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<C: sov_modules_api::Context>(
sender_address: &C::Address,
collection_name: &str,
token_id: u64,
base_uri: &str,
owner: &C::Address,
) -> CallMessage<C> {
let collection_address = get_collection_address::<C>(collection_name, sender_address.as_ref());
let token_uri = get_nft_metadata_url(base_uri, &collection_address.to_string(), token_id);
CallMessage::<C>::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<C: sov_modules_api::Context>(
collection_address: &CollectionAddress<C>,
token_id: u64,
to: &C::Address,
) -> CallMessage<C> {
CallMessage::<C>::TransferNft {
collection_address: collection_address.clone(),
token_id,
to: UserAddress::new(to),
}
}
25 changes: 0 additions & 25 deletions utils/nft-utils/Cargo.toml

This file was deleted.

Loading

0 comments on commit 3a2ed4b

Please sign in to comment.