5.2 Non-Fungible Token Tutorial Part 2

[Previous << 5.1 Non-Fungible Token Tutorial Part 1](https://qiita.com/flowcadence/items/ee22ed2ce344620c79da)
[Next >> 6. Fungible Token Tutorial](https://qiita.com/flowcadence/items/d4f98c76d5f88d447345)

https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a#cadenceの言語仕様を学ぶ

このチュートリアルでは、**Non-Fungible Tokens**(**NFT**)のフル実装について学びます。

:::note warn
💡 TIP
このチュートリアルのスターターコードをFlow Playgroundで開きます。
https://play.flow.com/63d827b3-0b49-48d5-91ba-4b222c23e217
チュートリアルでは、このコードと対話するためにさまざまな操作を行うよう求められます。
:::
:::note
ⓘ ACTION
このチュートリアルでは、操作が必要な指示は、常にこのような吹き出しボックス内に表示されます。ハイライトされた操作は、コードを実行するために必要な操作すべてですが、言語の設計を理解するためには、その他の部分も読むことが必要です。
:::
<h1 id="Storing-Multiple-NFTs-in-a-Collection">Storing Multiple NFTs in a Collection</h1>

[前回のチュートリアル](https://qiita.com/flowcadence/items/ee22ed2ce344620c79da)では、シンプルなNFTリソースを作成し、ストレージパスに保存し、マルチシグネチャトランザクションを使用して、それをあるアカウントから別のアカウントに転送しました。

前回のチュートリアルで使用したセットアップと操作は、あまり拡張性がないことはお分かりいただけたと思います。ユーザーは、すべてのNFTを1か所で管理できる方法が必要です。

これを実現する方法はいくつかあります。
* すべてのNFTを配列またはディクショナリに格納することが出来ます。
```ruby
/* Define a dictionary to store the NFTs in */
let myNFTs: @{Int: BasicNFT.NFT} = {}

/* Create a new NFT */
let newNFT <- BasicNFT.createNFT(id: 1)

/* Save the new NFT to the dictionary */
myNFTs[newNFT.id] <- newNFT

/* Save the NFT to a new storage path */
account.storage.save(<-myNFTs, to: /storage/basicNFTDictionary)
```

<h1 id="Dictionaries">Dictionaries</h1>

この例では、[Dictionary: 変更可能で順序のないキーと値の集合](https://qiita.com/flowcadence/items/c917af1e35cd46cf6a7f#dictionaries) を使用します。
```ruby
/* Keys are `Int`
   Values are `NFT` */
access(all) let myNFTs: @{Int: NFT}
```
ディクショナリでは、すべてのキーは同じ型でなければならず、すべての値も同じ型でなければなりません。この場合、整数（`Int`）のIDを`NFT`リソースオブジェクトにマッピングして、ディクショナリに存在する各`Int`に対して1つの`NFT`があることになります。

ディクショナリ定義では通常、型指定に`@`記号は使用しませんが`myNFTs`mapping がリソースを格納するため、フィールド全体もリソース型になる必要があります。そのため、このフィールドにはリソース型であることを示す`@`記号が付けられています。

つまり、リソースに適用されるすべてのルールは、この型にも適用されるということを意味します。

NFTを保存するためにディクショナリを使用すれば、NFTごとに異なる保存パスを使用しなければならないという問題は解決しますが、すべての問題が解決するわけではありません。このタイプは比較的曖昧であり、それ自体にはあまり有用な機能がありません。

その代わり、Cadenceの強力な機能である「リソースが他のリソースを所有する」という機能を使用できます。新しい`Collection`リソースをNFTの保存場所として定義し、NFTとのより高度なやりとりを可能にします。

次に見るスマートコントラクトは、`ExampleNFT`と呼ばれ、アカウント`0x06`のContract 1に保存されています。

このコントラクトは、`BasicNFT`を拡張し、以下を追加します。
1. 固有のNFT IDを追跡する`idCount`コントラクトフィールド(contractの直下のフィールド)を追加します。
2. `NFTReceiver`インターフェースを追加し、コレクション用の3つのパブリック関数を定義します。
3. NFTを直感的に保存および管理する場所として`Collection`と呼ばれるリソースを宣言し、そこに`NFTReceiver`インターフェースを実装します。
4. `Collection`は、`ownedNFTs`、`init()`、`withdraw()`、その他の重要な関数などを含む、NFTとやりとりするためのフィールドや関数を宣言します。
5. 次に、このコントラクトは新しいNFTを作成する関数（`mintNFT()`）と空のコレクション（`createEmptyCollection()`）を宣言します。
6. 最後に、このコントラクトはpathフィールドを初期化し、空のコレクションとそれへの参照を作成し、さらにminterリソースをアカウントストレージに保存するイニシャライザー(init関数)を宣言します。

このスマートコントラクトではいくつかの新しい概念が導入されています。新しいスマートコントラクトについて見ていき、導入されている新しい概念をすべて分解していきます。

:::note
ⓘ ACTION
`ExampleNFT`コントラクトを開きます。
エディタの右下にある「Deploy」ボタンをクリックしてコントラクトをデプロイします。
`ExampleNFT.cdc`には以下のコードが含まれているはずです。このコードには、すでに`BasicNFT.cdc`に含まれているものに加え、スマートコントラクト本体に追加のリソース宣言が含まれています。
:::
```ruby:ExampleNFT.cdc
/* ExampleNFT.cdc
  
   This is a complete version of the ExampleNFT contract
   that includes withdraw and deposit functionalities, as well as a
   collection resource that can be used to bundle NFTs together.
  
   Learn more about non-fungible tokens in this tutorial: https:\/\/developers.flow.com\/cadence\/tutorial\/non-fungible-tokens-1 */
access(all) contract ExampleNFT {

    /* Declare Path constants so paths do not have to be hardcoded
       in transactions and scripts */

    access(all) let CollectionStoragePath: StoragePath
    access(all) let CollectionPublicPath: PublicPath
    access(all) let MinterStoragePath: StoragePath

    /* Tracks the unique IDs of the NFTs */
    access(all) var idCount: UInt64

    /* Declare the NFT resource type */
    access(all) resource NFT {
        /* The unique ID that differentiates each NFT */
        access(all) let id: UInt64

        /* Initialize both fields in the initializer */
        init(initID: UInt64) {
            self.id = initID
        }
    }

    access(all) entitlement Withdraw

    /* The definition of the Collection resource that
       holds the NFTs that a user owns */
    access(all) resource Collection {
        /* dictionary of NFT conforming tokens
           NFT is a resource type with an `UInt64` ID field */
        access(all) var ownedNFTs: @{UInt64: NFT}

        /* Initialize the NFTs field to an empty collection */
        init () {
            self.ownedNFTs <- {}
        }

        /* withdraw
          
           Function that removes an NFT from the collection
           and moves it to the calling context */
        access(Withdraw) fun withdraw(withdrawID: UInt64): @NFT {
            /* If the NFT isn't found, the transaction panics and reverts */
            let token <- self.ownedNFTs.remove(key: withdrawID)
                ?? panic("Could not withdraw an ExampleNFT.NFT with id="
                          .concat(withdrawID.toString())
                          .concat("Verify that the collection owns the NFT ")
                          .concat("with the specified ID first before withdrawing it."))

            return <-token
        }

        /* deposit
          
           Function that takes a NFT as an argument and
           adds it to the collections dictionary */
        access(all) fun deposit(token: @NFT) {
            /* add the new token to the dictionary with a force assignment
               if there is already a value at that key, it will fail and revert */
            self.ownedNFTs[token.id] <-! token
        }

        /* idExists checks to see if a NFT
           with the given ID exists in the collection */
        access(all) view fun idExists(id: UInt64): Bool {
            return self.ownedNFTs[id] != nil
        }

        /* getIDs returns an array of the IDs that are in the collection */
        access(all) view fun getIDs(): [UInt64] {
            return self.ownedNFTs.keys
        }
    }

    /* creates a new empty Collection resource and returns it */
    access(all) fun createEmptyCollection(): @Collection {
        return <- create Collection()
    }

    /* mintNFT
      
       Function that mints a new NFT with a new ID
       and returns it to the caller */
    access(all) fun mintNFT(): @NFT {

        /* create a new NFT */
        var newNFT <- create NFT(initID: self.idCount)

        /* change the id so that each ID is unique */
        self.idCount = self.idCount + 1

        return <-newNFT
    }

	init() {
        self.CollectionStoragePath = /storage/nftTutorialCollection
        self.CollectionPublicPath = /public/nftTutorialCollection
        self.MinterStoragePath = /storage/nftTutorialMinter

        /* initialize the ID count to one */
        self.idCount = 1

        /* store an empty NFT Collection in account storage */
        self.account.storage.save(<-self.createEmptyCollection(), to: self.CollectionStoragePath)

        /* publish a capability to the Collection in storage */
        let cap = self.account.capabilities.storage.issue<&Collection>(self.CollectionStoragePath)
        self.account.capabilities.publish(cap, at: self.CollectionPublicPath)
	}
}
```
このスマートコントラクトは、プロジェクトが実際に運用で使用するスマートコントラクトにより近いものですが、それでも公式のNFTスタンダードを使用していないため、いかなるプロダクションコードでも使用すべきではありません。

1つ以上の`ExampleNFT`を所有するユーザーは、この`@ExampleNFT.Collection`リソースのインスタンスをアカウントに保存する必要があります。このコレクションは、整数IDを`@NFT`にマッピングするディクショナリに、ユーザーのNFTをすべて保存します。

各コレクションには、`deposit`および`withdraw`関数があります。これらの関数により、ユーザーは標準的な一連の関数を通じて、コレクションへのトークンの出し入れを行うことができます。

ユーザーがNFTをアカウントに保存したい場合、`ExampleNFT`スマートコントラクトの`createEmptyCollection()`関数を呼び出して、空の`Collection`を作成します。これにより、アカウントのストレージに保存できる空の`Collection`オブジェクトが返されます。

この例では、いくつかの新しい機能を使用します。それらについて説明します。
<h1 id="The-Resource-dictionary">The Resource Dictionary</h1>

ディクショナリがリソースを格納すると、そのディクショナリ自体もリソースになることを前述で説明しました。

つまり、コレクションは、そのコレクションが持つリソースを処理するための特別なルールを持つ必要があります。コレクションが誤って失われることは避けたいものです！

Resourceチュートリアルで学んだように、`destroy`コマンドを明示的に呼び出すことで、任意のリソースを破棄することができます。

`Collection`リソースが作成されると、イニシャライザー(init関数)が実行され、すべてのメンバー変数を明示的に初期化する必要があります。これにより、初期化されていないフィールドがバグの原因となるスマートコントラクトの問題を防ぐことができます。イニシャライザー(init関数)は、この後二度と実行されることはありません。ここでは、リソースタイプとして空のディクショナリを初期化しています。
```ruby
init () {
  self.ownedNFTs <- {}
}
```
ディクショナリに関するもう一つの特徴は、組み込みの`keys`関数を使用して、ディクショナリのキーの配列を取得できることです。
```ruby
/* getIDs returns an array of the IDs that are in the collection */
access(all) view fun getIDs(): [UInt64] {
    return self.ownedNFTs.keys
}
```
これは、ディクショナリ内を繰り返し処理したり、保存されているもののリストを表示したりするために使用できます。ご覧の通り、[可変長配列型](https://qiita.com/flowcadence/items/c917af1e35cd46cf6a7f#arrays)は、メンバーの型を角カッコ（`[UInt64]`）で囲むことで宣言されます。

<h1 id="Resources-Owning-Resources">Resources Owning Resources</h1>

この`ExampleNFT.cdc`のNFTコレクションの例は、重要な特徴を示しています。リソースは他のリソースを所有できるのです。

この例では、ユーザーは1つのNFTを別のユーザーに譲渡することができます。さらに、`Collection`はその中のNFTを明示的に所有しているため、所有者はコレクション1つを譲渡するだけで、すべてのNFTを一度に譲渡することができます。

これは重要な機能であり、さまざまな追加の使用事例が可能になります。簡単にバッチ転送できることに加え、これは、CryptoKittyが帽子のアクセサリーを所有しているように、独自のNFTが別の独自のNFTを所有したい場合、そのNFTが文字通り自身のフィールドに帽子を格納し、事実上それを所有することを意味します。

また、所有権に関するCadenceの興味深い特徴も挙げられます。他の台帳ベースの言語では、所有権はアカウントのアドレスによって示されます。Cadenceは完全にオブジェクト指向の言語であるため、所有権は台帳上のエントリだけでなく、オブジェクトがどこに格納されているかによって示されます。

リソースは他のリソースを所有することができます。つまり、興味深いロジックにより、リソースは、そのリソースが保存されている実際の人物よりも、所有するリソースをより制御できるということです。

Cadenceをより深く理解するにつれ、所有権と相互運用性について、さらに興味深い意味合いが明らかになっていきます。

それでは、チュートリアルに戻りましょう！


<h1 id="Restricting-Access-to-the-NFT-Collection">Restricting Access to the NFT Collection</h1>

NFTコレクションでは、`Collection`の重要な機能に誰もがアクセスできるCapabilityが保存されます。例えば、`deposit()`や`getIDs()`などです。

ここで重要なアクセス制御のレイヤーが関わってきます。Cadenceは[Capabilityセキュリティ](https://qiita.com/flowcadence/items/b70151356f4e548f2c91)を利用しており、これは任意のオブジェクトについて、ユーザーが以下のいずれかの条件を満たす場合に、そのオブジェクトのフィールドまたはメソッドへのアクセスが許可されることを意味します。
* オブジェクトの所有者である
* そのフィールドまたはメソッドへの有効な参照を持っている（参照はCapabilityからしか作成できず、Capabilityはオブジェクトの所有者によってしか作成できないことに注意してください）

ユーザーが自身のNFT`Collection`をアカウントストレージに保存すると、デフォルトでは他のユーザーはアクセスできません。これは、所有者が承認し署名したトランザクションのみがアクセスできる認証済みアカウントオブジェクト（`auth(Storage) &Account`）へのアクセスが必要となるためです。

外部アカウントに`access(all)`フィールドおよび関数へのアクセス権を与えるには、所有者はストレージ内のオブジェクトへのリンクを作成します。

このリンクにより、Capabilityが作成されます。 オーナーは、この機能を使用して、望むことを何でも行うことができます。 Capabilityは、一度限りの使用のためにパラメータとして関数に渡すこともできますし、`/public/`ドメインに置くことで、誰でもアクセスできるようにすることもできます。

Capabilityの作成と公開は、`ExampleNFT.cdc`スマートコントラクトのイニシャライザー(init関数)で確認できます。
```ruby
/* publish a capability to the Collection in storage */
let cap = self.account.capabilities.storage.issue<&Collection>(self.CollectionStoragePath)
self.account.capabilities.publish(cap, at: self.CollectionPublicPath)
```
`issue`関数は、その機能が`&Collection`として型付けされていることを指定します。次に、リンク(link関数)は`/public/`に公開され、誰でもアクセスできるようになります。リンク(link関数)のターゲットは、`/storage/NFTCollection`で、これは以前に作成したものです。

これで、ユーザーはアカウント`/storage/`にNFTコレクションを持つことになり、他のユーザーにそのユーザーが所有するNFTを確認させることができ、そのユーザーにNFTを送信したりできるCapability(機能)も利用できるようになります。

スクリプトを実行して、これが正しいことを確認してみましょう！

<h1 id="Run-a-Script">Run a Script</h1>

Cadenceのスクリプト(script)は、アカウントの権限を必要とせず、ブロックチェーンから情報を読み取るだけのシンプルなものです。

:::note
ⓘ ACTION
`Print 0x06 NFTs`という名前のスクリプトファイルを開きます。`Print 0x06 NFTs`には、以下のコードを含んでいるはずです。
:::
```ruby
import ExampleNFT from 0x06

/* Print the NFTs owned by account 0x06. */
access(all) fun main(): [UInt64] {
    /* Get the public account object for account 0x06 */
    let nftOwner = getAccount(0x06)

    /* Find the public Receiver capability for their Collection and borrow it */
    let receiverRef = nftOwner.capabilities
        .borrow<&ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)
        ?? panic("Could not borrow a receiver reference to 0x06's ExampleNFT.Collection"
                 .concat(" from the path ")
                 .concat(ExampleNFT.CollectionPublicPath.toString())
                 .concat(". Make sure account 0x06 has set up its account ")
                 .concat("with an ExampleNFT Collection."))

    /* Log the NFTs that they own as an array of IDs */
    log("Account 1 NFTs")
    return receiverRef.getIDs()
}
```
:::note
ⓘ ACTION
`Print 0x06 NFTs` を実行するには、エディタボックスの右上にあるExecuteボタンをクリックします。

このスクリプトは、アカウント`0x06`が所有するNFTのリストを返します。
:::
アカウント`0x06`は現在、コレクションに何も所有していないため、空の配列が出力されます。
```
"Account 1 NFTs"
Result > []
```
スクリプトが実行できない場合、おそらくNFTコレクションがアカウント`0x06`に正しく保存されていないことを意味します。問題が発生した場合は、アカウント`0x06`にコントラクトをデプロイしたこと、および前述の手順を正しく実行したことを確認してください。

<h1 id="Using-Entitlements">Using Entitlements</h1>

ネットワーク上の全員が`withdraw`関数を使用できる状態にはしたくないです。Cadenceでは、参照は、参照が準拠するサブタイプまたはスーパータイプに自由にアップキャストまたはダウンキャストできます。つまり、`&ExampleNFT.Collection`型の参照を持っていた場合、`Collection`上のすべての`access(all)`関数が公開されます。

これは非常に便利な強力な機能ですが、リソースにパブリックなCapabilityを持つ特権的な機能がある場合、その機能は`access(all)`では利用できないことを開発者は理解しておく必要があります。[Entitlements](https://qiita.com/flowcadence/items/572df270135946f5290e#entitlements)を使用する必要があります。

エンタイトルメントにより、アプリ作者はアクセス範囲をきめ細かく制限することができ、同様の名称のエンタイトルメントの下に制限をグループ化するオプションも用意されています。リソースの所有者は、これらのエンタイトルメントを使用して、認証済みの参照によって有効化されたアクションのサブセットへのアクセスを許可することができます。

NFTスマートコントラクトで確認できるとおり、エンタイトルメントを追加しました。
```ruby
access(all) entitlement Withdraw
```
また、`withdraw()` メソッドにもこの権限(Entitlement)を追加しました。
```ruby
access(Withdraw) fun withdraw(withdrawID: UInt64): @NFT {
```
アクセス権限(with entitled access)のある関数とは、その関数が、`access(all)`であるかのように、その関数を含む型の具体的なオブジェクトを持つ誰かによって呼び出せることを意味します。しかし、そのオブジェクトへの通常の参照からは呼び出せません。したがって、`Collection`のパブリック参照を借りた場合、`&ExampleNFT.Collection`の型、`withdraw()`関数を除いて、すべての関数を呼び出し、すべてのフィールドにアクセスできます。
```ruby
/* Get the public capability and borrow a reference from it */
let collectionRef = recipient.capabilities
    .borrow<&ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)
    ?? panic("Could not borrow a reference to the ExampleNFT.Collection")

/* Try to withdraw an NFT from their collection
   ERROR: The reference is not entitled, so this call is not possible and will FAIL */
let stolenNFT <- collectionRef.withdraw(withdrawID: 1)
```
参照を通じて**権限のある(entitled)フィールドまたは関数**にアクセスするには、参照も**権限**を持つ必要があります。つまり、参照またはCapabilityが作成される際には、そのオブジェクトの所有者が明示的にその権限(entitlement)を指定する必要があります。

オブジェクトの所有者は、権限付き(entitled)のCapabilityや参照を作成できる唯一の存在です。上記の例で、withdraw関数をpublicで公開(accessible)したい場合、`auth`キーワードを使用して、Capabilityの型指定ですべての権限(entitlement)を指定し、権限付きの機能(entitled capability)として発行します。
```ruby
/* publish an entitled capability to the Collection in storage
   This capability is issued with the `auth(ExampleNFT.Withdraw)` entitlement
   This gives access to the withdraw function */
let cap = self.account.capabilities.storage.issue<auth(ExampleNFT.Withdraw) &ExampleNFT.Collection>(self.CollectionStoragePath)
self.account.capabilities.publish(cap, at: self.CollectionPublicPath)
```
そしてそれは今、そのCapabilityは、権利付きバージョン(entitled version)として以下のように発行され、誰でも利用することができるようになります。（補足: あまりしないほうがいいはず）
```ruby
/* Get the public entitled capability and borrow a reference from it */
let entitledCollectionRef = recipient.capabilities
    .borrow<auth(ExampleNFT.Withdraw) &ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)
    ?? panic("Could not borrow a reference to the ExampleNFT.Collection")

/* Try to withdraw an NFT from their collection
   This will succeed because the reference is entitled */
let stolenNFT <- entitledCollectionRef.withdraw(withdrawID: 1)
```
もちろん、このようなパブリックな権限付き参照を作成したいとは思わないでしょう。なぜなら、誰もがあなたのwithdraw関数にアクセスできることを望まないからです。権限(Entitlement)は、主に信頼できるユーザーやスマートコントラクトの小規模なサブセットとプライベートな機能(private capability)を共有するために使用されるものであり、パブリックな機能(public capability)に使用されるべきではありません。

最も重要なことは、ネットワーク上の全員がリソース上の関数にアクセスできるようにしたくない場合は、デフォルトでその関数に権限(entitlement)を設定すべきであるということです。後悔するくらいなら、安全策を取る方が良いでしょう。


<h1 id="Mint-and-Distribute-Tokens">Mint and Distribute Tokens</h1>

NFTを作成する方法の1つは、管理者(Admin)が新しいトークンを発行(mint)し、それをユーザーに送信することです。学習を目的として、ここでは単純に発行(minting)をパブリック関数として実装します。通常は、NFT Minterリソースを使用して制限付きの発行(restricted minting)を実装するのが一般的です。このリソースの所有者(the owner of this resource)がトークンを発行(mint)できる唯一の存在であるため、発行が制限されます。

この例は、[Marketplaceチュートリアル](https://qiita.com/flowcadence/items/4ccd756d0a2a71c6059b)でご覧いただけます。

:::note
ⓘ ACTION
`Mint NFT`という名前のファイルを開きます。アカウント`0x06`を唯一の署名者として選択し、トランザクションを送信(send)します。

このトランザクションにより、アカウント所有者のNFTコレクションにNFTが発行されます。
:::
```ruby:mint_nft.cdc
import ExampleNFT from 0x06

/* This transaction allows the Minter account to mint an NFT
   and deposit it into its own collection. */

transaction {

    /* The reference to the collection that will be receiving the NFT */
    let receiverRef: &ExampleNFT.Collection

    prepare(acct: auth(BorrowValue) &Account) {
        /* Get the owner's collection capability and borrow a reference */
        self.receiverRef = acct.capabilities
            .borrow<&ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)
            ?? panic("Could not borrow a collection reference to 0x06's ExampleNFT.Collection"
                     .concat(" from the path ")
                     .concat(ExampleNFT.CollectionPublicPath.toString())
                     .concat(". Make sure account 0x06 has set up its account ")
                     .concat("with an ExampleNFT Collection."))
    }

    execute {
        /* Use the minter reference to mint an NFT, which deposits
           the NFT into the collection that is sent as a parameter. */
        let newNFT <- ExampleNFT.mintNFT()

        self.receiverRef.deposit(token: <-newNFT)

        log("NFT Minted and deposited to Account 0x06's Collection")
    }
}
```
:::note
ⓘ ACTION
`Print 0x06 NFTs` を再び開き、スクリプトを実行します。これにより、アカウント`0x06`が所有するNFTのリストが表示されます。
:::
```ruby:print_06_nfts.cdc
import ExampleNFT from 0x06

/* Print the NFTs owned by account 0x06. */
access(all) fun main(): [UInt64] {
    /* Get the public account object for account 0x06 */
    let nftOwner = getAccount(0x06)

    /* Find the public Receiver capability for their Collection */
    let capability = nftOwner.capabilities.get<&ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)

    /* borrow a reference from the capability */
    let receiverRef = capability.borrow()
        ?? panic("Could not borrow a receiver reference to 0x06's ExampleNFT.Collection"
                 .concat(" from the path ")
                 .concat(ExampleNFT.CollectionPublicPath.toString())
                 .concat(". Make sure account 0x06 has set up its account ")
                 .concat("with an ExampleNFT Collection."))

    /* Log the NFTs that they own as an array of IDs */
    log("Account 0x06 NFTs")
    return receiverRef.getIDs()
}
```
そのアカウント`0x06`は`id = 1`のNFTを所有していることがわかるでしょう。
```
"Account 0x06 NFTs"
[1]
```
<h1 id="Transferring-an-NFT">Transferring an NFT</h1>

NFTを他のアカウントに転送する前に、そのアカウントを独自のNFTCollectionで設定し、NFTを受け取れるようにする必要があります。

:::note
ⓘ ACTION
`Setup Account`という名前のファイルを開き、アカウント`0x07`を唯一の署名者として使用して、トランザクションを送信します。
:::
```ruby:SetupAccount.cdc
import ExampleNFT from 0x06

/* This transaction configures a user's account
   to use the NFT contract by creating a new empty collection,
   storing it in their account storage, and publishing a capability */
transaction {
    prepare(acct: auth(SaveValue, Capabilities) &Account) {

        /* Create a new empty collection */
        let collection <- ExampleNFT.createEmptyCollection()

        /* store the empty NFT Collection in account storage */
        acct.storage.save(<-collection, to: ExampleNFT.CollectionStoragePath)

        log("Collection created for account 0x07")

        /* create a public capability for the Collection */
        let cap = acct.capabilities.storage.issue<&ExampleNFT.Collection>(ExampleNFT.CollectionStoragePath)
        acct.capabilities.publish(cap, at: ExampleNFT.CollectionPublicPath)

        log("Capability created")
    }
}
```
アカウント`0x07`は、アカウントストレージに空の`Collection`リソースが格納されているはずです。また、`/public/`ドメインにコレクションの機能(Capability)を作成して格納しました。

:::note
ⓘ ACTION
`Transfer`という名前のファイルを開き、アカウント`0x06`を唯一の署名者として選択し、トランザクションを送信します。

このトランザクションは、アカウント`0x06`からアカウント`0x07`にトークンを転送します。
:::
```ruby:transfer_nft.cdc
import ExampleNFT from 0x06

/* This transaction transfers an NFT from one user's collection
   to another user's collection. */
transaction {

    /* The field that will hold the NFT as it is being
       transferred to the other account */
    let transferToken: @ExampleNFT.NFT

    prepare(acct: auth(BorrowValue) &Account) {

        /* Borrow a reference from the stored collection */
        let collectionRef = acct.storage
            .borrow<auth(ExampleNFT.Withdraw) &ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath)
            ?? panic("Could not borrow a collection reference to 0x06's ExampleNFT.Collection"
                     .concat(" from the path ")
                     .concat(ExampleNFT.CollectionPublicPath.toString())
                     .concat(". Make sure account 0x06 has set up its account ")
                     .concat("with an ExampleNFT Collection."))

        /* Call the withdraw function on the sender's Collection
           to move the NFT out of the collection */
        self.transferToken <- collectionRef.withdraw(withdrawID: 1)
    }

    execute {
        /* Get the recipient's public account object */
        let recipient = getAccount(0x07)

        /* Get the Collection reference for the receiver
           getting the public capability and borrowing a reference from it */
        let receiverRef = recipient.capabilities
            .borrow<&ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)
            ?? panic("Could not borrow a collection reference to 0x07's ExampleNFT.Collection"
                     .concat(" from the path ")
                     .concat(ExampleNFT.CollectionPublicPath.toString())
                     .concat(". Make sure account 0x07 has set up its account ")
                     .concat("with an ExampleNFT Collection."))

        /* Deposit the NFT in the receivers collection */
        receiverRef.deposit(token: <-self.transferToken)

        log("NFT ID 1 transferred from account 0x06 to account 0x07")
    }
}
```
CollectionとCapabilityを使用することで、トークンを転送するトランザクションに署名する必要があるのは、トークンを送信する側のアカウントだけです。

次に、両方のアカウントのコレクションをチェックして、アカウント`0x07`がトークンを所有しており、アカウント`0x06`が何も所有していないことを確認します。

:::note
ⓘ ACTION
`Print all NFTs`スクリプトを実行(Execute)して、各アカウントのトークンを確認します。
:::
```ruby:print_all_owned_nfts.cdc
import ExampleNFT from 0x06

/* Print the NFTs owned by accounts 0x06 and 0x07. */
access(all) fun main() {

    /* Get both public account objects */
    let account6 = getAccount(0x06)
	let account7 = getAccount(0x07)

    /* Find the public Receiver capability for their Collections */
    let acct6Capability = account6.capabilities.get<&ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)
    let acct7Capability = account7.capabilities.get<&ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)

    /* borrow references from the capabilities */
    let receiver6Ref = acct6Capability.borrow()
        ?? panic("Could not borrow a collection reference to 0x06's ExampleNFT.Collection"
                 .concat(" from the path ")
                 .concat(ExampleNFT.CollectionPublicPath.toString())
                 .concat(". Make sure account 0x06 has set up its account ")
                 .concat("with an ExampleNFT Collection."))

    let receiver7Ref = acct7Capability.borrow()
        ?? panic("Could not borrow a collection reference to 0x07's ExampleNFT.Collection"
                 .concat(" from the path ")
                 .concat(ExampleNFT.CollectionPublicPath.toString())
                 .concat(". Make sure account 0x07 has set up its account ")
                 .concat("with an ExampleNFT Collection."))

    /* Print both collections as arrays of IDs */
    log("Account 0x06 NFTs")
    log(receiver6Ref.getIDs())

    log("Account 0x07 NFTs")
    log(receiver7Ref.getIDs())
}
```
出力には以下のような内容が表示されるはずです。
```
"Account 0x06 NFTs"
[]
"Account 0x07 NFTs"
[1]
```
アカウント`0x07`にはID=1のNFTが1つあり、アカウント`0x06`にはありません。これは、NFTがアカウント`0x06`からアカウント`0x07`に転送されたことを示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3912821/51a7b6eb-ac4e-14dc-082f-921fb65d635a.png)
これで、動作するNFTが完成しました！おめでとうございます
<h1 id="Putting-It-All-Together">Putting It All Together</h1>

これは、Flow上でNFTがどのように機能する可能性があるかについての基本的な例にすぎません。Flow NFTスタンダードと、実際のNFTスマートコントラクトの実装方法については、[Flow NFT Standard repo](https://github.com/onflow/flow-nft)および[NFT Developer Guide](https://developers.flow.com/build/guides/nft)を参照してください。

<h1 id="Fungible-Tokens">Fungible Tokens</h1>

NFTが使えるようになったので、おそらくそれを取引できるようになりたいと思うでしょう。そのためには、Flow上でファンジブルトークンがどのように機能するかを理解する必要があります。次のチュートリアルに進みましょう！

翻訳元

https://cadence-lang.org/docs/tutorial/non-fungible-tokens-2

***
<b>[Previous << 5.1 Non-Fungible Token Tutorial Part 1](https://qiita.com/flowcadence/items/ee22ed2ce344620c79da)
</b>

[Flow blockchain / Cadence version1.0ドキュメント](https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a) (5.2 Non-Fungible Token Tutorial Part 2)

<b>[Next >> 6. Fungible Token Tutorial](https://qiita.com/flowcadence/items/d4f98c76d5f88d447345)</b>