Blockchain technology is perpetually becoming a game-changer for most enthusiasts. It came to the limelight with the advent of cryptocurrency— one of blockchain's financial use cases. It doesn't stop at that; blockchains were later developed to offer more than cryptocurrencies, leading to another development of Blockchain 2.0, which supports smart contracts.
Smart contracts paved the way for more use cases and utilities of blockchain technology like developing several tokens on a single blockchain, market makers in DeFi, development of non-fungible tokens—NFTs—fractionalization of NFTs, and many more. All these were possible due to the emergence of smart contracts.
Smart contracts are digital agreements encoded in a string of codes that self-execute once the pre-defined conditions have been met. They offer a more efficient, affordable, and secure approach to managing and automatically executing agreements in the blockchain ecosystem. Its remarkable features are the enabling factors that entrench its use cases like minting NFTs and more.
An NFT is a unique and unrepeatable digital asset that is second to none. It is usually marked by its rarity, which depicts its unequaled properties. It is unrepeatable because the unique property of an individual NFT is not repeated in other NFTs in cases of NFT collections. All NFTs can have some general features or properties, but each NFT has its unique property or characteristic, making it stand out and non-fungible to others.
An NFT can be artwork, paintings, journals, documents, music, etc. They are usually bought and sold on NFT marketplaces, but before they can be listed, they must be minted. Minting of NFTs is converting the digital file of the supposed NFT to create a blockchain-based NFT. It has to go through this blockchain process so that it is registered on the blockchain as a unique asset. Furthermore, prices and other terms of conditions of sales are inputted in the smart contract phase before it is finally minted.
NFTs can only be minted on blockchains that supports smart contract; examples of such blockchains are the Ethereum, Solana, Cardano, Tezos, Polkadot, BSC, NEAR protocol, and others. The purview of this discourse will be based on the NEAR protocol.
According to the NEAR whitepaper, "NEAR is a decentralized application platform with the potential to change how systems are designed, applications are built, and the web itself works. It is a complex technology with a simple goal — allow developers and entrepreneurs to easily and sustainably build applications which secure high-value assets like money and identity while making them performant and usable enough for consumers to access".
Summarily, the NEAR protocol is a proof-of-stake blockchain that supports smart contracts. Its native token is NEAR, which can also be staked in its consensus mechanism aside from settling transaction fees on the network. Since it supports smart contracts, the NEAR protocol is suitable for minting NFTs and executing other utilities that smart contract does.
Rust is a multi-paradigm, statically typed programming language emphasizing security and efficiency. It was created with the objectives of efficiency, speed, and safety. For most developers, Rust features resolve most of the drawbacks of traditional low-level languages, including memory issues and the creation of concurrent programs. Due to its capacity to manage numerous requests within a network with little or no computational work, Rust is used to power a series of blockchain applications.
To write a smart contract on the NEAR protocol, we need NEAR CLI and NEAR wallet alongside Rust.
The NEAR CLI (command-line interface) is used to access and interact with the NEAR blockchain.
The NEAR wallet is needed to pay the gas fee and accept payment for the NFT you're selling.
The following are the procedures involved:
To start with, you'll need to create a cargo project
cargo new nft –lib
Navigate to project folder NFT, and you’ll see
src/lib.rs
: This is where we’ll write our smart contract
Cargo.toml
: This is the configuration file for our project.
Navigate to the cargo.toml
and add the following
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
near-sdk = "*"
near-contract-standards = "*"
Then we can start with the smart contract scaffolding which is a framework of how Dapps work
use near_sdk::{
near_bindgen,
borsh::{self, BorshDeserialize, BorshSerialize}
};
near_sdk::setup_alloc!();
#[near_bindgen]
#[derive(BorshDeserialize, BorshDeserialize)]
pub struct Contract {}
#[near_bindgen]
impl Contract {}
Since NEAR adopts a stateful methodology, our contract state will be held in the "struct Contract," and we will code our state-changing functions in the "implementation Contract."
Note that we are implementing some macros and deriving traits.
The #[near_bindgen]
macro translates the state to a valid form to be live on the NEAR protocol.
BorshDeserialize
and BorshDeserialize
are the traits from the Borsh crate for the serialization and deserialization of state.
Let's now designate the state for our NFT. Recall that NFTs are special tokens with metadata attached to them. This makes our state appear like this:
use near_contract_standards::non_fungible_token::{
metadata::NFTContractMetadata, NonFungibleToken,
};
pub struct Contract {
token: NonFungibleToken,
metadata: LazyOption<NFTContractMetadata>,
}
The default value for this state must always be set; hence an initializer function is required to do so.
#[derive(BorshSerialize, BorshStorageKey)]
pub enum StorageKey {
NonFungibleToken,
Metadata,
TokenMetadata,
Enumeration,
Approval
}
#[near_bindgen]
impl Contract {
#[init]
pub fn new(owner_id: ValidAccountId) -> Self {
Self {
token: NonFungibleToken::new(
StorageKey::NonFungibleToken,
owner_id,
Some(StorageKey::TokenMetadata),
Some(StorageKey::Enumeration),
Some(StorageKey::Approval)
),
metadata: LazyOption::new(
StorageKey::Metadata,
Some(&NFTContractMetadata {
spec: NFT_METADATA_SPEC.to_string(),
name: "Example Name".to_string(),
symbol: "Example".to_string(),
icon: Some("ANY_SVG".to_string()),
base_uri: None,
reference: None,
reference_hash: None,
})
),
}
}
}
The #[init] macro function sets the default state and will consistently execute first.
Now that all is set for our initializer and state, let's write the NFT minting function.
impl Contract {
......
#[payable]
pub fn nft_mint(
&mut self,
token_id: TokenId,
receiver_id: ValidAccountId,
token_metadata: TokenMetadata,
) -> Token {
self.token.mint(token_id, receiver_id, Some(token_metadata))
}
}
Now deploy the above to the chain.
Run the below command to make a build:
cargo build --target wasm32-unknown-unknown --release
Run the below command to deploy:
near deploy --wasmFile target/wasm32-unknown-unknown/release/nft.wasm --accountId "your near account username"
This will give you a program ID that you can use to call the smart contract's RPCs—Remote Procedure calls.
Aside from the NEAR CLI, you can also use a javascript client to interact with the contract.