Create cw721-dapp project
This article will guide you in setting up an NFT smart contract, and show you how to build, test and deploy your NFT contract by using beaker toolchain. You can clone source code at cw721-repository.
Prerequisites
To complete this tutorial successfully, you'll need:
- Beaker toolchain.
- An Aura account.
- NFT Storage Account.
I think you familiar with Beaker
and know how to create an Aura account. There are a new thing I want to introduce, it is NFT Storage. Instead of store NFT image in public storage such like Google Drive, .. we will use third party to store NFT image distributive is IPFS. You can use free service such like NFT Storage.
Create project
Yeahhh, it's finally time to start coding our main contract! Let's use beaker to create cw721-dapp
:
beaker new cw721-dapp
When a notification appear, just choose minimal template.
? 🤷 Which starting template would you like to use?
❯ minimal
counter-example
Now we have basically project. But there are nothing contract at here. Just create our contract:
cd cw721-dapp
beaker wasm new cw721-factory
Now, source code for this contract can be find in contracts/cw721-factory
. We will start in the src/state.rs
file. Upon opening it you should see code like the following:
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use cosmwasm_std::Addr;
use cw_storage_plus::Item;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct State {
pub count: i32,
pub owner: Addr,
}
pub const STATE: Item<State> = Item::new("state");
Let's talk through this section by section.
The first 5 lines of code are importing other structs, interfaces and functions from other packages. To give a quick overview without getting bogged down too much:
- JsonSchema allows structs to be serialized and deserialized to and from JSON.
- Deserialize and Serialize provide the serialization described above.
- Addr is a Cosmos address, under the hood it is simply a string.
- Item is a helper provided by storage plus. It effectively means we can store an item in storage. In this case, the
STATE
variable is anItem
that stores a singularState
struct.
We want to make some changes to this, let's rename it accordingly. I prefer our global state to be called Config
as it is the configuration of our contract. Let's do this now and remove the count
and owner
variable from the struct and rename it to Config
. Let's also rename STATE
to CONFIG
.
It should look like this:
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use cosmwasm_std::Addr;
use cw_storage_plus::Item;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Config {
}
pub const CONFIG: Item<Config> = Item::new("config");
Now let's think about what global configs we want, we probably want an NFT Minter Contract that can mint some amazing NFTs for buyer with fixed price fee. Let's add some necessary fields representing the configuration parameters of our NFT minter contract.
// Previous code omitted
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Config {
pub owner: Addr,
pub cw20_address: Addr,
pub cw721_address: Option<Addr>,
pub max_tokens: u32,
pub unit_price: Uint128,
pub name: String,
pub symbol: String,
pub token_uri: String,
pub extension: Extension,
pub unused_token_id: u32,
}
// Following code omitted
Now I'll explain to you about the above data fields.
- The
owner
field specifies which account is the owner of your contract. - The
cw20_address
field specifies the address of which cw20 token contract used as a payment method. We will use the contract deployed in chapter 3. - The
cw721_address
field specifies the address of the cw721 token contract we will use. But the cw721 is created dynamically during contract instantiation, so there's no need to instantiate a cw721 token contract separately. - The
max_tokens
field specifies the maximum mint token amount of this contract. - The
unit_price
field specifies the unit price for each NFT. name
,symbol
,token_uri
,extension
fields specifies the NFT token info and metadata. Here, theextension
is of typecw721_base::Extension
. So you need to add a line like the following in the declaration of the library used by Rust.
use cw721_base::Extension;
Also need to declare the use of cw721-base as a dependency in Cargo.toml. Open Cargo.toml
and add this to the dependencies:
# ...
[dependencies]
cw721-base = { version = "0.15.0", features = ["library"] }
cw-utils = "0.12.1"
# ...
- The
unused_token_id
field is used to store the total number of NFT tokens have been minted. The reason it is named 'unused_token_id' is because, when executing the mint action, the NFT token will be assigned a token_id equal to the value of the current 'unused_token_id'.
Ok, here we have done all the necessary work. In the next chapter, we will move on to working in the src/contract.rs
.
Disclaimer!
If you currently run any command such as cargo test
or cargo wasm
the build will break! Don't worry this is expected as we have modified our storage code without changing code on the contract side.
We'll fix this in next chapters.