From 5948e6f3d20e7a9f63202f9971b26fba07b4e40c Mon Sep 17 00:00:00 2001 From: benjamin fuentes Date: Tue, 28 May 2024 17:10:28 +0200 Subject: [PATCH] Benjaminfuentes/lastligoupdatesbeforeleaving (#398) * part 1 is ready, many updates and fixes * fix broken link * fixes and upgrades on part 2 * part 3 fixed * missing indentation * part 4 dapp * fix typo * nft part 1 ok * part 3 * part 4 done * There is only one FA2 standard * Fix indentation so numbering doesnt breal * Edits --------- Co-authored-by: Tim McMackin --- .../build-an-nft-marketplace/part-1.md | 370 +++++---- .../build-an-nft-marketplace/part-2.md | 233 +++--- .../build-an-nft-marketplace/part-3.md | 738 +++++++++--------- .../build-an-nft-marketplace/part-4.md | 361 ++++----- docs/tutorials/dapp/part-1.md | 246 +++--- docs/tutorials/dapp/part-2.md | 87 +-- docs/tutorials/dapp/part-3.md | 96 +-- docs/tutorials/dapp/part-4.md | 689 ++++++++-------- 8 files changed, 1411 insertions(+), 1409 deletions(-) diff --git a/docs/tutorials/build-an-nft-marketplace/part-1.md b/docs/tutorials/build-an-nft-marketplace/part-1.md index 4cdd721aa..7bbc44248 100644 --- a/docs/tutorials/build-an-nft-marketplace/part-1.md +++ b/docs/tutorials/build-an-nft-marketplace/part-1.md @@ -1,7 +1,8 @@ --- title: 'Part 1: Minting tokens' authors: 'Benjamin Fuentes (Marigold)' -lastUpdated: 8th November 2023 +last_update: + date: 22 May 2024 --- To start working with the application, you create a Taqueria project and use it to deploy an FA2 contract. @@ -27,8 +28,8 @@ Follow these steps to set up a Taqueria project: 1. Install the `ligo/fa` library, which provides templates for creating FA2 tokens: ```bash - echo '{ "name": "app", "dependencies": { "@ligo/fa": "^1.0.9" } }' >> ligo.json - TAQ_LIGO_IMAGE=ligolang/ligo:1.1.0 taq ligo --command "install @ligo/fa" + echo '{ "name": "app", "dependencies": { "@ligo/fa": "^1.4.2" } }' >> ligo.json + TAQ_LIGO_IMAGE=ligolang/ligo:1.6.0 taq ligo --command "install @ligo/fa" ``` This command can take some time because it downloads and installs the `@ligo/fa` package. @@ -47,29 +48,25 @@ Follow these steps to create a contract that is based on the template and implem 1. Open the `contracts/nft.jsligo` file in any text editor and replace the default code with this code: ```jsligo - #import "@ligo/fa/lib/fa2/nft/nft.impl.jsligo" "FA2Impl" - - /* ERROR MAP FOR UI DISPLAY or TESTS - const errorMap : map = Map.literal(list([ - ["0", "Enter a positive and not null amount"], - ["1", "Operation not allowed, you need to be administrator"], - ["2", "You cannot sell more than your current balance"], - ["3", "Cannot find the offer you entered for buying"], - ["4", "You entered a quantity to buy than is more than the offer quantity"], - ["5", "Not enough funds, you need to pay at least quantity * offer price to get the tokens"], - ["6", "Cannot find the contract relative to implicit address"], - ])); - */ - - export type storage = { - administrators: set
, - ledger: FA2Impl.NFT.ledger, - metadata: FA2Impl.TZIP16.metadata, - token_metadata: FA2Impl.TZIP12.tokenMetadata, - operators: FA2Impl.NFT.operators - }; + #import "@ligo/fa/lib/fa2/nft/extendable_nft.impl.jsligo" "FA2Impl" + + /* ERROR MAP FOR UI DISPLAY or TESTS + const errorMap : map = Map.literal(list([ + ["0", "Enter a positive and not null amount"], + ["1", "Operation not allowed, you need to be administrator"], + ["2", "You cannot sell more than your current balance"], + ["3", "Cannot find the offer you entered for buying"], + ["4", "You entered a quantity to buy than is more than the offer quantity"], + ["5", "Not enough funds, you need to pay at least quantity * offer price to get the tokens"], + ["6", "Cannot find the contract relative to implicit address"], + ])); + */ + export type Extension = { administrators: set
}; + + export type storage = FA2Impl.storage; // extension administrators - type ret = [list, storage]; + + type ret = [list, storage]; ``` The first line of this code imports the FA2 template as the `FA2Impl` object. @@ -88,77 +85,51 @@ Follow these steps to create a contract that is based on the template and implem 1. Add code to implement the required `transfer`, `balance_of`, and `update_operators` entrypoints: ```jsligo - @entry - const transfer = (p: FA2Impl.TZIP12.transfer, s: storage): ret => { - const ret2: [list, FA2Impl.NFT.storage] = - FA2Impl.NFT.transfer( - p, - { - ledger: s.ledger, - metadata: s.metadata, - token_metadata: s.token_metadata, - operators: s.operators, - } - ); - return [ - ret2[0], - { - ...s, - ledger: ret2[1].ledger, - metadata: ret2[1].metadata, - token_metadata: ret2[1].token_metadata, - operators: ret2[1].operators, - } - ] - }; - - @entry - const balance_of = (p: FA2Impl.TZIP12.balance_of, s: storage): ret => { - const ret2: [list, FA2Impl.NFT.storage] = - FA2Impl.NFT.balance_of( - p, - { - ledger: s.ledger, - metadata: s.metadata, - token_metadata: s.token_metadata, - operators: s.operators, - } - ); - return [ - ret2[0], - { - ...s, - ledger: ret2[1].ledger, - metadata: ret2[1].metadata, - token_metadata: ret2[1].token_metadata, - operators: ret2[1].operators, - } - ] - }; - @entry - const update_operators = (p: FA2Impl.TZIP12.update_operators, s: storage): ret => { - const ret2: [list, FA2Impl.NFT.storage] = - FA2Impl.NFT.update_operators( - p, - { - ledger: s.ledger, - metadata: s.metadata, - token_metadata: s.token_metadata, - operators: s.operators, - } - ); - return [ - ret2[0], - { - ...s, - ledger: ret2[1].ledger, - metadata: ret2[1].metadata, - token_metadata: ret2[1].token_metadata, - operators: ret2[1].operators, - } - ] - }; + @entry + const transfer = (p: FA2Impl.TZIP12.transfer, s: storage): ret => { + const ret2: [list, storage] = FA2Impl.transfer(p, s); + return [ + ret2[0], + { + ...s, + ledger: ret2[1].ledger, + metadata: ret2[1].metadata, + token_metadata: ret2[1].token_metadata, + operators: ret2[1].operators, + } + ] + }; + + @entry + const balance_of = (p: FA2Impl.TZIP12.balance_of, s: storage): ret => { + const ret2: [list, storage] = FA2Impl.balance_of(p, s); + return [ + ret2[0], + { + ...s, + ledger: ret2[1].ledger, + metadata: ret2[1].metadata, + token_metadata: ret2[1].token_metadata, + operators: ret2[1].operators, + } + ] + }; + + @entry + const update_operators = (p: FA2Impl.TZIP12.update_operators, s: storage): ret => { + const ret2: [list, storage] = FA2Impl.update_operators(p, s); + return [ + ret2[0], + { + ...s, + ledger: ret2[1].ledger, + metadata: ret2[1].metadata, + token_metadata: ret2[1].token_metadata, + operators: ret2[1].operators, + } + ] + }; ``` You will add other entrypoints later, but these are the three entrypoints that every FA2 contract must have. @@ -177,48 +148,51 @@ Follow these steps to create a contract that is based on the template and implem 1. After those entrypoints, add code for the `mint` entrypoint: ```jsligo - @entry - const mint = ( - [token_id, name, description, symbol, ipfsUrl]: [ - nat, - bytes, - bytes, - bytes, - bytes - ], - s: storage - ): ret => { - if (! Set.mem(Tezos.get_sender(), s.administrators)) return failwith("1"); - const token_info: map = - Map.literal( - list( - [ - ["name", name], - ["description", description], - ["interfaces", (bytes `["TZIP-12"]`)], - ["artifactUri", ipfsUrl], - ["displayUri", ipfsUrl], - ["thumbnailUri", ipfsUrl], - ["symbol", symbol], - ["decimals", (bytes `0`)] - ] - ) - ) as map; - return [ - list([]) as list, - { - ...s, - ledger: Big_map.add(token_id, Tezos.get_sender(), s.ledger) as - FA2Impl.NFT.ledger, - token_metadata: Big_map.add( - token_id, - { token_id: token_id, token_info: token_info }, - s.token_metadata - ), - operators: Big_map.empty as FA2Impl.NFT.operators, - } - ] - }; + + @entry + const mint = ( + [token_id, name, description, symbol, ipfsUrl]: [ + nat, + bytes, + bytes, + bytes, + bytes + ], + s: storage + ): ret => { + if (! Set.mem(Tezos.get_sender(), s.extension.administrators)) return failwith( + "1" + ); + const token_info: map = + Map.literal( + list( + [ + ["name", name], + ["description", description], + ["interfaces", (bytes `["TZIP-12"]`)], + ["artifactUri", ipfsUrl], + ["displayUri", ipfsUrl], + ["thumbnailUri", ipfsUrl], + ["symbol", symbol], + ["decimals", (bytes `0`)] + ] + ) + ) as map; + return [ + list([]) as list, + { + ...s, + ledger: Big_map.add(token_id, Tezos.get_sender(), s.ledger) as + FA2Impl.ledger, + token_metadata: Big_map.add( + token_id, + { token_id: token_id, token_info: token_info }, + s.token_metadata + ), + operators: Big_map.empty as FA2Impl.operators, + } + ] + }; ``` The FA2 standard does not require a mint entrypoint, but you can add one if you want to allow the contract to create more tokens after it is originated. @@ -230,8 +204,8 @@ Follow these steps to create a contract that is based on the template and implem Then it creates a token metadata object with information from the parameters and adds it to the `token_metadata` big-map in the storage. Note that the `decimals` metadata field is set to 0 because the token is an NFT and therefore doesn't need any decimal places in its quantity. - Note that there is no built-in way to get the number of tokens in the contract code; the big-map does not have a function such as `keys()` or `length()`. - If you want to keep track of the number of tokens, you must add an additional element in the storage and increment it when tokens are created or destroyed. + Note that there is no built-in way to get the number of tokens in the contract code; the Bigmap does not have a function such as `keys()` or `length()`. + If you want to keep track of the number of tokens, you must add an element in the storage and increment it when tokens are created or destroyed. You can also get the number of tokens by analyzing the contract's storage from an off-chain application. 1. Run one of these commands to accept or decline LIGO's analytics policy: @@ -242,7 +216,7 @@ Follow these steps to create a contract that is based on the template and implem 1. Save the contract and compile it by running this command: ```bash - TAQ_LIGO_IMAGE=ligolang/ligo:1.1.0 taq compile nft.jsligo + TAQ_LIGO_IMAGE=ligolang/ligo:1.6.0 taq compile nft.jsligo ``` Taqueria compiles the contract to the file `artifacts/nft.tz`. @@ -253,43 +227,48 @@ Follow these steps to create a contract that is based on the template and implem ```jsligo #import "nft.jsligo" "Contract" - const default_storage : Contract.storage = { - administrators: Set.literal( - list(["tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" as address]) - ) as set
, - ledger: Big_map.empty as Contract.FA2Impl.NFT.ledger, - metadata: Big_map.literal( - list( - [ - ["", bytes `tezos-storage:data`], - [ - "data", - bytes - `{ - "name":"FA2 NFT Marketplace", - "description":"Example of FA2 implementation", - "version":"0.0.1", - "license":{"name":"MIT"}, - "authors":["Marigold"], - "homepage":"https://marigold.dev", - "source":{ - "tools":["Ligo"], - "location":"https://github.com/ligolang/contract-catalogue/tree/main/lib/fa2"}, - "interfaces":["TZIP-012"], - "errors": [], - "views": [] - }` - ] - ] - ) - ) as Contract.FA2Impl.TZIP16.metadata, - token_metadata: Big_map.empty as Contract.FA2Impl.TZIP12.tokenMetadata, - operators: Big_map.empty as Contract.FA2Impl.NFT.operators, - }; + #import "@ligo/fa/lib/fa2/nft/extendable_nft.impl.jsligo" "FA2Impl" + + const default_storage: Contract.storage = { + extension: { + administrators: Set.literal( + list(["tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" as address]) + ) as set
+ }, + ledger: Big_map.empty as FA2Impl.ledger, + metadata: Big_map.literal( + list( + [ + ["", bytes `tezos-storage:data`], + [ + "data", + bytes + `{ + "name":"FA2 NFT Marketplace", + "description":"Example of FA2 implementation", + "version":"0.0.1", + "license":{"name":"MIT"}, + "authors":["Marigold"], + "homepage":"https://marigold.dev", + "source":{ + "tools":["Ligo"], + "location":"https://github.com/ligolang/contract-catalogue/tree/main/lib/fa2"}, + "interfaces":["TZIP-012"], + "errors": [], + "views": [] + }` + ] + ] + ) + ) as FA2Impl.TZIP16.metadata, + token_metadata: Big_map.empty as FA2Impl.TZIP12.tokenMetadata, + operators: Big_map.empty as FA2Impl.operators, + }; + ``` This code sets the initial value of the storage. - In this case, the storage includes metadata about the contract and empty big-maps for the ledger, token metadata, and operators. + In this case, the storage includes metadata about the contract and empty Bigmaps for the ledger, token metadata, and operators. It sets the test account Alice as the administrator, which is the only account that can mint tokens. 1. Optional: Add your address as an administrator or replace Alice's address with your own. @@ -298,12 +277,12 @@ Follow these steps to create a contract that is based on the template and implem 1. Compile the contract: ```bash - TAQ_LIGO_IMAGE=ligolang/ligo:1.1.0 taq compile nft.jsligo + TAQ_LIGO_IMAGE=ligolang/ligo:1.6.0 taq compile nft.jsligo ``` 1. Use one of these options to set up a Ghostnet account to use to deploy (originate) the contract: - - To use your own account, open the `.taq/config.local.testing.json` file and add your public key, address, and private key, so the file looks like this: + - To use your account, open the `.taq/config.local.testing.json` file and add your public key, address, and private key, so the file looks like this: ```json { @@ -321,6 +300,8 @@ Follow these steps to create a contract that is based on the template and implem Then make sure that the account has tez on Ghostnet. Use the faucet at https://faucet.ghostnet.teztnets.com to get tez if you need it. + **OR** + - To let Taqueria generate an account for you, follow these steps: 1. Run the command `taq deploy nft.tz -e "testing"`, which will fail because you do not have an account configured in Taqueria. @@ -365,7 +346,7 @@ To save time, this tutorial provides a starter React application. taq generate types ./app/src ``` -1. If you are using a Mac, edit the default `dev` script in the `app/package.json` file to look like this: +1. **IF YOU ARE ON A MAC**, edit the default `dev` script in the `app/package.json` file to look like this: ```json { @@ -381,8 +362,7 @@ To save time, this tutorial provides a starter React application. ```bash cd app - yarn install - yarn dev + yarn && yarn dev ``` This application contains basic navigation and the ability to connect to wallets. @@ -391,7 +371,7 @@ To save time, this tutorial provides a starter React application. Because Taqueria automatically keeps track of your deployed contract, the application automatically accesses the contract and shows that there are no NFTs in it yet. The application looks like this: - ![The starter NFT marketplace application, showing no NFTs and a button to connect to wallets](/img/tutorials/nft-marketplace-starter.png) + ![The starter NFT marketplace application is showing no NFTs and a button to connect to wallets](/img/tutorials/nft-marketplace-starter.png) ## Adding a mint page @@ -406,7 +386,7 @@ The mint page uses a form that accepts information and an image and sends a tran {storage ? ( ) : ( - '' + "" )}
- + Mint a new collection ) : ( - '' + "" )}