- 01 Jul 2024
- 5 Minutes to read
- DarkLight
- PDF
Non-Fungible Resource Creation
- Updated on 01 Jul 2024
- 5 Minutes to read
- DarkLight
- PDF
It is possible to create non-fungible resources with transactions, but it is quite hard to create them manually - this is because a non-fungible resource includes a Scrypto SBOR Schema for the non-fungible data.
Instead, it’s better to build the transaction with the Rust Manifest Builder, and then submit it using the developer console.
Building the Transfer Manifest with the Rust Manifest Builder
First, set-up a Rust/Scrypto project as in the Rust Manifest Builder docs.
The below test can be run to output a manifest to ./transaction_manifest/create_non_fungible.rtm
with the CREATE_NON_FUNGIBLE_RESOURCE_WITH_INITIAL_SUPPLY
command.
You can tweak lots of the parameters, including renaming/restructuring MyNonFungibleDataType
.
This code uses .lock_fee_from_faucet()
to lock the standard test fee from the system faucet in Stokenet. Learn more about Lock Fees here.
use scrypto::prelude::*;
use scrypto_test::{ prelude::*, utils::dump_manifest_to_file_system };
// The top level struct must derive NonFungibleData.
// Note that marking top-level fields as `#[mutable]` means that the data under
// that field can be updated by the `resource_manager.update_non_fungible_data(...)` method.
//
// All types referenced directly/indirectly also need to derive ScryptoSbor.
// To work with the Manifest Builder, we recommend all types also derive ManifestSbor.
#[derive(ScryptoSbor, NonFungibleData, ManifestSbor)]
struct MyNonFungibleDataType {
pub name: String,
pub description: String,
#[mutable]
pub key_image_url: Url,
}
#[test]
fn create_nf_resource() {
let network = NetworkDefinition::stokenet();
let manifest_builder = ManifestBuilder::new()
.lock_fee_from_faucet()
.create_non_fungible_resource(
OwnerRole::None,
NonFungibleIdType::Integer,
true,
NonFungibleResourceRoles::default(),
metadata!(
init {
"name" => "Example NF", locked;
}
),
// Change the below Some expression to None::<IndexMap<NonFungibleLocalId, MyNonFungibleDataType>>,
// To output CREATE_NON_FUNGIBLE_RESOURCE (without initial supply)
Some(
indexmap! {
NonFungibleLocalId::integer(1) => MyNonFungibleDataType {
name: "hello world".to_owned(),
description: "lorem ipsum".to_owned(),
key_image_url: Url::of("https://assets-global.website-files.com/618962e5f285fb3c879d82ca/61b8f414d213fd7349b654b9_icon-DEX.svg"),
},
}
)
);
dump_manifest_to_file_system(
manifest_builder.object_names(),
&manifest_builder.build(),
"./transaction_manifest",
Some("create_non_fungible"),
&network
).err();
}
Example manifest output is as follows. Note that because it is automatically generated, it doesn’t use the manifest type aliases which are typically used for manual creation.
CALL_METHOD
Address("component_tdx_2_1cptxxxxxxxxxfaucetxxxxxxxxx000527798379xxxxxxxxxyulkzl")
"lock_fee"
Decimal("5000")
;
CREATE_NON_FUNGIBLE_RESOURCE_WITH_INITIAL_SUPPLY
Enum<0u8>()
Enum<1u8>()
true
Enum<0u8>(
Enum<0u8>(
Tuple(
Array<Enum>(
Enum<14u8>(
Array<Enum>(
Enum<0u8>(
12u8
),
Enum<0u8>(
12u8
),
Enum<0u8>(
198u8
)
)
)
),
Array<Tuple>(
Tuple(
Enum<1u8>(
"MyNonFungibleDataType"
),
Enum<1u8>(
Enum<0u8>(
Array<String>(
"name",
"description",
"key_image_url"
)
)
)
)
),
Array<Enum>(
Enum<0u8>()
)
)
),
Enum<1u8>(
0u64
),
Array<String>()
)
Map<NonFungibleLocalId, Tuple>(
NonFungibleLocalId("#1#") => Tuple(
Tuple(
"hello world",
"lorem ipsum",
"https://assets-global.website-files.com/618962e5f285fb3c879d82ca/61b8f414d213fd7349b654b9_icon-DEX.svg"
)
)
)
Tuple(
Enum<0u8>(),
Enum<0u8>(),
Enum<0u8>(),
Enum<0u8>(),
Enum<0u8>(),
Enum<0u8>(),
Enum<0u8>()
)
Tuple(
Map<String, Tuple>(
"name" => Tuple(
Enum<1u8>(
Enum<0u8>(
"Example NF"
)
),
true
)
),
Map<String, Enum>()
)
Enum<0u8>()
;