Skip to main content

Openbrush Wizard

Use the Wizard to generate generic PSP34 code

To create a smart contract which follows PSP34 standard use Openbrush Wizard:

  1. Open Openbrush.io website and go to bottom of the page.
  2. Select PSP34.
  3. Select the version to match the rest of the tutorial. Check What will be used in the opening chapter.
  4. Name your contract. In this tutorial we will use Shiden34.
  5. Add your symbol name. In this tutorial we will use SH34.
  6. Select extensions: Metadata, Mintable, Enumerable.
  7. Under Security pick Ownable.
  8. Copy lib.rs and Cargo.toml.
note

At the time of writing this tutorial, Openbrush wizard does not properly generate contract. Use code from this tutorial.

Your lib.rs file should look like this:

#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[openbrush::implementation(PSP34, Ownable, PSP34Enumerable, PSP34Metadata, PSP34Mintable)]
#[openbrush::contract]
pub mod shiden34 {
use openbrush::traits::Storage;

#[ink(storage)]
#[derive(Default, Storage)]
pub struct Shiden34 {
#[storage_field]
psp34: psp34::Data,
#[storage_field]
ownable: ownable::Data,
#[storage_field]
metadata: metadata::Data,
#[storage_field]
enumerable: enumerable::Data,
}

#[overrider(PSP34Mintable)]
#[openbrush::modifiers(only_owner)]
fn mint(&mut self, account: AccountId, id: Id) -> Result<(), PSP34Error> {
psp34::InternalImpl::_mint_to(self, account, id)
}

impl Shiden34 {
#[ink(constructor)]
pub fn new() -> Self {
let mut _instance = Self::default();
ownable::Internal::_init_with_owner(&mut _instance, Self::env().caller());
psp34::Internal::_mint_to(&mut _instance, Self::env().caller(), Id::U8(1))
.expect("Can mint");
let collection_id = psp34::PSP34Impl::collection_id(&_instance);
metadata::Internal::_set_attribute(
&mut _instance,
collection_id.clone(),
String::from("name"),
String::from("Shiden34"),
);
metadata::Internal::_set_attribute(
&mut _instance,
collection_id,
String::from("symbol"),
String::from("SH34"),
);
_instance
}
}
}

Your Cargo.toml should now look like this:

[package]
name = "shiden34"
version = "3.1.0"
authors = ["Astar builder"]
edition = "2021"

[dependencies]
ink = { version = "4.2.1", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }
openbrush = { tag = "v4.0.0-beta", git = "https://github.com/Brushfam/openbrush-contracts", default-features = false, features = ["psp34", "ownable"] }

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
"scale/std",
"scale-info/std",

"openbrush/std",
]
ink-as-dependency = []

Make the folder structure or use Swanky-cli like this:

.
└── contracts
└── shiden34
├── Cargo.toml
└── lib.rs

Add another Cargo.toml with workspace definition to your project's root folder:

[workspace]
members = [
"contracts/**",
]

exclude = [
]

And your folder structure will look like:

.
├── Cargo.toml
└── contracts
└── shiden34
├── Cargo.toml
└── lib.rs

You are now ready to check if all is set. Run in root project folder:

cargo check

Examine Openbrush Traits

Let's examine what we have inside module shiden34 (lib.rs) so far:

  • Defined structure Shiden34 for contract storage.
  • Implemented constructor new() for Shiden34 structure.
  • Implemented Openbrush traits PSP34, PSP34Metadata, PSP34Mintable, PSP34Enumberable, Ownable for structure Shiden34.
  • Overridden mint() method from trait PSP34Mintable. More about this in next section.

Each of implemented traits will enrich shiden34 contract with a set of methods. To examine which methods you now have available check:

  • Openbrush PSP34 trait brings all familiar functions from ERC721 plus a few extra:
    • collection_id()
    • balance_of()
    • owner_of()
    • allowance()
    • approve()
    • transfer()
    • total_supply()
  • Openbrush Metadata
    • get_attribute()
  • Openbrush Mintable
    • mint()
  • Openbrush Enumerable
    • owners_token_by_index()
    • token_by_index()
  • Openbrush Ownable
    • renounceOwnership ()
    • transferOwnership()
    • owner()

Major differences when compared with ERC721 are:

  1. Metadata trait brings possibility to define numerous attributes
  2. PSP34 trait brings collection_id() which can be used or ignored in contracts

We could have used Burnable trait as well but for simplicity it is skipped in this tutorial since burning can be performed by sending a token to address 0x00.

After this step your code should look like this.

Build, Deploy and Interact with the Contract

Build your contract:

cd contracts/shiden34
cargo contract build --release

Use shiden34.contract target to deploy contract. The file is located in this folder:

ls target/ink/shiden34/

To deploy your contract using the Polkadot.js apps portal, follow the previous guide, or use the contracts-ui.

You can start interacting with your contract. You will notice that one token is already minted. This is due to the mint() call in the contract's constructor new().

  • Try minting another token by calling mint().
  • Read the token ownerOf() for your newly minted token.
  • Check that totalSupply() has increased.