Hello, NFT!
From Wikipedia,
A non-fungible token (NFT) is a unique and non-interchangeable unit of data stored on a digital ledger (blockchain). NFTs can be associated with easily-reproducible items such as photos, videos, audio, and other types of digital files as unique items
In this example, we will show you how to build a ticket vending machine in Scrypto.
You can find the code for this example on github here.
Blueprint and Component
The blueprint we’re building is called HelloNft
. Each HelloNft
manages the following resources and data.
struct HelloNft {
/// A vault that holds all available tickets.
available_tickets: Vault,
/// The price for each ticket.
ticket_price: Decimal,
/// A vault for collecting payments.
collected_xrd: Vault,
}
The available_tickets
contains non-fungible Ticket
resource. Both fungible and non-fungible resources are stored in a Vault
.
Creating NFT Units
In our example, the supply of NFT units are fixed, and we allocate the resource upfront.
First, we create a struct that will represent the data associated with each ticket NFT.
#[derive(NonFungibleData)]
pub struct Ticket {
pub row: u32,
pub column: u32,
}
Then, we prepare the data for each NFT unit (every ticket is associated with a specific row and column number). We generate the id of each NFT with NonFungibleId::random()
which generates a random Id each time it is called.
for row in 1..5 {
for column in 1..5 {
tickets.push((
NonFungibleId::random(),
Ticket { row, column },
));
}
}
Then, the whole vector of NFT data is passed to ResourceBuilder
as the initial supply.
let ticket_bucket = ResourceBuilder::new_non_fungible()
.metadata("name", "Ticket")
.initial_supply(tickets);
After that, we get a bucket of NFT units stored in ticket_bucket
. We can put everything together and instantiate our HelloNFT component with a vault containing the tickets like this:
pub fn instantiate_hello_nft(price: Decimal) -> ComponentAddress {
// Prepare ticket NFT data
let mut tickets = Vec::new();
for row in 1..5 {
for column in 1..5 {
tickets.push((
NonFungibleId::random(),
Ticket { row, column },
));
}
}
// Creates a fixed supply of NFTs.
let ticket_bucket = ResourceBuilder::new_non_fungible()
.metadata("name", "Ticket")
.initial_supply(tickets);
// Instantiate our component
Self {
available_tickets: Vault::with_bucket(ticket_bucket),
ticket_price: price,
collected_xrd: Vault::new(RADIX_TOKEN),
}
.instantiate()
.globalize()
}
Allowing Callers to Buy Tickets
A HelloNft
component exposes three public methods:
-
buy_ticket
: allowing caller to buy one ticket; -
buy_ticket_by_id
: allowing caller to buy one specific ticket; -
available_ticket_ids
: returns the IDs of all available tickets.
The workflow of buy_ticket
and buy_ticket_by_id
is very similar.
pub fn buy_ticket(&mut self, mut payment: Bucket) -> (Bucket, Bucket) {
// Take our price out of the payment bucket
self.collected_xrd.put(payment.take(self.ticket_price));
// Take any ticket
let ticket = self.available_tickets.take(1);
// Return the ticket and change
(ticket, payment)
}
pub fn buy_ticket_by_id(&mut self, id: u64, mut payment: Bucket) -> (Bucket, Bucket) {
// Take our price out of the payment bucket
self.collected_xrd.put(payment.take(self.ticket_price));
// Take the specific ticket
let ticket = self
.available_tickets
.take_non_fungible(&NonFungibleId::from(id));
// Return the ticket and change
(ticket, payment)
}
Both involves:
-
Taking a payment according to pre-defined price and putting it into the
collected_xrd
vault; -
Taking a ticket from the
available_tickets
vault:-
take(1)
returns one NFT unit; -
take_non_fungible(&id)
returns the specified NFT unit.
-
-
Returning the ticket and payment change.
To write the available_ticket_ids
method, you can use the non_fungible_ids()
method on the tickets vault:
pub fn available_ticket_ids(&self) -> BTreeSet<NonFungibleId> {
self.available_tickets.non_fungible_ids()
}
Buy That Ticket
-
Create a new account, and save the account address
resim new-account
-
Publish the package, and save the package address
resim publish .
-
Call the
instantiate_hello_nft
function to instantiate a component with a ticket price of 5 XRD, and save the component addressresim call-function <PACKAGE_ADDRESS> HelloNft instantiate_hello_nft 5
-
Call the
available_ticket_ids
methodresim call-method <COMPONENT_ADDRESS> available_ticket_ids
-
Call the
buy_ticket_by_id
methodresim call-method <COMPONENT_ADDRESS> buy_ticket_by_id <TICKET_ID> "100,030000000000000000000000000000000000000000000000000004"
-
Check out our balance
resim show <ACCOUNT_ADDRESS>