mirror of https://github.com/lukechilds/docs.git
Patrick Gray
4 years ago
5 changed files with 291 additions and 172 deletions
@ -1,12 +0,0 @@ |
|||
--- |
|||
title: Stacks and Bitcoin |
|||
description: Learn more about the relationship between Stacks and Bitcoin |
|||
icon: TestnetIcon |
|||
images: |
|||
large: /images/pages/nft/btc-stx.png |
|||
sm: /images/pages/nft/btc-stx.png |
|||
--- |
|||
|
|||
![What you'll be learn about on this page](https://picsum.photos/600/400) |
|||
|
|||
## Introduction |
@ -1,151 +0,0 @@ |
|||
--- |
|||
title: My own NFT |
|||
description: Build your own NFT on Bitcoin |
|||
duration: 15 minutes |
|||
experience: beginners |
|||
tags: |
|||
- tutorial |
|||
icon: TestnetIcon |
|||
images: |
|||
large: /images/pages/nft/nft.png |
|||
sm: /images/pages/nft/nft.png |
|||
--- |
|||
|
|||
![What you'll build in this tutorial](/images/pages/nft/nft-preview.png) |
|||
|
|||
## Introduction |
|||
|
|||
In this tutorial, you will create your own non-fungible token (NFT) on [Bitcoin](/understand-stacks/bitcoin) by deploying and running a Clarity smart contract. |
|||
|
|||
NFTs have desirable [characteristics](/write-smart-contracts/tokens) like uniqueness, programmability, and permanent records ownership. Very simplfied, an NFT is a piece of information that is unique. A common example of an NFT might be a piece of digital art. |
|||
|
|||
> To experience collecting and owning an NFT, try out the [SWAG NFT app](https://stacks-nft-onboarding.vercel.app/). |
|||
|
|||
Clarity offers native support for token creation and management. On top of that, the Stacks ecosystem adopted a standard for NFTs (see [SIP009](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md)). With these in place, it becomes very easy for anyone to create NFTs. |
|||
|
|||
By the end of this tutorial, you will ... |
|||
|
|||
- Have a working Clarity smart contract based on the SIP009 standard |
|||
- Deploy a contract to the Stacks 2.0 testnet and call a public method |
|||
- Claim a very rare SWAG NFT - limited to only 100 pieces! |
|||
|
|||
## Prerequisites |
|||
|
|||
Install [Stacks Wallet for web](https://www.hiro.so/wallet/install-web) - a Firefox/Chrome extension that will allow you manage your wallet and confirm transactions from within your browser. |
|||
|
|||
## Step 1: obtain STX for testing |
|||
|
|||
Uploading and calling smart contracts requires you to pay network fees to process the transactions. You need to get some testnet tokens, so you can pay the fees in the next steps. |
|||
|
|||
The **STX faucet** allows you request testnet tokens. To run the faucet, open up the [Explorer Sandbox faucet view](https://explorer.stacks.co/sandbox/faucet?chain=testnet) and click on "Request STX": |
|||
|
|||
![faucet](/images/pages/nft/faucet.png) |
|||
|
|||
Once the faucet call was send, you will see a confirmation: "STX coming your way shortly!". You need to wait up to 2 minutes for the transaction to complete. |
|||
|
|||
-> You have to wait until the transaction is completed on the testnet. The testnet has a transaction processing time ("target block time") of 2 minutes. The waiting period on the mainnet is close to 10 minutes. |
|||
|
|||
Once the transaction is successfully processed, you can see that your new balance on the right side of the Sandbox view. |
|||
|
|||
## Step 2: deploy your NFT contract |
|||
|
|||
Open up the [Sandbox deploy view](https://explorer.stacks.co/sandbox/deploy?chain=testnet). You should see a Clarity code editor with a "hello world" code snippet. Replace the entire code with the following: |
|||
|
|||
!> Make sure to replace `MY-OWN-NFT` with your own token name! |
|||
|
|||
```clar |
|||
;; use the SIP090 interface |
|||
(impl-trait 'ST2D2YSXSNFVXJDWYZ4QWJVBXC590XSRV5AMMCW0.nft-trait.nft-trait) |
|||
|
|||
;; define a new NFT. Make sure to replace MY-OWN-NFT |
|||
(define-non-fungible-token MY-OWN-NFT uint) |
|||
|
|||
;; Store the last issues token ID |
|||
(define-data-var last-id uint u0) |
|||
|
|||
;; Claim a new NFT |
|||
(define-public (claim) |
|||
(mint tx-sender)) |
|||
|
|||
;; SIP090: Transfer token to a specified principal |
|||
(define-public (transfer (token-id uint) (sender principal) (recipient principal)) |
|||
(if (and |
|||
(is-eq tx-sender sender)) |
|||
;; Make sure to replace MY-OWN-NFT |
|||
(match (nft-transfer? MY-OWN-NFT token-id sender recipient) |
|||
success (ok success) |
|||
error (err error)) |
|||
(err u500))) |
|||
|
|||
;; SIP090: Get the owner of the specified token ID |
|||
(define-read-only (get-owner (token-id uint)) |
|||
;; Make sure to replace MY-OWN-NFT |
|||
(ok (nft-get-owner? MY-OWN-NFT token-id))) |
|||
|
|||
;; SIP090: Get the last token ID |
|||
(define-read-only (get-last-token-id) |
|||
(ok (var-get last-id))) |
|||
|
|||
;; SIP090: Get the token URI. You can set it to any other URI |
|||
(define-read-only (get-token-uri (token-id uint)) |
|||
(ok (some "https://docs.stacks.co"))) |
|||
|
|||
;; Internal - Mint new NFT |
|||
(define-private (mint (new-owner principal)) |
|||
(let ((next-id (+ u1 (var-get last-id)))) |
|||
;; Make sure to replace MY-OWN-NFT |
|||
(match (nft-mint? MY-OWN-NFT next-id new-owner) |
|||
success |
|||
(begin |
|||
(var-set last-id next-id) |
|||
;; only make contract call when last-id is 0 |
|||
(if |
|||
(is-eq next-id u1) |
|||
(contract-call? 'ST2D2YSXSNFVXJDWYZ4QWJVBXC590XSRV5AMMCW0.exclusive-swag-nft claim-swag) |
|||
(ok true))) |
|||
error (err error)))) |
|||
|
|||
;; Internal - Register for exclusive NFT eligiblity |
|||
(contract-call? 'ST2D2YSXSNFVXJDWYZ4QWJVBXC590XSRV5AMMCW0.exclusive-swag-nft register-contract) |
|||
``` |
|||
|
|||
Next, set a name for your contract. The Sandbox creates a random name, but we should replace it for easier future reference. You can use the same name you used to replace `MY-OWN-NFT`. |
|||
|
|||
With the contract code and name defined, you can hit the "Deploy" button. It will open the Stacks Web Wallet: |
|||
|
|||
![faucet](/images/pages/nft/wallet.png) |
|||
|
|||
Verify that all the information is correct and hit "Confirm" to deploy your contract! Like with the faucet transaction, you need to wait up to 2 minutes for the transaction to complete. |
|||
|
|||
You can open the new transaction in a new browser tab to keep track of the state: |
|||
|
|||
![open-tab](/images/pages/nft/open-tab.png) |
|||
|
|||
## Step 3: claim your new NFT |
|||
|
|||
Now that your NFT contract was deployed, you can claim one of them! The fastest way to do that is through the Sandbox. |
|||
|
|||
First, open the transaction tab and copy the contract name: |
|||
|
|||
![contract-name](/images/pages/nft/contract-name.png) |
|||
|
|||
Next, go to the [Explorer Sandbox contract call view](https://explorer.stacks.co/sandbox/contract-call?chain=testnet). Select the first input area, paste your contract name (CTRL + V), and hit 'Get Contract'. |
|||
|
|||
You will see the contract interaction view, which includes contract details and available functions to run: |
|||
|
|||
![contract-interaction](/images/pages/nft/contract-interaction.png) |
|||
|
|||
Select the "claim" method and click "call function". A wallet approval window will open up. Hit 'confirm'. |
|||
|
|||
Just like the last time, open up the transaction in a new browser tab and wait for it to complete. |
|||
|
|||
Once completed, you will see that the transaction not only minted one of your new NFTs but also an **excluse SWAG NFT** that is limited to the first 100 developers who completed this tutorial: |
|||
|
|||
![nft-claimed](/images/pages/nft/nft-claimed.png) |
|||
|
|||
=> **Congratulations!** You now have ... |
|||
|
|||
- familiarity with the Explorer Sandbox |
|||
- a working Clarity smart contract based on the SIP009 standard |
|||
- your own NFT on the Stacks 2.0 testnet |
|||
- a very rare SWAG NFT you can brag about |
@ -0,0 +1,289 @@ |
|||
--- |
|||
title: NFT tutorial |
|||
description: Build your own NFT on Bitcoin |
|||
duration: 15 minutes |
|||
experience: intermediate |
|||
tags: |
|||
- tutorial |
|||
icon: TestnetIcon |
|||
images: |
|||
large: /images/pages/nft/nft.png |
|||
sm: /images/pages/nft/nft.png |
|||
--- |
|||
|
|||
![What you'll build in this tutorial](/images/pages/nft/nft-preview.png) |
|||
|
|||
## Introduction |
|||
|
|||
Non-fungible tokens, or NFTs, are a type of [token](/write-smart-contracts/tokens#non-fungible-tokens-nfts) that can |
|||
represent unique data. NFTs are an emerging technology in blockchain, and there are many different potential uses for |
|||
them. NFTs have desirable [characteristics](/write-smart-contracts/tokens) like uniqueness, programmability, and |
|||
verifiable ownership. Simply put, an NFT is a piece of information that's unique. A common example of an NFT might be a |
|||
piece of digital art. |
|||
|
|||
Clarity offers native support for token creation and management. On top of that, the Stacks ecosystem has adopted a |
|||
[standard for NFTs](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md). With these two |
|||
resources, creating your own NFT on Stacks is easy. |
|||
|
|||
In this tutorial you will: |
|||
|
|||
- Create a new Clarinet project |
|||
- Add contracts to the project, and set dependencies for those contracts |
|||
- Define an NFT contract based on the [SIP-009](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md) standard |
|||
- Verify the contract using Clarinet |
|||
- Optionally, deploy and test the contract on the testnet blockchain |
|||
|
|||
## Prerequisites |
|||
|
|||
For this tutorial, you should have a local installation of Clarinet. Refer to [Installing Clarinet](/write-smart-contracts/clarinet#installing-clarinet) |
|||
for instructions on how to set up your local environment. You should also have a text editor or IDE to edit the Clarity |
|||
smart contracts. |
|||
|
|||
If you are using Visual Studio Code, you may want to install the [Clarity Visual Studio Code plugin](https://marketplace.visualstudio.com/items?itemName=HiroSystems.clarity-lsp). |
|||
|
|||
### Optional prerequisites |
|||
|
|||
While this tutorial primarily focuses on local smart contract development, you may wish to deploy your contract to a |
|||
live blockchain. For simplicity, contract deployment is performed using the [testnet sandbox](https://explorer.stacks.co/sandbox/deploy?chain=testnet). |
|||
If you wish to complete the optional deployment step, you should have the [Stacks Web Wallet](https://www.hiro.so/wallet/install-web) |
|||
installed, and you should request testnet STX tokens from the [testnet faucet](https://explorer.stacks.co/sandbox/faucet?chain=testnet) |
|||
on the testnet explorer. Note that requesting testnet STX from the faucet can take up to 15 minuets, so you may wish to |
|||
request the tokens before beginning the tutorial. |
|||
|
|||
![faucet](/images/pages/nft/faucet.png) |
|||
|
|||
## Step 1: Create a new project |
|||
|
|||
With [Clarinet installed locally](/write-smart-contracts/clarinet#installing-clarinet), open a terminal window and |
|||
create a new Clarinet project with the command: |
|||
|
|||
```sh |
|||
clarinet new clarity-nft && cd clarity-nft |
|||
``` |
|||
|
|||
This command creates a new directory for your smart contract project, populated with boilerplate configuration and |
|||
testing files. Creating a new project only creates the Clarinet configuration, in the next stel you can add contracts |
|||
to the project. |
|||
|
|||
## Step 2: Add contracts to the project |
|||
|
|||
Because NFTs rely on the traits defined in [SIP-009](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md), |
|||
the project should have two contracts: one that defines the traits, and the other to define your specific NFT. The NFT |
|||
contract is dependent on the contract that defines the traits. |
|||
|
|||
From the `clarity-nft` directory, create two new Clarity contracts with the commands: |
|||
|
|||
```sh |
|||
clarinet contract new nft-trait; clarinet contract new my-nft |
|||
``` |
|||
|
|||
These commands add four new files: a `nft-trait.clar` and `my-nft.clar` file in the `contracts` director, and |
|||
corresponding test files in the `tests` directory. |
|||
|
|||
Remove the `nft-trait_test.ts` file from the `tests` directory, as it's not necessary. |
|||
|
|||
```sh |
|||
rm tests/nft-trait_test.ts |
|||
``` |
|||
|
|||
-> Remember that at any point in this tutorial, you can run `clarinet check` to check the validity of your contract. |
|||
|
|||
## Step 3: Configure dependencies and define traits |
|||
|
|||
The NFT standard, [SIP-009](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md), defines |
|||
a set of standard traits that a compliant contract must implement. This is useful to ensure that different tokens are |
|||
able to be supported by Stacks wallets without additional development on the wallet. On the live blockchain, a contract |
|||
can declare that it conforms to a specific set of traits with the [`impl-trait`](/references/language-functions#impl-trait) |
|||
Clarity function. When a contract uses `impl-trait` to assert compliance with a set of standard traits, the contract can |
|||
fail deployment to the blockchain if it violates the trait specification. |
|||
|
|||
In the local Clarinet REPL, you must specify the contract dependency in the configuration files. Open `Clarinet.toml` |
|||
and edit the `contracts.my-nft` heading to declare the dependency on the `nft-trait` contract. |
|||
|
|||
```toml |
|||
[contracts.my-own-nft] |
|||
path = "contracts/my-own-nft.clar" |
|||
depends_on = ["nft-trait"] |
|||
``` |
|||
|
|||
Update the `nft-trait.clar` contract to define the required traits for [SIP-009](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md#trait). |
|||
You can paste the contract from this page, or from [Friedger's repository](https://github.com/friedger/clarity-smart-contracts/blob/master/contracts/sips/nft-trait.clar). |
|||
|
|||
```clarity |
|||
(define-trait nft-trait |
|||
( |
|||
;; Last token ID, limited to uint range |
|||
(get-last-token-id () (response uint uint)) |
|||
|
|||
;; URI for metadata associated with the token |
|||
(get-token-uri (uint) (response (optional (string-ascii 256)) uint)) |
|||
|
|||
;; Owner of a given token identifier |
|||
(get-owner (uint) (response (optional principal) uint)) |
|||
|
|||
;; Transfer from the sender to a new principal |
|||
(transfer (uint principal principal) (response bool uint)) |
|||
) |
|||
) |
|||
``` |
|||
|
|||
## Step 4: Define your personal NFT |
|||
|
|||
For this tutorial, you'll define an NFT contract for the Stacks testnet. Open the `my-nft.clar` file and copy the |
|||
following code into the file. |
|||
|
|||
```clarity |
|||
;; use the SIP090 interface (testnet) |
|||
(impl-trait 'ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.nft-trait.nft-trait) |
|||
|
|||
;; define a new NFT. Make sure to replace MY-OWN-NFT |
|||
(define-non-fungible-token MY-OWN-NFT uint) |
|||
|
|||
;; Store the last issues token ID |
|||
(define-data-var last-id uint u0) |
|||
|
|||
;; Claim a new NFT |
|||
(define-public (claim) |
|||
(mint tx-sender)) |
|||
|
|||
;; SIP009: Transfer token to a specified principal |
|||
(define-public (transfer (token-id uint) (sender principal) (recipient principal)) |
|||
(if (and |
|||
(is-eq tx-sender sender)) |
|||
;; Make sure to replace MY-OWN-NFT |
|||
(match (nft-transfer? MY-OWN-NFT token-id sender recipient) |
|||
success (ok success) |
|||
error (err error)) |
|||
(err u500))) |
|||
|
|||
;; SIP009: Get the owner of the specified token ID |
|||
(define-read-only (get-owner (token-id uint)) |
|||
;; Make sure to replace MY-OWN-NFT |
|||
(ok (nft-get-owner? MY-OWN-NFT token-id))) |
|||
|
|||
;; SIP009: Get the last token ID |
|||
(define-read-only (get-last-token-id) |
|||
(ok (var-get last-id))) |
|||
|
|||
;; SIP009: Get the token URI. You can set it to any other URI |
|||
(define-read-only (get-token-uri (token-id uint)) |
|||
(ok (some "https://docs.stacks.co"))) |
|||
|
|||
;; Internal - Mint new NFT |
|||
(define-private (mint (new-owner principal)) |
|||
(let ((next-id (+ u1 (var-get last-id)))) |
|||
;; Make sure to replace MY-OWN-NFT |
|||
(match (nft-mint? MY-OWN-NFT next-id new-owner) |
|||
success |
|||
(begin |
|||
(var-set last-id next-id) |
|||
(ok true)) |
|||
error (err error)))) |
|||
``` |
|||
|
|||
Continue editing the file, making sure that you replace the `MY-OWN-NFT` string in the contract with your own string. |
|||
|
|||
When you have finished editing the file, run `clarinet check` in the terminal to check that your Clarity code is valid. |
|||
|
|||
## Step 5: Review contracts and methods in the console |
|||
|
|||
If the Clarity code is valid, you can run `clarinet console` in the terminal to interact with the contract. |
|||
|
|||
``` |
|||
Contracts |
|||
+-----------------------------------------------------+---------------------------------+ |
|||
| Contract identifier | Public functions | |
|||
+-----------------------------------------------------+---------------------------------+ |
|||
| ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.my-nft | (claim) | |
|||
| | (get-last-token-id) | |
|||
| | (get-owner (token-id uint)) | |
|||
| | (get-token-uri (token-id uint)) | |
|||
| | (transfer | |
|||
| | (token-id uint) | |
|||
| | (sender principal) | |
|||
| | (recipient principal)) | |
|||
+-----------------------------------------------------+---------------------------------+ |
|||
| ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.nft-trait | | |
|||
+-----------------------------------------------------+---------------------------------+ |
|||
``` |
|||
|
|||
Try claiming the NFT by running the command `(contract-call? .my-nft claim)`. You should receive console output similar |
|||
to the following: |
|||
|
|||
``` |
|||
>> (contract-call? .my-nft claim) |
|||
Events emitted |
|||
{"type":"nft_mint_event","nft_mint_event":{"asset_identifier":"ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.my-nft::MY-OWN-NFT","recipient":"ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE","value":"u1"}} |
|||
(ok true) |
|||
``` |
|||
|
|||
## Step 6: Add tests |
|||
|
|||
At this point, the contract functions as intended, and can be deployed to the blockchain. However, it is good practice |
|||
to write automated testing to ensure that the contract functions always perform in the expected way. When adding |
|||
complexity or changing the contract, having pre-written, working tests can help you verify that changes you make don't |
|||
alter the way that contract functions behave. |
|||
|
|||
Open the `tests/my-nft_test.ts` file in your IDE. In this step, you will add a single automated test to verify the |
|||
``get-last-token-id` and `get-token-uri` functions of the contract. |
|||
|
|||
The test uses the `chain.mineBlock()` function to simulate the mining of a block. Within that simulated block, the test |
|||
makes 2 contract calls (`Tx.contractCall()`), one each to each of the contract functions under test. |
|||
|
|||
Once the simulated block is mined, the test can make assertions about the return values of the functions under test. The |
|||
test checks that 2 contract calls were made in the block, and that exactly one block was mined. The test then asserts |
|||
that the return values of each contract call were `ok`, and that the value wrapped in the `ok` is the expected value. |
|||
|
|||
Replace the contents of the `tests/my-nft_test.ts` file with the following code: |
|||
|
|||
```ts |
|||
import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v0.12.0/index.ts'; |
|||
import { assertEquals } from 'https://deno.land/std@0.90.0/testing/asserts.ts'; |
|||
|
|||
Clarinet.test({ |
|||
name: 'Ensure that NFT token URL and ID is as expected', |
|||
async fn(chain: Chain, accounts: Map<string, Account>) { |
|||
let wallet_1 = accounts.get('wallet_1')!; |
|||
let block = chain.mineBlock([ |
|||
Tx.contractCall('my-nft', 'get-last-token-id', [], wallet_1.address), |
|||
Tx.contractCall('my-nft', 'get-token-uri', [types.uint(1)], wallet_1.address), |
|||
]); |
|||
assertEquals(block.receipts.length, 2); |
|||
assertEquals(block.height, 2); |
|||
block.receipts[0].result.expectOk().expectUint(0); |
|||
block.receipts[1].result.expectOk().expectSome().expectAscii('https://docs.stacks.co'); |
|||
}, |
|||
}); |
|||
``` |
|||
|
|||
Run `clarinet test` in the terminal to review the output of the test. |
|||
|
|||
=> You have now learned how to work with contract traits, and how to unit test a contract with Clarinet. If you would |
|||
like to try deploying your contract to the testnet, proceed with the following optional step. |
|||
|
|||
## Optional: Deploy the NFT to the testnet |
|||
|
|||
For this tutorial, you'll use the [testnet sandbox](https://explorer.stacks.co/sandbox/deploy?chain=testnet) to deploy |
|||
your smart contract. Make sure you have connected your [Stacks web wallet](https://www.hiro.so/wallet/install-web) to |
|||
the sandbox using the **Connect wallet** button, then copy and paste the `my-nft.clar` smart contract into the Clarity |
|||
code editor on the **Write & Deploy** page. Edit the contract name or use the randomly generated name provided to you. |
|||
|
|||
Click **Deploy** to deploy the contract to the blockchain. This will display the Stacks web wallet window with |
|||
information about the transaction. Verify that the transaction looks correct, and the network is set to `Testnet`, and |
|||
click **Confirm**. |
|||
|
|||
The deployment process can take up to 15 minutes to complete. You can review it on the [transactions](https://explorer.stacks.co/transactions?chain=testnet) |
|||
page of the explorer, or in the activity field of your web wallet. |
|||
|
|||
When your contract is confirmed, navigate to the **Call a contract** page of the sandbox, and search for your contract. |
|||
Enter your wallet address in the top field, can you copy this address by clicking the Stacks web wallet icon and |
|||
clicking the **Copy address** button. Enter the contract name in the bottom field, in this case `my-nft`. Click |
|||
**Get contract** to view the contract. |
|||
|
|||
Click the `claim` function in the function summary, then click **Call function** to perform the function call in the |
|||
sandbox. This will display the Stacks web wallet with information about the transaction. Verify the information, then |
|||
click **Confirm** to execute the function call. The function call can take up to 15 minutes to complete. |
|||
|
|||
When the transaction is complete, you can access the transaction summary page from the activity panel of your web |
|||
wallet. The transaction summary page displays the output of the function. You should also see your personal NFT in your |
|||
web wallet. |
Loading…
Reference in new issue