From 93697c7b025c0f55afb71cd6191345b052bc6fd8 Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Tue, 12 Oct 2021 09:58:28 -0700 Subject: [PATCH 01/23] Kickstart the Flow tutorial This is a start of a tutorial outlining the petshop app I've created using Flow and nft.storage. See also: https://github.com/jochasinga/flowwow --- docs/tutorial/flow-nft-marketplace.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/tutorial/flow-nft-marketplace.md diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md new file mode 100644 index 0000000..22a5132 --- /dev/null +++ b/docs/tutorial/flow-nft-marketplace.md @@ -0,0 +1,12 @@ +--- +title: Building an NFT Marketplace on Flow +description: A step-by-step guide to build an NFT pet marketplace on Flow. +issueUrl: https://github.com/protocol/nft-website/issues/146 +related: + 'How to Create NFTs Like NBA Top Shot With Flow and IPFS': https://medium.com/pinata/how-to-create-nfts-like-nba-top-shot-with-flow-and-ipfs-701296944bf + 'Mint an NFT with IPFS': https://docs.ipfs.io/how-to/mint-nfts-with-ipfs/#a-short-introduction-to-nfts +--- + + # Building an NFT Marketplace on Flow + + From bf062b5b2bade335a488a77673c6f3a032d00360 Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Tue, 19 Oct 2021 10:47:47 -0400 Subject: [PATCH 02/23] Add some intro sections --- docs/tutorial/flow-nft-marketplace.md | 214 +++++++++++++++++++++++++- 1 file changed, 213 insertions(+), 1 deletion(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index 22a5132..63bdc3d 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -7,6 +7,218 @@ related: 'Mint an NFT with IPFS': https://docs.ipfs.io/how-to/mint-nfts-with-ipfs/#a-short-introduction-to-nfts --- - # Building an NFT Marketplace on Flow +# Building an NFT Marketplace on Flow + +This tutorial will teach you to create a simple NFT marketplace on [Flow][flow] from scratch. + +## Table of content + +- [Prerequisites](#prerequisites) +- [What you will learn](#what-you-will-learn) +- [Ownership and Resource](#ownership-and-resource) + - [Resource](#resource) + - [Accounts](#accounts) + - [Contract](#contract) + + +## Prerequisites + +Although this tutorial is built specifically for Flow, it will build up a general understanding of smart contract and NFTs. Therefore, you are expected to have a working JavaScript and [React.js][react] knowledge and a basic familiarity with blockchains, web3, and NFTs. + +You will need to install [Node.js][nodejs] and npm (comes with Node.js), [Flow command line tool][flow-cli], and [Docker compose][docker-compose] to follow this tutorial. + +> **💡 Why React?** +> React.js was chosen for this tutorial because it is the most widely-used UI library that +> arguably takes away the tediousness of setting up a JavaScript app +> and the state management. You are welcome to work with another library +> or framework if you are happy to take on the additional work of +> taking the [road less travelled][robert-frost-poem] approach. + +If you are not familiar with the concept of smart contract and NFTs, it is worth zipping through [blockchain][blockchain-basic] and [NFT][nft-basic] basics before diving in. + +## What you will learn + +You will learn the basic NFT building blocks from the ground up, such as: + + - Smart contract and the [Cadence language][cadence] + - User authentication + - Minting and storing tokens' metadata on [Filecoin/IPFS][nft-storage] + - Transferring tokens + +As we build a minimal version of the [Flowwow NFT pet store][flowwow] together. + +If you would like to go through a structured, step-by-step tutorial, the chapter-by-chapter project is available on [Github][mini-petstore]. + +## Ownership and Resource + +Let's get a leveled understanding here. A blockchain, or distributed ledger, is all about tracking *ownership* of some *thing*, or a resource. There is nothing new about the ledger part--Your bank account's ledger keeps track of how much money you *own* and how much is *spent* or transferred to a new owner at any time. The key components to a ledger are: + +1. The [resource](resource) at play, in this case money. +2. The [accounts](accounts) owning the resource, or the right to access it in some ways. +3. The [contract](contract) or rule to govern them. + +### Resource +The resource can be any *thing*--from money to crop to digital monster--as long as the type of resource is agreed upon by all accounts. + +### Accounts +Each account owns a ledger of its own to keep track of the spending (transferring) and imbursing (receiving) of the resource. + +### Contract +The contract is a rule governing how this "game" is played. Accounts that break the contract can be punished in some way. Normally, it is the authority like the bank who creates this contract for all accounts. + +Because these ledgers are owned and managed by trusted agents like your bank, when you transfer the ownership of a few dollars (`-$4.00`) to buy a cup of coffee, the bank needs to be consistent and update its ledger with new money that the cafe owner now owns (`+$4.00`). Because both your and the cafe owner's ledgers are not visible to both of you and the `$4.00` money is purely digital, there is nothing to gaurantee that the bank to mistakenly update the ledger with the incorrect value. + +> **💡 Your bank owes you** +> If you have a saving account with some money in it, you might be loaning +> your money to your bank. You are trusting your bank to have your money when +> you want to withdraw. Meanwhile, your money is just part of the stream of +> your bank is free to do anything with. If you had a billion dollars in your +> bank and you want to withdraw tomorrow, your teller might freak out. + +What is novel about blockchain is the distributed part. Because the ledger is *decentralized*, there is central authority like a bank for you to trust with the bookkeeping. You are simply trusting other people running the same ledger software as you to keep track of everyone's book. Think of it as a sport game without the referee or empire where any dispute is distributed to all the audience to judge. The only difference is these audience are also playing in the arena, with the stake that makes it extremely bad for them to cheat. This way, any small inconsistencies are likely caught and rejected fair and square. You are no longer trusting your bank. The eternal flow of ownerships hence becomes *trustless* because everyone is doing what's best for themselves. + +"Why such emphasis on ownership?" You asked. This is because Flow had baked the concept of resource ownership right into the smart contract core. In fact, it is why Flow is one of the easiest blockchain for building NFT apps, which you shall see very soon. + +## Cadence + +Like Solidity for Ethereum, we use [Cadence][cadence] to code smart contracts, transactions, and scripts for Flow. Cadence's design is inspired by [Rust][rust] and the *move semantic*. Basically, the runtime tracks when a resource is being *moved* from a variable to another variable and make sure it can never be used twice in the program. + +Three types of Cadence program you will be writing are [contracts](contract), [transactions][transaction], and [scripts][script]. + +### Contract + +A contract is an initial program that gets deployed to the blockchain, initiating the logic for your app and allow access to resources you create and the capabilities that come with them. + +Two most common constructs in a contract are resources and interfaces. + +#### Resources + +Resources are items stored in user accounts that are accessible +through access control measures defined in the contract. They are usually the assets being tracked. They are akin to classes or structs in some languages. + +#### Interfaces + +Interfaces define the behaviors or capabilities of resources. They are akin to interfaces in some languages. They are usually implemented by resources. + +Here is an example of an `NFT` resource and a `Ownable` interface (ala [ERC721][erc-721]) in the `PetStore` contract: + +```cadence + +pub contract PetStore { + + // A map recording owners of NFTs + pub var owners: {UInt64 : Address} + + // NFT resource implements Transferrable + pub resource NFT: Transferrable { + + // Unique id for each NFT. + pub let id: UInt64 + + // Constructor method. + init(initId: UInt64) { + self.id = initId + } + + pub fun owner(): Address { + return owners[self.id]! + } + + pub fun transferTo(recipient: Address) { + // Code to transfer this NFT resource to the recipient's address. + } + } + + // A Transferrable interface declaring some methods or "capabilities" + pub resource interface Transferrable { + pub fun owner(): Address + pub fun transferTo(recipient: Address) + } +} + +``` + +Note the access modifier `pub` before the definition of `contract`, `resource`, and variable `id`. This declares full access to all user accounts. Writing a Cadence contract revolves around designing access control. + +### Transaction + +Transactions tell the on-chain contract to change the state of the chain. Because the change is synchronized throughout the peers and cannot be undone, like Ethereum, a transaction is considered a *write* operation that incurs a network gas fee. They also require one or more account to sign and authorize. +Minting and transferring tokens are transactions. + +Here is an example of a transaction, requiring a current account who owns the `NFT` resource to authorize a certain action (in this case, just logging `"Hello, transaction"`). + +```cadence + +transaction(tokenId: UInt64, recipientAddr: Address) { + + // The field holds the NFT as it is being transferred. + let token: @PetStore.NFT + + // Takes the sending account as a parameter to + prepare(acc: AuthAccount) { + // This is the code that requires a signature, such as + // withdrawing a token from the signing account. + } + + execute { + // This is the code that does not require a signature. + log("Hello, transaction") + } +} + +``` + +### Script + +Scripts are Cadence programs that are run on the client to *read* the state of the chain. Therefore, they do not incur any gas fee and do not need an account to sign them. + +Here is an example of a script reading an NFT's current owner's account address by accessing the map field named `owners` on the contract by an `NFT` id: + +```cadence + +// Take a target NFT's id as a parameter and return an Address +// of the current owner of that NFT. +pub fun main(id: UInt64) : Address { + return PetStore.owner[id]! +} + +``` + +Both transactions and scripts are invoked on the client side. + +> **💡 Interface in other languages** +> If you have programmed in a typed language like Java, +> Rust, or TypeScript, you might be familiar with the +> interface, which is a description of capabilities +> versus a concrete entity like a class or struct. +> In Cadence, a resource is similar to a class or struct +> and an interface is the same as those in Java or Rust. + +## Getting started + +Now that we have a basic understanding of how to think in Flow's way, we are ready to start building the mini NFT pet store! + +If you have [cloned the structured tutorial][mini-petstore], in your shell, type `cd 1_getting_started` and run `npm install` to get started. + +Otherwise, create a new React app with `npx create-react-app petstore`. + +## TBC + + [flow]: https://www.onflow.org/ + [flow-cli]: https://www.onflow.org/cli/ + [docker-compose]: https://docker.com/compose/ + [blockchain-basic]: ../concepts/blockchain.md + [nft-basic]: ../concepts/non-fungible-tokens.md + [nodejs]: https://nodejs.org/ + [cadence]: https://docs.onflow.org/cadence/language/ + [nft-storage]: https://nft.storage/ + [flowwow]: https://github.com/jochasinga/flowwow/ + [react]: https://reactjs.org/ + [robert-frost-poem]: https://www.poetryfoundation.org/poems/44272/the-road-not-taken + [mini-petstore]: https://github.com/jochasinga/flow-react + [rust]: https://rust-lang.org/ + [diem]: https://diem.org/ + [erc-721]: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721 + From 49823f2783bca6e56d55c582635c0484d54b3f9f Mon Sep 17 00:00:00 2001 From: Pancy Date: Wed, 20 Oct 2021 13:16:35 -0700 Subject: [PATCH 03/23] Apply suggestions from code review Co-authored-by: Yusef Napora --- docs/tutorial/flow-nft-marketplace.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index 63bdc3d..ccd3778 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -34,13 +34,13 @@ You will need to install [Node.js][nodejs] and npm (comes with Node.js), [Flow c > or framework if you are happy to take on the additional work of > taking the [road less travelled][robert-frost-poem] approach. -If you are not familiar with the concept of smart contract and NFTs, it is worth zipping through [blockchain][blockchain-basic] and [NFT][nft-basic] basics before diving in. +If you are not familiar with the concept of smart contracts and NFTs, it is worth zipping through [blockchain][blockchain-basic] and [NFT][nft-basic] basics before diving in. ## What you will learn You will learn the basic NFT building blocks from the ground up, such as: - - Smart contract and the [Cadence language][cadence] + - Smart contracts and the [Cadence language][cadence] - User authentication - Minting and storing tokens' metadata on [Filecoin/IPFS][nft-storage] - Transferring tokens @@ -66,7 +66,7 @@ Each account owns a ledger of its own to keep track of the spending (transferrin ### Contract The contract is a rule governing how this "game" is played. Accounts that break the contract can be punished in some way. Normally, it is the authority like the bank who creates this contract for all accounts. -Because these ledgers are owned and managed by trusted agents like your bank, when you transfer the ownership of a few dollars (`-$4.00`) to buy a cup of coffee, the bank needs to be consistent and update its ledger with new money that the cafe owner now owns (`+$4.00`). Because both your and the cafe owner's ledgers are not visible to both of you and the `$4.00` money is purely digital, there is nothing to gaurantee that the bank to mistakenly update the ledger with the incorrect value. +Because these ledgers are owned and managed by trusted agents like your bank, when you transfer the ownership of a few dollars (`-$4.00`) to buy a cup of coffee, the bank needs to be consistent and update its ledger with new money that the cafe owner now owns (`+$4.00`). Because both your and the cafe owner's ledgers are not visible to both of you and the `$4.00` money is purely digital, there is nothing to guarantee that the bank to mistakenly update the ledger with the incorrect value. > **💡 Your bank owes you** > If you have a saving account with some money in it, you might be loaning @@ -75,19 +75,19 @@ Because these ledgers are owned and managed by trusted agents like your bank, wh > your bank is free to do anything with. If you had a billion dollars in your > bank and you want to withdraw tomorrow, your teller might freak out. -What is novel about blockchain is the distributed part. Because the ledger is *decentralized*, there is central authority like a bank for you to trust with the bookkeeping. You are simply trusting other people running the same ledger software as you to keep track of everyone's book. Think of it as a sport game without the referee or empire where any dispute is distributed to all the audience to judge. The only difference is these audience are also playing in the arena, with the stake that makes it extremely bad for them to cheat. This way, any small inconsistencies are likely caught and rejected fair and square. You are no longer trusting your bank. The eternal flow of ownerships hence becomes *trustless* because everyone is doing what's best for themselves. +What is novel about blockchain is the distributed part. Because the ledger is *decentralized*, there is no central authority like a bank for you to trust with the bookkeeping. You are simply trusting other people running the same ledger software as you to keep track of everyone's book. Think of it as a sport game without the referee or umpire where any dispute is distributed to all the audience to judge. The only difference is these audience members are also playing in the arena, with the stake that makes it extremely bad for them to cheat. This way, any small inconsistencies are likely caught and rejected fair and square. You are no longer trusting your bank. The eternal flow of ownerships hence becomes *trustless* because everyone is doing what's best for themselves. -"Why such emphasis on ownership?" You asked. This is because Flow had baked the concept of resource ownership right into the smart contract core. In fact, it is why Flow is one of the easiest blockchain for building NFT apps, which you shall see very soon. +"Why such emphasis on ownership?" you may ask. This is because Flow has the concept of resource ownership baked right into the smart contract core. In fact, it is why Flow is one of the easiest blockchains for building NFT apps, which you shall see very soon. ## Cadence -Like Solidity for Ethereum, we use [Cadence][cadence] to code smart contracts, transactions, and scripts for Flow. Cadence's design is inspired by [Rust][rust] and the *move semantic*. Basically, the runtime tracks when a resource is being *moved* from a variable to another variable and make sure it can never be used twice in the program. +Like Solidity for Ethereum, we use [Cadence][cadence] to code smart contracts, transactions, and scripts for Flow. Cadence's design is inspired by [Rust][rust] and the *move semantic*. Basically, the runtime tracks when a resource is being *moved* from a variable to another variable and makes sure it can never be used twice in the program. -Three types of Cadence program you will be writing are [contracts](contract), [transactions][transaction], and [scripts][script]. +The three types of Cadence program you will be writing are [contracts](contract), [transactions][transaction], and [scripts][script]. ### Contract -A contract is an initial program that gets deployed to the blockchain, initiating the logic for your app and allow access to resources you create and the capabilities that come with them. +A contract is an initial program that gets deployed to the blockchain, initiating the logic for your app and allowing access to resources you create and the capabilities that come with them. Two most common constructs in a contract are resources and interfaces. @@ -142,7 +142,7 @@ Note the access modifier `pub` before the definition of `contract`, `resource`, ### Transaction -Transactions tell the on-chain contract to change the state of the chain. Because the change is synchronized throughout the peers and cannot be undone, like Ethereum, a transaction is considered a *write* operation that incurs a network gas fee. They also require one or more account to sign and authorize. +Transactions tell the on-chain contract to change the state of the chain. Because the change is synchronized throughout the peers and cannot be undone, like Ethereum, a transaction is considered a *write* operation that incurs a network gas fee. They also require one or more accounts to sign and authorize. Minting and transferring tokens are transactions. Here is an example of a transaction, requiring a current account who owns the `NFT` resource to authorize a certain action (in this case, just logging `"Hello, transaction"`). From 3c9ddef867202e1ace3e55998757d4749ca32c57 Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Wed, 20 Oct 2021 16:19:27 -0400 Subject: [PATCH 04/23] Add first part of contract section --- docs/tutorial/flow-nft-marketplace.md | 97 ++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index ccd3778..b8bd51d 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -194,13 +194,100 @@ Both transactions and scripts are invoked on the client side. > In Cadence, a resource is similar to a class or struct > and an interface is the same as those in Java or Rust. -## Getting started +## NFT Pet Store Now that we have a basic understanding of how to think in Flow's way, we are ready to start building the mini NFT pet store! If you have [cloned the structured tutorial][mini-petstore], in your shell, type `cd 1_getting_started` and run `npm install` to get started. -Otherwise, create a new React app with `npx create-react-app petstore`. +Otherwise, create a new React app by typing `npx create-react-app petstore` on your shell, then enter the directory with `cd petstore`. + +### Project structure + +If you are working on the cloned project, you can skip this section. + +Because `create-react-app` forbids importing code from outside of the `src` directory, the majority of the code we write will be inside this directory. + +Create a directory named `flow` inside `src` directory, and create three more named `contract`, `transaction`, and `script` under `flow`. This can be combined into one command: + +```shell +$ mkdir -p src/flow/{contract,transaction,script} +``` + +As you might have guessed, each directory will contain the corresponding Cadence code for each type of interaction. + +Now, in each of these directories, create a Cadence file with the following names: `contract/PetStore.cdc`, `transaction/MintToken.cdc`, and `script/GetTokenIds.cdc`. + +The structure of the `src` directory should now look like this: + +```shell +. +|-- flow +| |-- contract +| | | +| | `-- PetStore.cdc +| |-- script +| | | +| | `-- GetTokenIds.cdc +| `-- transaction +| | +| `-- MintToken.cdc +| +... + +``` + +### `PetStore` contract + +We will be taking some time to write the contract while also learn Cadence. Once you have grasped it, writing transactions and scripts will be relatively easy. + +First, create the contract structure and define an `NFT` resource: + +```cadence + +pub contract PetStore { + + // A map recording owners of NFTs + pub var owners: [Address?] + + pub resource NFT { + + // Unique id for each NFT. + pub let id: UInt64 + + // Constructor method. + init(initId: UInt64) { + self.id = initId + } + } +} + +``` + +Note that we have also declared a variable named `owners` of type [Variable-sized Array][cdc-array-type] which contains [Optional][cdc-optional-type] type that either is an [Address][cdc-address-type] or `nil`. We will use `owners` to keep track of all the current owners of NFTs that will be minted on this contract globally. And because each `NFT` has an `id` of type [UInt64][cdc-integer-type], an `Array` is perfect for keeping track of the owners' `Addresses`. + +However, because we start the NFT id from 1 instead of 0, we want a way to store a `nil` Address in the first position of `owners` Array. We decide to make our Array to store an Optional Address type (`Address?`). + +> **💡 The Billion-dollar mistake** +> In language like Python, JavaScript, and Java, an empty +> value is represented by `None`, `null` or `undefined`, and +> `null`, respectively. However, this value is *not* a type, +> and any data can be empty at any time, making it extremely +> risky. For example, the `NullPointerException` is infamous for +> crashing Java programs. +> +> Some strongly-typed language such as Ocaml, Haskell, Rust, and +> Cadence have a class of type called `Optional` (or `Maybe` in +> Haskell) to represent a value that may be empty. This means it +> is impossible to have a `null` value if the type isn't Optional. + + + + + + + + ## TBC @@ -219,6 +306,10 @@ Otherwise, create a new React app with `npx create-react-app petstore`. [rust]: https://rust-lang.org/ [diem]: https://diem.org/ [erc-721]: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721 - + [cdc-array-type]: https://docs.onflow.org/cadence/language/values-and-types/#array-types + [cdc-optional-type]: https://docs.onflow.org/cadence/language/values-and-types/#optionals + [cdc-address-type]: https://docs.onflow.org/cadence/language/values-and-types/#addresses + [cdc-integer-type]: +https://docs.onflow.org/cadence/language/values-and-types/#integers From 62ed84e41f74427ed6c5acdb14106d9d2991d833 Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Thu, 21 Oct 2021 00:07:16 -0400 Subject: [PATCH 05/23] Add NFTCollection type --- docs/tutorial/flow-nft-marketplace.md | 175 ++++++++++++++++++++++---- 1 file changed, 153 insertions(+), 22 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index b8bd51d..0649c45 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -96,6 +96,8 @@ Two most common constructs in a contract are resources and interfaces. Resources are items stored in user accounts that are accessible through access control measures defined in the contract. They are usually the assets being tracked. They are akin to classes or structs in some languages. +Resources can only be in one place at a time, and they are said to be *moved* rather than *assigned*. + #### Interfaces Interfaces define the behaviors or capabilities of resources. They are akin to interfaces in some languages. They are usually implemented by resources. @@ -247,45 +249,174 @@ First, create the contract structure and define an `NFT` resource: pub contract PetStore { - // A map recording owners of NFTs - pub var owners: [Address?] + // An array that stores NFT owners + pub var owners: {UInt64: Address} + + // Constructor method + init() { + self.owners = {} + } pub resource NFT { - // Unique id for each NFT. + // Unique ID for each NFT pub let id: UInt64 - // Constructor method. - init(initId: UInt64) { - self.id = initId + // String mapping to hold metadata + pub var metadata: {String: String} + + // Constructor method + init(id: UInt64) { + self.id = id + self.metadata = {} } } } ``` -Note that we have also declared a variable named `owners` of type [Variable-sized Array][cdc-array-type] which contains [Optional][cdc-optional-type] type that either is an [Address][cdc-address-type] or `nil`. We will use `owners` to keep track of all the current owners of NFTs that will be minted on this contract globally. And because each `NFT` has an `id` of type [UInt64][cdc-integer-type], an `Array` is perfect for keeping track of the owners' `Addresses`. +Note that we have declared a [Dictionary][cdc-dict-type] and store in a variable named `owners`. This dictionary has the type `{UInt64: Address}` which maps [unsigned 64-bit integers][cdc-integer-type] to users' [Addresses][cdc-address-type]. We will use `owners` to keep track of all the current owners of NFTs globally. + +Next, we instantialize the array with a `nil` stored as the first element in the `init()` constructor method. In Cadence, it is an error + +Also note that the `owners` variable is prepended by a `var` keyword, while the `id` variable is prepended by a `let` keyword. In Cadence, a mutable variable is defined using `var` while an immutable one is defined with `let`. + +> **💡 Immutable vs mutable** +> In Cadence, a variable stores a mutable variable that can be changed +> later in the program while a *binding* binds an immutable value that +> cannot be changed. + +In the body of `NFT` resource, we declare `id` field and a constructor method to assign the `id` to the `NFT` instance. + +Next, we create an `NFTReceiver` interface that defines the capabilities or methods of a receiver of NFTs, or the rest of the users who are not the contract user. + +To reiterate, an interface is *not* an instance of an object, like a user account. It is a set of behaviors, or capabilities in Cadence's speak, that a resource can implement to become capable of certain actions, like withdrawing and depositing tokens. + +```cadence + +pub contract PetStore { + + // ... The previous code ... + + pub resource interface NFTReceiver { + + // Withdraw a token by its ID and returns the token. + pub fun withdraw(id: UInt64): @NFT + + // Deposit an NFT to this NFTReceiver instance. + pub fun deposit(token: @NFT, metadata: {String: String}) + + // Get all NFT IDs belonging to this NFTReceiver instance. + pub fun getTokenIds(): [UInt64] + + // Get the metadata of an NFT instance by its ID. + pub fun getTokenMetadata(id: UInt64) : {String: String} + + // Update the metadata of an NFT. + pub fun updateTokenMetadata(id: UInt64, metadata: {String: String}) + } +} + +``` + +Let's not get over ourselves and go through the `NFTReceiver` interface line-by-line. + +The `withdraw(id: UInt64): @NFT` method takes an NFT's `id`, withdraws a token, or an *instance* of `NFT` resource, which is prepended with a `@` to mean reference to a resource. -However, because we start the NFT id from 1 instead of 0, we want a way to store a `nil` Address in the first position of `owners` Array. We decide to make our Array to store an Optional Address type (`Address?`). +The `deposit(token: @NFT, metadata: {String : String})` method takes a token reference type and the metadata dictionary with a `String` key and `String` value, and deposits or transfers the `@NFT` to the current instance of `NFTReceiver`. -> **💡 The Billion-dollar mistake** -> In language like Python, JavaScript, and Java, an empty -> value is represented by `None`, `null` or `undefined`, and -> `null`, respectively. However, this value is *not* a type, -> and any data can be empty at any time, making it extremely -> risky. For example, the `NullPointerException` is infamous for -> crashing Java programs. -> -> Some strongly-typed language such as Ocaml, Haskell, Rust, and -> Cadence have a class of type called `Optional` (or `Maybe` in -> Haskell) to represent a value that may be empty. This means it -> is impossible to have a `null` value if the type isn't Optional. +The `getTokenIds(): [UInt64]` method access all tokens' IDs owned by the instance of the `NFTReceiver`. +The `getTokenMetadata(id: UInt64) : {String : String}` method takes an ID of an `NFT`, read the metadata, and return the it as a dictionary. +Now let's create an `NFTCollection` resource to implement the `NFTReceiver` interface. Think of this as a "vault" where NFTs are deposited to and withdrawn from. + +```cadence + +pub contract PetStore { + // ... The previous code ... + pub resource NFTCollection: NFTReceiver { + // Keeps track of NFTs this collection. + pub var ownedNFTs: @{UInt64: NFT} + + // Constructor + init() { + self.ownedNFTs <- {} + } + + // Destructor + destroy() { + destroy self.ownedNFTs + } + + // Withdraws and return an NFT token. + pub fun withdraw(id: UInt64): @NFT { + let token <- self.ownedNFTs.remove(at: id) + return <- token! + } + + // Deposits a token to this NFTCollection instance. + pub fun deposit(token: @NFT) { + self.ownedNFTs[token.id] <-! token + } + + // Returns an array of the IDs that are in this collection. + pub fun getTokenIds(): [UInt64] { + return self.ownedNFTs.keys + } + + // Returns the metadata of an NFT based on the ID. + pub fun getTokenMetadata(id: UInt64): {String : String} { + let token <- self.ownedNFTs[id]! + return token.metadata + } + + // Updates the metadata of an NFT based on the ID. + pub fun updateTokenMetadata(id: UInt64, metadata: {String: String}) { + self.metadataObjs[id] = metadata + let token <- self.ownedNFTs[id]! + token.metadata = metadata + } + } +} + +``` + +First we declare a mutable dictionary and store it in a variable named `ownedNFTs`. This dictionary stores the NFTs for this collection by mapping the ID to NFT resource. Note that because the dictionary stores `@NFT` resource, we prepend the type with `@`, making itself a resource too. + +In the contructor method, `init()`, we instantiate the `ownedNFTs` with an empty dictionary. We also need a `destroy()` destructor method to destroy the dictionary. + +The `withdraw(id: UInt64): @NFT` method remove an NFT from the collection's `ownedNFTs` array and return it. + +```cadence + +pub fun withdraw(id: UInt64): @NFT { + // Remove an NFT from the array and "move" it into a variable. + // Then return the NFT stored in the token variable. + let token <- self.ownedNFTs.remove(at: id) + return <- token! +} + +``` + +Note the left-pointing arrow character. It is knowned as a *move* symbol, and we use it to move a resource around. Once a resource has been moved, it can no longer be used from the old variable. + +Here is an example: + +```cadence + +let token <- create NFT(id: 1) +let nft <- token + +// Error: cannot access token any longer +log(token) + +``` +Because resources are core to Cadence, their types are annotated with a `@` to make them explicit. For instance, `@NFT` and `@NFTCollection` are two resource types. @@ -306,10 +437,10 @@ However, because we start the NFT id from 1 instead of 0, we want a way to store [rust]: https://rust-lang.org/ [diem]: https://diem.org/ [erc-721]: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721 + [cdc-dict-type]: https://docs.onflow.org/cadence/language/values-and-types/#dictionaries [cdc-array-type]: https://docs.onflow.org/cadence/language/values-and-types/#array-types [cdc-optional-type]: https://docs.onflow.org/cadence/language/values-and-types/#optionals [cdc-address-type]: https://docs.onflow.org/cadence/language/values-and-types/#addresses - [cdc-integer-type]: -https://docs.onflow.org/cadence/language/values-and-types/#integers + [cdc-integer-type]: https://docs.onflow.org/cadence/language/values-and-types/#integers From 0a44d91902857d55dd1a82d8332f005993875daa Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Thu, 21 Oct 2021 19:48:48 -0400 Subject: [PATCH 06/23] Finish the NFTCollection part --- docs/tutorial/flow-nft-marketplace.md | 68 ++++++++++++++++----------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index 0649c45..31dabff 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -304,7 +304,7 @@ pub contract PetStore { pub fun withdraw(id: UInt64): @NFT // Deposit an NFT to this NFTReceiver instance. - pub fun deposit(token: @NFT, metadata: {String: String}) + pub fun deposit(token: @NFT) // Get all NFT IDs belonging to this NFTReceiver instance. pub fun getTokenIds(): [UInt64] @@ -340,7 +340,7 @@ pub contract PetStore { pub resource NFTCollection: NFTReceiver { // Keeps track of NFTs this collection. - pub var ownedNFTs: @{UInt64: NFT} + access(account) var ownedNFTs: @{UInt64: NFT} // Constructor init() { @@ -354,7 +354,7 @@ pub contract PetStore { // Withdraws and return an NFT token. pub fun withdraw(id: UInt64): @NFT { - let token <- self.ownedNFTs.remove(at: id) + let token <- self.ownedNFTs.remove(key: id) return <- token! } @@ -370,55 +370,64 @@ pub contract PetStore { // Returns the metadata of an NFT based on the ID. pub fun getTokenMetadata(id: UInt64): {String : String} { - let token <- self.ownedNFTs[id]! - return token.metadata + let metadata = self.ownedNFTs[id]?.metadata + return metadata! } // Updates the metadata of an NFT based on the ID. pub fun updateTokenMetadata(id: UInt64, metadata: {String: String}) { - self.metadataObjs[id] = metadata - let token <- self.ownedNFTs[id]! - token.metadata = metadata + for key in metadata.keys { + self.ownedNFTs[id]?.metadata?.insert(key: key, metadata[key]!) + } } } } ``` -First we declare a mutable dictionary and store it in a variable named `ownedNFTs`. This dictionary stores the NFTs for this collection by mapping the ID to NFT resource. Note that because the dictionary stores `@NFT` resource, we prepend the type with `@`, making itself a resource too. +That's a handful of new code. It will soon become natural to you with patience. -In the contructor method, `init()`, we instantiate the `ownedNFTs` with an empty dictionary. We also need a `destroy()` destructor method to destroy the dictionary. +First we declare a mutable dictionary and store it in a variable named `ownedNFTs`. Note the new access modifier `pub(set)`, which gives public write access to the users. + +This dictionary stores the NFTs for this collection by mapping the ID to NFT resource. Note that because the dictionary stores `@NFT` resources, we prepend the type with `@`, making itself a resource too. + +In the contructor method, `init()`, we instantiate the `ownedNFTs` with an empty dictionary. A resource also need a `destroy()` destructor method to make sure it is being freed. + +> **💡 Nested Resource** +> A [composite structure][cdc-comp-type] including a dictionary +> can store resources, but when they do they will be treated as +> resources. Which means they need to be *moved* rather than +> *assigned* and their type will be annotated with `@`. The `withdraw(id: UInt64): @NFT` method remove an NFT from the collection's `ownedNFTs` array and return it. -```cadence +The left-pointing arrow character is knowned as a *move* symbol, and we use it to move a resource around. Once a resource has been moved, it can no longer be used from the old variable. -pub fun withdraw(id: UInt64): @NFT { - // Remove an NFT from the array and "move" it into a variable. - // Then return the NFT stored in the token variable. - let token <- self.ownedNFTs.remove(at: id) - return <- token! -} +Note the `!` symbol after the `token` variable. It [force-unwraps][cdc-force-unwrap] the `Optional` value. If the value turns out to be `nil`, the program panics and crashes. -``` +Because resources are core to Cadence, their types are annotated with a `@` to make them explicit. For instance, `@NFT` and `@NFTCollection` are two resource types. -Note the left-pointing arrow character. It is knowned as a *move* symbol, and we use it to move a resource around. Once a resource has been moved, it can no longer be used from the old variable. +The `deposit(token: @NFT)` function takes the `@NFT` resource as a parameter and store it in the `ownedNFTs` array in this `@NFTCollection` instance. -Here is an example: +The `!` symbol reappears here, but now it's after the move arrow `<-!`. This is called a [force-move or force-assign][cdc-force-assign] operator, which only move a resource to a variable if the variable is `nil`. Otherwise, the program panics. -```cadence +The `getTokenIds(): [UInt64]` method simply reads all the `UInt64` keys of the `ownedNFTs` dictionary and returns them as an array. -let token <- create NFT(id: 1) -let nft <- token +The `getTokenMetadata(id: UInt64): {String : String}` method reads the `metadata` field of an `@NFT` stored by its ID in the `ownedNFTs` dictionary and returns it. -// Error: cannot access token any longer -log(token) +The `updateTokenMetadata(id: UInt64, metadata: {String: String})` method is a bit more involved. -``` +```cadence -Because resources are core to Cadence, their types are annotated with a `@` to make them explicit. For instance, `@NFT` and `@NFTCollection` are two resource types. +for key in metadata.keys { + self.ownedNFTs[id]?.metadata?.insert(key: key, metadata[key]!) +} +``` + +In the body of the method, we loop over all the keys of the given metadata, inserting into the current metadata dictionary the new value. Note the `?` in the call chain. It is used with `Optional`s values to keep going down the call chain only if the value is not `nil`. +Now we have implemented the `@NFTReceiver` interface for the `@NFTCollection` resource. ## TBC @@ -438,9 +447,12 @@ Because resources are core to Cadence, their types are annotated with a `@` to m [diem]: https://diem.org/ [erc-721]: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721 [cdc-dict-type]: https://docs.onflow.org/cadence/language/values-and-types/#dictionaries + [cdc-force-unwrap]: https://docs.onflow.org/cadence/language/values-and-types/#force-unwrap- [cdc-array-type]: https://docs.onflow.org/cadence/language/values-and-types/#array-types [cdc-optional-type]: https://docs.onflow.org/cadence/language/values-and-types/#optionals [cdc-address-type]: https://docs.onflow.org/cadence/language/values-and-types/#addresses + [cdc-comp-type]: https://docs.onflow.org/cadence/language/composite-types/ [cdc-integer-type]: https://docs.onflow.org/cadence/language/values-and-types/#integers - + [cdc-force-assign]: +https://docs.onflow.org/cadence/language/values-and-types/#force-assignment-operator-- From 69600feb3bdcdf12ba96ae326db113d847ad4b0a Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Mon, 25 Oct 2021 13:38:20 -0700 Subject: [PATCH 07/23] Wrap up contract section --- docs/tutorial/flow-nft-marketplace.md | 158 +++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 5 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index 31dabff..82b5d56 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -288,6 +288,8 @@ Also note that the `owners` variable is prepended by a `var` keyword, while the In the body of `NFT` resource, we declare `id` field and a constructor method to assign the `id` to the `NFT` instance. +### `NFTReceiver` + Next, we create an `NFTReceiver` interface that defines the capabilities or methods of a receiver of NFTs, or the rest of the users who are not the contract user. To reiterate, an interface is *not* an instance of an object, like a user account. It is a set of behaviors, or capabilities in Cadence's speak, that a resource can implement to become capable of certain actions, like withdrawing and depositing tokens. @@ -296,7 +298,7 @@ To reiterate, an interface is *not* an instance of an object, like a user accoun pub contract PetStore { - // ... The previous code ... + // ... The @NFT code ... pub resource interface NFTReceiver { @@ -329,13 +331,17 @@ The `getTokenIds(): [UInt64]` method access all tokens' IDs owned by the instanc The `getTokenMetadata(id: UInt64) : {String : String}` method takes an ID of an `NFT`, read the metadata, and return the it as a dictionary. +### `NFTCollection` + Now let's create an `NFTCollection` resource to implement the `NFTReceiver` interface. Think of this as a "vault" where NFTs are deposited to and withdrawn from. ```cadence pub contract PetStore { - // ... The previous code ... + // ... The @NFT code ... + + // ... The @NFTReceiver code ... pub resource NFTCollection: NFTReceiver { @@ -427,7 +433,149 @@ for key in metadata.keys { In the body of the method, we loop over all the keys of the given metadata, inserting into the current metadata dictionary the new value. Note the `?` in the call chain. It is used with `Optional`s values to keep going down the call chain only if the value is not `nil`. -Now we have implemented the `@NFTReceiver` interface for the `@NFTCollection` resource. +We have successfully implemented the `@NFTReceiver` interface for the `@NFTCollection` resource. + +### `NFTMinter` + +The last and very important component for our `PetStore` contract is `@NFTMinter` resource, which will contain an exclusive code for the contract owner to mint all the tokens. Without it, our store will not be able to mint any pet for the users. It is very simplistic though, since we have already blaze through the more complex components. Its only `mint(): @NFT` method creates an `@NFT` resource, give it an ID, save the address of the first owner to the contract (which is the address of the contract owner, although you could change it to mint and transfer to the creator's address in one step), increment the universal ID counter, and return the new token. + + +```cadence + +pub contract PetStore { + + // ... NFT code ... + + // ... NFTReceiver code ... + + // ... NFTCollection code ... + + access(self) resource NFTMinter { + + // Declare a global variable to count ID. + pub var idCount: UInt64 + + init() { + // Instantialize the ID counter. + self.idCount = 1 + } + + pub fun mint(): @NFT { + + // Create a new @NFT resource with the current ID. + let token <- create NFT(id: self.idCount) + + // Save the current owner's address to the dictionary. + PetStore.owners[self.idCount] = PetStore.account.address + + // Increment the ID + self.idCount = self.idCount + 1 as UInt64 + + return <-token + } + } +} + +``` + +By now, we have all the bolts and nuts we need for the contract. The only thing that is missing is a way to initialize this contract once during the deployment. Let's create a constructor method to create an empty `@NFTCollection` instance for the deployer of the contract (you) so it is possible for the contract owner to mint and store NFTs from the contract. As we go over this last hurdle, we will also learn about the last important concept Cadence: [Storage and domains][cdc-domain]. + + +```cadence + +pub contract PetStore { + + // ... @NFT code ... + + // ... @NFTReceiver code ... + + // ... @NFTCollection code ... + + // This contract constructor is called once when the contract is deployed. + // It does the following: + // + // - Creating an empty Collection for the deployer of the collection so + // the owner of the contract can mint and own NFTs from that contract. + // + // - The `Collection` resource is published in a public location with reference + // to the `NFTReceiver` interface. This is how we tell the contract that the functions defined + // on the `NFTReceiver` can be called by anyone. + // + // - The `NFTMinter` resource is saved in the account storage for the creator of + // the contract. Only the creator can mint tokens. + init() { + // Set `owners` to an empty dictionary. + self.owners = {} + + // Create a new `@NFTCollection` instance and save it in `/storage/NFTCollection` domain, + // which is only accessible by the contract owner's account. + self.account.save(<-create NFTCollection(), to: /storage/NFTCollection) + + // "Link" only the `@NFTReceiver` interface from the `@NFTCollection` stored at `/storage/NFTCollection` domain to the `/public/NFTReceiver` domain, which is accessible to any user. + self.account.link<&{NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection) + + // Create a new `@NFTMinter` instance and save it in `/storage/NFTMinter` domain, accesible + // only by the contract owner's account. + self.account.save(<-create NFTMinter(), to: /storage/NFTMinter) + } +} + +``` + +Hopefully, the high-level steps are clear to you after you have followed through the comments. We will talk about domains briefly here. Domains are general-purpose storages accessible to Flow accounts common used for storing resources. Intuitively, they are similar to common filesystems. There are three domain namespaces in Cadence: + +#### `/storage` + +This namespace can only be accessed by the owner of the account. + +#### `/private` + +This namespace is used to stored private objects and [capabilities](https://docs.onflow.org/cadence/language/capability-based-access-control/) whose access can be granted to selected accounts. + +#### `/public` + +This namespace is accessible by all accounts that interact with the contract. + +In our previous code, we created an `@NFTCollection` instance for our own account and saved it to the `/storage/NFTCollection` namespace. The path following the first namespace is arbitrary, so we could have named it `/storage/my/nft/collection`. Then, something odd happened as we "link" a *reference* to the `@NFTReceiver` capability from the `/storage` domain to `/public`. The caret pair `<` and `>` was used to explicitly annotate the type of the reference being linked, `&{NFTReceiver}`, with the `&` and the wrapping brackets `{` and `}` to define the reference type. Last but not least, we created the `@NFTMinter` instance and saved it to our account's `/storage/NFTMinter` domain. + +> For a deep dive into storages, check out [Account Storage][cdc-domain]. + +As we wrap up writing our `PetStore` contract, you can try to deploy it to Flow emulator (local net) to verify that the contract is correct. Check `flow.json` file and verify that the following two fields are set as the following: + +``` +{ + // ... + + "contracts": { + "PetShopContract": "./src/flow/contracts/PetStore.cdc" + }, + + "deployments": { + "emulator": { + "emulator-account": ["PetStore"] + } + }, + + // ... +} + +``` + +Then, run the Flow cli command to start the emulator and deploy your first contract! + +```shell + +# Start the emulator +$ flow emulator + +# In another shell, deploy the contract +$ flow project deploy + +``` + +If all went well, you should receive a nice happy message informing you that your contract was deployed. + +### `MintToken` transaction ## TBC @@ -453,6 +601,6 @@ Now we have implemented the `@NFTReceiver` interface for the `@NFTCollection` re [cdc-address-type]: https://docs.onflow.org/cadence/language/values-and-types/#addresses [cdc-comp-type]: https://docs.onflow.org/cadence/language/composite-types/ [cdc-integer-type]: https://docs.onflow.org/cadence/language/values-and-types/#integers - [cdc-force-assign]: -https://docs.onflow.org/cadence/language/values-and-types/#force-assignment-operator-- + [cdc-force-assign]: https://docs.onflow.org/cadence/language/values-and-types/#force-assignment-operator-- + [cdc-domain]: https://docs.onflow.org/cadence/tutorial/02-hello-world/#account-filesystem-domain-structure-where-can-i-store-my-stuff From 6bf787a789b6c5ae54a4c00d7cfd914fe2e3fd60 Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Wed, 27 Oct 2021 15:53:25 -0700 Subject: [PATCH 08/23] Conclude minting transaction --- docs/tutorial/flow-nft-marketplace.md | 74 +++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index 82b5d56..9ea10ba 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -266,9 +266,9 @@ pub contract PetStore { pub var metadata: {String: String} // Constructor method - init(id: UInt64) { + init(id: UInt64, metadata: {String: String}) { self.id = id - self.metadata = {} + self.metadata = metadata } } } @@ -460,10 +460,10 @@ pub contract PetStore { self.idCount = 1 } - pub fun mint(): @NFT { + pub fun mint(_ metadata: {String: String}): @NFT { // Create a new @NFT resource with the current ID. - let token <- create NFT(id: self.idCount) + let token <- create NFT(id: self.idCount, metadata: metadata) // Save the current owner's address to the dictionary. PetStore.owners[self.idCount] = PetStore.account.address @@ -536,7 +536,7 @@ This namespace is used to stored private objects and [capabilities](https://docs This namespace is accessible by all accounts that interact with the contract. -In our previous code, we created an `@NFTCollection` instance for our own account and saved it to the `/storage/NFTCollection` namespace. The path following the first namespace is arbitrary, so we could have named it `/storage/my/nft/collection`. Then, something odd happened as we "link" a *reference* to the `@NFTReceiver` capability from the `/storage` domain to `/public`. The caret pair `<` and `>` was used to explicitly annotate the type of the reference being linked, `&{NFTReceiver}`, with the `&` and the wrapping brackets `{` and `}` to define the reference type. Last but not least, we created the `@NFTMinter` instance and saved it to our account's `/storage/NFTMinter` domain. +In our previous code, we created an `@NFTCollection` instance for our own account and saved it to the `/storage/NFTCollection` namespace. The path following the first namespace is arbitrary, so we could have named it `/storage/my/nft/collection`. Then, something odd happened as we "link" a [reference][cdc-reference] to the `@NFTReceiver` capability from the `/storage` domain to `/public`. The caret pair `<` and `>` was used to explicitly annotate the type of the reference being linked, `&{NFTReceiver}`, with the `&` and the wrapping brackets `{` and `}` to define the *unauthorized reference* type (see [References][cdc-reference] to learn more). Last but not least, we created the `@NFTMinter` instance and saved it to our account's `/storage/NFTMinter` domain. > For a deep dive into storages, check out [Account Storage][cdc-domain]. @@ -577,6 +577,69 @@ If all went well, you should receive a nice happy message informing you that you ### `MintToken` transaction +The first and most important transaction for *any* NFT app is perhaps the one that mints tokens into existence! Without it there won't be any cute tokens to sell and trade. So let's start coding: + +```cadence + +// Import the `PetStore` contract instance from the master account address. +// This is a fixed address for used with the emulator only. +import PetStore from 0xf8d6e0586b0a20c7 + +transaction(metadata: {String: String}) { + + // Declare an "unauthorized" reference to `NFTReceiver` interface. + let receiverRef: &{PetStore.NFTReceiver} + + // Declare an "authorized" reference to the `NFTMinter` interface. + let minterRef: &PetStore.NFTMinter + + // `prepare` block always take one or more `AuthAccount` parameter(s) to indicate + // who are signing the transaction. + // It takes the account info of the user trying to execute the transaction and + // validate. In this case, the contract owner's account. + // Here we try to "borrow" the capabilities available on `NFTMinter` and `NFTReceiver` + // resources, and will fail if the user executing this transaction does not have access + // to these resources. + prepare(acct: AuthAccount) { + + // Note that we have to call `getCapability(_ domain: Domain)` on the account + // object before we can `borrow()`. + self.receiverRef = acct.getCapability<&{PetStore.NFTReceiver}>(/public/NFTReceiver) + .borrow() + ?? panic("Could not borrow receiver reference") + + // With an authorized reference, we can just `borrow()` it. + // Note that `NFTMinter` is borrowed from `/storage` domain namespace, which + // means it is only accessible to this account. + self.minterRef = acct.borrow<&PetStore.NFTMinter>(from: /storage/NFTMinter) + ?? panic("Could not borrow minter reference") + } + + // `execute` block executes after the `prepare` block is signed and validated. + execute { + // Mint the token by calling `mint(metadata: {String: String})` on `@NFTMinter` resource, which returns an `@NFT` resource, and move it to a variable `newToken`. + let newToken <- self.minterRef.mint(metadata) + + // Call `deposit(token: @NFT)` on the `@NFTReceiver` resource to deposit the token. + // Note that this is where the metadata can be changed before transferring. + self.receiverRef.deposit(token: <-newToken) + } +} + +``` + +The first line of the transaction code imports the `PetStore` contract instance. + +The `transaction` block takes an arbitrary number of named parameters, which will be provided by the calling program (In Flow CLI, JavaScript, Go, or other language). These parameters are the only channels for the transaction code to interact with the outside world. + +Next, we declare references `&{NFTReceiver}` and `&NFTMinter` (Note the first is an unauthorized reference). + +Now we enter the `prepare` block, which is responsible for authorizing the transaction. This block takes an argument of type `AuthAccount`. This account instance is required to sign and validate the transaction with its key. If it takes more than one `AuthAccount` parameters, then the transaction becomes a *multi-signature* transaction. This is the only place our code can access the account object. + +What we did was calling `getCapability(/public/NFTReceiver)` on the account instance, then `borrow()` to borrow the reference to `NFTReceiver` and gain the capability for `receiverRef` to receive tokens. We also called `borrow(from: /storage/NFTMinter)` on the account to enable `minterRef` with the superpower to mint tokens into existence. + +The `execute` block runs the code within after the `prepare` block succeeds. Here, we called `mint(metadata: {String: String})` on the `minterRef` reference, then moved the newly created `@NFT` instance into a `newToken` variable. After, we called `deposit(token: @NFT)` on the `receiverRef` reference, passing `<-newToken` (`@NFT` resource) as an argument. The newly minted token is now stored in our account's `receiverRef`. That concludes our minting transaction! + ## TBC [flow]: https://www.onflow.org/ @@ -603,4 +666,5 @@ If all went well, you should receive a nice happy message informing you that you [cdc-integer-type]: https://docs.onflow.org/cadence/language/values-and-types/#integers [cdc-force-assign]: https://docs.onflow.org/cadence/language/values-and-types/#force-assignment-operator-- [cdc-domain]: https://docs.onflow.org/cadence/tutorial/02-hello-world/#account-filesystem-domain-structure-where-can-i-store-my-stuff + [cdc-reference]: https://docs.onflow.org/cadence/language/references/ From 2f69fb96091bea2778eb7c4f292615e2ddd84a46 Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Thu, 28 Oct 2021 15:04:52 -0700 Subject: [PATCH 09/23] Wrap up TransferToken and refactor for clarity --- docs/tutorial/flow-nft-marketplace.md | 294 +++++++++++++++++++++++--- 1 file changed, 267 insertions(+), 27 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index 9ea10ba..f361042 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -202,12 +202,44 @@ Now that we have a basic understanding of how to think in Flow's way, we are rea If you have [cloned the structured tutorial][mini-petstore], in your shell, type `cd 1_getting_started` and run `npm install` to get started. -Otherwise, create a new React app by typing `npx create-react-app petstore` on your shell, then enter the directory with `cd petstore`. +Otherwise, create a new React app by typing the following commands on your shell: + +```shell + +$ npx create-react-app petstore; cd petstore +$ flow init # Initialize a Flow project and create a flow.json file. + +``` + +You should see a new React project created with a `flow.json` configuration file inside. Let's take a closer look at the structure and configure the project. ### Project structure If you are working on the cloned project, you can skip this section. +First of all, note the `flow.json` file under the root directory. This configuration file was created when we typed the command `flow init` and tells Flow that this is, indeed, a Flow project. We will leave most of the initial settings as they are, but make sure your file contains these fields by adding or changing them accordingly: + +``` +{ + // ... + + "contracts": { + "PetStore": "./src/flow/contracts/PetStore.cdc" + }, + + "deployments": { + "emulator": { + "emulator-account": ["PetStore"] + } + }, + + // ... +} + +``` + +These fields tell Flow where to look for the contract so we will be able to run the command line to deploy it to the Flow emulator. Now we will need to add some directories for our Cadence code. + Because `create-react-app` forbids importing code from outside of the `src` directory, the majority of the code we write will be inside this directory. Create a directory named `flow` inside `src` directory, and create three more named `contract`, `transaction`, and `script` under `flow`. This can be combined into one command: @@ -540,40 +572,36 @@ In our previous code, we created an `@NFTCollection` instance for our own accoun > For a deep dive into storages, check out [Account Storage][cdc-domain]. -As we wrap up writing our `PetStore` contract, you can try to deploy it to Flow emulator (local net) to verify that the contract is correct. Check `flow.json` file and verify that the following two fields are set as the following: - -``` -{ - // ... - - "contracts": { - "PetShopContract": "./src/flow/contracts/PetStore.cdc" - }, +As we wrap up our `PetStore` contract, let's try to deploy it to Flow emulator to verify the contract! In your shell, start the emulator with `flow emulator`. You should see an output similar to this: - "deployments": { - "emulator": { - "emulator-account": ["PetStore"] - } - }, +```shell - // ... -} +INFO[0000] ⚙️ Using service account 0xf8d6e0586b0a20c7 serviceAddress=f8d6e0586b0a20c7 serviceHashAlgo=SHA3_256 servicePrivKey=bd7a891abd496c9cf933214d2eab26b2a41d614d81fc62763d2c3f65d33326b0 servicePubKey=5f5f1442afcf0c817a3b4e1ecd10c73d151aae6b6af74c0e03385fb840079c2655f4a9e200894fd40d51a27c2507a8f05695f3fba240319a8a2add1c598b5635 serviceSigAlgo=ECDSA_P256 +INFO[0000] 📜 Flow contracts FlowFees=0xe5a8b7f23e8b548f FlowServiceAccount=0xf8d6e0586b0a20c7 FlowStorageFees=0xf8d6e0586b0a20c7 FlowToken=0x0ae53cb6e3f42a79 FungibleToken=0xee82856bf20e2aa6 +INFO[0000] 🌱 Starting gRPC server on port 3569 port=3569 +INFO[0000] 🌱 Starting HTTP server on port 8080 port=8080 ``` -Then, run the Flow cli command to start the emulator and deploy your first contract! +Take note of the `FlowServiceAccount` address, which is a base-16 number `0xf8d6e0586b0a20c7`. This is the address of the contract on the emulator. + +Open up a new shell, making sure you are inside the project directory, then type `flow project deploy` to deploy our first contract. You should see an output similar to this if it was successful: ```shell -# Start the emulator -$ flow emulator +Deploying 1 contracts for accounts: emulator-account + +PetShopContract -> 0xf8d6e0586b0a20c7 (a0555f1b56b28c982bf65a74f3ecdb92a9b0d2245c455fca98349ed81eb6f8b5) -# In another shell, deploy the contract -$ flow project deploy + +✨ All contracts deployed successfully ``` -If all went well, you should receive a nice happy message informing you that your contract was deployed. +Congratulations! You have learned how to write and deploy your first Flow smart contract. + +> **⚠️ Oops, it didn't work!** +> Check `flow.json` configuration and make sure the [path to the contract](#project-structure) is correct. ### `MintToken` transaction @@ -581,6 +609,8 @@ The first and most important transaction for *any* NFT app is perhaps the one th ```cadence +// MintToken.cdc + // Import the `PetStore` contract instance from the master account address. // This is a fixed address for used with the emulator only. import PetStore from 0xf8d6e0586b0a20c7 @@ -600,18 +630,18 @@ transaction(metadata: {String: String}) { // Here we try to "borrow" the capabilities available on `NFTMinter` and `NFTReceiver` // resources, and will fail if the user executing this transaction does not have access // to these resources. - prepare(acct: AuthAccount) { + prepare(account: AuthAccount) { // Note that we have to call `getCapability(_ domain: Domain)` on the account // object before we can `borrow()`. - self.receiverRef = acct.getCapability<&{PetStore.NFTReceiver}>(/public/NFTReceiver) + self.receiverRef = account.getCapability<&{PetStore.NFTReceiver}>(/public/NFTReceiver) .borrow() ?? panic("Could not borrow receiver reference") // With an authorized reference, we can just `borrow()` it. // Note that `NFTMinter` is borrowed from `/storage` domain namespace, which // means it is only accessible to this account. - self.minterRef = acct.borrow<&PetStore.NFTMinter>(from: /storage/NFTMinter) + self.minterRef = account.borrow<&PetStore.NFTMinter>(from: /storage/NFTMinter) ?? panic("Could not borrow minter reference") } @@ -638,7 +668,216 @@ Now we enter the `prepare` block, which is responsible for authorizing the trans What we did was calling `getCapability(/public/NFTReceiver)` on the account instance, then `borrow()` to borrow the reference to `NFTReceiver` and gain the capability for `receiverRef` to receive tokens. We also called `borrow(from: /storage/NFTMinter)` on the account to enable `minterRef` with the superpower to mint tokens into existence. -The `execute` block runs the code within after the `prepare` block succeeds. Here, we called `mint(metadata: {String: String})` on the `minterRef` reference, then moved the newly created `@NFT` instance into a `newToken` variable. After, we called `deposit(token: @NFT)` on the `receiverRef` reference, passing `<-newToken` (`@NFT` resource) as an argument. The newly minted token is now stored in our account's `receiverRef`. That concludes our minting transaction! +The `execute` block runs the code within after the `prepare` block succeeds. Here, we called `mint(metadata: {String: String})` on the `minterRef` reference, then moved the newly created `@NFT` instance into a `newToken` variable. After, we called `deposit(token: @NFT)` on the `receiverRef` reference, passing `<-newToken` (`@NFT` resource) as an argument. The newly minted token is now stored in our account's `receiverRef`. + +Let's try to send this transaction to the running emulator and mint a token! Because this transaction takes a `metadata` of type `{String: String}` (string to string dictionary), we will need to pass that argument when sending the command via Flow CLI. + +```shell + +$ flow transactions send src/flow/transactions/MintToken.cdc '{"name": "Max", "breed": "Bulldog"}' + +``` + +With a bit of luck, you should get a happy output telling you that the transaction is *sealed*. + +```shell + +Transaction ID: b10a6f2a1f1d88f99e562e72b2eb4fa3ae690df591d5a9111318b07b8a72e060 + +Status ✅ SEALED +ID b10a6f2a1f1d88f99e562e72b2eb4fa3ae690df591d5a9111318b07b8a72e060 +Payer f8d6e0586b0a20c7 +Authorizers [f8d6e0586b0a20c7] + +# ... + +``` + +Note the transaction ID returned from the transaction. Every transaction returns an ID no matter if it succeeds or not. + +Congratulations on minting your first NFT pet on Flow! It does not have a face yet besides just a name and breed, but we will touch on uploading static images (and even videos) onto the IPFS/Filecoin network using [nft.storage][nft-storage] later. + +### `TransferToken` transaction + +As we, the contract owner, are able to mint Flow NFTs. The next natural step is to learn how to transfer them to different users. Since this transfer action writes to the blockchain and mutates the state, it is also a transaction. + +Before we can transfer a token to another user's account, we need another receiving account to deposit a token to (We could transfer a token to *our* address, but that wouldn't be very interesting, would it?). At the moment, we have been working with only our emulator account so far. So, let's create an account through the Flow CLI. + +First, create a public-private key pair by typing `flow keys generate`. The output should look similar to the following, while **the keys will be different**: + +```shell + +🔴️ Store private key safely and don't share with anyone! +Private Key f410328ecea1757efd2e30b6bc692277a51537f30d8555106a3186b3686a2de6 +Public Key be393a6e522ae951ed924a88a70ae4cfa4fd59a7411168ebb8330ae47cf02aec489a7e90f6c694c4adf4c95d192fa00143ea8639ea795e306a27e7398cd57bd9 + +``` + +Take note of both the public and private keys, and type this command, replacing `` with your generated public key to create a new account based on the key: + +```shell + +$ flow accounts create --key --signer emulator-account + +``` + +Here is the output: + +``` + +Transaction ID: b19f64d3d6e05fdea5dd2ac75832d16dc61008eeacb9d290f153a7a28187d016 + +Address 0xf3fcd2c1a78f5eee +Balance 0.00100000 +Keys 1 + +// ... + +``` + +Take note of the new address, which should be different from the one shown here. Also, you might notice there is a transaction ID returned. Creating an account is also a transaction, and it was signed by the `emulator-account` (hence, `--signer emulator-account` flag). + +Before we can use the new address, we need to tell the Flow project about it. Open the `flow.json` configuration file, and at the "accounts" field, add the new account name ("test-account" here, but it could be any name), address, and the private key: + +``` +{ + // ... + + "accounts": { + "emulator-account": { + "address": "f8d6e0586b0a20c7", + "key": "bd7a891abd496c9cf933214d2eab26b2a41d614d81fc62763d2c3f65d33326b0" + }, + "test-account": { + "address": "0xf3fcd2c1a78f5eee", + "key": + } + } + + // ... +} + +``` + +With this new account created, we are ready to move on to the next step. + +Before we can deposit a token to the new account, we need it to "initialize" its collection. We can do this by creating a transaction for every user to initialize an `NFTCollection` in order to receive NFTs. + +Inside `/transactions` directory next to `MintToken.cdc`, create a new Cadence file named `InitCollection.cdc` with the follow code: + +```cadence + +// InitCollection.cdc + +import PetStore from 0xf8d6e0586b0a20c7 + +// This transaction will be signed by any user account who wants to receive tokens. +transaction { + prepare(acct: AuthAccount) { + // Create a new empty collection for this account + let collection <- create PetStore.NFTCollection() + + // store the empty collection in this account storage. + acct.save<@PetStore.Collection>(<-collection, to: /storage/NFTCollection) + + // Link a public capability for the collection. + // This is so that the sending account can deposit the token to this account's + // collection by calling its `deposit(token: @NFT)` method. + acct.link<&{PetStore.NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection) + } +} + +``` + +This small code will be signed by a receiving account to create an `NFTCollection` instance and save it to their own private `/storage/NFTCollection` domain (Recall that anything stored in `/storage` domain can only be accessible by the current account). In the last step, we linked the `NFTCollection` we have just stored to the public domain `/public/NFTReceiver` (and in the process, "casting" the collection up to `NFTReceiver`) so whoever is sending the token can access this and call `deposit(token: @NFT)` on it to deposit the token. + +Try sending this transaction by typing the command: + +```shell + +$ flow transactions send src/flow/transactions/InitCollection.cdc --signer test-account + +``` + +Note that `test-account` is the name of the new account we created in the `flow.json` file. Hopefully, the new account should now have an `NFTCollection` created and ready to receive tokens! + +Now, create a Cadence file named `TransferToken.cdc` in the `/transactions` directory with the following code. + +```cadence + +// TransferToken.cdc + +import PetStore from 0xf8d6e0586b0a20c7 + +// This transaction transfers a token from one user's +// collection to another user's collection. +transaction(tokenId: UInt64, recipientAddr: Address) { + + // The field holds the NFT as it is being transferred to the other account. + let token: @PetStore.NFT + + prepare(account: AuthAccount) { + + // Create a reference to a borrowed `NFTCollection` capability. + // Note that because `NFTCollection` is publicly defined in the contract, any account can access it. + let collectionRef = account.borrow<&PetStore.NFTCollection>(from: /storage/NFTCollection) + ?? panic("Could not borrow a reference to the owner's collection") + + // Call the withdraw function on the sender's Collection to move the NFT out of the collection + self.token <- collectionRef.withdraw(id: tokenId) + } + + execute { + // Get the recipient's public account object + let recipient = getAccount(recipientAddr) + + // This is familiar since we have done this before in the last `MintToken` transaction block. + let receiverRef = recipient.getCapability<&{PetStore.NFTReceiver}>(/public/NFTReceiver) + .borrow() + ?? panic("Could not borrow receiver reference") + + // Deposit the NFT in the receivers collection + receiverRef.deposit(token: <-self.token) + + // Save the new owner into the `owners` dictionary for look-ups. + PetStore.owners[tokenId] = recipientAddr + } +} + +``` + +Recall that in the last steps of our `MintToken.cdc` code, we were saving the minted token to our account's `NFTCollection` reference stored at `/storage/NFTCollection` domain. + +Here in `TransferToken.cdc`, we are basically creating a sequel of the minting process. The overall goal is to move the token stored in the sending source account's `NFTCollection` to the receiving destination account's `NFTCollection` by calling `withdraw(id: UInt64)` and `deposit(token: @NFT)` on the sending and receiving collections, respectively. Hopefully, by now it shouldn't be too difficult for you to follow along with the comments as you type down each line. + +Two new things that are worth noting are the first line of the `execute` block where we call a special built-in function `getAccount(_ addr: Address)`, which return an `AuthAccount` instance from an address passed as an argument to this transaction, and the last line, where we update the `owners` dictionary on the `PetStore` contract with the new address entry to keep track of the current NFT owners. + +Now, let's test out `TransferToken.cdc` by typing the command: + +```shell + +$ flow transactions send src/flow/transactions/TransferToken.cdc 1 0xf3fcd2c1a78f5eee + +``` + +Recall that the `transaction` block of `TransferToken.cdc` accepts two arguments--A token ID and the recipient's address--which we passed as a list of arguments to the command. Some of you might wonder why we left out `--signer` flag for this transaction command, but not the other. Without passing the signing account's name to `--signer` flag, the contract owner's account is the signer by default (a.k.a the `AuthAccount` argument in the `prepare` block). + +Hopefully, you should see an output similar to this: + +```shell + +Transaction ID: 4750f983f6b39d87a1e78c84723b312c1010216ba18e233270a5dbf1e0fdd4e6 + +Status ✅ SEALED +ID 4750f983f6b39d87a1e78c84723b312c1010216ba18e233270a5dbf1e0fdd4e6 +Payer f8d6e0586b0a20c7 +Authorizers [f8d6e0586b0a20c7] + +// ... + +``` + +Well done! You have just withdrew and deposited your minted NFT to another account! ## TBC @@ -667,4 +906,5 @@ The `execute` block runs the code within after the `prepare` block succeeds. Her [cdc-force-assign]: https://docs.onflow.org/cadence/language/values-and-types/#force-assignment-operator-- [cdc-domain]: https://docs.onflow.org/cadence/tutorial/02-hello-world/#account-filesystem-domain-structure-where-can-i-store-my-stuff [cdc-reference]: https://docs.onflow.org/cadence/language/references/ + [nft-storage]: https://nft.storage/ From c8f20e33d85085a8d59cf5af4419b5d69f2ae5bd Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Sat, 30 Oct 2021 09:23:27 -0700 Subject: [PATCH 10/23] Finish up Cadence script --- docs/tutorial/flow-nft-marketplace.md | 64 ++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index f361042..c41d96b 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -877,7 +877,69 @@ Authorizers [f8d6e0586b0a20c7] ``` -Well done! You have just withdrew and deposited your minted NFT to another account! +Well done! You have just withdrawn and deposited your an NFT to another account! + +### `GetTokenMetadata` script + +We have learned to write and send transactions. Now, we will learn how to create scripts to read state from the blockchain. + +There are many type of data we can query, but we will work on a `GetTokenMetadata.cdc` which, as the name suggests, get the metadata of an NFT based on the given ID. + +Recall that there is a `metadata` variable in the `NFT` resource definition in the contract which stores a `{String: String}` dictionary of that `NFT`'s metadata. Our script will have to query the right `NFT` and read the variable. + +One challenge here is the token we want to target might be owned by some other account. As the contract owner, it is our responsibility to keep track of the owners of every token we will ever mint so we can always access *all* tokens' metadata. This is why we included the `owners` dictionary in the [contract](#petstore-contract) in the first place. + +As you can see, it is straightforward after we can access the address owning the token. + +```cadence + +// GetTokenMetadata.cdc + +import PetStore from 0xf8d6e0586b0a20c7 + +// All scripts start with the `main` function, +// which can take an arbitrary number of argument and return +// any type of data. +// +// This function accepts a token ID and returns a metadata dictionary. +pub fun main(id: UInt64) : {String: String} { + + // Access the address that owns the NFT with the provided ID. + let ownerAddress = PetStore.owners[id]! + + // We encounter the `getAccount(_ addr: Address)` function again. + // Get the `AuthAccount` instance of the current owner. + let ownerAcct = getAccount(ownerAddress) + + // Borrow the `NFTReceiver` capability of the owner. + let receiverRef = ownerAcct.getCapability<&{PetStore.NFTReceiver}>(/public/NFTReceiver) + .borrow() + ?? panic("Could not borrow receiver reference") + + // Happily delegate this query to the owning collection + // to do the grunt work of getting its token's metadata. + return receiverRef.getTokenMetadata(id: id) +} + +``` + +It's worth repeating myself this: Scripts do not require any gas fee and authorization. They are just programs that reads public data on the blockchain. + +Executing a script with Flow CI is also pretty straightforward: + +```shell + +$ flow scripts execute src/flow/scripts/GetTokenMetadata.cdc + +``` + +Note that `` is an unsigned integer token ID whose metadata we want, starting from 1. If we have minted an NFT with the metadata `{"name": "Max", "breed": "Bulldog"}` in the [previous minting step](#minttoken-transaction), then that is what you will get after running the Flow `scripts` command. + +Et voila! You have come very far and dare I say you are ready to start building your own Flow NFT app. + +However, user experience is a crucial part in any app. It is more than likely that your users won't be as proficient at the command line as you do. Moreover, it is a bit boring for an NFT store to have faceless NFTs. In the next section, we will start tackling the fun part--building the UI on top and using [nft.storage][nft-storage] service to upload and store images of our NFTs. + +### Front-end app ## TBC From f4e4f7345d20a7d60df5f9a1a1667df3e28418d2 Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Sun, 31 Oct 2021 10:36:15 -0700 Subject: [PATCH 11/23] Start the front-end section and add `GetTokenOwner` script - Add a start paragraph of the front-end section - Add `GetTokenOwner` script as an immediate step from `TransferToken` - Correct typos and test run all the code in a real app project --- docs/tutorial/flow-nft-marketplace.md | 177 ++++++++++++++++++-------- 1 file changed, 124 insertions(+), 53 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index c41d96b..c13f55b 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -11,7 +11,7 @@ related: This tutorial will teach you to create a simple NFT marketplace on [Flow][flow] from scratch. -## Table of content +## Table of content (WIP) - [Prerequisites](#prerequisites) - [What you will learn](#what-you-will-learn) @@ -27,6 +27,8 @@ Although this tutorial is built specifically for Flow, it will build up a genera You will need to install [Node.js][nodejs] and npm (comes with Node.js), [Flow command line tool][flow-cli], and [Docker compose][docker-compose] to follow this tutorial. +You can use any code editor of your choice, but I recommend [VSCode][vscode] with [Cadence Language support][vscode-cdc-ext] for smooth sailing experience. + > **💡 Why React?** > React.js was chosen for this tutorial because it is the most widely-used UI library that > arguably takes away the tediousness of setting up a JavaScript app @@ -284,11 +286,6 @@ pub contract PetStore { // An array that stores NFT owners pub var owners: {UInt64: Address} - // Constructor method - init() { - self.owners = {} - } - pub resource NFT { // Unique ID for each NFT @@ -419,6 +416,12 @@ pub contract PetStore { } } } + + // Public factory method to create a collection + // so it is callable from the contract scope. + pub fun createNFTCollection(): @NFTCollection { + return <- create NFTCollection() + } } ``` @@ -482,7 +485,7 @@ pub contract PetStore { // ... NFTCollection code ... - access(self) resource NFTMinter { + pub resource NFTMinter { // Declare a global variable to count ID. pub var idCount: UInt64 @@ -583,7 +586,7 @@ INFO[0000] 🌱 Starting HTTP server on port 8080 port=8080 ``` -Take note of the `FlowServiceAccount` address, which is a base-16 number `0xf8d6e0586b0a20c7`. This is the address of the contract on the emulator. +Take note of the `FlowServiceAccount` address, which is a base-16 hexadecimal number `0xf8d6e0586b0a20c7` (In fact, these numbers are so ubiquitous in Flow that it has its own type: an `Address` type). This is the address of the contract on the emulator. Open up a new shell, making sure you are inside the project directory, then type `flow project deploy` to deploy our first contract. You should see an output similar to this if it was successful: @@ -591,7 +594,7 @@ Open up a new shell, making sure you are inside the project directory, then type Deploying 1 contracts for accounts: emulator-account -PetShopContract -> 0xf8d6e0586b0a20c7 (a0555f1b56b28c982bf65a74f3ecdb92a9b0d2245c455fca98349ed81eb6f8b5) +PetStore -> 0xf8d6e0586b0a20c7 (11e3afe90dc3a819ec9736a0a36d29d07a2f7bca856ae307dcccf4b455788710) ✨ All contracts deployed successfully @@ -658,6 +661,14 @@ transaction(metadata: {String: String}) { ``` +> **⚠️ Ambiguous type warning** +> If you are using VSCode, chances are you might see the editor flagging the +> lines referring to `PetStore.NFTReceiver` and `PetStore.NFTMinter` types +> with an "ambiguous type not found". Try to reset the running emulator +> by pressing `Ctrl+C` in the shell where you ran the emulator to interrupt it +> and run it again with `flow emulator` and on a different shell, don't forget +> to redeploy the contract with `flow project deploy`. + The first line of the transaction code imports the `PetStore` contract instance. The `transaction` block takes an arbitrary number of named parameters, which will be provided by the calling program (In Flow CLI, JavaScript, Go, or other language). These parameters are the only channels for the transaction code to interact with the outside world. @@ -674,7 +685,7 @@ Let's try to send this transaction to the running emulator and mint a token! Bec ```shell -$ flow transactions send src/flow/transactions/MintToken.cdc '{"name": "Max", "breed": "Bulldog"}' +$ flow transactions send src/flow/transaction/MintToken.cdc '{"name": "Max", "breed": "Bulldog"}' ``` @@ -713,7 +724,18 @@ Public Key be393a6e522ae951ed924a88a70ae4cfa4fd59a7411168ebb8330ae47cf02aec489 ``` -Take note of both the public and private keys, and type this command, replacing `` with your generated public key to create a new account based on the key: +For convenience, let's create a JSON file named `.keys.json` in the root directory next to `flow.json` so we can read them later on: + +```json + +{ + "private": "f410328ecea1757efd2e30b6bc692277a51537f30d8555106a3186b3686a2de6", + "public": "be393a6e522ae951ed924a88a70ae4cfa4fd59a7411168ebb8330ae47cf02aec489a7e90f6c694c4adf4c95d192fa00143ea8639ea795e306a27e7398cd57bd9" +} + +``` + +After you have taken down the keys, type this command, replacing `` with the public key you generated to create a new account: ```shell @@ -775,10 +797,10 @@ import PetStore from 0xf8d6e0586b0a20c7 transaction { prepare(acct: AuthAccount) { // Create a new empty collection for this account - let collection <- create PetStore.NFTCollection() + let collection <- PetStore.NFTCollection.new() // store the empty collection in this account storage. - acct.save<@PetStore.Collection>(<-collection, to: /storage/NFTCollection) + acct.save<@PetStore.NFTCollection>(<-collection, to: /storage/NFTCollection) // Link a public capability for the collection. // This is so that the sending account can deposit the token to this account's @@ -795,7 +817,7 @@ Try sending this transaction by typing the command: ```shell -$ flow transactions send src/flow/transactions/InitCollection.cdc --signer test-account +$ flow transactions send src/flow/transaction/InitCollection.cdc --signer test-account ``` @@ -856,7 +878,7 @@ Now, let's test out `TransferToken.cdc` by typing the command: ```shell -$ flow transactions send src/flow/transactions/TransferToken.cdc 1 0xf3fcd2c1a78f5eee +$ flow transactions send src/flow/transaction/TransferToken.cdc 1 0xf3fcd2c1a78f5eee ``` @@ -879,17 +901,59 @@ Authorizers [f8d6e0586b0a20c7] Well done! You have just withdrawn and deposited your an NFT to another account! -### `GetTokenMetadata` script +### `GetTokenOwner` script We have learned to write and send transactions. Now, we will learn how to create scripts to read state from the blockchain. -There are many type of data we can query, but we will work on a `GetTokenMetadata.cdc` which, as the name suggests, get the metadata of an NFT based on the given ID. +There are many things we can query using a script, but since we have just transferred a token to `test-account`, it would be nice to confirm that the token was actually transferred. -Recall that there is a `metadata` variable in the `NFT` resource definition in the contract which stores a `{String: String}` dictionary of that `NFT`'s metadata. Our script will have to query the right `NFT` and read the variable. +Let's create a script file named `GetTokenOwner.cdc` under the `script` directory. As you can see it is quite straightforward: -One challenge here is the token we want to target might be owned by some other account. As the contract owner, it is our responsibility to keep track of the owners of every token we will ever mint so we can always access *all* tokens' metadata. This is why we included the `owners` dictionary in the [contract](#petstore-contract) in the first place. +```cadence + +// GetTokenOwner.cdc -As you can see, it is straightforward after we can access the address owning the token. +import PetStore from 0xf8d6e0586b0a20c7 + +// All scripts start with the `main` function, +// which can take an arbitrary number of argument and return +// any type of data. +// +// This function accepts a token ID and returns an Address. +pub fun main(id: UInt64): Address { + + // Access the address that owns the NFT with the provided ID. + let ownerAddress = PetStore.owners[id]! + return ownerAddress +} + +``` + +All scripts have an entry function called `main`, which can take any number of arguments and return any data type. + +This script simply accesses the `owners` dictionary in the `PetStore` contract using the token ID and returns the address of the token's owner, or fail if the value is `nil`. + +It's worth repeating myself this: Scripts do not require any gas fee and authorization. They are just programs that reads public data on the blockchain. + +Executing a script with Flow CLI is also pretty straightforward: + +```shell + +$ flow scripts execute src/flow/script/GetTokenOwner.cdc + +``` + +`` is an unsigned integer token ID starting from 1. If you have [minted an NFT](#minttoken-transaction) and [transferred it to the `test-account`](#transfertoken-transaction), then replace `` with the token ID. You should get back the address of the `test-account` you have created. + +### `GetTokenMetadata` script + +From `GetTokenOwner.cdc` script, it takes only a few more steps to create a script that returns a token's metadata. + +We will work on `GetTokenMetadata.cdc` which, as the name suggests, get the metadata of an NFT based on the given ID. + +Recall that there is a `metadata` variable in the `NFT` resource definition in the contract which stores a `{String: String}` dictionary of that `NFT`'s metadata. Our script will have to query the right `NFT` and read the variable. + +Because we already know how to get an NFT's owner address, all we have to do is to access `NFTReceiver` capability of the owner's account and call `getTokenMetadata(id: UInt64) : {String: String}` on it to get back the NFT's metadata. ```cadence @@ -923,17 +987,15 @@ pub fun main(id: UInt64) : {String: String} { ``` -It's worth repeating myself this: Scripts do not require any gas fee and authorization. They are just programs that reads public data on the blockchain. - -Executing a script with Flow CI is also pretty straightforward: +Now, execute the script: ```shell -$ flow scripts execute src/flow/scripts/GetTokenMetadata.cdc +$ flow scripts execute src/flow/script/GetTokenMetadata.cdc ``` -Note that `` is an unsigned integer token ID whose metadata we want, starting from 1. If we have minted an NFT with the metadata `{"name": "Max", "breed": "Bulldog"}` in the [previous minting step](#minttoken-transaction), then that is what you will get after running the Flow `scripts` command. +If we have minted an NFT with the metadata `{"name": "Max", "breed": "Bulldog"}` in the [previous minting step](#minttoken-transaction), then that is what you will get after running the script. Et voila! You have come very far and dare I say you are ready to start building your own Flow NFT app. @@ -941,32 +1003,41 @@ However, user experience is a crucial part in any app. It is more than likely th ### Front-end app -## TBC - - [flow]: https://www.onflow.org/ - [flow-cli]: https://www.onflow.org/cli/ - [docker-compose]: https://docker.com/compose/ - [blockchain-basic]: ../concepts/blockchain.md - [nft-basic]: ../concepts/non-fungible-tokens.md - [nodejs]: https://nodejs.org/ - [cadence]: https://docs.onflow.org/cadence/language/ - [nft-storage]: https://nft.storage/ - [flowwow]: https://github.com/jochasinga/flowwow/ - [react]: https://reactjs.org/ - [robert-frost-poem]: https://www.poetryfoundation.org/poems/44272/the-road-not-taken - [mini-petstore]: https://github.com/jochasinga/flow-react - [rust]: https://rust-lang.org/ - [diem]: https://diem.org/ - [erc-721]: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721 - [cdc-dict-type]: https://docs.onflow.org/cadence/language/values-and-types/#dictionaries - [cdc-force-unwrap]: https://docs.onflow.org/cadence/language/values-and-types/#force-unwrap- - [cdc-array-type]: https://docs.onflow.org/cadence/language/values-and-types/#array-types - [cdc-optional-type]: https://docs.onflow.org/cadence/language/values-and-types/#optionals - [cdc-address-type]: https://docs.onflow.org/cadence/language/values-and-types/#addresses - [cdc-comp-type]: https://docs.onflow.org/cadence/language/composite-types/ - [cdc-integer-type]: https://docs.onflow.org/cadence/language/values-and-types/#integers - [cdc-force-assign]: https://docs.onflow.org/cadence/language/values-and-types/#force-assignment-operator-- - [cdc-domain]: https://docs.onflow.org/cadence/tutorial/02-hello-world/#account-filesystem-domain-structure-where-can-i-store-my-stuff - [cdc-reference]: https://docs.onflow.org/cadence/language/references/ - [nft-storage]: https://nft.storage/ +Our NFT pet store is fully functional at the command line, but without a face, it is too hard for end users to use. + +Very often, especially for [decentralized applications][dapps] whose back-ends rely heavily on blockchains and other decentralized technology, the user experience is what makes or breaks them. Quite often, the user-facing part *is* the only crucial part in a dapp. + +In this section, we will be working on the UI for the pet store app in React.js. While you're expected to have some familiarity with the library, I will do my best to use common features instead of trotting into advanced ones. + +## WIP + +[vscode]: https://code.visualstudio.com/ +[vscode-cdc-ext]: https://docs.onflow.org/vscode-extension/ +[flow]: https://www.onflow.org/ +[flow-cli]: https://www.onflow.org/cli/ +[docker-compose]: https://docker.com/compose/ +[blockchain-basic]: ../concepts/blockchain.md +[nft-basic]: ../concepts/non-fungible-tokens.md +[nodejs]: https://nodejs.org/ +[cadence]: https://docs.onflow.org/cadence/language/ +[nft-storage]: https://nft.storage/ +[flowwow]: https://github.com/jochasinga/flowwow/ +[react]: https://reactjs.org/ +[robert-frost-poem]: https://www.poetryfoundation.org/poems/44272/the-road-not-taken +[mini-petstore]: https://github.com/jochasinga/flow-react +[rust]: https://rust-lang.org/ +[diem]: https://diem.org/ +[erc-721]: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721 +[cdc-dict-type]: https://docs.onflow.org/cadence/language/values-and-types/#dictionaries +[cdc-force-unwrap]: https://docs.onflow.org/cadence/language/values-and-types/#force-unwrap- +[cdc-array-type]: https://docs.onflow.org/cadence/language/values-and-types/#array-types +[cdc-optional-type]: https://docs.onflow.org/cadence/language/values-and-types/#optionals +[cdc-address-type]: https://docs.onflow.org/cadence/language/values-and-types/#addresses +[cdc-comp-type]: https://docs.onflow.org/cadence/language/composite-types/ +[cdc-integer-type]: https://docs.onflow.org/cadence/language/values-and-types/#integers +[cdc-force-assign]: https://docs.onflow.org/cadence/language/values-and-types/#force-assignment-operator-- +[cdc-domain]: https://docs.onflow.org/cadence/tutorial/02-hello-world/#account-filesystem-domain-structure-where-can-i-store-my-stuff +[cdc-reference]: https://docs.onflow.org/cadence/language/references/ +[nft-storage]: https://nft.storage/ +[dapps]: https://ethereum.org/en/dapps/ From 008eae9287f18225c3d3984d27750061ccdc727e Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Wed, 3 Nov 2021 13:58:40 -0700 Subject: [PATCH 12/23] Front end tutorial up to minting --- docs/tutorial/flow-nft-marketplace.md | 212 ++++++++++++++++++ .../images/flow-nft-marketplace/form.png | Bin 0 -> 49469 bytes .../nft-storage-api-keys.png | Bin 0 -> 86599 bytes 3 files changed, 212 insertions(+) create mode 100644 docs/tutorial/images/flow-nft-marketplace/form.png create mode 100644 docs/tutorial/images/flow-nft-marketplace/nft-storage-api-keys.png diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index c13f55b..4ff6368 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -1003,12 +1003,222 @@ However, user experience is a crucial part in any app. It is more than likely th ### Front-end app +> **💡 Do you know React?** +> In this part of the tutorial, we will work with React.js extensively. +> Because learning React is unfortunately out of the scope, if you think +> you will need some introduction or brush-up on React, please head over +> to [Intro to React][react-intro] tutorial before returning here. + Our NFT pet store is fully functional at the command line, but without a face, it is too hard for end users to use. Very often, especially for [decentralized applications][dapps] whose back-ends rely heavily on blockchains and other decentralized technology, the user experience is what makes or breaks them. Quite often, the user-facing part *is* the only crucial part in a dapp. In this section, we will be working on the UI for the pet store app in React.js. While you're expected to have some familiarity with the library, I will do my best to use common features instead of trotting into advanced ones. +### Setting up + +Make sure you are in the project directory (next to `package.json`). Install the following packages: + +```shell + +$ npm install --save @onflow/fcl @onflow/types nft.storage + +``` + +The Flow packages will help in connecting our React app to the Cadence code. The `nft.storage` package will help in uploading the image during minting and retrieving data from Filecoin/IPFS network. In order to do so, you will need to [sign up][nft-storage] and generate an API key. After you have signed up, navigate to the "API Keys" tab, and click to create a new key, as shown here: + +![Screenshot of NFT.Storage API Keys page](./images/flow-nft-marketplace/nft-storage-api-keys.png) + +Take down the key as we will need it later on when we work on the [minting logic](#minting-logic). + +Also, to get styling out of the way, please download [Skeleton CSS][skeleton-css-download] and unzip all the CSS files into the `src` directory. Then, in the `App.css` stylesheet, after the last line, import all the stylesheets you just unzipped with: + +```css + +/* App.css */ + +@import "./skeleton.css"; +@import "./normalize.css"; + +/* import all other CSS files if any */ + +``` + +Because we want to focus on the UI integration, I'll tell you when to just copy the code that isn't important. + +Run the app with `npm run start`, the React app should open in the browser on `http://localhost:3000`. Keep the browser open to see the updates as you save your progress. + +In your editor, open `App.js` and remove all the current HTML, leaving only the `
` level. + +```jsx + +function App() { + return ( +
+ {/* Remove this code */} +
+ ); +} + +``` + +After you save the file, the app in the browser should become blank. + +Now, create a new directory named `components` inside `src` to keep our reusable components. + +Inside the newly created directory, create a new file named `Form.js`. This will be a form component that will allow users to submit and mint new NFTs. + +```jsx + +// components/Form.js + +import FileSelector from './FileSelector'; + +// Collect the information of a pet and manage as a state +// and mint the NFT based on the information. +const Form = () => { + const [pet, setPet] = useState({}); + + // Helper functions to be passed to input elements' onChange. + + const setName = (event) => { + const name = event.target.value; + setPet({...pet, name}); + } + + const setBreed = (event) => { + const breed = event.target.value; + setPet({...pet, breed}); + } + + const setAge = (event) => { + const age = event.target.value; + setPet({...pet, age}); + } + + return ( +
+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ ); +}; + +const style = { + padding: '5rem', + background: 'white', + maxWidth: 350, +}; + +export default Form; + +``` + +The `FileSelector.js` component we imported does not yet exist. So, next to `Form.js`, create another component named `FileSelector.js` to handle the image upload logic. + +```jsx + +// components/FileSelector.js + +import { useState } from 'react'; + +// We are passing `pet` and `setPet` as props to `FileSelector` so we can +// set the file we selected to the pet state on the `Form` outer scope +// and keep this component stateless. +const FileSelector = ({pet, setPet}) => { + + // Read the FileList from the file input component, then + // set the first File object to the pet state. + const readFiles = (event) => { + const files = event.target.files; + if (files.length > 0) { + setPet({...pet, file: files[0]}); + } + }; + + return ( +
+ + {/* Add readFiles as the onChange handler. */} + +
+ ); +}; + +export default FileSelector; + +``` + +If you import `Form.js` component into `App.js` and insert it anywhere inside the main `App` container, you should see your form that looks similar to the one you see below: + + ![Screenshot of form component](./images/flow-nft-marketplace/form.png) + + +> **đź’ˇ How I write code** +> The way I write code is to write very small unit at a time, then +> test that it works as intended, then if it might be reusable, wrap +> it in a function. Then I repeat. +> +> This makes perfect sense. The more code we write, the harder it is +> to debug and go back to fix something. It is always better to write > less code in general. +> +> Here, I would include `console.log()` in each event handler, then +> test clicking the form to make sure the logged values are what I +> expected before moving on. + +## Minting logic + +Here comes the interesting part. We will hook up the `Mint` button to actually mint a token based on user's input! + +Switching gears here, let's head over to `/src/flow/transaction` and create a new JavaScript file named `MintToken.tx.js`. + +This module will send the Cadence transaction `MintToken.cdc` similarly to how we have used Flow CLI to do so previously. + +```js + +// MintToken.tx.js + +// Minting module here + +``` + + + + + + + + ## WIP [vscode]: https://code.visualstudio.com/ @@ -1037,7 +1247,9 @@ In this section, we will be working on the UI for the pet store app in React.js. [cdc-integer-type]: https://docs.onflow.org/cadence/language/values-and-types/#integers [cdc-force-assign]: https://docs.onflow.org/cadence/language/values-and-types/#force-assignment-operator-- [cdc-domain]: https://docs.onflow.org/cadence/tutorial/02-hello-world/#account-filesystem-domain-structure-where-can-i-store-my-stuff +[react-intro]: https://reactjs.org/tutorial/tutorial.html [cdc-reference]: https://docs.onflow.org/cadence/language/references/ [nft-storage]: https://nft.storage/ [dapps]: https://ethereum.org/en/dapps/ +[skeleton-css-download]: https://github.com/dhg/Skeleton/releases/download/2.0.4/Skeleton-2.0.4.zip diff --git a/docs/tutorial/images/flow-nft-marketplace/form.png b/docs/tutorial/images/flow-nft-marketplace/form.png new file mode 100644 index 0000000000000000000000000000000000000000..9d7c4e361401480d0c78162edf17b4919109efb9 GIT binary patch literal 49469 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%0DdT+s4OoiK365ug(hMv{jivQ*K8LL8x*KOy; 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
Js#jOCgPeF{7-W^e;#-e)>Yw{`s!hDT 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 literal 0 HcmV?d00001 From 85e702861e765c74df250c56076ecc7b4843e502 Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Sun, 7 Nov 2021 21:15:55 -0800 Subject: [PATCH 13/23] Wrap up mintToken javascript code --- docs/tutorial/flow-nft-marketplace.md | 280 +++++++++++++++++- .../flow-nft-marketplace-finish.png | Bin 0 -> 173068 bytes 2 files changed, 269 insertions(+), 11 deletions(-) create mode 100644 docs/tutorial/images/flow-nft-marketplace/flow-nft-marketplace-finish.png diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index 4ff6368..5e78928 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -9,7 +9,7 @@ related: # Building an NFT Marketplace on Flow -This tutorial will teach you to create a simple NFT marketplace on [Flow][flow] from scratch. +This tutorial will teach you to create a simple NFT marketplace on [Flow][flow] from scratch and deploying it on the testnet. ## Table of content (WIP) @@ -275,6 +275,13 @@ The structure of the `src` directory should now look like this: ### `PetStore` contract +> **đź’ˇ Don't reinvent the wheel** +> As you move toward building on testnet and finally mainnet, you will want to take a +> look at [existing interfaces](https://docs.onflow.org/dapp-development/smart-contracts/#nft-sales-and-trading-nft-sales-and-trading) and implement them. +> +> However, for this tutorial, we want to focus on understanding the basics. So, we will be building +> our own version of things from the ground up. + We will be taking some time to write the contract while also learn Cadence. Once you have grasped it, writing transactions and scripts will be relatively easy. First, create the contract structure and define an `NFT` resource: @@ -1015,6 +1022,10 @@ Very often, especially for [decentralized applications][dapps] whose back-ends r In this section, we will be working on the UI for the pet store app in React.js. While you're expected to have some familiarity with the library, I will do my best to use common features instead of trotting into advanced ones. +After we are done, we will end up with a simple marketplace app that users can mint and query their NFTs that is similar to this: + +![finished-marketplace](./images/flow-nft-marketplace/flow-nft-marketplace-finish.png) + ### Setting up Make sure you are in the project directory (next to `package.json`). Install the following packages: @@ -1055,7 +1066,9 @@ In your editor, open `App.js` and remove all the current HTML, leaving only the function App() { return (
- {/* Remove this code */} + + {/* Remove code here */} +
); } @@ -1121,13 +1134,17 @@ const Form = () => {
- +
- + @@ -1200,15 +1217,247 @@ If you import `Form.js` component into `App.js` and insert it anywhere inside th Here comes the interesting part. We will hook up the `Mint` button to actually mint a token based on user's input! -Switching gears here, let's head over to `/src/flow/transaction` and create a new JavaScript file named `MintToken.tx.js`. +Switching gears here, let's head over to `/src/flow/transaction` and create a new JavaScript file named `MintToken.tx.js`. This module will send the Cadence transaction `MintToken.cdc` similarly to how we have used Flow CLI to do so previously. -This module will send the Cadence transaction `MintToken.cdc` similarly to how we have used Flow CLI to do so previously. +Here I create a JavaScript module that interacts with each Cadence transaction or script and name it to reflect the Cadence code, appended by `.tx.js` or a transaction or `.sc.js` for a script. This is not a requirement and you're free to name them however you want. + +Since there will be quite a lot going on, we will go slowly on this one. We will create a `mintToken` function that takes a `pet` object and does the following: + +1. Upload the metadata and image asset to NFT.storage, and retrieve the returned metadata that includes the [CID][ipfs-cid] of the data. +2. Send a minting transaction with the metadata to Flow (in this case, the name, age, breed, and the CID of the data stored on IPFS). +3. If successful, return the Flow transaction ID. + + +Here we sketch up some placeholder functions to reflect the steps: ```js // MintToken.tx.js -// Minting module here +async function mintToken(pet) { + let metadata = await uploadToStorage(pet); + let txId = await mintPet(metadata); + return txId; +} + +/* We will fill in these functions next */ + +async function uploadToStorage(pet) { + return {}; +} + +async function mintPet(metadata) { + return ''; +} + +``` + +Next, we will fill in the body of `uploadToStorage` function. This is where you will need your API key from NFT.Storage: + +```js + +// Import these modules from nft.storage on top of the file. +import { NFTStorage, File } from 'nft.storage'; + +const API_KEY = "DROP_YOUR_API_KEY_HERE"; + +// Initialize the NFTStorage client. +const storage = new NFTStorage({ token: API_KEY }); + +async function uploadToStorage(pet) { + // Call `store(...)` on the NFTStorage client with an object + // containing all of pet's attributes, and required image and + // description attributes. + let metadata = await storage.store({ + ...pet, + image: pet.image && new File([pet.image], `${pet.name}.jpg`, { type: 'image/jpg' }), + description: `${pet.name}'s metadata`, + }); + + // If all goes well, return the metadata. + return metadata; +} + +``` + +The step was simple. `NFTStorage.store(...)` takes an object with arbitrary attributes and two required `image` and `description` attributes. + +Confusingly, `image` attribute does not necessarily take only an image file. It takes a `File` object (which means any type of assets). We used the `File` contructor imported from the library to create the object. + +The `description` attribute can obvious be any arbitrary text you want. + +Then, we return the metadata returned from the call to the caller. + +Once we have the metadata from uploading to NFT.storage, we will have to send a transaction to mint the token on Flow with the metadata. Let's fill up the `mintPet` function. + +```js + +import * as fcl from '@onflow/fcl'; +import * as t from '@onflow/types'; +import cdc from './MintToken.cdc'; + +async function mintPet(metadata) { + + // Convert the metadata into a {String: String} type. See below. + const dict = toCadenceDict(metadata); + + // Build a list of arguments + const payload = fcl.args([ + fcl.arg( + dict, + t.Dictionary({ key: t.String, value: t.String }), + ) + ]); + + // Fetch the Cadence raw code. + const code = await (await fetch(cdc)).text(); + + // Send the transaction! + // Note the `userAuthz` function we have not implemented. + const encoded = await fcl.send([ + fcl.transaction(code), + fcl.payer(fcl.authz), + fcl.proposer(fcl.authz), + fcl.authorizations([fcl.authz]), + fcl.limit(999), + payload, + ]); + + // Call `fcl.decode` to get the transaction ID. + let txId = await fcl.decode(encoded); + + // This waits for the transaction to be sealed, which is a recommended way. + await fcl.tx(txId).onceSealed(); + + // Return the transaction ID + return txId; +} + +// Helper function to convert `pet` object to a {String: String} type. +function toCadenceDict(pet) { + // Copy the pet object so we don't mutate the original. + let newPet = {...pet}; + + // Delete the `image` attribute that contains a `File` object. + delete newPet.image; + + // Return an array of [{key: string, value: string}]. + return Object.keys(newPet).map((k) => ({key: k, value: pet[k]})); +} + +``` + +As you can see, our `mintPet` function is a little involved. + +The first step we took was to convert the `pet` data to a type our Cadence contract understand, which a dictionary of type `{String: String}`. Basically, if the object looks like this: + +```js + +{ + name: "Max", + age: 3, + breed: "Bulldog", + // ... +} + +``` + +We then have to convert it to an array of `{key: string, value: string}` in JavaScript: + + +```js + +[ + {key: "name", value: "Max"}, + {key: "age", value: "3"}, + {key: "breed", value: "Bulldog"}, +] + +``` + +This was what `toCadenceDict` function did, plus deleting the `image` attribute from the pet object because we didn't need it for minting on Flow. + +After properly converting the object, we had to build a "payload" by calling `fcl.args` and pass an array of arguments. In this case, the metadata of type `[{key: string, value: string}]`. To facilitate this, we used types from `fcl.types` library. + +Next, we fetch the corresponding `MintToken.cdc` raw code. This is a standard way of fetching raw text from another module. + +Now comes the meaty part of this function: Sending a transaction. + +```js + +const encoded = await fcl.send([ + fcl.transaction(code), + fcl.payer(fcl.authz), + fcl.proposer(fcl.authz), + fcl.authorizations([fcl.authz]), + fcl.limit(999), + payload, +]); + +``` + +There is a few ways to send a transaction, but `fcl.send([...])` is the most explicit way. + +We pass the Cadence `code` to `fcl.transaction`, and any integer from 0 - 999 to `fcl.limit` for the gas fee limit we are happy with. The `payload` is the metadata we converted previously. + +The `payer`, `proposer`, and `authorizations` are a little complicate. While we won't dig deep into them, they accept a function known as *authorization function* that decides the account (and effectively the keys) used to authorize the transaction. (If you're interested in deep-diving, check out [Authorization Function][cdc-auth-function]). Here, `fcl` provided an `authz` default authorization function to makes signing with the emulator account easier. + +Now all that is left to do is to return to the main `mintToken` function to complete it: + +```js + +// This is a fallible function. +async function mintToken(pet) { + + // The metadata contains the attribute `ipnft` which + // contains the CID of the uploaded metadata. + const { ipnft } = await uploadToStorage(pet); + + // We want to include the CID to mint to the blockchain, + // so we create a new object with all of the pet's + // attributes, plus the `cid`. + const txId = await mintPet({...pet, cid: ipnft}); + return txId; +} + +// Don't forget to export the function. +export default mintToken; + +``` + +Our `mintToken` function is ready to work. We should return to `Form.js`,add a `handleSubmit` handler (right after `setAge` function), and pass to the `onSubmit` prop on the `
` element. + +```js + +// Form.js + +// On the top most of the module +import mintToken from '../flow/transaction/MintToken.tx'; + +const Form = () => { + + // ... setAge function ... + + const handleSubmit = async (event) => { + event.preventDefault(); + try { + await mintToken(pet); + } catch (err) { + console.error(err); + } + } + + return ( +
+ + + {/* ... other elements ... */} + + +
+ + ); + ``` @@ -1219,6 +1468,13 @@ This module will send the Cadence transaction `MintToken.cdc` similarly to how w + + + + + + + ## WIP [vscode]: https://code.visualstudio.com/ @@ -1252,4 +1508,6 @@ This module will send the Cadence transaction `MintToken.cdc` similarly to how w [nft-storage]: https://nft.storage/ [dapps]: https://ethereum.org/en/dapps/ [skeleton-css-download]: https://github.com/dhg/Skeleton/releases/download/2.0.4/Skeleton-2.0.4.zip +[ipfs-cid]: https://proto.school/anatomy-of-a-cid/01 +[cdc-auth-function]: https://docs.onflow.org/fcl/reference/api/#authorization-function diff --git a/docs/tutorial/images/flow-nft-marketplace/flow-nft-marketplace-finish.png b/docs/tutorial/images/flow-nft-marketplace/flow-nft-marketplace-finish.png new file mode 100644 index 0000000000000000000000000000000000000000..175184d646a343b7779674c9c2b1e2cc25138002 GIT binary patch literal 173068 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

YO8IS@xn$a>hiOm+oQ_y}t?A?0 zOq#D~ti{mA;gu=OzH8F0A`YJft-0swnpL)o(&%}X*6b+(N44hCG|l#LNpHi7kPlIH zi2~SR&>Vn3N3#@m*W;vCLW1nL&(>UYtde!?@U5t@nH3*UTmHgm9HuI{cjopLUVsJ!E%@up ziySdT7(+OUlDgQExJ?Zq!H#I|uD`U|6|d`T#{BnHHHfBl(SNuDjaM=Q zhtnoV{pvZ{np3`T_hoWLt@PN=wRr=;Jjx$6Mx97q@pU#6j0uF0>R$Y7)Izc}bP{iK zY1T19g%P3u`2vIMpg1Eb=>!FTPN<#a-o1O#d1?0-E7|$3?9bWKv`>g=D_(h-7L_lu z1t_x^(76EYPtz#`zkW5b?RC#vX?u==!NJT)qRc$@?Lp62k(E8OZks9RML*)xDc-52 zilFNIhPc#8LpZaoU}R=qkLtEFY-{^{!wsuq)1N*iqSC);tzVr`Aqr3%vW7!h z=C4y_Z*OlwMa5b95?rCn^Z4?4atli_{Tr@iE!JHm9sW_pU>_*Z7>|ZLE^g*Y|2kqP{OHBzUO9SO2t7m4Gea! zu0(3t73+Uj4@H(6m>j>i~C=F^7`PcK{Iv+I40=Rq=A1XFGBdh-;TvNL#sfnOQB>`HsA^;CC&TaVr!z=|I3 z1^LuOi)i*Jq8YyT^BGf-o3yCW>y)8cN8!6tJutkkgFr-`?hPOAkNg*`I1e9uf6{_` z3wF^dxPUiIUqcGxVZd7PkwH!;RpcF_CLzzvFt|2Ny$V7T{dt$h zF2n`5Tg;gw>}h zZ+&m(`e0u{ImbC^P?S(lq4DgI)h7gf!$`mb6w|bWWXJ5{B40)qc}@{SpFW*3$S;2C zU^iyHZ!l(7Yxwi8yi8@X#wg~84bxoN)^f5@=+@4u^I{|_0rKMD+rOW#!w7^mR!8rQ zsK{i}v$0CC8TDR}NEYNAe=|W+2awH zwA0MOLiR@-9Nqa29z58j3@GBvh;t|>0#5ITKwsyU=S$5#VQ9mrf0WXden$CX@hNd4 zkb>)rSbq}hB;%_o)~;Q9Q$IA}^mH*PHr{D>uj7s`%awy+7e5O<738+{v7p9M2FQuBH_*th_kt06<&L z)X*m99y?8hWF;dYwoL=F z8UO;Mddq(G>eU%29ubb<<)r6grfJG#xl_*%mz8hDMtyMm$c6ABo7?p;a)|R^NnRSN zqGWB~2pDjiB6~`|f4{WWXmDWQCa}QooPe+?ZLJL0yV)XU%)TcM#-9{b}xJ<)X5KM2K%Rs|l|l3PxGABTcjA zgzyuN3}0JY+ml$9%yVSt&RN|;`~eq14gn|m4#Phz+Wiha(wt&GR~E z9dqA8xfr4zFe$A(gHb_O_TV8VlXELOD@)PfyYpPds`_PoyHQoUQOz^kin(u5g)E#c z>GhLyF{Q@%O&eRgyTk5TT)GtS|7a*5K77cz<>KO!0YMT)g8|HN{AF1o(lHsE8w*3K zWKJP6rcdE?oT+RXg*i-vvKt#4ONZ7Mxy@}5ZejaaLZ*ND*6eibqq@{U=oB=J)lM~8 zAa-)ri^GVINd8$<$qrj|QUGl2yS2uUbJS2*XB`+Caz6j|N!^hxv$fl}hLw@i<46}? z3X^;%Vy%xfI0E{c$AEDLTzIe07er?dAIi?!f`Xm!xZ%n3%j$Uop?V z0DRc}6xQ>@rfM_ndaB^Refx$eI*g&mm`_B6S?FwndD$ER#tE8FzKm5hHQUC<$202d z{mXW3()gD9aLyRw6T!Z=Ynl?(g%eE$@fOHa^>6CrZJ@-CY*xiD@Zqc*j9OSPZ&6c8 z{4CrG7!K3*){TF7|2L>6l9|6{c4Pkc2mj?DJL_;}YZf+oB;zkeu+nZ`ro@Aso4t9a zu3#CMxK*r)_hE(^XI>5Q!d zjtaeHFC`Yf8_7-lnPI7Hihcf++hi3A{x18rn2R$1{!3#2ACkNz_KR;T5POwDpYNo5 z7N5ybKGb;J>Pr5%o`Fxt8JQ0=yGnCANYYd6SM-mfOTSZ_*Hek#*^ug6w?%buuj3Q2DVP3Q1FX4^$OS6n+rc9msB0nzq^&TZ}k1Lf;Mx9Efn$_xM^S2v5 z7&OtmlQ~*&RyML>(OylfzBDVTlUj`AOuXSgs=IoqJyYNHGFY6LuZVC|lxETOPuD%0 zzkLc~$toVl-HfZWu$Jr$NTVIk&PaXIZB85wK|38sL8jbAMsi;%^4Mgnulls6k(-<& zM#xe*QvWiu9rcZyKOf|a#qr^UDHN^4C8)KBhK!xOx}W_C5gQd6Zy%%p+=uD+ip+aD z1Z1S$&d^;B-6hUSYc;lVIO#A+WDn9K%p`j7`*C%vPM9|+6fL9>a{MUlcT$ml-pyI_ zK`UCTZqve^|IlbCcN*ITpDp57Ak!!m?k38;{F+?yibM2P#0u&9GEZA#&xZJS z?7vDP0=JDxk@I)KB4ekizec_A>tk>F>t_C^VL2uLk;8b`i`?|z1ps0H&)+d4+*E_~u)Ea;LtS)PABi-fqMxd#s zU0Wi(W0mK{l>C7MV=33}x0y%jPc?N@-`tTc{W?lYRaaN;?Q6BS7b!K`kNc%uaLsHq z-G4HqE$raf0peIGlGB1IvVD`e!IL_M&O~DSYi7B(8}~+rK3Mr)sz%ns&&piSs-swB z(1E43i=yJfdm<9$-`ey%g%z0`r8Yn5E4Od5(i-KzEjoJPXUM8LW7VIJ6WdcpxY$zq z(*qkU^I6s%&Iwai{uJBM)7z6O;8tvAU$ruM`#5h^7F$qy^7OQmSCfk{J|ruHfn?6k zt%O+N8&0Fz`EP*Vv?1Q&M8570`;0GYm6m{bEX|ee4+HA~Rj#o7Ib{?l3F<()pe7`BC%(gJ;iOE{ev5$E=6(eO0 zul5L)7Aa6p_sdM`Sf2>O<{Eyh(Wfeq$g$WWHapna4_>>&o70XTxHj=opN>~3Xy4jF zbLXj@klOp=dGVxqee0!>rcWPV-s^c^wce@svD;8$dvi@gu<3h-K-aqqsQQI%(f9M} zFSq*JyqnnnuH8a%l>bf`@9MXQq*6qChnuz78dXY|?~hj>7T7$MpZCq`1GQQui%qao zlj>|f38RvAc%gc4$T^f)xuCTPKSkyyMQu#zTw^5PTlhGf`I&{mo}6ie;fLWJJrOy9 z`9Iqzy46bJ7`2s2t5yB$!7#2&T|U4aN&^82TGC2;d%Ip?0-7V7Gk-FB$%DhRl#1u;!`tMZZHK81w-n1syYrX*~*hr>u ztNmL`c9Xnxitrw;p`y6pm44hVAE&z2vVzr!(tW=NbtN8rkBrkQ59Z$s=l_t*O~`Q| z`itSHqBiW;)@{OGr>x+=BnxH`}B;R)qKuJaA}38X-B#J!0V)-Lht$+m}9^FCQFaJ+t7v#q`P|4@KHdCkOR;ABIe>3dWDT{pHDT*31<+fH=n3@2g= zCx2@*)|-VFtyhr7RUh!;yA6ct940*@?AGO6q1caOwe9ohkzK(`663wH1x`k>3gWc$ z1L>8L>Q#5M(~sF^r5Ej>Z5WOC%-2@e5L300K~rQ~;CcCk=pAU18**EQojYY-%vR^j zDq6fJoXJ%q3l_tKp4}|}e!!i_y9Sid&5u9KlbE@?FJ{gb{D%j!khvwq>CJm4iCO(^ zcW;4{@bKtddVI0#{@wn%;3^i{ge>4i&iEFyrV2hRYKNBw{=d+S7pUZQ%wPd5i49hM zmy<2Y`vPWvNyE&lX3U2rQCSj||2Cep7I{fjmPBPqRIrwI$x~d&C0_E)7uY3BqOv3^ zOXp9l3@rKM7&m;$$y&g-nl-gcqOv3^Sk*Br7^EMTL}f`-mPCapS+nGiFZtuM{>6WP zSQ3^0qeNx;b8RwiSf*r@tWMW!|2lio-7zZ_#Uo^$@35ZGC^)x#!=@c6#!ogs(+CYe zseSn~eS+(&H?3de$$1axTdVA?U*Y1i*XjiRToTzGDoR%ga&B&?*z%+7_CdN{yK;rc z)uw``9Ur~cJvBZ3*i=M^@Swjob+TgmojZL>jW^Le>v2MzpOO(_ej0ZXVP&NUSOoUY znflKE8+Gp=3%SCW!BVD$4C4Y%I1lO8C+UCM+p;wDg{OaM))z`Gg9t7O%#y$?X_y6d z_mW;;L}x6Sq=j|HlE5qp%>PQBBmP^P?>3^d_rFoi@eRKqihZUNboKlkt7e-jAHoV8pJ?)vI0eE6dlN#*wSv`GNLbHf zzllo)hJyV3PMLQZT=E^K`WtTTaAh1YU2W3@(7hS1+YT3I?~?Z2`ufbXN6emXR~-@BSfe`Mn2jYQH)Q3z4vCT(Ejnoy4>SfDea&Br0AT8Uh7J%u_W-qrpsKO9dT+0 z>zl0kY?;)wP2avV5ZJjBIzek?%cxRMM znFp7`MuemGBrmyLCXCX^wyG*Pl-i6&XT#n6X(0IE;vNNR3|?nIrQ-uSZ$8r>UyXk} z)A6V+tE@a!ps#n$u)X+ggY%ZF6bOkfBUiA_C$^%bgnnw&Z~CBJhf0Es7}IMOuFCB`03H?a^^@aw@-2$p=f7GRh;gB=GNwq(z03 znLTwU;vC202I8-^AaU*GylS#5_`|CB%F4?06AUU(M3hGJ7*wsc3-x}aZ2$`2b$-(m zEg66p$Rc&1;bx9fd!(pd@td9qH+wFG`4{M~kZ>#KS$mu0e>2;Geb?!$mxVO02PN!4xcGA|~*?0Rs zv&v*QYuwcurpDww1@2CU!2A*Duc*yk6=y^Z*jph&+N6l3tT)I?9IbQI@#3Tf#*EL% zV2;)Z)>>)#HMZn>d-{$e%4dg(CYGRVNlD3uiT-@*FzZ}>@s_Ul-6=h(g&7 zNwo79eK}bLRF>!D$-lWt6uxrrQl66WrAw+Ox4 z*ym*5bA#nfH?Yb(Yzj?m4=yMuc;FjSCgpg9fH09SNF5AR-Kpz2|2=T(G!QSjfxAWu zmYDTzcO-Ut#MyQwumQqkgq^(Co{+ssGATV}j+12#rAkKJP8p2} zuylv*U`?r$JvF)NL<@$25yp9>q;8W=MR2ujsIr1rZwG+pta;2P&7*P%Xp=J@ON%I2 zEA2{Gitq=xNtqIQYSbBNain`G}%iuhB>h;0{1!e+afQZ!dzHajBspX-4<74B@I#qY}>1HZ?%&9^sor)*- z@Ki8nXouPV)R)+&DCxETt)0Z>lrXi_6lrd;ttQ9)Or=PkO>qe@#W(X}ewDW4|*x5geA`!)IhoPKJ)~KQ#op^BW02uXjBYG*XT4%i+vGApOBD7TvYm zm>nm23(EvkM|1f=0~iBwxf7y`YTI~MhPt?ol}D+<_Ap?2+WK9?Qj-50?|QHYPMi1E z-MU8NLhq2Q2Z5!6-6o@*y*bYH;E$(#4ih86{qC#Xzp%;rvV5KTKw+F|!5d%yPvIG* zx{)lwbmSi(QU;OckGQg9)WR3X#)2Hjf{X#YZYUv$nGNWwLyc$s|S6-7_4hgc~Kp${^1{E zV3=`r}nh%G8oFn|fKt0`0IUO;TZc)!_faqH$_r$B) zv~04^O&9P#gkgR<@87;dGCxi<<+>BB#x2jWW)16<#WpddEG*ucP@ zTfqqm3`?D?N?iwD$o9Oi^g3HPje4hm5Gy)#c_?7WI!S|90bC9t%Cc?aE}|0+!CI>q z&?SI-g;dQQf?7uf?tLqnOC#v{s+J+yu!$4B}_r~3=b4%ha&hC57- zi44p5%D=ZTY;N!$5*>I!?F0lh0+V+ncFSHR5C{$3(^K8Yy0zDE9#eB4X8Af<{sUp^ zB4(DtciPjFb<-*()xdn40o$P~*mOO~WROR)tFH<*aJ?H$@>2qXRgrfsos`Jv_Z~#X z1#nzcyf=}6Zs(DZSN+Rmw6FP>-9={pg4@2l13S+6i3kh39!~Z&wyVdlP9IJgPVNbo zOXm3aEZS9MtTlLi{O|#!4Q^12JXO6DWteH|-mMy=C)2uZy=_g0s;7u)xCKwjNMk{H z`O_4Uyx181ve1F};XD95lr?wk?r@#gZv>MW34l)S&m+#R`FCRH1i{~!%x|(&C!<2g z?>Wyj!woqjyQVD}FI<=biHj+{KVMW$d++71PtSh}X}@$W9El*XPy_`DUae+CXpV&@ zJ`o-5Az@NsKlNJ(M)vygLzVi+l*RX4Mf^FALpqMK!Mv-oz->mY^yTXi%#^! z+n$Zf%gZ~GESk4*C?VgT2eDb*NUW12m5xD%@SB>G*|?SSf5N9zeq2^umNFC*yg8<^ z?>boG4#A|s!7xW*F6H=ex3XtCz6$&oWc{|RGHC=pG0NV_J%7vAYF_WGgC);E9^p-I zqm+Yye;Z@>cE3b9QZWPZwTBUtneORKpzjE)4ZK2X+Uu7==g|6PUkLL@w45x1x95jo z>Or7*6aK}i@sv;FsNbrSZp_>A07>%~L5S#Nk?2oTVPE;^E9c^;#yT=ivpuIfd7o*_ zFRAg-ZIgs7%j6Cf4WQVYjOEwPd4dp0oe)jWSD%AXb+&pY*MR;@qpLb0up$;Q&7HT> z=aV>5`clA z{%W9I929&O#Umx&O=%w(7{~1TR;LPZlfKJ>->{Z}6-;1Oeg0={S*%~+as}pXF{DSJ zg>L@cF_@SX8vx?&l1QDS*=ldQjeuR2MV|TR4U5Ojnb>{O{ z1dt-Xun{3~ZKf}e*1hmHGTz|jv$(y7PUXy=P#L3f6VC;vu5O9z>rTAkP<|gflb<4B zCS6y{mCa2%4Pylv_+^3XVFGn8SsovpyIytU?&+&aQz{EEq+G!63H&`ufXnva(AU-d ze8p*QD6GcnWX*MkUs5}m%Zs`&n5WlqBCu)t4u;xb97f!FavK{P@nn##Pe+W*=3W;^ z@$XIc5(hW-8IP_dn%l*df&W#q%k{xqlATGtD$GR8y8F8mVV1s$E+1RK9%4jrMz!H; z5Srx4Fb%9YEk3e;aoeP?Pt9)&pPP8>pw0sJS!m)n{iln+9%fAVm!RwDCu#ks7rq;I z+0gilB|+-14lrRCXYmL9%kDBw-YPot?u%cG24xPo7E9BPUCrO~|9{f7^Ch+}o41on z$bOy0QH;X=8jTpltN?UZBNDy-A; ztuT9mNJjq!lkrS!g=ZE8_y?wCe zfM`HN?5t@q(o7fwM#Fmc3_gBsqq@gYJBVDTqhk%ktZ0yn&j%(?^PjdEiUd{{GHTHY zWBDYj-sOV^yYsi6U2zq=1%UzUWOKAyB1S#J!&jeEhZqY2Xzxt(I1me z@ncj5nWnue#C|QK4`f2>eX5CP=yn=Js<&MQF4aewnG9-Mfo1*EENdAceBNc?U9UD) z=s7lUS#5jhRJu}c!-(q$53y6b7NR`*#!_9gCKVY5{PB7!wUMK@BzEUL-Lpg4s=;ZF zubD7T?a4c*P?zWb0kX$xnn)dvxUOQeJ4>AJ`GPLf3!C(R|pIJ zflpuau_6wri_W!Mt~KHZ;?O*t%ZyDuL3?X^DC5`4x?M>zqT9=6zDBRX7Y>V+CK5`N zX2ShtoW0F5U_DS)N+lZ2`i`u1WaEfY1Q1CEG5$Ti5+OooDR5%#8e;^RQry*bh305Z!ObGh;aQ=PUEIQ~oK zSIv4HyIpbJh%&__OH~^4W(P*dUQs*HD$cqNzeMFB7X+>+{4Y+uIrl2P3gX=ltLbBA zH)zsYJYyq=k^6YFDX}(7D*!(N@?Gp$KZ5S4|NHlcCx+ruxd+YB*}gJb*Qqs5YIZ6I zcv<61Qtw91zA}8UI!NYm*6yHT-L^|nKY4@)EqjkUfqtyQX7?937apzp!S z37asl%F3rn?!l|(TYAWGYQn5;(egq*AvmCCG_`VL2lOcrr+r<&Hpx1n^Oe*hM{&Fj zz?zj@P^%*P$M`g$OE09%O>;kAKD!!3(+v@uT?GY?4KjuVc!{^H2CWt3oyEHkL6p$> z=X2FLR7C$;GKYM^FaLWE2tn*@#~&hyDIf%~%3E({x6xvD@Yeu=JPf?V%WupVqcJ0X4VW1?KpP$}CQV4s z_b1Um;xs^xDfbm0!je?YsCFV7`fE~grEqzqz+df)EiYCcevw9zDLZY}+lG~G(!0)l z!>W(sMO!uu62H=7!#T?cdNh4)jO*(dyMN1uR6G{?X7@cfSZFvaiedNF zLR0wf=szLq#nO|6%NzA{ctH??LfA zlnGy6hDBIB>6!-R1QMB{z^w=ITNqF%WJwbuU~ zZgz@Wjuke%I0&!!x^e6yKD|jpg$>T@w(M1W9yuq&oocLkN&Mi%PS&-qpIaev3Oe9* z3sGMY`W|zad&(%tf!Ov6N=r9^!MU-83nH@KNYf$)TX`5 z*E)s?@1#9IrtssB?bt{C$4*SL!=K}wrmw}m9(JkcUgY;pjzJcj>5i8U$q$&}6ndGsIcfjE}TP*Zv3Ue7=0~jEfRvizUXV?~dTH0AOJuiQ3{=?~6djp2U z$7-|kKj&obY0iJKrt#D9=VNAo``ndpgNoG5|%H zf9)+*tSkB-Z-RdT)@|L1+$7HLDr6)>t6t1b-|Z*J_5sGq)8_7Z?W%(St+E7mqhy)ilCRnX7B3cWWa2*Kyv=$J>ha_$$w}PR!^+vl$$7RE-(~A1_bH{T<+6BI zO|>!3-xvOntdjBQc|QL>#t&A8THEPc*=e;LxtiIr=DS#ZIeTt{R<_h9X_o<#il_p& zv^NfU2Z=)rXg2u1l9~MAZXjr!989dH`snv;B#MO9g5o>M$K}5YF~ZCrQUQ<`fT@Z! z(A;(M=!PBVSa}XIIXaVItmBj5G}LTJ4}j; z=NHYa1uRPa@bg`>4#CV+uRW*U_Cw{EU7;2^1^Q=^`K25w)+)C`oH$MfZnU*Tv*?;nCw>gIMns z7P>>SBNn{=U#c2r75hKDya+7zGiK(@MIsyXW@$;>7jq^T6?fdpl_%ZwDAsKzNRkwa z!|dv@^Cf!R*PX8_Ay^Pkb~ngvO}3Qodef}?>-Q;b$;j*S4NoL+z7tPz=(Q7*FRD$J z_Idqx&)`F+rlJl}rK%0gRprm1O(5(3Rf=h?54Gc6Q%$bnX8oak&4t63dAjSwcJZ&Z z7_W0PS|>~|==Wrx>B(j-j=X`In(BnZ6A^^NLL*1Tu5?$9o1IsUZ*^}vreA$Jmhj#t zeVz2x5F5u)Lzme7&eU7&PmS^kJsW5W4G<-c7o$Cjw_CGzidSZ|J+78iK8sJWI=l1u zi1PS6CHLcqi}*Y$C#A<>pWH^z-S8tQ)@A%LtDK|KqdH2*M>kV*s*F@Sx76M#Yy7I} zK0K*clh?PSZTMA|pp(;M@4_bN!T8&7xHDJXiSb>qftG(uzKEv5h^Rzd;P%L;&58*G zP9IStBfgF{!u#}mzmBTz;ce!pDy1HoQ^g4<+xx#(Cfy*6FCg}~^~H)HgZVlu?G(+e z?F3G>!*8Xn^dd-N|GT=W@ACU=j# z4tmr~o;p&+tatJXlAoZa*mYW+2)kLu;BHE$8NUb&kcw@ZSpt5Y~eE=iqq)jv6b)#PzL z$3|jgD`*dFS0Lld+eIJuRCMgz-hNp}rK-@H1|5Bi0MAWl^e9bAgC8cv{A^$I-bT|$ zKYvJBbl;s!b=ho*5AU2mwyh9zqk86RutMDzz=6w2ylfO?{*~0}T}-FKTsj4|Pw68c?^S?r$njTJtJ{ zZIk3$Zr;FJ&Df6jpU>Z2Xh&0ZPI4-yjK8%c^(*){S)JJUf3f$SVNGURxQZyE6bD8H zm0|&uB2twa5GyDk5PDH*Lg+{*M3gd)A}Z2LM5ToiB!qwvKoL+{kQRDCq!U6*0)&M7 zWzO*&LFQb4-upcB&nJF-W$(4ude^)6de;YWcWGaaBlmj?QRT(oZl*jGcSBx@vw7Ue zAvhy-8oXS&JbJI`NjOCt(#ADW1N1n`C(yHA6rT8DWKmQev`O1Lkb=+^p=;wAf#ns(kf*fD= zmXSVVw4vRptf^n0KWLT~DHVq|dx_bWxu^6L;drXxk}>}7JZ-)B12h50o_A}d55X9h zYt^%s1MAOYz-N^=!46;m_~x0<4B74T17P@9lze(U^juI!x^(kN1)rwu?T=DgKL@QEWi92fe2H9=a&iH7WnB{W5?y2t~?*i<$d(}Jk8#pJ8EKbQPk-+&s}Bj zu5J%n19AA%x4_YoOp|-La_n+M?aW-~Luf~1vBEUDK6-R#7dppU_e8 z6;XhGeRdt3aYV-L?((D8TQu14BF0@J_<>%hRi zGzjc@ic03Op(U){O10hXTy=|-FS!*`Y3iaM&9LH_(%@mU{hEc{b#B$b}bqS2l;4Z9wJN+(1_Ehgh*|T%Myf=Xg6a z>UDQRFn*EyE&Rd@PFUIP7Mgvn43r@{r*+b1}E~q zper+?P?$aRx*&>5kYIR#gC@k-jLEY=u;sE*3efH>8)P#9m58jpJ*L`43xw6b9caGl zpXalb#{d57W80ryz;>1`HyxP)zPfPt$Dwu;K?2X|z|rM1rxL@(>k=G98ineRjFQi` zhiY!+M`h?6lcptNGT3cTPFE=9XXw0+$?N*?`FaL&kb3&M17j5lnw@~Y!+8eRf221( z>Evs7)avdMYt)E~4@@!oP4e)ET-BcsS3I>1#jc-_sZZ@t_<0;x*;SuOiY-stxN~tA z^zoP6rC7CjXw}coEusZ7dP*}-XlAXW@7xBQ*Vifca&MoYWdqscUBD`(TX7{nH$%Dw z7fA)L_*qa?XvS62e04Fo&#Qsjn1rS!`=29-a4TFxORtFR_x|S-siDKaEhn>CA4xkn z`c2BSF86qS6jyl+NT;^Cv)3-JV3`eJB8GB;+6{-z85K!>MOP8xB;TH!gXhtwaZSv|G?xA0>dgp%F3;4c-^rb^VqA1UvpI7tl->=>@dZwI< z+;p#>jQ`)a`owG-8Fxu*!%pkZzV`bsziR_by?#yphMnT?|M@e_v;&$jO6a^!Kk$=X zY)WVT1?KmN0F+DiaMv$(RxASgbjD_U(f_$dYs;?<2M&^mcX&)oZu#t=hSH3A?xgKb z_J59kd>{NR6OYjI%uOlhQlFok+SZp#GIv9rH~o+{E%DffA_n#dWr9YDy#!sZgz+h{quZp^^5*VfN43;iD~)I zmep2Q{pW#CMS#mCqaw_Invg9m`s541e$yi?95?Yue>2B_XYLxOcOE+I)gzU9OkWP5 zv=m-ZQ24+dii5>>Q|j@bp`|$jY0nUDyy9u*5@L zwVOXY;CCjU89mS@#-hMGCubz*%(*%H_W?F_5If$9Zu-2?+w1@hnqex?Wx-M-2~cSX zn%K>h$|vT~WJYMoY#KNX!JT8*`2+^#4gZjcw$0UJvk_*@e1sqUiNO&@0`DClU`F3? zA_5o}S&=!D#rkmDEO|$SXzjjF?=;pDAA(eV3ReITcqs6>6i%SyC+-O_(SMV%m*cbz z_J&^D^Dz-#ZRLnI-5Getm)5}L zT z*l*}xblJlIxwvQjw{745{C|&+1{Uz}QzFP8j~)Dv*V+SgsRS=aZ`-!XGyOc^fBiDW zjKk6a#LWw4>!tCJsc${<|Csvb_4Y6O(fG&Iw_N1^cy*1Tb(Ei&N1T;UYPBAioD}YI z;soml(Nf{l!Y{M=);NYv9#1~@VDIcPuH9Ty!It}c?sh~EovXQ5{^~0m8|(hx4jNf% zK2$y_oS-4R{XCDb1*sFPuudo=Rfd4EC+;E)b2_NL_#kRpP)pf(2w@s=j8w0*rBCZ# z0K$$1L+V!pY!nbkdQS%6e=P>yYy`r$>k8&q-LWE&bU|K!v-(AFAO$#k^eEw1<5V{j zpGQ6S`^8XF0z^T49Bl6|2JoA&fk36GJ?QgmG4`K<|J%6#&%kdTW&t?-kH!BTuKUN; z{0+q2z3(4a^N*_mlF5I9>)*Q7e^AZey48PB&EHWh|G>$=b*ulln*U#1O`&V!PYF)4 z=}lak8qo(CG=Na?2c+xznF_36doJ_xu_Q=u^59bDsal{0iI#+1Nt%2s0Qipn*qwub zP$34k5)Vrp{2G4Hx?7OeU9THW+{}y|7CVD6=&Q3DjNOzch4vk1ioIcUq_KCw2i-#! zll!nFD2e%4T5{D&G6j%&_12aFfe5Fo))xNr*pM;DkWB1ShIH!S*W*GfpC}epWQ%rN zl}*`hbsf9udC!p-S(Zb^?YfYaqRsmze(FG0 zt3rU>zVPEGpVJ-!1an=g)@fA)#uSo)umXy8iL28{_e70;Sew zt(N#0kZD5@O$&>QJFM4N@fbkl59YkShFkCF5>W0?Uz7u()YgW%-EHRg+k7dgT%Dq* zR4uC5l6?WOE_4=%KG{CflM1C#&}S=#If;N6u47QW%HS7u%G+pkr0Tb$L==dI>N_qt z{|9A=4dh?)j?THy)Y^I$ISyisfGGHrNUCFSo#PVqb9~5TyrB05rdV0OTjk2wWEqn7 z4haF2VXeynS=?UaS_5*4dfl4F6b!%y=am)}wJ~OJjJzt$LK;&@kw#lc=Z4;)<>6NI zDA||Q5K@wok2|Aj%&K54y_v9f9cBY4_|YxOX#=D|f?qLO%*_VFuG4sj7vzp ze-O|jr`)~~7@ z5Id$$j|8{Hq;<@~)@c~#d$qm51G^PFD@9hPf zSScJ3Ag>7!<^_l=I9Nz>a9r5imgMbJA^H~~bQq!%Sb(rjnzYcE4IqMmosI^yB8Wki<3X!zYxVDp8+gQ5 zfWLzYoxPdxvQv-Vp@+6vC)<#OUI^vFbxxPPp|?rX4Y(r&rh{2zaUm2`A5+OsX!ueujz$P@^mO;<6d84T?|D5e(Y?*~M~0o~Tisg-Jk z8r}}=D=xk?rfMsLz9!B1D(&zni^krxhFPZ&R&fvDYoFmMak$wNYsoOZ6?Ir%Gj+u_ z$AwV@UrB^JNkb=fvd%vD=y0cZxnGaw?bX%r>_4x&UDhNblK@k+UVRbt+8g(GyzAsL*10{oDJ`R}!gM0u3uQ zT!gk@(8tt6=Gb6`kwNu~RjavGy)eb7`jN$4Ychgj3!>Q8SHyQ*eJS@O8aiWJFPgS7 zj#R$`$|~~rzSjNGNIaZb7t)>1Z--!l?>#PR_0;HA3%9+Mqrokc)HO^0og91X)w_=uF zc!2MN#d>+VR?#*zG#K2qt2AUe5>`rY<)*hHl$H2)SSEg!5~{?t^!gZ$Ue&xGo;WOl zDr$0Lw^k!M=}pTao8p8ZAQT67ZB*Kwy|KwHar~0D^=iFnytRs}c(p=C zq1^F83ZY{%3qGhmYN(ze(sB2dlw4Ono$3`-6)7Z99ZPCn!T423z3&N=mmB%|u!II^ zd=t4DL)?MF_9bbgMbsg`e7c@|wGV`Zo=~0jjGjh$3#j_fm71=ko{+0+j`&Tk5zJzX zLfy`I5NtCD$ZX2&Fk;^;_DP{!#3T0U{KJy4(l$pH+rI|q{w({0f!;V7(KCvNcUPhn zTmGZj@K?+awcrKh((~Dsew2j@<>dLqa~|-gAk5U%ioQC^@`Ow5Xhl)~mq*r1uO{;@ zV*0&w;A$gGtX3^X7iA zNDMuu(>Gf4rD;zLH=~~0bMfdM^)-5g`*K90^~~UJEbQ*~0T|ZTMb{kSG8z_)P}{Sb zPQDl|D3b4-FKJryt&k#BH7&>;9$Z}lrZ;YsA7yEL{>;j~V2*3{5wSum&xDkw)KdBH z%T{`p58dC}YZ5YQLaDy?!OE;lPUp6LBf4@?r3xke1r3NtzP(BV1jbju)HvQmGttIB z9r)`cvl6Jw=i;2SR9M{}XjC2?r`b8+8>r1;`v{9^)TO!hnU9o!XeizfiofenPF@wP z^!Okn zy(~;ZOTv-~YDJdbao1Yf?xRRFNs9!jM(?rl&rVsXT|2DIbp85y&IvsTwKa2KByqZD zScM4MJ^awLFCK2fZZzum$g|M^i`rhWc34M0*te0SU_cT}-Be(so0-5SqZX#^{Ak1^-6$mu{ zP;W-a|4HLMwb4_0PVv@(PBe=9It47$P~dFDW)^aX!*R5q8LBXjYSU)z&g|P&m)~={ z;erMiGwl;SPodhr?YP+|Aoi5I5H@l)q_v9vW0YN>8RFv1%`AsY0=m7B-s9`3O-F0UZTKI$t~`*xJui3yO@1u6UwicX9(W4d*TAfYBJ6*pri z`i`}jC*gCPzHKG0lAQ0Jn$x+;<1QW4)0nx=#l#6YY^@IQWftXO*`&qX-2yK(Dv^$H z`JNUkk4zd+A6*&H6HTc^C0u|m_dZ+%vW_&2J+F4+u|m21sqtW>5N0WnAX9W=vDE%5 zVa-~ek@8vc`B?yr_`hR2BzGL|k}ZC8A=t{d$G$xf;>UBKrRd>EoV^fg=IeNH%lPAq z#Ls#mUfh?m);)FnmmL9R+7~k#CN{-AjW+q z&Yhn-3%4?-pc(1e=yh@A+8Nu#aYN~+_BjqU7G<{`UIj81;Z;*#1FRO`lOL8ey`w-0 zR4DPcX#Wc;(|M@CQt{9V6CjY~CfT#+&TEn9KLUFF9ZbLw9jPX~tW&k5(>~HvEvpjK zhyt{P!Nd7RP3W|hCP&>qJF)bSZ>=0)N*+ViOTi8&RzroBLthV$z^MUnjQEi=t~VpA zUH2{bBI&)rpUGb>^>95$24uODDgT<)=9v?Zl_D?(DKbqhuM~`%uU$kDrWc_qa_!A- z0KB7RMpokVPR(3nm3=hk;xAEa!x*&*d7;OUdOVz;a;-X;801C4#*qkSZg5m(m0V{K;b z=6f}-=Uoq+UM*k0a?{&)SK^B(i}RN50gV=QHDFvB5Qay~c`B8qpR z#5!OU@wI8$KaV)f4fE#~#_j5LoNjKpowN{kKbnj>IlkcTy0#L(59+|5%xRSQWYY=! z6hH?B|2f;~$BLFv6@6hLNe1Bie0AnVnMfPKt?BN{$NsL#4Fb|nRl`so(0jZgpl;kb zAhI58HddNt)C1|*NSgQ8j@@~jj3|@O*VGdCLlnE?=_5N!flRPIFUi$z#*s)nD&z}$ zMT%^CC9CpeDb=3eeL9ZX`If~}Y*m<^My(ikC0eVodU0Gr+$ng z_o`igGo{=3x#{fn`4RJJ3~}*ZIXUK!(y-h0NHQiq@sF-u=(_D`A*+)kZm>a&I{`7$ z^I&vzw5JA4L!lb-lsyk~1(yaKe%e*m(2Iwb(9q*gsG+{SM@ThueTyNVXbMN;)q*I+ zwY9Z9>Cp4E02}*=!X7iLHcbaUmBF~g4=${_mxeC9&*CNf0eB3aP2x>Wwi+W|AUog& z(qQ;X98SA8x;h`r#_O{74oGT!>14bRujKHjNcE+3A`^>02~a0Yqb^L$kWK^NEgE}% z1D())0@$lh{uVBK?T4RB(y9Spf)v<4xcU{iMV$SR`brF_jA;>&tW;lf8WRPL>?&o- zr-MDr;!ZZU3QRxfOi>x6++$O{*_nd_l4my@km{Ml3Xq0O<-Cs|3l_+~eUcQdPj+=C za)<>83Ai`%c-Hswq$CY2Xs$L|Q;K?CSqy-HHZ3#`bCg{6i5V|At$*COFZ08>h`+KH zB9QA9H!I*RoD~eAdHi)5c8q=!P04*eWH&Un`tSHF>5{b(S0pwg8XopJ_Dpqj0nyrNCzuRA}tu0@G9k_mY zGutF~%3}%Etor$h{D#_sP%`f)Ni$OLxQdC-azeeTL#HQKQE-3xAi3amL;d~Bs$YL2 z0yza02*x&Z_YW3JAS=Ag<3u7Ejj$DIB9nhBb|LCIto6RNtuJ0?_H+oM^%2z7(gs!l z;X*RyqnNtqDmfmY2~k-@!gdyq&Sa#LDi1DH{*}I8!`ZX9_7MG{KtTf%Ah^;fivSe^>74;Q?DAcZ3VTj- z$Uab76Ne--v#$e?hi*=FAe0-F@xZdZjI;>RIFcqC3S_}Bt4_bcpo3O-zaWZ^tTFSB z8}_K3%q|X&+~|qwq~4)5;nteKSp+6$Vjp6%zf1GOdmwQosL;)p z03bJ+t-sW(xTzgMwJXrT_8nmEKVsq`uHQfLL;`J~x)2hA5@*PUAhMy9&Tj(l7N(|m z2&?&ocT;yAa{Xs)h5G*D7aUbb_@#~_gG~coNi_~MM>z24`U5D2z@(3yP)o`8TB8{g z(Iw2rP4sC1P(YZ7<7~d|_>9{sG66oh5E;| z0S113)kaM?mU^j#b;mlB!l`nvqiUt0I3`L*)qGl%#veyQbMoNJ58)H8hNS>W2#9Gi z(yJl~t6dmff(eS01CVVvW#>y}q?I(%B!GP*3M;sojWVzz{&$XhT;83Br_QO|&Kptq zD^-CzhgRSnFwCSI>VWGhlANxfI2yM^gKUe|zlniXvWHAUy6pMunEZ@8dnG_}z!uqI zdfK4B5Pv_8-akW;0?DNSJLnpLsZV#cnQpPC_2(~WY(A-)9Hngm~Llr%*h z%@5wT3S=AZ?SY9_(-Kt+5`7XjD-BLPy{TcYAtY)DlF9u|WG$?LZOC8%y@-a%spR6j z6@~7#wDc>)1*wx+U;wkx%ItYnEGd(~oqq7(K@%%^WDk%CR)Fo{pET8HS{|DR=-KJq zlt?c)h@H4d1e#pUl48*KwIXR+5hVveCGCyhu#3#RkVvZ}(K79;mLFAhKe#t}T+V#} z=pWSX-SheJtpGx^TcYMHi7`vcyvUxHA=&7a>IWEtalzzO71mkdP$1C^VdKQu)KOPd z41%VuA;g(SE7tIyyal=2_yQ}nyyVd|cc5k5>!&>CI4Z>@k#nIQwpJh1?M(55C%e7N zp);3svFrJ&vN~oP5#}&3MOJ6=LJz8d)eP&boOo23ZRPQPr&MSY8ubEb;efL7a|LZ) z9-sFlgihOy7XRpps!tubQfR;SR5rfBa&JS)~IR#%hm3wJnJ{lI> z>B(ZmWE1d}BPx}zn{h&P&<>#W+7g91IYiIspnilegnQwZ{Rahh5rA14oirWJZ`u~i zU+LnPr*YqmK(-=uN+-Rvc2G1s{H7Nh55lFl)ks`Qc8T1bC|Lr5-V1ZXNUPxnjdA!I!P&&Q}w#`HGYFc!da&ZCZZ91f}( z`%jO<7~|3A+RLW&|0~G~HmXKs83_xCI$Zr=3%!@P*YVUlW-Oa+ z+|kT+LZ?MaS{iqN+;f~~>a*-)WMF6Rs;q|Do(X263-H~8UGYjyc^6TAK;sc504WR2 z-D6wg)?S~6(UC+!fW5jgZunG$fAPJ8gGQ0-V~r1iW`BARWmEXCJE(uoGh5N8QQQKe zztvDG1_P=DC4>|Bt3xItL$V`xJ8GGapyg0k{<+3J|!@b)B=Uu?D#tO zV)xpo=(Q`f5_eh&Sd%<|p8c4kLw+z0m-h(3s$=Hmql8{KV-jA%?296WiVYd}3vqjD8%Kw(uCx6vtg<IqSSC2a1n z>s`Ku!}hg3CdKai$lo=lyieVkHDl02k%C zRT7w7R>gJD*@eLrD+fT|Cr&ZV*T5tv?=*-yy!2COA7W~A-IsZWos5}hREG&XfG zsZPCE&GV;UoXLlCKc*BOdsr8Szx8G;Fp0_H_^P#0q7^C7r17z}nXycILC>7AL-jI1 z*tZ#;IcD{}q`Qg)t!%bA=dsxu$-f zlz%;UOIC8L?cINm)6falF}+t&qCbw$-=2X09#O~zeCPDN?7bU3!e9HjTis);M1SKH zZFpKJvt-Vk?Sj>YbolR%s{nMH9dJTf_tN#lzxXvC}nWT?K$FHhH>H>PR({>%jzgDSwuUeV8kG12~=^vNK-&zh3W@MQu?(0m>zyHDw zmeHjNoHh{H=ku#7kuiV-s4~BT>o2~}Kc@db=1Suq)8Bf@{%h$0rte}AseX5(X=Z5* zA#w|pcVeghTbNv(sx<`kcPR7b_;~V1!z_Fcbg=0iI#@mWBSro9Gt&Kz#ajaHI?(4g z{)?->`y9Z5yQW|zHbm+TD{R9H)dEG2CCi7aiNAQgC$O3iJCYiH?R8u{0beA=*yXW> zrqfn;aACHCsOu@0{WWJ{VF~z(M^)SV)qm~!!(xEv^`sPRLUun6$PaT-eE_Is?fvS$ z4fEqp@!vlQILZYS75y{xTN>LxL;v^T{{J;+L#HL?8mLFIgDi5Yyz@H= zvAwCt@loZp-4NyGC=RWu`QP5w<$%R=Ub1_$CM@LmI7oD z7Al>*WD{#BjknOP3C^(c^>Q;XO6Az0JMYCc<&loA={%`MZSXk_mF#GZ6ZJ8Ww&hEs zwa@K~bSd-qsjMirEMKhgp*<|lEs9Ogqw3R}Oz0hlXEB$|#8yo>a(y_)5H0%Fw#k;F zR$Ns1-|aw9DKLt$UQj@hh>?*p8Cm*Lo>Hu1HfvkSkj$4vm#qx1$6!-v$alE)weoSE zjS>po-vED?ytY$5cuQ=WV*2ufR*c3CZhr6|CviPICmXBjl3&D7x}_mD-wI>lM?t$o|mN- z!6^Zm?mAlF?A}dEFPsruGlITp?668aeEz1=6q@&sDO>IG8ROckFP%%H#gSE79=Z3e zz+%v7x=5*lC9JLFNzm@xXlQo445yXxfU^oKx(#vuHvPhRJFtXp-jhXnN>7a^djfbR zI7$J39riFjN)u^4uw#Q~b8!wGUN1o9xW6CTI#dmrvZzhSpSbU!E7och-o9|*eqDY` zr<-;}(Gl*W8Fo2ED)NV0GCTQ{4z+MP1|=h8I}k-qy?5GzTEJ$=)H#C|>Vu_oJpu^b zDWhnx7~P&yc${uubvEZ|=eNo!rHfjg4Wd4xuO!?5*+&c<_<*&wE+li*cS8ib*$(iQkoL;EyMB^_ zWBQ;_-o1Ht4!OM!TIaRy-nqga^;?FZ{Gql>xiJ?%3zE4a1S19m<6|Plh5E?ZV3un= zbxHf(lEUI6c3r;I=6w|`)yS)R`J9^ClANT>xs>Z?5bFZdN6Idc0(U=-5?fTA<+h!` z=hm`C&Uck17Mimd$M6{oUB7QM;9!tXINuN$aQdvZYI%>)Y&!LsBG%4}9-?k3$imcy)wFUjTQB}F{GXPl>SN7gn-U>nNJ6E8=VyU)f zD3dN|dg%Ox8lL-F_p;&00jE}k*aMzGgmrrb+n+qBhKI*()ciZA$ELtAo&$|H4pGNO z6dXSo#K>U@GkWFOsg?+v0zU(8EUx3zvUftMVR&U6uZwbRY637>bvn4-U49H$cuOD`KQgn8WKkn|477ltr-ixLsM&xOP7hwz{W+liU zq>LQ&CccBG51fhVxqENt^D!(Hfj3@&lMl*DZq$J3zsM???td`g=)kTG%kz60`h;g9 zG^{IVF)`1*<)Ueg^xeN&Wr$N%y|Yba4mqoa*FJJ)~k&VBHRj z76;Rm3A992s;(&1xLokMwqyIB^dhjeMTDAVHuc$uqjtt4X&k{vF2h>xK@NY_)rwbY zSzhql9RWUv5Te{L35a&)#82-p+Wn1>(E!c3)yst)Q;+O6jm0azz?a>6>8id1-ZiOU z(7OKZWNLHPS`sJ_(Hcbl0dxW2A=yHBT@JsA zn)zw|l1V+o>DrN4a0r?~x%tNO$45nc?w=%BbDWN`xzg zN2Vd<-Ar@aeE*y1tD4;EBHxUB;!NTKX}tm^f+2|2Q+$Xe`c+W`#qb=aHf_}JYxY}uUD`Pvj*b~d#*`HA?b+DC;Ylc+$tHO74Di*E1I z@KyKlPd|QSXgTvZh1FQb-Q!ytwyDG!*2w3F8rnkC)Q#9*vX%z=XS0Py6y7+drFGJ2 zw*Wa!bewmfVMSG}@Wty}Iv?*8iQJS|i_sn_OZOEtY|}EHd^5$R5D_AV<?ng zTqI-RT!?^EJi{w|_)tyAeQT}cP$#471D0OPIP!yQ+6Xs(UGt?gJ*99|J1@O@Q&4dL z(t+l+DSnkLjagT?bq6?RU)R1AZ5QY6RoIbPHrpBJ-71`yFWQqJwt}Nv)tpvw&{H}N&LDbXu_m8F_2U? zv7{bZ+2-3VTQ@mMgLcfnxyf-SqqKgY?$41$f9U{Sv$Uz)zOewWs9~d7iJe+cC4#h0 zyKwki?;X8BlH6a1J!B7_<-K>krA3+gB=ab$81v(%>Ah+PKvlc937PhG%RFjGaW_YL zE^F+Qus@%#Pp-UjhVN_RyFWEBdSU&LuYuq0+nu&xK^8ssV`Sc%CW9R1o`}_$GX`zf z+NH>m{_SUucl#f+_iemoNw43bgXW7V3 z*RiRF$;x-P)9NAQ@uTNDk?NLjoI89^5WDK(hBgq1EcmidYk6+tx6-P#_Fn!!Httm_qNgv?!1T; zG@82XqyJlm^1vhU0fm-AbV|t7%s1JQo0(tU$Yj5OWL=LFK2mcFTVq*OQWoMq>1e9$ zsk|0a&>&a5dgziI-%`D}g>}o|zBFw@dp;)TKn#tKWXETjI^#o|5K9$us!*Lonjmyh zcJ>74wHr+<>Pf%jO{#F#_NEK6D0lL2qYGNGflZ2kydidv>d!vgki-CAUDB-TZ~L?A z>R$nMWN#BXLvZD46zlznRy_kvpBF}kdjhZANl)VuKXl(X-=R!9ESnAPr5^e!tw{Uc z9T!Cq>oXpYDOh_YW{Nx>*ACOosPN52-Te}OIOPSWm}q+L@JmpE-4QgW6r`$`D?Uek zOoCP6sx;@(qQS{lYH-x4NUfR>V(jDNJ0=D0hFhn#4aa%u@sAeYARI1`Ih8-nJ&*1< zas(AGr6XlrA!(BB8k~pf42V!FzLq~6EKO42)jnL%cUgaA8hJIg#T&L0*u0#39frfAn3soDkz0$)%&Wi)$ho&OIuj{{Y&v{0S;m2SMOyrILLMS z;3o#^tWi&9xDN^h=GLbfAkN9+7|gd-C-(|RIE2}E04c%t^XXvGR!Z_xqs-$o@+H4F_!vmSIB+|a^m@QyA3Ik!Ujnv z?#S0g2VTfwxuRuxid?4~xf;`;^d(5g*eUy&bp)L?#cQ&%lhF;^!)uqsh@2YqIM-J% z=k)yTiJ1;eV%9B1B*N@qDmF&_-rM9sMXCC|ChjqsT%m7SOT2~?ksSX0&X{Nr{V{m8 zY?!6Yt)xuwfQoGC(NA&Ds4?__gic5{D2^L1u-d?Qa}f!@MalQAGyCk-FIDHQ#TmU} zy%s*)nYmu>&r5iazNN#uSN)b%{9C)+Wi5w0JPL=zDf$Mn)J3p7>of5~#fp4J`-4;w zAiZ8nmdrc(7n~1Y&xpCa(2b}x#vf7im^yD%Tnj#13J_u?4gR}2kSc8^s3@pv@1F>Ig-j&1?*q~@#b*Lq6c6)Zxv@?fQ zhVy9!r_LQ7uBjxx%eUq@quBRKjfBSFaaHcC(L|2zwR||Zw<+K7R&2A&0>iJazgF^{u z)h@y#fA&(C=yeJG4lQu(?L%^gYL=bRk#k@6$sRgpqV?^hX70yI9!c${J3NXA#m|QX z5=S8?Nt)T8eGGyM>fwQ?K+{9M5pJ39^I01Wy#2Kwy@M-X`RKGev2*2}v5=N@Gjwzn zY}$8D%QSG3+L^0&9-M=+w81WR%pT8^IMM@e?aX=SE3o7?twfER_Ko_I4L?$Fcyv%F zHp8xP`l9Qp>ygd@BBHj_HF*lvE!wYZ$w9?4qknI0Uf~zY|5E>GsyR} z!5~blbJ$Z|kE~$h6Iec6Xd{Q^RLUK`Msja6yhL!sBSx-9#*BZC4Ll^d9)K+{!5`6f zM5YLOp#lmPW-{LhdRZt;gjfcF6;rOw%P+^$t=;t#ePidki*dt&lQo;Xt_3qGr;+6X zw@k{9m8{!E8|&_oOK5lv;|%M=IuX9eUM+v| zvez!ysc)Vt#qEqNT*j)h!6xx#H=<|K3aNOqueB5n7%$fh~*Zmftd%wY}!xzTZM%Rx9-S*`uy zn$|)Vi1R%{obSPvp8UCvefi2fl1Y?qoYS%VQ%Pp`@ zmDP& z8SV8+f5667yn{wX*F?ro1?ub{Qf@uKeIzfYFY;;E+LHzdSRX=bDKV0B60lZRZE8Zh z23ho!popr|_kCB@PCM-_Z^@RC>^K8iV-I#ESiLV0fU5GF3F$>i2Jzd11ozVZh-~A# zzJEFxUo>XO#W{~NLuy2 zok=OFyAa}rFTNSEXkKOSjN51Y;Sx&c7}u?gkI&*FtMv^oU|09u)-HF^DxJ8JeNu|& z@)S6CeRxlq1JqvW!=STyZ%{(=w4pWlCFG70rlR|Rdqt&G$g&!7=<*=mm@?2h7I&cU z2`k1qMa9=8x3%kb`AX*Dsp&EG#V#)aY2$7PE)A=8u0&tXMdU^77xzANxkEqVrg1pR z*tsNsB`F-@x49v$AL5 z0i|BjBg?sxRD~wo{;(q)dh;#TEz~I=cP049YK(rr$L*7(P>k|GjipZkayelNU1e!_ zpM1ttz)|zlU3~n#Jjadfic1JHA6-B6ZpYT{#_%Oug!q*g+it`izZRGs`B3~!=W8(b zL#w=;nn;@bg`9{#TKCCnx19X^x<^Vgx1-rx?*y5#x_?36LYkx!bDuTInS;Kn)-N9x zV~~N9Rlsx{;F@*ad)B($xBy$x44v8GY9SR>in?#Af-AGg_bUwL`!>7270@QO+h2r>T1OHV!KQ;rZT4ZjI|$hd{(>^yQ;ZIu4FW$#8NZw0lu_VCS~OAibE##A*4(eSAe@PjDxl;K=ZD{$aA#h3Jm^ko+M8QN<`4e{vneJ_UbZMo>`HtvK)a@dJDA?)5w zoet_o)bUQ5F7fh)StIcci3Ulms5Vua63EI|hWynWNI3Dr6FDZZh#^aG;YKFG>O62B z58rNOpIJ(x-#ZL zS?P*vi<~SiX&QtY`2Kf3(r_t6> zUw_hdVl^9~U44}TazATLp7kB$cPvJoepi?pFqY^#USOp#(nU#1&5Z-)X@Kt=_ZS;H zJLwgLeb)k5shRRV`XIj$>@aF>ig;?03P%>2IApvV1^S;DBL~J)+kN3BVUR$zl?gLk(O?V(eKH3ps=8M1Gr zfA}LLu%KaTkoR;l#gH~4dB;{^6F3QA#$^)3@!N(N^c|d>ORwIy`_O@You?=3)$S^! z?$2$A;MD@f%z?QDt}mRoeR(pn)BoBD@`IyZ^1q9g%7^_~6w~_Ujp}d)Yc`jl%Ojeb z3r&4#(6@d6dN(CxLd|uZ6JLVDDVN8IRlG^{qwei}l3MO8n&+y<5gS>Va+4g})OEZ0 zPjp3bx3RNm{sT6Oap9DP6wFjdz!)2D?kuw%QJbVDaVsnA%YhR7HLF;95>lhxh zXcK)ypunCdUOWMfio0W(cMtzwt5G4y#LaE@eS6UY0fk5SufwB)Ye~8LIpPtdskhL) z^Oh3cBxDbUflc+D-_iF(u4NXVirnt7t6(-RH0-4}i z@W>*J47mA3c6ZbmvvXXpH1PYxO-^aG{BQy6E^-Rd3s0G%lXHznEBkFo>BlbTSO6SA zZl(A&bTKKeWQl>na9dHthyw1w5o^`Ls#Iv56LAfw@)?K2G|Pj+F}|~dR>S##g-FJh zTB2?Xs0SB*FhYmj4$tjvGf*+2A5kx^0?zM8xvr=0ac>5DErbIDJO< zZi%X89$xQG?ximzwe)JrG(cHeCL{C8{o*H(KnprQ_GuL^V9`bJe|Z8Oc;+PSp{&R`FcjQ*RQ$#G9=!~wr0bZl5`;?|RX^qa zT7G25b<>=;?y;%3fnrMCGjK`xRpUqVuT_=87^>@y@>a zE0uP)$p-0}O~JAJZ=dIxo8|DAatS%psIyLI2mNi!ok)xl+T(KY+;p+Cg>9a)qtWh} zv6}r$h@MJ{P_{iE=lv>H^?Y?Fq*}b_(>sKSr%4sy7;+KOeIO=KhU*iJePC^2LJ- ze~9KAAbBq35OgFwVh^dYfpI-o15`?@1WL!S#05X=u0iU1h&YE&c4}FWqsn+5s<-wa z0hQ`KI{=ydIDme9D;&e4jnIuN`gWV>l54+@H>sh?B1qy48is2o>Ngk0j{Xh`97!w5 z~5r9?#&nei(B=DfdtEKmq|q6*S_*sd@8989DzA-@h3NjYZUqzcUU!DS3b7)H z^Q^gCgim5=2RvycS1{T|ty{4pn|3Xrz~{{+_W@G6F8OOqxQyiMOeRQzD)aZUO)_(O@rTuFk7SX*Iz zm@LG|;Zf4>A{s`HvN1;7C&-9XS4-~%ADQ~*pOLv}uI!=HxA=^T=C-E4Puf3yllxL? zK;0xzMOTq+6^si)rv=lS{&n;gKg&WZ>DwF-2V!`x~s+aSK6Y_%atNkT`s ztI3iK{Q5tC<&dC6ka2QjOy?Ok7v zI!l!qnAS2WNgcN|qT?kk%LpOuulqe(<1eSI)?%h=rL&!G=ov03KY^@%o|Mc;2+|%H zgxNUD&^I2VA1VOa*7%qNDhzDhx5j~$=eH?~`!~?-S|()GrmrvLY@SjoZuu#5-e!21{!EAxCeI)?k)j>=Uv>Y`@iSxz0djZ zR&`Y<@`ai`*PPFI##qnK>kP=D;vQ`_U=`7QUh*ZCdAx9|++5eRun{4%^xlQ_^YxRi zTs}+it~;n`sZ-JJ!%wsGbn;zHsB^c>Wu4N9LD4>1sjrvRnbsK|;}vUz00V^DA!S+9 zE0RLG)YUhrF4Ig=F!096m*KKbU3@Zw1>+j~%e5KaC<|?jW;io^VMQCJj>@NzbweQ? zrgqpICi{$A9IwG_Yar6%<*ebNsNdz*(Y>m_w84*0uNcecwkaaFUJBXb7t9)g6I(h@ zGyRYJ85Oex6*^AG!i=OIhPLr@O-cQT5?@7R|J&XK1`mK+Z6ST6{hvDp2XOCi(9gc+ zI$5RebKB6nq{yp@#4Fy!b5Nk(0}lX_+Z_YxPTCPk8Lg z(BJZI6=`|VY1#YhXr|}FvEk7=8oSL-j<-tA7SpUzsb0}-oQVkXw4XdvjXrtlvsoaW zX0oE?IN#BDco|k>)wQ5wS_zNhH+>!>gvACELBfPB33=BZd7il+dx5Z=3myNIo=&%l z)Yk^q|9qRa#MeIkddN7ZEhqB zAG=I#4jj>5cfzkbC^j>Zz&j=dW7%M>{|O#s0Lm5inTZ&*MfjI>dub1x*}QmOu)P~H zjK#y{uL9Sqc?Oq$g_X9g4i4M@9%pd;G&t@3IejAjA2osrOgv8_P_{VcB|YX8ysUdF zsFYPb*VXxxZ`rz{h+@jb;gpUT#jX6}m#@rATCNc>cCuyEt-J{7k(w zta8bttIr))=ihfFni>A>icI+DcDh|rdE*g&Gl^A^{v0g-3uqYKq9M7?=hTGN^Ln{X zL!v4X?CxlMd%BGpaAZ&p)p>VNTQlPH=445TWl?vy+H$_Li+4se<%_GmTtq{iqj7N| zq4zWk82`TXky~muj*<_m#gx|23rma8{o}?f&zOi~*s%OK}vyt18tU z3kX&bx8)MkQbnEaqLbzc4~u*T%K6OPQGgnV5t8pm#&prH&aOO(cZDkOxncU$+IB@5 zSl7S;tCfY7t66j%>#WxAS-uO$b!Cr(7YY&eX@ms(Hzqa77^ za(y8c3(}0}*8jejh8lJxGIlki5nWFv`15L|xs5gWB2wSzY^+MlOnbqo#?i4%ueI7^ zUE{JvE0ASY3A|_*S!pQbJu`QO*#QOIm=Z}Nq~FUES4MY_bqAq$=n}P`)6F@E;S78+ zB6WWj8~vsn5KQKjb+LR|PMR5&@G|9~Qt`LaaB0prJ7I+GNI!~yd+<%%pH%Mg?C9>l z{rdl1Vc?L|q2(!S$5JNPV!rlUOzr#E!I~ekD7g2jOPR{U)k-%#u&@gkYICwFNcMkM zL^Vl~l^fH*?rs{tDgyC(>?_@8H z2>){8&6piQH6!0OUorCZK3bSR&(y2NoM8bYU1{sGhHfJEcw6{eCr1VozX@F>m(7!uZKk0#Y zRE}S==V$!mSZVt>-22B1`s-fJR3e4L*B#~5U=KP3p78g*F&}TT{2u;{F8jax;hDB` zcXCn9Z7@ZzShpD(|9sITEdIGBdMol$t=b1QxaV;9@YG}m@0>&my~6fsMb#G8%zMz>AY`%^%AXy3J2dwbOt+Z*jZ6ENSq_Eup(Vyhmt%u`0c4yfG5 zd8FE?bxu0=Z)SdLcw>U;U)N5Cgs=lO1ZnYq|Crd~B(=k8kJCZSa5Pl^@gt}DPW{U# zxbeY9wuBe!gKC<2Hroh3`gPYNs+(yzxTd(gSvkm+kpJ>w zp@cgNZe-6Pw|)Iz77zHZkavUHDbZ9s<3^He+_dgT#(BX^UgiuJ=K9?|rrDuN(wuh5 zA_&~?t;0wtL#N(r`j zy-yRWBbwgBN#h~0ZKhQTg>v@mGTMs|tfy+=LC4ZEfW?cV$G$Nb3l6R7kykr*?V--3 zLdRGa=hPsiaU7@4Bf~T5!qH@?ru;>Cs}%jI$M;sG!0dv-_^_{09p!x~^zvI;>FB~? zKo3du*DXZDrX-l%G|6Uh=;gd~&`i^f9=eiVz39o#?2=FH!|wh8KT?fb&wjSHFJAZ1|=rH7e49=p01ONWy4!c77A^+?JS8pR-!0^J}!;w?(;} zx^KptB;43d8b8@@53|0&Zu|ym4dSV@*7sBM(~5Z^)4l!jKNSkNTV;#v3QkiMsPhQJ zt1vBU<_DVLGa4QQnG+x|k6Z_lbJy#TJa~HbM^~MMOO+ID{)?TY(htJEBrCLJZt|3Y z_m5{}eU8zVOn={agJJIwDz;oHVQ4tm&;R46fiwi$6D<$xfk{CZI}EIlhrZc@GmV0T zrvBCM#VpFhr}9x&nncka3KSdZ4#xF&RBo5lOw^{iXmX;I1?7)CdCp6&sg_BvTw52! zrCFJ@h}M(qI579{eSg~VLUgiVvfde6ax?mDsdZT&x$EqA8XUnQX5YkYVu$O7RKzYH z`Pv>-%8nWbdsPI6W&1$-n4uuS=_)em@_<(x$#oM+rPUAU-5dqxN`*-y*q4sAgfVH- zS`|hd-^OM+FmdXtiir!v$$+&duLe=4L(OO677~^|%5UhtvzWOQeOn$YJSX;XaE*u& zALZrj?sE|0hk1qx{*eS{nAa~dMZzcvgaB$Y0Rt{?xNvOUn-WE%#g7P z!e?y07wlTq**g-@E)tDce>Rc=slTmc2)&#*-0X+%mikqHJsAG=oFF3)k?DZ`e5S7! z#6XBh&oI;-tv?oa%h;@e<3`SWu}0mbac_3kuc7WA%BN+)=&DpNY8_AdxYz^}DC5fj zNz}5Uf<=%Gn)Oj=w9zifKD;BEcB(I*`4oAWQggmwe%Sp{1$y4E@ZJ7kxK4Ds?xTwE zveR8pyw}ftP6PLKcx*pZMl4P?Gy!HLQ|2feuvE{7LVZ?)Fb2cP8h46Z=Gdv|C6&qT zogxv)dTFXTA18OzmG6|d2rw5mjyhZU1wzca(1hQW)ZjKF&QnRZ$r3D%ePFuATA(mw zd7h@Q14WSDP@q{S%!`EbaHzt@tM*!iAev=*WF}jVx3p z;`WHmWd^AQct_-tW6_CYW344N&T&QF|NV6Eg?rhMcF#KX;;)z1zusZ1*a%dJoVs|1 zxeX00cqrnz*`rfs1b#QkVo*^$1t}K1W}5Ut``uU23u-0LE2sK>^4ax<_8K*d}GYz0>;65$IG!E&LQ0}-sf6hiJxz9x%S`TDqwBgn&9UJLepdQmz~Fc z?3ie=;s1S~Zb^B;r)Nxr-yP>X<@~8EDYjkKnq=qO4#3`c#K{|QRXyy`h@L!4ZMOz8|U+;oI|#6?`R?zC+^wX?RPRbA+8q}*{SjiVV7P6ujIOXRt4;+%{`SqzM8 zBJI2Svp#u)xGQ`O1sW~_r3!@2Dt^JwA7h{CBr{b z9A4S`e`B5r$4-^QD;t+wn2E!K%wO+qm(zVYQ56;fNrL%^})Z_89ppp7LWe8MrD zKeW?&X_{KD0h(0OxgHYlZGn zkfw!prz+6M5@?dcNnq_{wyo@$V@i)ISi=vz>)VMT_N#ka`97N?VSLQ&nmU4hu`@d2 zl~N=wszJ(k-FulAI`49=|GFi0Um(!Au!dz6v;ONr`PZM@JCHdTDYafX=MmIWIY%s` zx#2_C`2RSoiw!HsC8o1@4CY&EZ4EB@^yUnToD!F)|KI!Sf-S0enri z1ay3CM`r8EX@Ut7t`<{LbLzJe7Xw(->mc94QN4_Y!EI*lnJr!_%Z|?Kn?01Wk&(Hd zKg*SehjIliySA!RqZIgf!tr7v@&>ht`>k!m$X@Klmf#AAevF)p=m#^#z3oC){QZGK zdyv-B^!UZh#IXlbB3pPl-AuNGzT9znSRKMJ^E-39F3}14qGU^idDz_mfO;S`LmV%J0b{NE56dSBHcBXN>DN~g zxyv8T2$AIcaFp}-WAJFqJomLTvVS7Y7u$C+&xjY8X_Y2uCpU4VcZkVIIhEEe_(UK_ z2)yK1m0CTQW3oq-2glX&@;3Hq)J%dX3@mTftK@p@HUveGbyC`63TYxpA zjYRPkcXR`Op0_1-V5}hFB9U+G3vEhCqMmY zgdNg_y(@)jR`7~2Vl0tH?WLp|oTr03D{^HJ#c7F#Uq)N?%vaxp zT1Lq_`kDO{{DqHfM4XwZYwN)4PC`;7x!=uJ0BKH*ib|4njlf9N(Pc8I^7F*32B9(G z(|GYRMXyHC#+hzHB-!k@G&)M6NPw-+2`>6wV$I>KgE}K4`&hCD?}SsnM{{DM*6=e-xnhcb3{I+% zj2F@i>VK5jSCBk+qm8p7+`e#a_7<&CEqMLcryM)PQvBT?jDJ|`e=Rht&`}zG!>cpw zivKiW!Icrj$@Xp1Q=lY$ z+lgTDT0zNCUIiwLs}5;0RTz@=jTwguLW<{*c|84 z=~#suuu-jc_w6^Y@g}J{P3&pibaONa1x#vCR8iiy>N>|T5>ThXk|dMEN<6o!cE({D zEFUR5iH78?h<13)b#tFjk7qvaFCi)M>jYHhG3H^4lIv`29W{F$?(%~I*S3r{OKI3yoJEj@8njf z6^8V(SvR{8J6cKH!6t1-w7xD9C1VB7xen?czEE`kv_x=1;$8T#*~vrp$?GT^(ya+p)~^dFY*n7oUZ|uW6FwJt@X>IC*?kcPDBSp@WezX zX;+RI;KI$S5q9E9L-3@_&rge|llVBtPbBXL{F!7>O_ZQ>_HQ6OalVTAtwj`lYkqi* z)s1HTwTu%I&5o-8h+&cA`yUQ}0$;B%bA^ z;4w>T#W9c}o-tHdS`UCl_^Q7K|4fv6AaYmt+?R?QAdKR8GeIFK$wbYhO`r~mp=CZS zsG)HDE%a;rs>b;~_R?XdkXb>~Q2MxZcvQbpuO8u+3l&823ok~d0OX)o!LhlA{!6Nh zXcFoqWS(C(qf5Alc5wN&{I#iav_4)3$#A5iO#*8X{F2tV*|BHr&%qzG;!X2iGg?xV^lFp|6|0d`^hs#&d9JLZ}E zw^idi?NeY}l-Ca=t389sdPBI5DRpx>8(mAt0iP3+fvxfV#Uq ziEg%X)-^L7ywup8IjdL6XTzh1%ciVFmfgg%5*M8zls9P{d~@0X`~aGd1YsH!O;>q15~xG1AckcIg(CLTvXk2ftoBq9W@R6%K7Y0? z8ydY^R_uWrf}|8;r^XtX>~~pwJ2*I|)hC>!dX=n^%!o~_9Cv3U!IaNGEpbuF7GC3o z=D%t(rOtFf1E-6Xr<1fE9g$qKn-YT6DBClsDJUcgt-_dfKUP}W)r?%&{bavOQ)m%@ zv@R+NR{cK>_`g@KVm4q7(Fdc!{&tLFhujH<7&tZd1Obu2lmt>Sd&Q`Tq~zoAc0o7jcGjuOgWRgtD~zf%14*6t#DkGDj;~!+a-txDIn3vZ?BNey-JMs zwiyT%7XFgEPYd^(?KzS3MU)Mq`Udx+H54jR!JL)*SP$^fEz~x}@2e?c8eO zF8vo$uhdI!#6dvxubdc4H?C(YiP@C!XwXWgbtakl&RGfR8Kj>`d|@&v3LhRBlI(7p zRIpK`QKorGppUtSRaSGy5+7|OmCwkGSy?5zSy3Y5Po-(vI+TP_Ndlut5K1Z9wx$YI zHo!dxk&M@VnT}CbQCU=i*s{tlO7cKd#4LxsM)N%_ImIS<>I8YEmr?g{YCf}w)QfWX=K*)bEkV=W&15_rxkP^6!_T za7*kZceF;ebPm2-Y`MGH$!W`JzXU?~Mp_?^oEM9fGlJ+tn3sP6Nv;YgU^##Dsgq)4 zu>TS4F0g2((%4xH_}ak=0;MQpdvap8$t?h9f!yPp8cm4(J1L~Lw{RDMHt^5}H9SmO zIb=^8a~??XLMqi&7gk1qS^x!>WuVrMh;R0=L0c{AxdzS$-DtcXgb-F~7!wr}p%}cO zbj6pD^fYe*|>ZiQt>Y7ne zp^BL@6)-dNo#Iwy@*QeptIMfv00iv#6ysFo*Ik(^z(-{gL@mi7oC zYhrB-iu6Y52rbu4z%Wx!Ekv599PiZ+YRS#xvzD|_3tmBRHP0i(0TmaB^jxPpHSBu2 zdqOB$X}X4M;aATGxhu2M9`ZL}%MJfNoGoTs&jPc|%umt-J#q1>O7jV4fuJ&82Zo%e z;^|t+RtLrcU!IJ!UA;yyfh|eR%x;uNf>L@vvYDYnle#kp9s%H-B+XIfySS>dMq9EX zgDDJ$CP`@c2o?;m$G5MJLVZBohcumvTxbiRVsuFYd)xj9WP?LXw%my2amQ5~o~mJI zlT-OKR)#np5Fr~)G zea02Wxpt%ym*}^oW_g8>>PEi%t}u6ml?7EwX|^vskiqACDp0uS9$!F;jo7!XIDrf2 zPfA~T+ZK!c%6Z_yv^6BYSld>5TOVo#Ur?5}l_YysUPI~RZR8&@JnOAXD5)bzO!o~` z7Tfl1Kl`{Ig_vn@C|15@Zm*Wg00#D0-};zT6eC4D<2bZfkx^N95_It0YioG1Tv)&E zwb(vO#%C$PUad~2R`c|wD)Yoa6KgV^T9WOZWiIU6YV79DNb1C`^C!vg!H#UEi-lOB z6?23kJ-?re8Fej`a`=I0MG}t_aJK6tNPc-l*&O7Jc8@GV1m2hz;vblYlA*_)ky915 z12TL)P0{@@RlJ$YIi`S(V=L%p(@(Ub!)w9p*cGb$H%CO;WF8xE>KvR3`Tp`~Rgt08 zCPi8U7f>q7xKj_)zzxQ=|9G9_@Ma~T22C%8O0jSP$GhL7E?%}Pe~r^{&#`{VtDAG53a*U} zj?G1)bzRjZ>MQ&>LLOqbm<<|I0Olo1WOnC#Xmigb6`m^vs!@Lok0xWV=p(xy=KcCV`hQuPNXx zdLy4PZ(Rl=8z3SZFnTz=flBskJ4!d^@oP(ULwpMq%6SZCK3D1!l#qPXokX8tIL^+5 zHR?}-FtO(4ZIO?ZS!(Se3uNG0(M@I|GaqFMrUR-89m>cwG_A~c;8S^1G3roa`L8YM zhtzpG(|4M2DrPLR(-FlpkqDru=6PE#dfK1>VYVNQG%9Q+GJQeZV~J;DWd7oN=29N4 z9%L6=3!T!Li#un@#hN&OlN`DP2*=r)rg{#Fe?9B4L$)N|8Q*$iM}%%Y0%_;xb17bD z!75Z?>tj z`P5s4AVUP8kyDuUJIs{bB)fqlXr+jFpMPS(1g#XSkgr-=M z_&iV=s({yr-BN0=EM2N#T>}aiA5t8b*q0o$<5}d>bd+okC5ni|>@L-!iLZ_DOmFxw zAG#IU%VJC+;n(w$u1ej0rVNyoC7#1X;}2sopa85DeIe696t83AZ}Ticw~96iQ6xoq^t zhaKDyo`-SQ7&x~~o2f@6H_bo{>We+slHAsSg!L8Q9^hS+jpP;r{a{$WZ9qrRyk+`4 z0Jhk`Qg{|;;n9IeIQKF8q98vzz{;w3r}6RjAU;uW^O-EEe>*&dNs4`f&~XFp|I9i7 z&#LMn5Mg*;Ykj_OyAbJFc#`PU8uCMKtj-Jt_qu^@ zAM6rST0)}EaGrfg`A9XU-S<)nCwk;7UmO0S19<6 zUDX}#;&uE5yLk!*lO<>PAVg3N&XY2Wg>I&B2e>k20`7Tv z4ttZRInvGQDvExvE*?EoQ3QGnzJ{`w!LDXEnBR-K+zBGPB!HVGw*rTohe;&x+G@K+ z9Gz3aEVpKoS~Roz8>P(bn%*keE0iQ(%DlI(|Dz_D~LH7V*V>=0su&q1c4$7^`y-XTWLT>!9uk*dT z+DEj=ub}E23t)s^Jtf_{PD&&O1G;zd!1&MY_hBi_jPvT;V~G_T{oY`}c$zQ-qgH$UnX68mz+s{ppt?E8!Q4KA9& zmfJjH>Gi!D{1q_h{X}?tXz{8N6}ufz1d@~QKDGRLIH`XkjNEzNY`4SJ#TUy6)z_E!6SuSL(^r;YCX&M-gw@r3$Z0CMorgs$ey|A!>J{)(L47ES^= z?*x}n(4Z=c>e<)R zwsL{cq4%PEur3r;gZ|3RQn8jKjs1mk3C8=fur}j~=*Vfi-^rq`bF&KvU)es^@NKRM zYP|N^$3QkxRn)b`{ZHsL-I5yl%vk!-}@hpy~S*78)wqZ0|t_`>z*)lP&nyufOTdq3*B~f z_Ro;BZdx?aW?_bV+LVq&jH^WP@#+mvTnjRfV5J%YGy_%N0Zs5rVVl0<4=0eGa{}7V zGZC)^+iwyyw)3evMz%om{qOrs($#oPA^-15dT~-4BB}gg#zwvpE3SN0E_} z^ShB~_`qaqMCjyVlxUHmJc27b;EUhW+Y_7L@BRk)8HUQ{U2CKur_jgRt0$Q*^D zT(ZF??HTj@MzP$}sV~0|c&Ygdt1X!^B_<^8ksMRXu9jRglus5N z^FPC8RH&w@=_+mmrYFgZCKv(zQ2 zcQiW*cU*z>efbp3kW&}E|s@G4{8@ z4Uc>lT#+MY1Ks@bypzRw`QJ1=KM(qU(3=&!F2_tnH$XHaLaag8c=JJ<(B3tHpV~jh z(P0(K${HhxKUr+UFRAICXougZ!CVEOTz%sJ>!E8b!aVz*hPn)&jJ)HP$pM*v@!El6 z`xqX}y&e9v zy>f=C(`yt}pUylAt9yr*D_A9O(f7Oa^frel#?H|=fVlXIHGA6orgeOa71bclLHfP5 zt7!p8jD?cWx9VmT4t>&y@+YkL*>5!3@9O(PRi`cOzbVlsUsIF>0Dmoo^s?W9!HONb z-+1g5>3MZ}%Me@R=F2Anu6?kW=C0tUcKo)T##ahaalJhZ`Y9?!MMZJRFe1*AC(Om6 z8XVytHq$jSSHa!~g8Yy4ieJ%i-j`sl{gG}vp?fhEX@EpMqkg`zS^Gel zxrkb1OEJ1sTP~)nmG^8{e<0>2(-MuoY*<;pe!5mP=uQ2&HO2UMjZbVs!e;Kjcr!}1 z0$c2pBF`!UHQ7MC3x;L&IA>_db)`w=4QZlR>5>)|c*bXwcKH;HST&8*2OBLbvMYT1 zACh3#zn>vi;ysEgtY-Gb4dJW3!`wEB=^^W`)UZWP?i}E2vI@hG)bSOGY;ci@!K?If z`~#8rwCcPnMJoVuQWu1mZ1187PTO!u89A?)AF%l2wI z4gmpMTwL7NH?m|4&Av4v0A{x4iEzfY@&ppN0%*$1`4SvyD$0u837*VY_ITqP>&}rk3m~l6RDbuor*0{~ zlO#Z7lNGn{;FI+fstN!^&qe7*qS*2ZB~l%wPf!oOj?eVm+}zS8q=CKiZp6EFGU5xV zQ$q(pTCna`4x4>aieiIt<$dz5v1mQZXlxDbmMep7--qbsgAy=V0!C|Gj1Oij#j5mn z6hDc(U?f*z$zm5$kw^(2za~Ot5|l$*WNvPs$n+D#ql)S!=(q)pnlO2Ba$`6 zy!b&k2?}$8ktSEE2%D#FDO$F%^(?UDnL(CZGUHbQ@E?BmzI@53Xq|jsGkeAp^H%!%K#_Y=$*b~bd!<|=@VR!0!y_ZKh-M23B34<$G^K`U zc_7RQ{Fik4E%0loomvFyEgDJX`|>OO5e?mlT})U4AFBxvYdr)}a?#Z5sazW~5nXIW zIS75H>@n^G$359XKyZ$XD_E046-yp@S+~3278$O#C-8fHP^J5I;NP-|#&En?;P1HH zpwu^7R%$KAawD^9w@qBv@zA-axX-ozgjfDUniuj&BD=q0#*Ag!c&|+^Iw?E_8JQ+7hVg;eKDK+tcKrBFVpOU z+3*?-BWLw&2MrDRd7hnaHNpu0272(Q3%pXkkBO;Y4}jSFZWP$?duZMD-th5>Jq6J2 z107s1gZhB#9PK5t-OZz@mT7nu{1&9)ID!1+>9DE)(^Co zgnxK{5+>3`R+an6yS_nOD@PJ2gYSqxUn zSm5c!7JG1GAV9?v+1+r@xy$?pXEDVXwAbvuqjI+)_GnJ{JeXANXb#$y?z;Wi+OGi8 z1=(v)iFP#}!BJ$}$@XV!b?AGu8pRl!&%l{w26p;>#3v?vR6B`zJ0E*@7W=1WM37l@ z`he>-cC4OKAitPc+lmu$M~tob=g?E2mj79Kr1l!Gf{6UU6w7}u$dDh4mYvI!ZIXdP zS%BkVeVr2^wI`5A@p!R7R~s5c5?W+W_xh+S&QQc~d;P}z*bNoxyV0t1-59~@U8Gmh$oufYN`%-g3SrzJd5hiYIKO%VSP ziu=WhZ-(OrBJG^+22a1*Ihcu{>Ya+skI0PQtl8GvfPxlrx4y!7Nunu4b4&2%ox*@&W>li+S1;%lU$0T{O})c?nz4y9 zjDu)NU{-uRW+YIo`JFM+(Wk_emKfEwu~FiXSo>dsQ#J1oqSJN*FaDvWc7ehWu|Clj zweXjj1K^n8;Nh`&ofB~06#EGI-z)$eZ}0t*Z+-)1C&7R2dKs9MRJ+;nw2w0oW-1Kr z160W>gfB}}VStYDXLCM_wJmyHP@6n#h4toec7czZJB&xSCMe$XV21ompOnwxl>OHR z`~1f9So(6Hqtrbe6=3+&8mKLn6O&T_zmt+u*#nZl=iZN4Z2A_*rPL9AiU}z|2@rIL7L1j|gH0?k=*H3(gzsXve%etAIH#Q&|8|kN6Y;+wM z_nX9l^#H39x{iWX!#(3zmwJ%6r4~$=TvSzSHuT}9+PE^L7__7^Shoc2Lfs1oW@8N^ zaiAPn2Y1&iJSYQ-OKFzX0+~`xl0581Ad3Pf@K9;0Kt^U(4NAX(r$<%>L?7-!p|Lz( ze3{8)+&w;xms-!&H}0Y*o(?*KFv0VRI^5MD?tHr2I-~M_NUb<;RaQ#-BE7 zikV)|p;{kX8;jTBBK+OE-tdo@asB!niGgQl7}!e6lL*)7UOzM9q094hpUhRcP2L1Q zF*efI7&D>h*JXE~D`iW}|2C`tU;CQH5bl=nq0s-N*6pw3J4D$0K-GQUkiaf^)6)#p zPVd`+9*{GDhTCXfyuNNQ^qkg~BRRKstOS*X-9$x2A@xNGes%ETSc^N?2FuFIu6Ieb zvK`~1S)4H=adyrmE0CQX$UjWAUZAKA`E}D$tdZ(v$|mD11_46CCy=c0z{YFQ4tDs& zqKeDu^9B@2Z@*DooSeiN0A@;&TQRd!mmLxU=bjp{rDM(ZXok2^DA2}E9xeN9#IyZz ztD1OCL9q70@uFXaC%w6W5s!{09i~RCCmwf^BCoL=lP3XPj;F!wz7A2x)017sYH48v zy>|(<0olrf3`T+zvQDe|=EhFtu~WyAC#Dnv5>$MzH^7H^9CJ>dNlvZbr&2-EOeLHO>T3HI<(l&iA4 zO0y}lj+eizhB%4rf}$jO=l`j^!Fnoh`U0oe{?b1@-av4>&f{j&)YLr7HIiAaLQnyK zfD*usyol5%=ikmC{Trm$7W5$AzW}qJ#rc+%oqgruiR2YG*$sUJ+&ccM8>gMvyKZyF z(eBQnl27zaH2{D`+B_jJv3S9k zil3e#((kLX-~}<@Us7X@52Kc9(4osj;`le|ZjR`NfUZIK1jSvL8 zc9d{%MXnSo=@D6(lSow1hiOH=&UQ_66gV;kvBV>&6Tt6mZ$pgaP^E@4*a;jww(P)N zNnffWIc28eX;5`281a%ZImI?-PpAvcFjOWzAhsJx?XS-IO|xpSD|^2&gF*sQ<>)F* zrPmcC;0CRegE)JpqELoDy1ivCA1$ADF!)BE=d508Cs|dXHuLSYr)6W!0=(=-HWK=Q zc#_i#>NEh3-)~-W9acRo8f*oUd5>H>zj}pW5X6WQ9b022=hl;v@V{cPc2xi+Ihqh_ z=xCI0=vH(I`BI(?X#PXdL9hDsqC#85!e5^Mb~pNV_A6rmb3Km)v}Ywys4IrVW11r{alILyQ=y8CSF;k> zVaZtn4lKbpwgA90vnMnTfXXcix(H9MQyh!YzBsY`SifG$UD}k8IAP>@tXfmSIqMH@Hy7eh)_mW=JqCVbYjdQU{%RyMEK|vM!IP zjZ{`GnRApl4b|;oJz5a5oONEYT=Y6}wY57^8>uU2Sp0DSnk#}?Pl&L`c{Q4vNbTZEct|`>pj2Lnm<|+cKq6FL5S=GHW*C zTV`+{sqS9jrRv>?)Ze`PDkbFIw)3?J%}|>i4j%5l^X7bzoidX>p}9+5#8m(+{YDn0>CGU(tK6I9WSU9deu6){ zk11B_Ld~c`1Ak?#@DxP}Yvua<0x%V$%R|uyapHKug2z>O0(D{cw6}i?0Kszr969eY zot|@g@s52)Ro@urr16?-ifCHc(#MPU0OWA^*#vIRwRF1d3;bBjinhnwu1E5!;sYG4 zQIX$2J9fg<8$Ml~0hX4>-l46k{{l{5S=R(zd%sG@S}20F97C~W$MbX6+z+6ak5$vc zMue)8e546T^EtrJ`1!rhX|ZWKC}@yx(QlSbal@}H z3au^=T%{puUM5TJOg#Z2+|ZBC=H)f6H}FbwO-2(^NEDA|zhCD& z-1-enhbFtSZ{qMS^zu6o{>*5b*v;vK?*$|w7VJ>?Y$gJIMk>(LY9i?Ba#Ytoh~$B> zJDG5jH_tqe6pAk0b>Dz_o3z%s1*fB6dKP$QW3mkI&UVoa{fDl##?vq<!UsfFybv5+Z#10RXBdJxo!-aXt^~xKBArGf>w}r{O9s6otECSm zah@ce_Mjq1yIH>P?u)}-lQWK2H3v@Bfs@>Pf?8F)`CWBziDaAQ0n{h6`>UbUtwa@A zC=O;7Rzld&*fQeCcI|H~&Mp86tlZGgqyWOAPP=Q@#MYX?>t2yvXR z?rE!CjM>F~>R`mFN9wAGTx#8giCu?n#Yy`Mp~3+@b6yP@e9VQ9=SYRLvdd;#lmxQb zet?C9--?_d>-e)_oGAIAm(;lyaHyvE92YFw{@k@CvN!$Wc5gI?u^v5IEhfabli=cM^_uAIt?8rHI zF&jA17Bg1vt^CKrC@elJ^@?dy-p%zL0)62xSXQ_4L3IR#sE%j)48CW-t~&`^pYrm^ z@`15Vl<2opA5Ex}r&wutb zD{y0-oP1Kzw9oX!qTM;UYoN@ZL@Ytzl4J4dj+eU`7=;&;3OG5h={^4mcqj~VWs4JZ ztNV%UStLn+9^<-mdYoy!!*tiu)Py6Ow0DQDZ04Rx6~{toKqo7+qj^>%cz#q(1e#bh zo!S&_A7{(KAt_-WXJn_!SSE-oLfTVh0$V^(2fL{c*ri_}rSVdqn%Axzy$(iNW)3fa zH&kbOr_Ix(@E|-iexA6t8DGkD^W%3xKT_zEBXQQMlF(n`z{c7%ovCkiu=YsYufIQg zphYQL{@UDw7Zd#$@%V_^@geh*VDjMh0J(B4e4YAgUYJag16t18)2(HqK=jcgzhS4A zb%VOS=U6b0ig%BUO1=DGGVD3G=06>DnX@~co_@RDt1X>V&H4>8t!eF3SpPKQ`5&7& zBM31)dFoug%fGU%@40fSQ7Gpf2pEGk*pj>NtfvTrs7L)$@AyKV0N1^J<9M=}e!zL- z_$||IAZY1^{^G#sx1c}JirK5~f9i%1zWtQVoq#8uyFZ!pSle=F$zJ2HxVlcm;J|4% zvm^G$m(cu)Onbd>?HgU$HTsNplv81*0~H8{(0{U>jsS5KVhBvOJqqNN%Z9|lfxro3 zk3M&&QDTRox?sY#$Ny;=+C9maSix9Y>YhL&pc%OcSS$1{J>thwm2sUt@~$i?!cX&& z{W-3fV7LPVneAMu}Hyg3kzp*G^1>Di~F>fu0YAKy2qMyCw*t zZ~4?NIuZl!?S~p!(_d>?!Po5Esl6=taHnCy-ZVyQNHTfJ%x@)>^lPz-CD?E=@?1D~ zXcT<5)v+TxZF2ZaQOwkTn&UH6>@Upr`S&{DrtEA`mt!OpxkAU50T}7S<^FPWTXxbjxVX zRt7N~Jhskg!F=A4gNeWqWMt|nV0W%)W`d;Na&^=&?6u+*k1wmESHVwqeb<~x+?6kI zszZ2;mE(e;DzP`r<_aBVm)>JW|8+YiHTqYZiwbTqq`mY1YwtUwnq0SaRRlC3Akw8H zy$K3RmnH~E2_!%gM4D*my+{!d=^#n~rFTe3kQzE7z4zWrsM3qlOE*?Z|)YoBw* z{d0e8eq?-KVDQ1rtk0bDd3|3B1x-CC|NWbvIEmJSe;lq& z28;*Vhsos-Cuq5Jgnhm>%y< zr$czv@|k?dl2IaIW6s1%bm5si2b4gP}g zo9w`&BEc{jIxQEkjOr;2-7w;9mdYCb{ z%6x>A;s`t|@~2uhfjn#%)*x>y38~XCxhoxM#JCa3POIA|60xdZcRXbL3=&Zhbz}DA zeci-|;kWNNvlQFI@$C`}p1`kLj+h^uZ!qZ`6?G(Q5hK&i7H8!8fyt>;I7w2*`zzPsORcI*hw4?MC0z%$q2G$bVR-3oKJgU*6N^n?HY& z=G@2w{Mz+oj$WmPRghECCSlAqZ0hh#cmiW}-mTn!}_EUv| zXv-P!YrP7`jppnYN2WA2l*6BA2ymut!* zTf9o{ZM;+a0y4L7hiB46Jg$%(NGEk8u{bhifq68_=Ke7A!IyiZ*~Cc;g1n?lb?pSY zgDlpQ-GIi?r5m$>E#1i)Y#*M| zs`hzZ&*bjR#|Y7n)GX(;fvbyn3Z72mqyEI@{Q)hxH4prR5nRgk>-G;kHklkIBrTzx zXwrBqJKZcK1923RtTf9^f&V;B*;s=3sNCp}Hl}&Pzczh2+IMTOk>AI9kzG;Jdu8zD z&Jer%1KX=jF8LuRsZ&`3)v4`-0%Y=23c_;Ec_A|2gG7NV5d^KO`_bDi>)$)inFh8v zDyjRaz^;?FRhZ-C$2L{Nl7dR}3M!$6hF7%Os7h`r#kQM=#y*I_jDKZT1%Wb^zv5YG z^1Fs@3vaQ%aA5;K9-^?Ynn-e;FT5#e+{&S5KK$;0D}7XcR1GH>@HN=xis1+6en9ZF z7#?XP!VEQTZH9O+#3m;ddcS`Uwi=XC(8u1Ke}4r2;#%P>Abeau`bn$caKUeONSQ?y z1oNeg{_Gjt*|Y+q;^fyov3_<+HetU@AKON4WW8?M&HB;-*u5p} z72Vy}r7MQN)+s;NuV4IJzb@HI0srOt^|lqOeTN`+6lHt{PdUOLY%8J$Bj(rOln4<% zH%vZVnFG&R{)gc+WeciBB zo8|bmt-Gs7m`sL00d3LgpjN7W({-%B^P(pu;jMAi{YW~({He0FqzUUSu^H^ty|ck3 z%k7gJ4Ie`BLrDX?G^}@*96i)6Nn+oe4r01k;ou&^d}^}>aKKxa4Z#WP8S5_B#~M%) zh0(H|7Zc^4%BsG>&Y@Q`*)~LuY5KW#cPp&Ni*nU7&;|kq0Do#6@~DD7pQv2(nkV#_WtM5+cBv?MRT0a|M8qV*Al-*u*axkhr zswd&A7Sh|pY;!x4*vwVlZw4Clyn_9&nM3?uMqda-Ujmwl;O6neRGu#Z3f<* zu;aYw=Jj2P_(}oEI~wX#KLsUj9=^dh2BVP+&%Y}4^wO+l$U4~VA26g`Ff-vj0*%tM z>(ageSlU*#yBeLFB{0mqkjVrxDwyX}^#^mYN#lwn&ZjzJYASYfT|^Dn-4dpoeKH!9 zQJV>cQf`~2Qd~=KfrU%`L&>Y5Q?4V-nGZV`M2%F9nDVPE@c@uVD-`s43VBZ_$R+za zE-W2fGq&#>7V%6f*f_kl^g>X=;%1@GOn6FAPViM|uhR9fg=*Sw>o>_{%<{te%DG&p zZTIkN=vOQ1D_CK)l7Zcf9ZC^iIGOqPb@RO>nRqUWia@m|F(cnl%{BXcf1YM~{d1b> z%RMdi-=>-Va|Pi00R<0D*kOp4*Owrz4!@nx4)uQray4j8h=d?Mhj?vb0| z`i!ebp*p#cjbsqgCDS0ke5Fwpswk0Z)_7XuGY+S_p_l*q+m>m=hGq+#u16JSgo)}{ zDsz}^kyeq2;b`*A*7&ND4I)>dIcI))td#xAT&&02wo?vkhkZO*IwYRxEIB?<%bdcZ zMPS?4MAMz|5d2Bt3Ec<-{IAMi(koxjjVC*vDvtaA9^n78H!^wia<}Y>^+|tRt+}v! zJxM-H2zt%G9uInc-KD9EnfYWAfQLK^itK#$5?t|3vgw;~gN*FkR&2ApQ|TY{F|Zdb z_;Ip`Ejz>s(Zz=^0O?xR=dbVF`sO6Td*un+$H7b2vF}e~=3~^2sNUVDH6BwCA(c<_ zv=t@n3lYh3r+yN>`s4Ic?pMWF_~wMgg4{V~W1^xV``NQ-Kw;n~-Eb+3VP&ZjL8~$b za6z}>%{-!)N!eKg^y;`b0t6wb6s1K0jWH!;gBn2|-MXMw%IHIYa=XEXW{>-%GIzG% zqU2lQen}m;QmEeJOym4}5FuyK&bWeF3)y8M@;B6iaSP?k^3MD-ELOazK$UoD#yj)0 z>rw^;1%9XDCQ}rB^Uk5x1>KJ)8Y7`^9~W-IzI5{rMPEqq#{Si+0Sf4SrGfA!sk;{HzK4H;k`9u6|cPp*_%_Av7!)Lg$7wm@W4 z&Rt+Yr_j$eny*%6x5^jDv>vPMyuDtow<&cQmYL4H=tD8cH~L8ET$?O3ldWCRnp~Cw z$L|)tB2Y-!PeYht9?wA^Vr;GSsOVZc)d{^)hW34y7W?Gnp+kk~%H0XqmIKw4ax1J# zZ@M%{nF>vW-=O5Psem!mg1-Fh<}ufGG-bhFbb5xw;5ztio+}T8?p^E99I+`YZ29=M z)Qd2>5x+()X26`8|2jv<8mk-ImE$!9Q=t{rznMn~bP^bn#cf3_@Zqr(p zJ^#^UquG&Rr=#CMe>h=%S1_O?%IodbaG~Wct8Wf9V=``Yg?MTOx4K|4OH$g|nky=^ zZw{VHF88?B5l`B%ocmf2lEc$ooM`?6`_Foe$Nu|keIYe}rRbmSUR?-`GmDgHW~uY* zSfymQFQ9B?6P!BRbgO&mqWQSY`AT`bGDn9PYwcyG;3BG|RQV81UN4byU6}uyrl-)i z(X0-f@lUREc7|P@6#o)t%>J&dw|kI02030@`FP49Y|3My2ao#7h~G0-ED?CvmHr^m zaA$ikU}Xj_jt+4Sa=N3-+=nc2obEpot>$bs$_vK@wa2zD=7(N^RsZm5`WB!TuN4ka zmOusO!dAgIHbj9$ z+(tqPRcm2no(@s@3CdJOHCSUy6sx_%vwYLrPP6n&c(lX8gh_g~(w%A8fsrId(N}Kh z{hy@t`Q1(^9bz?3ev zs8b;gpeAm)o|(Sc=Ov>w3h#Ps$P^_W$$LixomOy}c0O_SZa!;o4?|iZvq(g^UP76zpg#B4a|3k(C z^;bnv>W^$$2}9N|ob!f9Ndl!qgX|f#Xopfm=7BsKOmvzs-rbJd$6#r8zgAsdSyon^ zZUEu#jY{1IYH3bHTGbB|!G-vCO8Cbn9;1qf4RF=Cax2s3&5!ZL4yrJ!&+gvF1Vj%Z zA63rgrNe`9eaPE#CtP$S?ySD@b5fjyyGg3L#UIKVslxKj5=Cgb8~Bw<<{|kW zbCuKF&MFoZmA6Zc>st0hGz96dnj}{iO%<(9NqsHjueBhTK6exR(6u1xM^iv!j(+|n zSgK(8;0MFE`nK6bm%m7;)RXYrwpl*Jr9Qvotbg3?yH9%Y;hS_zPRTL4lH4gIWS4sF=)Iqwa!8n> z)Y}|}2pV>S1A&wdNUSLc`;4hXs|#e~={d}WZ%O72pE2wwOS`ZUWrTGAt6ksr@VrSQ z=%zR6$cUKZ)3AzH@}^aw|CTH-*=#LtvnQr%syek|O2>6I8ttzquFBn3BUnp=Ro4k0 znG<1_XWTP@#1pLE)HSqX7t9<8Ub0~aj69C>9T-k>|A-#-LRu6NhBqqgaJNgXI_>a2 zYdG_eS}TvMm#n6=`71g7{Z{fHmWJ-%_haSI5l<(49js0D%6@Q5I9O$qys)3dnS)M2 z=3%6QR=a}GwYhTI4hZ8b&$6ANiJDMEqLg1SG+(8%@!{0c^0S@XR6k0(Wii?k9ysTF zUMsd^YK~_j^I$+8mDJnv%_XL@vOs?uWuXkkHmvm`i`S&?@5+A zWgn*O8d9sdJd)?`S?R0VDkU^kl+;+ zrdC}+RiH+c>Z8y=Q`+SY^EVc*6?BaA7E zk9b1cum8pfQyV0t zR0YQjSJsGEnR%P|(4sz}Sgquf{VfoWEWILQ+%gTQFAHtfO(;7HpWgD+xY(xmmiNvn ze>KOK%qh*D%JS*%1RvWfyV~(csRii{Dz$KnHy2=uDdlaWDGm%eIRDe|g1L)5f3-6B z;pfVrhHYjUehKrx7UlnZK}?zYYSFL2U;Z*-D>)G##fdKQuEH1zWi;_jD17xA;Z9f? z#Y7sFh#nOuCU?6vi?cR8L?I44A3uMCL)YR?a9JRc+S9MP0yH+bP--wAEUz4?eroL)t^`vSQ4dU5%Ra}q+e2BkX#i2ikj}M!T_rz(R|m> zkntSvTjQewo;MYGnLDZWT`{yCdOlVlrJHwjx_Vbvfc?OX_GLZ)NGDNkQN#4j2f%G$*a(T6t_|dMwj5Sbr9&-ppSN^a z%vQ=Bs0xBid^G6=+1FfZi7f>KNa*x_&*+{S{*<7AZHD;2l%N|o1&Mx41dsGiy@CW! z48=l73+8I!bXIO)D?~O_;ku+dQJs4}iLtc>VG|DkGNmByrBj6ahS{*=jwq2Q}gpFwndhD7i5M4;l0-9>5^)7jCQ4$L~(H8iq|ql=9& zpB+zVflJ2$U!c+>qcL=x9TvAss#X@$sg^gP7hh1`D9QPB$QTJ#g|_ZT#MF3^uIbvQ z(=VdSdRkm8=}btXzuZO)QBa3V@y?FNl=0Yeidd3uFtt9Hepac{ZVn@7YkIi=^CKI% z;jaDJR8LSg9vQ7u@v$`2rfvxUtvLEowz+u6p@z_-!_SP;c~2s-%85*C4kLi7YfpjsP9uhp^D=%4xmr_hA$v+C=>n%IVrSh6t~QA-3NWy}MqOO1OFb zGQUzca@wV;YqPxmBZ4>TVbhACQLvuIJyjjJxitb!$K*PPUuJnjF;se&-Hwxf15h+X zk#0i0LnlaUUH;b1B$S<|piS?M(Ziz9^gx|(h1-H1>B;2ndROvst7I>EA8x>*Y1*Xg zDsm6!Ohc70K?-1^bbYcBQ**(nT@bTER5BuZO{B>=Jf`G!=sbu#I*2RZDfRAA7lgqrpyVNphgGqoX#R1+w z6MB#&^-??pGJMepPY;wG{J_KGa4AYU&`J2d<+*FAX+)b8TI61cvtO_yE}aU$=~x-@SWfkqfq@mMZgzFKVzUM=pRnTHINaPJJe!wv?#)4vIf zRq6k7z~ihlsdfj!-py-)*rm_3Q<7BdoM`aidt4zve^r~R?N~J-z8L~A&?V|CbF2*n z7h7;-rTNWLoAar{u5=AO7b{GO#M4B;ST4~sSDU5?umclc4*~;9B4yD9{EkK1eyt`l znNO|7tpVv1r?p1nMRTuj2RJp6n&W5rh25X(e_ycL@eGBTJozgwWAC41bw6YV$$s~Z ze_SG(O})%xr*pyagnM(Y==z2rK;G*r7X>bzl_c;-J}y8ZMh?IBv96d7j;pp0mkXys z_~#=O+8dowrdNaYH(YxL=)i1GyM`gbubW{$cEJ`c}Pv{w}z=(5lHF73VXdj*=2C|hx?9e29Dk!?=J=O*gwoH#*d zDQA|36fA{NEPz@sanF^iS>!=5IPxf6R}6zxJ!To|>yfg z*7-_b9vzgQGS~}75&7lL&H3)HP4ds>lX|vt-eyCtp|rM&!QRZ#SjpsCd$PRi{0B7V zxvyn5$lTR}^I8>nq+dHcYZKA=pAm$hH+NNvuCD%%!VDzx{%;4RnmGZZUE`@3T zD^0C>FEYh5S7Nr;xupe7g0%y`gTucEsxWYYwCi(aIi=gdvI!0!!mN7OKaPx!#$SsH zH!OXEZHEySh=p%JWur#2>KW zbv+uxwJuJWwsvl})$PeCRUjH!@G?EO}`V6sFv> zw{OnKol(f=QMzynV-qcPg28?GK|@F?hCP>BEp_TIu_S~&RFjn-HHO6d(5yqEeaxEn zs{?S(YFuwDzc+&Q!vr_;xKtVSXnUA*S&4A{w#&V;$?~l7yj`wt9tp0JyTnNIsFyZR~Ih*`Q@|IC9+dI|{9q@GIUauq_F97V8JTa-nz87fThC0rFmcsz0;RhX1{K@os@pPuEb4O<;2RpIRYza5Q=y`< zAB(|a&v8oym3hVt4t4Lze-Y*dzibkUS@`h{^B;c}E_H2Co^T@ z;`VRS+Wrcf^b)L#J4HbKt5=U|!hslvS1X?oBJFn64ejgeo=nB-IfW3kX==NnaNRN7 zYXfBrvbSO+C>8w5t6e&9`Pm+SsbF=u+sv`Dz_UAqc^K-L~Eyr=)__LsK;VOdmuc7qVI`se2&%^(XU%P~(qb#@U^{u>}*~WTY88 z08q(I*U+Q>BMi-wO#x!&9;Ct4^~U*LF-z0JIhY!A@BDVc;u!^&v{w)4!d*k@6$ zrjYGd&0`F_a1oQ?9xia{A8Ptfh5dQu{)E*DEJgf5ZPap|4+3C7JkY)hKT^%xkMT&8 z9vI7f*9SD7n&*MWX}M# zHsWZ=+dLc^C|jEO+^tGPpT7z9ZCbyh#R5G2v5Qs*Z%GYiLZL%_o@yj&g1d@Pa1)TV z%jt2`W$>uRNI5X>9{HS^Ald{a5f^I28NYOUV*VL}-1!ge!lo7S%Mi=)B252D#@2*U zjV~8YwT3+4i1y~MR{fxu@QS_I$&tv*Uv>8%vYu_a1hCtn(%t3fS6tQ5cLqkP%HOKP zjZ{xxL<8B{caW6Fj^-V6Tr5>GBWZ%<(IY35yZV|sCF&Qmp1i?Iipa`Dgq?hw*I+|# zJY#a(oYq%wu$_d}vS_i?-i(W4;pGtnxuYTxPu3wRYL-XXAQq%&9LnO_6p@a6ZW0QV zS2ZGgtgJr-VUuON zDMyg`IDknUJZ{wOY5Ux5VwCgd;*NljFgM)6ASZ%$s~y@mm3JJc00TU?p{{J71wX39 zc@3D`EdXHMPDd755zVsCu~JNgMdEOR)!|UIG?@SM;dIYA$Mktq)O@Mj_cx?Hwq0kJ znSdRZiz;)(sWR5TnRp8u{*c=H$sDLZc-LRlHu=VAsW`nT@J*}HjVZBSmTtQj=W|eI zL8c4d0#5R>hH~v6B=u>Rjx3@YK%B~p!lB#}dV{WnLWB{{(_l7!&cyk8b@KW7>40*# zEgx_*1K(&V=!zAZ6+!Tbsg&P2J`WQ&ZpwUkBYlm)FyNwQXRjSEb+*c!6`Q{0L z@t&5fHG1h(ETkdrBB-m^a>qwJNQpA;cesY+cxV;4yL#W)s{8gtfOapw$FeYYSOnyP z9;zHZ@D3~=4;{rH$Jqv?*s5tEbCYVa?^z2b_e*BCYpCy_AV13&;36 zxz57}B_8MOk!~U_|M!xm)&8WW!%pZUs*s7)3PL`{xmA3Y=kV5j)%KD+Wqh3xw5fZm zqss0cR%gFHztg2^#Mmq1N!SZ#5X-vK96IboxJ7lSfU(ul-x`Gk8-c1Toz4-~#rNHJ zRS{(vQ^};}2!CB$?%YJZ?uiXbC_wo|qrpp9jksq85@ftVNqjR4dpEm5n?JB<7!X+j z%HW5h^~2(&Uou!RCBP?}6l`vQaP6SlSb*(gT2y|y7-N(fW;nt&ZdA8?Z54;`f6@Uf zATadt*gSip|IG$hQ*<1=wCZu+3zvnfI8Iu&oKQNsKe;q!)!`9WI2qe#f+2N3f+h8@ z?^E%hOm|n3z8^%@(Q*SEk|uSl9z}RaDK&3>ojr*)@lg3i0V$r>;vMUfoFe#FfBVnC z@0Ix~Low3gXkZ4W%!&S5U*9P{ZJs%sg&wDgdc{xpkEP`K=cN} zh#Zf(q>1R>yHA!n#PB>Lj^80d?*kHIS2Q%|Ix(~?8QW2T8T6(a{Mfa$vk0dQLwqrk zw9CL#bfz|zo=Jrwn3`{5g8?T`abu+;8riMo3SHTKl8wUda< z`G_&7?Cb0dt}02-yMww~_CeRlQ6)M#0A*il)TjQEiDIv!(#0Cw@x9CU$L-D9-G^SA zQ9tw$O@q@q^@36Q$P7eTc!IZb$yut`Kc(~^S9c9wUC<9;Vb=Xk#{aN>AJi)aIYWUC zHg+3KA`YoTrYpgQ7-MJIbvu)|wa9XmX6K=5hi1%7jN@Lu&TeQ(qg@H9|cV}1O-*V1fEY_bjI!I*=4N5%|8oQSA> zc4`~}3@dCXVO}=H)5ytRFfvjlmO%hBLN~A#uNDXG3`f=7{5JF5angGu>`2vUZMJ;qS`qAMk9r3>|h)iCP7WrlMuA}DW^<;9&{{M zW@SWy7>ZGAMmK9>X4@A6e2mu%Vy;g#6}$OR%N=!N=huA??jg-xKWg=Av9WyMI`=wh zuNJiXEe>6yV*NbyIjA}2`ae|h7peQ5r#!fNgI6=Nu3r|vpy@cQI+sNrHNU1w=Cb;# zLDh55ME@(WK9|%Rrz~4UZi_RN^trj&f8Zh(1^1!u7!wdDd;Hi=kFe8^161Gt6rrHM zx2mym%@QC|kq%Zj$@76tYb-p=6Mutr#CaPMs;6L7K`$>o36LFDvmTmo*|b6x#ZrQh z@KR8))eX0;#5`qFYNx=GNwUI;0T4W@++Yn$14W_J2~2`k?Wl>YcvBGxn=S^A>SnqM z8B4}qd?XWcuy`x?7f+pXF)W<6?ZnwGk2toeUA|r@1mPFXs5FKoOI4G zXP$ca+UtGZM*7l%{gkIfm`dI0i|T_6-^hD)KP-`w!z25rUW^w8Y7Sc7{96CmGhVt} z9=RY<1o*o|Ny3kkm9I)YDc3uxH%k z>W!p&c5c%H9bNKPEu39CH;A~kgg{5|EC3iFgFB;Y1&;Cs=m11*m}>hib0)ezBNPp8 z66Q;T^=&APYWVnJt5}qo1))%2!lar5PDG5`c^=aen;Nr%%?F5IsoI-Drus`h3#jPLJrc*Yi?4N@QIaDsZKG5YYNyQNo2f+EmherW6d)>S4m<< z*WE+5d<)&4nT7k8mjxA}k~2eC*bslu$d6iNj7!OkAQjXm0v&i@Q&b*ee7ZiXOjwsc zk|9bNdw1*JNx_#bld*My4N$V{N4KIr^>AB=GmSy%Hnsrn9fq*bE}+Cy&q3NoGZ8oF+{qlc{!50ve?U|lRmv06tn<28^moV?G9l( z*%mf5=8L}inX7xdiJ>UM82O*-aU)fy=U+<$i}Rf>+vWr55_9@#|ce$r;o9|oepw) z2)pL(uj!=mZnM9cbm}VJ8Gih6`GefA)#N{9UW0)Z>y~1LhyW14QG#*lr6oeBYr=-jsI4Pq+EvmvZa!;_#otKN>BZ*p zhl}FAajtLXrG4w98zPz=EDF>FX=<`b^D5pM*0BtAfliNuW`H;>C2&y$uuVF`m@03!bH@>EzdQYspq@= z5p(TO48{VAHTl|BPmLXBfDW|Lgd@H@#i%ZIwM+sX-1Y=~&bDUyD%Za6-a8kK+W9SB z_j+Bt1ncsBlmBbc^Uo~kyXr;a6t&K_pl_==idXpLnE3g`kgzVbtC^&%xbAS;BlkRJ z(BZ~RN1jCH)@&CVVB}|YUou1rk9X;FVp_K9ShSL*L&nlW10g(;Z}nD(hQ!90wb2AB z=-MwvpnXXh!bB=azlYFs901~7DmlrssKjh_;)oCMBQr}8^qd^sn zWsVk@Kx7blfA!Oc0E^^vO+*tsFpm-A=pqzU!N|NfDY)EjLe^et0bPHYbz)QULmD~5 z)YY@s|KMnqCc;RfTp^xTkGB`30P9$>!@F~{Ev}tzt#jpRFa^ZZU= ze*AaNGwd=|4e)?(xrmYSxX!WyshjCI?xYh1A_cAk}$S?lMN0j_RwnvGEWWxEg^n#~Xo& z3h9q6&YT!K19x90FiA4`y+0^f!+5BiG1k#SI4A{- zuriaF#I+khBj)IZT~b6b4F)xRw>J}$nmfpu8&}(ssx9$liv(GOOS&9g;{zK~rv5b# z0nSd*PY8Q}AxDaB0RyRg>JZOM0bW z43Y;7F6P0D2i>qhfuRaUS?HFcer>=6xg;yDD@`!y^P2$5%X?==ym8WOyTP*PJeufwICGa<=O+oNhEgyTuXJ+7cPJ zN~rYuwV0e9tWvy^Ok8=@uPM)+6E-R9;Jv3;m=*jJ3e&)&SC9~)yIIvS zmteN(xhH(DT%6ElVaHQY7gOAH$T*`v(q&g|#KS3-*G%K1B6NeCsdm>{pFgm~%hv|4 zc+m5%hmQ3&3AhhFqFuE;szUi5+~vM!@U@&F4hijA9=I5 z>mY*9H%}&GX%6oY45!L!t$Z-$pghqy@9HBC0$6gYIB_ulx zVlmd~KpJG&X*E=FxGfg601d1^tZSE@{w+8BAJoz1*Uor07wSl9Bup250fGpMUN%5X z9|sd(otuy#?s0uu8JUHT>RkHdAoHgzRxmhxH>Z25liIQ0D$`z1D-B(>Y}q$9?67Rk zIM4($K}mAKb5>rBXr^&ufzIDwWX;AvSt~_HzEBs(m`my3cx@G|MX4oMS9 z*SF0hra-J(MngeL9KeZelsGmEOp9^nh z7?6kkGi3btR}37V&y{mTqC!L1{|3YOv+w*|c$H#SvW&ak^Owr}9|-)fCh?ztL#hZq z0b|(9jpz9r!}yD*GbZ5iKC_qU`NaS7SO4-j1`a$V^Y4G>-=EIEmF?fQ^KV!1Z%6(A zb=+}|vXTESy4|Po!_N4e7{q>&{6lng*PJ~VG4{1(k@~}%4=(s=rC*>fmkrTM68pOr z#9xH(7KPN(fy2W1tMC8&{NSIDlQp?yvkOi6#NF-qTl)6TpZU|(tQm*>ghDjyNXycP m+t+^u68=?C|Ni>QsW95AMLULAtmXp#>yfgi5=P;v|NjBTz~C$Z literal 0 HcmV?d00001 From 5703bba4bb96e1ee0d881006ced556606932348b Mon Sep 17 00:00:00 2001 From: Pan Chasinga Date: Tue, 9 Nov 2021 17:26:47 -0800 Subject: [PATCH 14/23] Wrap up final draft --- docs/tutorial/flow-nft-marketplace.md | 311 +++++++++++++++++- .../flow-nft-marketplace/query-form.png | Bin 0 -> 63991 bytes 2 files changed, 301 insertions(+), 10 deletions(-) create mode 100644 docs/tutorial/images/flow-nft-marketplace/query-form.png diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index 5e78928..bfcbd3c 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -1004,6 +1004,25 @@ $ flow scripts execute src/flow/script/GetTokenMetadata.cdc If we have minted an NFT with the metadata `{"name": "Max", "breed": "Bulldog"}` in the [previous minting step](#minttoken-transaction), then that is what you will get after running the script. +### `GetAllTokenIds` (Bonus) + +This script is very short and straightforward, and it will become handy +when we build a UI to query tokens based on their IDs. + +```cadence + +// GetAllTokenIds.cdc + +import PetStore from 0xPetStore + +pub fun main() : [UInt64] { + // We basically just return all the UInt64 keys of `owners` + // dictionary as an array to get all IDs of all tokens in existence. + return PetStore.owners.keys +} + +``` + Et voila! You have come very far and dare I say you are ready to start building your own Flow NFT app. However, user experience is a crucial part in any app. It is more than likely that your users won't be as proficient at the command line as you do. Moreover, it is a bit boring for an NFT store to have faceless NFTs. In the next section, we will start tackling the fun part--building the UI on top and using [nft.storage][nft-storage] service to upload and store images of our NFTs. @@ -1206,7 +1225,7 @@ If you import `Form.js` component into `App.js` and insert it anywhere inside th > test that it works as intended, then if it might be reusable, wrap > it in a function. Then I repeat. > -> This makes perfect sense. The more code we write, the harder it is +> This makes perfect sense. The more code we write, the harder it is > to debug and go back to fix something. It is always better to write > less code in general. > > Here, I would include `console.log()` in each event handler, then @@ -1252,6 +1271,15 @@ async function mintPet(metadata) { ``` +> **đź’ˇ Where to catch an error?** +> Asynchronous functions which return a `Promise` are *fallable*. Conventionally, you should prepare for the worst +> by catching the error. But the question is where to do this? +> The general rule here is if a function does not know what to do with the error, don't catch it. Delegate this responsibility +> to the caller who, hopefully, will know better. In the previous `mintToken` function, we didn't +> handle the error because we wanted the calling function to handle it. +> This way, you are not making things worse by complicating the code with unnecessary `try-catch` blocks that only obscure +> the errors. Let the one who knows better deals with it. + Next, we will fill in the body of `uploadToStorage` function. This is where you will need your API key from NFT.Storage: ```js @@ -1280,7 +1308,7 @@ async function uploadToStorage(pet) { ``` -The step was simple. `NFTStorage.store(...)` takes an object with arbitrary attributes and two required `image` and `description` attributes. +The step was simple. `NFTStorage.store(...)` takes an object with arbitrary attributes and two required `image` and `description` attributes. Confusingly, `image` attribute does not necessarily take only an image file. It takes a `File` object (which means any type of assets). We used the `File` contructor imported from the library to create the object. @@ -1409,14 +1437,14 @@ Now all that is left to do is to return to the main `mintToken` function to comp // This is a fallible function. async function mintToken(pet) { - // The metadata contains the attribute `ipnft` which - // contains the CID of the uploaded metadata. - const { ipnft } = await uploadToStorage(pet); + // The metadata contains the attribute `url` which is an IFPS URL + // pointing to the data.json. + const { url } = await uploadToStorage(pet); - // We want to include the CID to mint to the blockchain, - // so we create a new object with all of the pet's - // attributes, plus the `cid`. - const txId = await mintPet({...pet, cid: ipnft}); + // We want to include the IPFS URL to the blockchain, so we can + // "unpack" the token data when we query it later. So we create + // a new object with all of the pet's attributes plus `url`. + const txId = await mintPet({ ...pet, url }); return txId; } @@ -1461,21 +1489,281 @@ const Form = () => { ``` +And we wrap up the minting step! Here is the [full code][source] if you have lost your track. Test out the UI, select an image file, fill up the metadata on the form, and click the mint button. + +Feel free to sit back and appreciate what you have achieved. Now is the time to fill up your coffee and take a wholesome break you deserve. +We will be back to work on the last bit--Querying a token. + +## Querying the token + +Now that we have a mean to mint pet tokens, let's build another form UI to query them for metadata and image. + +This form should make a good use of the minting form. Once we're done, it will look similar to this: + +![Query form to query tokens](./images/flow-nft-marketplace/query-form.png) + +I know Mary is obviously *not* a Bulldog, but you will get a chance to add your breed options later. + +Let's start by creating `QueryToken.jsx` file inside the `/components` directory. + +```js + +import { useState, useEffect } from 'react'; + + +// QueryForm.jsx + +const style = { + padding: '1rem', + paddingTop: '5rem', + background: 'white', + maxWidth: 350, + margin: 'auto', +}; + +const QueryForm = () => { + const [selectedId, setSelectedId] = useState(null); + const [metadata, setMetadata] = useState(null); + const [allTokenIds, setAllTokenIds] = useState([]); + + useEffect(() => { + let getTokens = async () => { + // Set mock IDs for now + setTokenIds([1, 2, 3]); + }; + getTokens(); + }, []); + + // Empty handler for now... + const handleSubmit = async (event) => { + event.preventDefault(); + } + + return ( +

+
+
+
+ + +
+
+ +
+ { + // We only display the table if there's metadata. + metadata ? : null + } +
+ ); +}; + +const MetadataTable = ({ metadata }) => ( + + + + { + Object.keys(metadata).map((field,i) => ( + // Skip the `url` attribute in metadata for the table headings. + field === 'url' ? null : + )) + } + + + + + { + Object.keys(metadata).map((field, i) => { + switch (field) { + // Skip displaying the url. + case 'url': + return null; + // Display the image as tag. + case 'image': + return ( + + ); + // Default is to display data as text. + default: + return ; + } + }) + } + + +
{field}
+ + {metadata[field]}
+); + +export default QueryForm; + +``` + +Ok, I did promise this to be simpler, but you will soon see that in fact, it is. + +As usual, we need to create JavaScript "bindings" to two Cadence scripts `GetTokenMetadata.cdc` and `GetAllTokenIds.cdc`. We will start with `GetAllTokenIds.sc.js`. + +```js + +// GetAllTokenIds.sc.js + +import * as fcl from '@onflow/fcl'; +import raw from './GetAllTokenIds.cdc'; + +async function getAllTokenIds() { + + // Fetch the `GetAllTokenIds.cdc` script as text. + let cdc = await(await fetch(raw)).text(); + + // Read the script, send it, and wait for the response. + const encoded = await fcl.send([fcl.script(cdc)]); + + // Decode the response into a JavaScript array of IDs. + const tokenIds = await fcl.decode(encoded); + + // Sort the IDs in ascending order and return the array. + return tokenIds.sort((a, b) => a - b); +} + +export default getAllTokenIds; + +``` + +Hopefully, by now you are an expect at this. You can follow the comments to see what went on in each step. It's worth switching back and take a look at `GetAllTokenIds.cdc` to see how they both interact. + +Next up, we create `GetTokenMetadata.sc.js` that will take care of executing `GetTokenMetadata.cdc` script. + +```js + +// GetTokenMetadata.sc.js + +import * as fcl from '@onflow/fcl'; +import * as t from '@onflow/types'; +import raw from './GetTokenMetadata.cdc'; + +async function getTokenMetadata(id) { + let script = await(await fetch(raw)).text(); + const encoded = await fcl.send([ + fcl.script(script), + fcl.args([fcl.arg(id, t.UInt64)]), + ]); + const data = await fcl.decode(encoded); + return data; +} + +export default getTokenMetadata; + +``` + +I'll leave you to figure out what this did (Again, check out `GetTokenMetadata.cdc` to see what kind of interface it offers). + +Now we are ready to return to `QueryForm.jsx`. Import the functions we worked on as the following: + +```js + +// QueryForm.js + +import { useState, useEffect } from 'react'; + +// Import these functions +import getAllTokenIds from '../flow/script/GetAllTokenIds.sc'; +import getTokenMetadata from '../flow/script/GetTokenMetadata.sc'; +import { toGatewayURL } from 'nft.storage'; + +const QueryForm = () => { + const [allTokenIds, setAllTokenIds] = useState([]); + const [selectedId, setSelectedId] = useState(null); + const [metadata, setMetadata] = useState(null); + + useEffect(() => { + let getTokens = async () => { + // Instead of dummy token IDs, we call `getAllTokenIds` + // to get real IDs of all existing tokens. + const ids = await getAllTokenIds(); + setTokenIds(ids); + }; + getTokens(); + }, []); + + const handleSubmit = async (event) => { + event.preventDefault(); + + // Add this block to the submit handler. + try { + // Call the `getTokenMetadata` function and extract the + // IPFS URL from the data returned. + let metadata = await getTokenMetadata(selectedId); + let dataURL = toGatewayURL(metadata.url); + + // Fetch the URL to get a JSON response, which contains + // an `image` attribute. + // create a new metadata object and set the metadata to the value. + let { image } = await (await fetch(dataURL)).json(); + let newdata = { ...metadata, image: toGatewayURL(image) }; + setMetadata(newdata); + } catch(err) { + window.alert('Token ID does not exist!'); + } + } + + // ...The component code unchanged... +} + +``` +In the `useEffect` callback, we replaced the stubbed ID array with the call to +`getAllTokenIds` function, which executed `GetAllTokenIds.cdc` and returned an +array of existing token IDs. We then call `setTokenIds` to set `allTokenIds` to +the array. This is used to fill up the `
m3Uku00%Skv`I(E9f= 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 Date: Wed, 17 Nov 2021 13:10:37 -0800 Subject: [PATCH 15/23] Apply suggestions from @mishmosh Co-authored-by: Mosh <1306020+mishmosh@users.noreply.github.com> --- docs/tutorial/flow-nft-marketplace.md | 124 +++++++++++++------------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/docs/tutorial/flow-nft-marketplace.md b/docs/tutorial/flow-nft-marketplace.md index bfcbd3c..85aa78c 100644 --- a/docs/tutorial/flow-nft-marketplace.md +++ b/docs/tutorial/flow-nft-marketplace.md @@ -9,7 +9,7 @@ related: # Building an NFT Marketplace on Flow -This tutorial will teach you to create a simple NFT marketplace on [Flow][flow] from scratch and deploying it on the testnet. +This tutorial will teach you to create a simple NFT marketplace on the [Flow][flow] blockchain from scratch and deploy it on the testnet. ## Table of content (WIP) @@ -23,11 +23,11 @@ This tutorial will teach you to create a simple NFT marketplace on [Flow][flow] ## Prerequisites -Although this tutorial is built specifically for Flow, it will build up a general understanding of smart contract and NFTs. Therefore, you are expected to have a working JavaScript and [React.js][react] knowledge and a basic familiarity with blockchains, web3, and NFTs. +Although this tutorial is built specifically for Flow, it will build up a general understanding of smart contract and NFTs. Therefore, you are expected to have a working JavaScript and [React.js][React] knowledge and a basic familiarity with blockchains, web3, and NFTs. You will need to install [Node.js][nodejs] and npm (comes with Node.js), [Flow command line tool][flow-cli], and [Docker compose][docker-compose] to follow this tutorial. -You can use any code editor of your choice, but I recommend [VSCode][vscode] with [Cadence Language support][vscode-cdc-ext] for smooth sailing experience. +You can use any code editor of your choice. [VSCode][vscode] with [Cadence Language support][vscode-cdc-ext] is one option that provides a smooth sailing experience. > **💡 Why React?** > React.js was chosen for this tutorial because it is the most widely-used UI library that @@ -40,14 +40,13 @@ If you are not familiar with the concept of smart contracts and NFTs, it is wort ## What you will learn -You will learn the basic NFT building blocks from the ground up, such as: +As we build a minimal version of the [Flowwow NFT pet store][flowwow] together, you will learn the basic NFT building blocks from the ground up. Topics include: - Smart contracts and the [Cadence language][cadence] - User authentication - - Minting and storing tokens' metadata on [Filecoin/IPFS][nft-storage] + - Minting tokens and storing metadata on [Filecoin/IPFS][nft-storage] - Transferring tokens -As we build a minimal version of the [Flowwow NFT pet store][flowwow] together. If you would like to go through a structured, step-by-step tutorial, the chapter-by-chapter project is available on [Github][mini-petstore]. @@ -60,7 +59,7 @@ Let's get a leveled understanding here. A blockchain, or distributed ledger, is 3. The [contract](contract) or rule to govern them. ### Resource -The resource can be any *thing*--from money to crop to digital monster--as long as the type of resource is agreed upon by all accounts. +The resource can be any *thing* — from money to crop to digital monster — as long as the type of resource is agreed upon by all accounts. ### Accounts Each account owns a ledger of its own to keep track of the spending (transferring) and imbursing (receiving) of the resource. @@ -83,7 +82,7 @@ What is novel about blockchain is the distributed part. Because the ledger is *d ## Cadence -Like Solidity for Ethereum, we use [Cadence][cadence] to code smart contracts, transactions, and scripts for Flow. Cadence's design is inspired by [Rust][rust] and the *move semantic*. Basically, the runtime tracks when a resource is being *moved* from a variable to another variable and makes sure it can never be used twice in the program. +Like Solidity for Ethereum, we use the [Cadence][cadence] language to code smart contracts, transactions, and scripts for Flow. Cadence's design is inspired by the [Rust][rust] and [Move][move] languages. In Cadence, the runtime tracks when a resource is being *moved* from a variable to another variable and makes sure it can never be used twice in the program. The three types of Cadence program you will be writing are [contracts](contract), [transactions][transaction], and [scripts][script]. @@ -91,7 +90,7 @@ The three types of Cadence program you will be writing are [contracts](contract) A contract is an initial program that gets deployed to the blockchain, initiating the logic for your app and allowing access to resources you create and the capabilities that come with them. -Two most common constructs in a contract are resources and interfaces. +Two of the most common constructs in a contract are resources and interfaces. #### Resources @@ -104,7 +103,7 @@ Resources can only be in one place at a time, and they are said to be *moved* ra Interfaces define the behaviors or capabilities of resources. They are akin to interfaces in some languages. They are usually implemented by resources. -Here is an example of an `NFT` resource and a `Ownable` interface (ala [ERC721][erc-721]) in the `PetStore` contract: +Here is an example of an `NFT` resource and an `Ownable` interface (à la [ERC721][erc-721]) in the `PetStore` contract: ```cadence @@ -119,7 +118,7 @@ pub contract PetStore { // Unique id for each NFT. pub let id: UInt64 - // Constructor method. + // Constructor method init(initId: UInt64) { self.id = initId } @@ -146,7 +145,7 @@ Note the access modifier `pub` before the definition of `contract`, `resource`, ### Transaction -Transactions tell the on-chain contract to change the state of the chain. Because the change is synchronized throughout the peers and cannot be undone, like Ethereum, a transaction is considered a *write* operation that incurs a network gas fee. They also require one or more accounts to sign and authorize. +Transactions tell the on-chain contract to change the state of the chain. Like Ethereum, the change is synchronized throughout the peers and cannot be undone. Therefore, a transaction is considered a *write* operation that incurs a network gas fee. Transactions require one or more accounts to sign and authorize. Minting and transferring tokens are transactions. Here is an example of a transaction, requiring a current account who owns the `NFT` resource to authorize a certain action (in this case, just logging `"Hello, transaction"`). @@ -282,7 +281,7 @@ The structure of the `src` directory should now look like this: > However, for this tutorial, we want to focus on understanding the basics. So, we will be building > our own version of things from the ground up. -We will be taking some time to write the contract while also learn Cadence. Once you have grasped it, writing transactions and scripts will be relatively easy. +Now, we will write our first contract. First, create the contract structure and define an `NFT` resource: @@ -311,7 +310,7 @@ pub contract PetStore { ``` -Note that we have declared a [Dictionary][cdc-dict-type] and store in a variable named `owners`. This dictionary has the type `{UInt64: Address}` which maps [unsigned 64-bit integers][cdc-integer-type] to users' [Addresses][cdc-address-type]. We will use `owners` to keep track of all the current owners of NFTs globally. +Note that we have declared a [Dictionary][cdc-dict-type] and stored it in a variable named `owners`. This dictionary has the type `{UInt64: Address}` which maps [unsigned 64-bit integers][cdc-integer-type] to users' [Addresses][cdc-address-type]. We will use `owners` to keep track of all the current owners of NFTs globally. Next, we instantialize the array with a `nil` stored as the first element in the `init()` constructor method. In Cadence, it is an error @@ -359,17 +358,17 @@ pub contract PetStore { Let's not get over ourselves and go through the `NFTReceiver` interface line-by-line. -The `withdraw(id: UInt64): @NFT` method takes an NFT's `id`, withdraws a token, or an *instance* of `NFT` resource, which is prepended with a `@` to mean reference to a resource. +The `withdraw(id: UInt64): @NFT` method takes an NFT's `id`, withdraws a token, or an *instance* of `NFT` resource, which is prepended with a `@` to indicate a reference to a resource. The `deposit(token: @NFT, metadata: {String : String})` method takes a token reference type and the metadata dictionary with a `String` key and `String` value, and deposits or transfers the `@NFT` to the current instance of `NFTReceiver`. -The `getTokenIds(): [UInt64]` method access all tokens' IDs owned by the instance of the `NFTReceiver`. +The `getTokenIds(): [UInt64]` method accesses all tokens IDs owned by the instance of the `NFTReceiver`. -The `getTokenMetadata(id: UInt64) : {String : String}` method takes an ID of an `NFT`, read the metadata, and return the it as a dictionary. +The `getTokenMetadata(id: UInt64) : {String : String}` method takes an ID of an `NFT`, reads the metadata, and returns it as a dictionary. ### `NFTCollection` -Now let's create an `NFTCollection` resource to implement the `NFTReceiver` interface. Think of this as a "vault" where NFTs are deposited to and withdrawn from. +Now let's create an `NFTCollection` resource to implement the `NFTReceiver` interface. Think of this as a "vault" where NFTs can be deposited or withdrawn. ```cadence @@ -449,7 +448,7 @@ In the contructor method, `init()`, we instantiate the `ownedNFTs` with an empty The `withdraw(id: UInt64): @NFT` method remove an NFT from the collection's `ownedNFTs` array and return it. -The left-pointing arrow character is knowned as a *move* symbol, and we use it to move a resource around. Once a resource has been moved, it can no longer be used from the old variable. +The left-pointing arrow `<-` is known as a *move* symbol, and we use it to move a resource around. Once a resource has been moved, it can no longer be used from the old variable. Note the `!` symbol after the `token` variable. It [force-unwraps][cdc-force-unwrap] the `Optional` value. If the value turns out to be `nil`, the program panics and crashes. @@ -457,7 +456,7 @@ Because resources are core to Cadence, their types are annotated with a `@` to m The `deposit(token: @NFT)` function takes the `@NFT` resource as a parameter and store it in the `ownedNFTs` array in this `@NFTCollection` instance. -The `!` symbol reappears here, but now it's after the move arrow `<-!`. This is called a [force-move or force-assign][cdc-force-assign] operator, which only move a resource to a variable if the variable is `nil`. Otherwise, the program panics. +The `!` symbol reappears here, but now it's after the move arrow `<-!`. This is called a [force-move or force-assign][cdc-force-assign] operator, which only moves a resource to a variable if the variable is `nil`. Otherwise, the program panics. The `getTokenIds(): [UInt64]` method simply reads all the `UInt64` keys of the `ownedNFTs` dictionary and returns them as an array. @@ -479,7 +478,7 @@ We have successfully implemented the `@NFTReceiver` interface for the `@NFTColle ### `NFTMinter` -The last and very important component for our `PetStore` contract is `@NFTMinter` resource, which will contain an exclusive code for the contract owner to mint all the tokens. Without it, our store will not be able to mint any pet for the users. It is very simplistic though, since we have already blaze through the more complex components. Its only `mint(): @NFT` method creates an `@NFT` resource, give it an ID, save the address of the first owner to the contract (which is the address of the contract owner, although you could change it to mint and transfer to the creator's address in one step), increment the universal ID counter, and return the new token. +The last and very important component for our `PetStore` contract is `@NFTMinter` resource, which will contain an exclusive code for the contract owner to mint all the tokens. Without it, our store will not be able to mint any pet tokens. It is very simplistic though, since we have already blaze through the more complex components. Its only `mint(): @NFT` method creates an `@NFT` resource, give it an ID, save the address of the first owner to the contract (which is the address of the contract owner, although you could change it to mint and transfer to the creator's address in one step), increment the universal ID counter, and return the new token. ```cadence @@ -520,7 +519,7 @@ pub contract PetStore { ``` -By now, we have all the bolts and nuts we need for the contract. The only thing that is missing is a way to initialize this contract once during the deployment. Let's create a constructor method to create an empty `@NFTCollection` instance for the deployer of the contract (you) so it is possible for the contract owner to mint and store NFTs from the contract. As we go over this last hurdle, we will also learn about the last important concept Cadence: [Storage and domains][cdc-domain]. +By now, we have all the bolts and nuts we need for the contract. The only thing that is missing is a way to initialize this contract once during the deployment. Let's create a constructor method to create an empty `@NFTCollection` instance for the deployer of the contract (you) so it is possible for the contract owner to mint and store NFTs from the contract. As we go over this last hurdle, we will also learn about the last important concept of Cadence: [Storage and domains][cdc-domain]. ```cadence @@ -533,7 +532,7 @@ pub contract PetStore { // ... @NFTCollection code ... - // This contract constructor is called once when the contract is deployed. + // This contract constructor is called once when the contract is deployed. // It does the following: // // - Creating an empty Collection for the deployer of the collection so @@ -713,13 +712,13 @@ Authorizers [f8d6e0586b0a20c7] Note the transaction ID returned from the transaction. Every transaction returns an ID no matter if it succeeds or not. -Congratulations on minting your first NFT pet on Flow! It does not have a face yet besides just a name and breed, but we will touch on uploading static images (and even videos) onto the IPFS/Filecoin network using [nft.storage][nft-storage] later. +Congratulations on minting your first NFT pet on Flow! It does not have a face yet besides just a name and breed. Later in this tutorial, we will upload static images (and even videos) for your pets onto the IPFS/Filecoin networks using [nft.storage][nft-storage]. ### `TransferToken` transaction -As we, the contract owner, are able to mint Flow NFTs. The next natural step is to learn how to transfer them to different users. Since this transfer action writes to the blockchain and mutates the state, it is also a transaction. +Now that we know how to mint Flow NFTs, the next natural step is to learn how to transfer them to different users. Since this transfer action writes to the blockchain and mutates the state, it is also a transaction. -Before we can transfer a token to another user's account, we need another receiving account to deposit a token to (We could transfer a token to *our* address, but that wouldn't be very interesting, would it?). At the moment, we have been working with only our emulator account so far. So, let's create an account through the Flow CLI. +Before we can transfer a token to another user's account, we need another receiving account to deposit a token to. (We could transfer a token to *our* address, but that wouldn't be very interesting, would it?) At the moment, we have been working with only our emulator account so far. So, let's create an account through the Flow CLI. First, create a public-private key pair by typing `flow keys generate`. The output should look similar to the following, while **the keys will be different**: @@ -742,7 +741,7 @@ For convenience, let's create a JSON file named `.keys.json` in the root directo ``` -After you have taken down the keys, type this command, replacing `` with the public key you generated to create a new account: +Next, type this command, replacing `` with the public key you generated to create a new account: ```shell @@ -792,7 +791,7 @@ With this new account created, we are ready to move on to the next step. Before we can deposit a token to the new account, we need it to "initialize" its collection. We can do this by creating a transaction for every user to initialize an `NFTCollection` in order to receive NFTs. -Inside `/transactions` directory next to `MintToken.cdc`, create a new Cadence file named `InitCollection.cdc` with the follow code: +Inside `/transactions` directory next to `MintToken.cdc`, create a new Cadence file named `InitCollection.cdc`: ```cadence @@ -906,7 +905,7 @@ Authorizers [f8d6e0586b0a20c7] ``` -Well done! You have just withdrawn and deposited your an NFT to another account! +Well done! You have just withdrawn and deposited your NFT to another account! ### `GetTokenOwner` script @@ -914,7 +913,7 @@ We have learned to write and send transactions. Now, we will learn how to create There are many things we can query using a script, but since we have just transferred a token to `test-account`, it would be nice to confirm that the token was actually transferred. -Let's create a script file named `GetTokenOwner.cdc` under the `script` directory. As you can see it is quite straightforward: +Let's create a script file named `GetTokenOwner.cdc` under the `script` directory: ```cadence @@ -938,11 +937,11 @@ pub fun main(id: UInt64): Address { All scripts have an entry function called `main`, which can take any number of arguments and return any data type. -This script simply accesses the `owners` dictionary in the `PetStore` contract using the token ID and returns the address of the token's owner, or fail if the value is `nil`. +In this script, the `main` function accesses the `owners` dictionary in the `PetStore` contract using the token ID and returns the address of the token's owner, or fails if the value is `nil`. -It's worth repeating myself this: Scripts do not require any gas fee and authorization. They are just programs that reads public data on the blockchain. +As a reminder, scripts do not require any gas fee or authorization because they only read public data on the blockchain rather than writing to it. -Executing a script with Flow CLI is also pretty straightforward: +Here's how to execute a script with the Flow CLI: ```shell @@ -956,7 +955,7 @@ $ flow scripts execute src/flow/script/GetTokenOwner.cdc From `GetTokenOwner.cdc` script, it takes only a few more steps to create a script that returns a token's metadata. -We will work on `GetTokenMetadata.cdc` which, as the name suggests, get the metadata of an NFT based on the given ID. +We will work on `GetTokenMetadata.cdc` which, as the name suggests, gets the metadata of an NFT based on the given ID. Recall that there is a `metadata` variable in the `NFT` resource definition in the contract which stores a `{String: String}` dictionary of that `NFT`'s metadata. Our script will have to query the right `NFT` and read the variable. @@ -1025,7 +1024,7 @@ pub fun main() : [UInt64] { Et voila! You have come very far and dare I say you are ready to start building your own Flow NFT app. -However, user experience is a crucial part in any app. It is more than likely that your users won't be as proficient at the command line as you do. Moreover, it is a bit boring for an NFT store to have faceless NFTs. In the next section, we will start tackling the fun part--building the UI on top and using [nft.storage][nft-storage] service to upload and store images of our NFTs. +However, user experience is a crucial part in any app. It is more than likely that your users won't be as proficient at the command line as you do. Moreover, it is a bit boring for an NFT store to have faceless NFTs. In the next section, we will start tackling the fun part: building the UI on top and using [nft.storage][nft-storage] service to upload and store images of our NFTs. ### Front-end app @@ -1242,12 +1241,12 @@ Here I create a JavaScript module that interacts with each Cadence transaction o Since there will be quite a lot going on, we will go slowly on this one. We will create a `mintToken` function that takes a `pet` object and does the following: -1. Upload the metadata and image asset to NFT.storage, and retrieve the returned metadata that includes the [CID][ipfs-cid] of the data. -2. Send a minting transaction with the metadata to Flow (in this case, the name, age, breed, and the CID of the data stored on IPFS). -3. If successful, return the Flow transaction ID. +1. **Upload to NFT.storage.** This uploads the metadata and image asset to NFT.storage, and retrieves the returned metadata that includes the [CID][ipfs-cid] of the data. +2. **Send a minting transaction** with the metadata to Flow (in this case, the name, age, breed, and the CID of the data stored on IPFS). +3. **Return the Flow transaction ID** if successful. -Here we sketch up some placeholder functions to reflect the steps: +First, let's sketch up some placeholder functions to outline the steps: ```js @@ -1259,7 +1258,7 @@ async function mintToken(pet) { return txId; } -/* We will fill in these functions next */ +// We will fill in these functions next async function uploadToStorage(pet) { return {}; @@ -1280,16 +1279,16 @@ async function mintPet(metadata) { > This way, you are not making things worse by complicating the code with unnecessary `try-catch` blocks that only obscure > the errors. Let the one who knows better deals with it. -Next, we will fill in the body of `uploadToStorage` function. This is where you will need your API key from NFT.Storage: +Next, fill in the body of `uploadToStorage` function. You will need your API key from NFT.Storage: ```js -// Import these modules from nft.storage on top of the file. +// Import required modules from nft.storage import { NFTStorage, File } from 'nft.storage'; const API_KEY = "DROP_YOUR_API_KEY_HERE"; -// Initialize the NFTStorage client. +// Initialize the NFTStorage client const storage = new NFTStorage({ token: API_KEY }); async function uploadToStorage(pet) { @@ -1308,14 +1307,17 @@ async function uploadToStorage(pet) { ``` -The step was simple. `NFTStorage.store(...)` takes an object with arbitrary attributes and two required `image` and `description` attributes. +### 1. Upload to NFT.Storage + +`NFTStorage.store(...)` takes an object with arbitrary attributes and two required attributes, `image` and `description`. (Contrary to its name, the `image` attribute does not require an image file. It takes a `File` object which can contain any type of asset.) -Confusingly, `image` attribute does not necessarily take only an image file. It takes a `File` object (which means any type of assets). We used the `File` contructor imported from the library to create the object. -The `description` attribute can obvious be any arbitrary text you want. +The `description` attribute can be any arbitrary text up to $MAXLENGTH. Then, we return the metadata returned from the call to the caller. +### 2. Send a minting transaction + Once we have the metadata from uploading to NFT.storage, we will have to send a transaction to mint the token on Flow with the metadata. Let's fill up the `mintPet` function. ```js @@ -1377,7 +1379,7 @@ function toCadenceDict(pet) { As you can see, our `mintPet` function is a little involved. -The first step we took was to convert the `pet` data to a type our Cadence contract understand, which a dictionary of type `{String: String}`. Basically, if the object looks like this: +The first step we took was to convert the `pet` data to a type our Cadence contract understands, which a dictionary of type `{String: String}`. Basically, if the object looks like this: ```js @@ -1428,7 +1430,9 @@ There is a few ways to send a transaction, but `fcl.send([...])` is the most exp We pass the Cadence `code` to `fcl.transaction`, and any integer from 0 - 999 to `fcl.limit` for the gas fee limit we are happy with. The `payload` is the metadata we converted previously. -The `payer`, `proposer`, and `authorizations` are a little complicate. While we won't dig deep into them, they accept a function known as *authorization function* that decides the account (and effectively the keys) used to authorize the transaction. (If you're interested in deep-diving, check out [Authorization Function][cdc-auth-function]). Here, `fcl` provided an `authz` default authorization function to makes signing with the emulator account easier. +The `payer`, `proposer`, and `authorizations` accept a function known as *authorization function* that decides the account (and effectively the keys) used to authorize the transaction. (If you're interested in deep-diving, check out [Authorization Function][cdc-auth-function]). Here, `fcl` provided an `authz` default authorization function to makes signing with the emulator account easier. + +### 3. Return the Flow transaction ID Now all that is left to do is to return to the main `mintToken` function to complete it: @@ -1453,7 +1457,7 @@ export default mintToken; ``` -Our `mintToken` function is ready to work. We should return to `Form.js`,add a `handleSubmit` handler (right after `setAge` function), and pass to the `onSubmit` prop on the `
` element. +Our `mintToken` function is ready to work. We should return to `Form.js`, add a `handleSubmit` handler (right after `setAge` function), and pass to the `onSubmit` prop on the `` element. ```js @@ -1489,14 +1493,13 @@ const Form = () => { ``` -And we wrap up the minting step! Here is the [full code][source] if you have lost your track. Test out the UI, select an image file, fill up the metadata on the form, and click the mint button. +This wraps up the minting step! Here is the [full code][source] for a handy reference. Now you can test the UI, select an image file, fill up the metadata on the form, and click the mint button. -Feel free to sit back and appreciate what you have achieved. Now is the time to fill up your coffee and take a wholesome break you deserve. -We will be back to work on the last bit--Querying a token. +Feel free to sit back and appreciate what you have achieved. Now is the time to fill up your coffee and take a well -deserved break before we move on to the last bit: querying a token. ## Querying the token -Now that we have a mean to mint pet tokens, let's build another form UI to query them for metadata and image. +Now that we can mint pet tokens, let's build another form UI to query them for metadata and image. This form should make a good use of the minting form. Once we're done, it will look similar to this: @@ -1610,7 +1613,6 @@ export default QueryForm; ``` -Ok, I did promise this to be simpler, but you will soon see that in fact, it is. As usual, we need to create JavaScript "bindings" to two Cadence scripts `GetTokenMetadata.cdc` and `GetAllTokenIds.cdc`. We will start with `GetAllTokenIds.sc.js`. @@ -1640,9 +1642,9 @@ export default getAllTokenIds; ``` -Hopefully, by now you are an expect at this. You can follow the comments to see what went on in each step. It's worth switching back and take a look at `GetAllTokenIds.cdc` to see how they both interact. +Hopefully, by now you are an expert at this. It's worth switching back and taking a look at `GetAllTokenIds.cdc` to see how the Javascript bindings and Cadence scripts interact. -Next up, we create `GetTokenMetadata.sc.js` that will take care of executing `GetTokenMetadata.cdc` script. +Next up, we create `GetTokenMetadata.sc.js` to execute the `GetTokenMetadata.cdc` script. ```js @@ -1666,9 +1668,9 @@ export default getTokenMetadata; ``` -I'll leave you to figure out what this did (Again, check out `GetTokenMetadata.cdc` to see what kind of interface it offers). +Again, you can review `GetTokenMetadata.cdc` to see the relationships between the interfaces offered and how we used them in this function. -Now we are ready to return to `QueryForm.jsx`. Import the functions we worked on as the following: +Now we are ready to return to `QueryForm.jsx`. Import the functions we worked on: ```js @@ -1728,11 +1730,11 @@ array of existing token IDs. We then call `setTokenIds` to set `allTokenIds` to the array. This is used to fill up the `