Flowを用いたNBATopShotライクなNFTの発行手順


はじめに

CryptoKittiesの開発チームにより、ゼロから設計、開発された基盤技術で、NBATopShotに用いられていることで注目されたFlowの情報をまとめています。

特徴

Ethereumが中央台長で所有権を管理していたことに対して、Flowではアカウントのストレージにリソースオブジェクトを配置することで、スケールできるモデルを構築していることが特徴となっています。
node自体も、トランザクション収集、コンセンサス(PoS)、計算、検証それぞれのnodeに分断されており、このことが高速化につながっているわけですが、それぞれの領域に対して適切にコードを記述していくことが重要です。FlowにはEthereumのEOAとContractアドレスの区別はなく、スマートコントラクトは存在するアカウントにデプロイされ、そのアカウントのストレージを利用します。ストレージ容量はアカウントの保有FLOW数に応じて割り当てます。
(アーキテクチャの説明な)
https://medium.com/dapperlabs/enter-the-octagon-ufc-on-flow-brings-mma-to-crypto-480618408510)

FlowCLIのインストール

brew install flow-cli
sh -ci "$(curl -fsSL https://storage.googleapis.com/flow-cli/install.sh)"
flow cadence install-vscode-extension

EmulatorでFlowCLIを使ったコントラクトのdeployまで

一連の流れを試すために、FlowPlaygroundを実行する方法と、cliでPC上のシミュレーターで試す方法の2通りがあります。
ここではCLIを通じたデモを実行する方法を残しておきます。
How to Create NFTs Like NBA Top Shot With Flow and IPFS
で書かれているpinata-partyの事例が非常にわかりやすいので、こちらの手順を参考にしています。


1.プロジェクト作成

$ mkdir pinata-party
$ cd pinata-party
$ flow project init
$ flow accounts create
$ flow accounts get 0x01cf0e2f2f715450

2.ディレクトリとファイルを下記のように作成
contract...実行したアカウントのストレージにdeployされる
transaction...実行したアカウントで、デプロイ済のコントラクトを操作する
script...ストレージの状態をReadする

cadence
    |-contracts
        |-PinataPartyContract.cdc
flow.json   
scripts
    |-CheckTokenMetadata.cdc
transactions
    |-MintPinataParty.cdc

3.flow.jsonを整形

"contracts": {
     "PinataPartyContract": "./cadence/contracts/PinataPartyContract.cdc"
}
"deployments": {
     "emulator": {
          "emulator-account": ["PinataPartyContract"]
     }
}

4.PinataPartyContract.cdcを作成。下記をcopy

https://gist.github.com/polluterofminds/17e961796b795a4c001c2e644bda6a41

5.Contractのdeployを行う

$flow project start-emulator
$flow project deploy --network=emulator

6.MintPinataParty.cdcを作成

https://gist.github.com/polluterofminds/35f6b46abe8ad59237e491b280d30665

7.keyの生成
$flow keys generate

8.mint - transactionの実行
flow transactions send --code ./transactions/MintPinataParty.cdc --signer emulator-account


9.CheckTokenMetadata.cdcの作成

import PinataPartyContract from 0xf8d6e0586b0a20c7

pub fun main() : {String : String} {
    let nftOwner = getAccount(0xf8d6e0586b0a20c7)
    // log("NFT Owner")    
    let capability = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver)

    let receiverRef = capability.borrow()
        ?? panic("Could not borrow the receiver reference")

    return receiverRef.getMetadata(id: 1)
}

10.実行
flow scripts execute ./scripts/CheckTokenMetadata.cdc

11.成功!
Result: {"name": "The Big Swing", "swing_velocity": "29", "swing_angle": "45", "rating": "5", "uri": "ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"}

Testnetで、FlowCLIを使ったコントラクトのdeployまで

次に、Testnetを利用して、これらのコントラクトをdeployします。コードは、emulatorで使ったものをそのまま利用しますので割愛。


1. keyを作成する
$flow keys generate

2. publickeyをfaucetに登録して、addressを登録する
https://testnet-faucet-v2.onflow.org/

3. cliを通じて、アカウントに1000flowがあることを確認する
$flow accounts get 0x5262a052b4e278ae --network=testnet

4. flow.jsonに取得したaddressとkey(pk)を記述する

    "accounts": {
        "emulator-account": {
            "address": "",
            "keys": "",
            "chain": "flow-emulator"
        },
        "testnet-account": {
            "address": "",
            "keys": "",
            "chain": "flow-testnet"
        }
    },

5.flow.jsonのdeploymentにtestnetの項目をたす

    "deployments": {
        "emulator": {
            "emulator-account": ["PinataPartyContract"]
        },
        "testnet": {
            "testnet-account": ["PinataPartyContract"]
        }
    }

6. deployする
flow project deploy --network=testnet

(balanceが減る)
Balance  1000.00100000
->
Balance  1000.00099000

7. mintする
$ flow transactions send ./transactions/MintPinataParty.cdc --network=testnet --signer testnet-account

8. チェックする
$ flow scripts execute ./scripts/CheckTokenMetadata.cdc --network=testnet 

Testnetでscriptを用いて、Webアプリなどから読み込む

// npm install onflow/fcl --save

const fcl = require('@onflow/fcl');

(async () => {
  fcl.config()
  .put("accessNode.api", 'https://access-testnet.onflow.org');

  const resp = await fcl.send([
    fcl.script(`
      import DemoContract from xxxxxxxxxx

      pub fun main(): {String: String}? {
        let nftOwner = getAccount(xxxxxxxxxx) 
        let capability = nftOwner.getCapability<&{DemoContract.NFTReceiver}>(/public/NFTReceiver)
        let receiverRef = capability.borrow()
          ?? panic("Could not borrow the receiver reference")

        return receiverRef.getMetadata(id: 1)
      }
    `)
  ]);

  const result = await fcl.decode(resp);
  console.log(result);
})();

NFTの雛形

Ethereumではopenzippelinなどの雛形を利用して、NFTを作るようなケースがありました。
Flowの場合も、同様にして、いくつかの雛形が用意されているため、これらを読み込みながら、最低限の実装を行うことが可能です。

https://docs.onflow.org/core-contracts/non-fungible-token/
https://www.npmjs.com/package/@onflow/six-topshot-transfer-moment

const mainnetContracts = {
    FungibleToken: '0xf233dcee88fe0abe',
    FlowToken: '0x1654653399040a61',
    FlowFee: '0xf919ee77447b7497',
    StakingTable: '0x8624b52f9ddcd04a',
    LockedTokens: '0x8d0e87b65159ae63',
    NonFungibleToken: '0x1d7e57aa55817448',
    StakingProxy: '0x62430cf28c26d095',
    Topshot: '0x0b2a3299cc857e29'
}

const testnetContracts = {
    FungibleToken: '0x9a0766d93b6608b7',
    FlowToken: '0x7e60df042a9c0868',
    FlowFee: '0x912d5440f7e3769e',
    StakingTable: '0x9eca2b38b18b5dfe',
    LockedTokens: '0x95e019a17d0e23d7',
    NonFungibleToken: '0x631e88ae7f1d7c20',
    StakingProxy: '0x7aad92e5a0715d21',
    Topshot: '0x877931736ee77cff'
}

Flow開発に必要な様々なツールやデモ

node

Flow 公式
(main)https://access-mainnet-beta.onflow.org
(test)https://access-testnet.onflow.org

Blocto ウォレット公式
(main)https://flow-access-mainnet.portto.io
(test)https://access-testnet.onflow.org

Tools

Faucet

flowcliで生成したpublic keyを用いてfaucetを利用することが可能です。
https://testnet-faucet-v2.onflow.org/

Exploler

Wallet

Blocto、Ledgerなど利用可能。Flowのライブラリで提供されるウォレットディスカバリーでもこの2つにくわて、DapperWalletというものが提供されている。
https://fcl-discovery.onflow.org/testnet/authn
https://blocto.portto.io/en/
https://port.onflow.org

dAppsとの連携(EthereumのWeb3.js的な位置付け)

Flow Client LibraryがWalletとの接続やコントラクトの実行などを
行なっている。EthereumのWeb3.js的なイメージ。
https://github.com/portto/fcl-demo
この辺りがサンプルで、FCL wallet interactionsなどを試すとイメージが湧く。
https://fcl-demo.portto.io/

GasFee

Ethereumと同様にして、トランザクションの送信者がトランザクションに署名してGasを支払うことでブロックに取り込まれるのは同じだが、トランザクション送信に必要なロールが3つにに分かれている。

Proposer
トランザクションを発行するアカウント。
Payer
トランザクション手数料を払うアカウント。
Authorizers
トランザクションを承認するアカウント。複数アカウントを指定することが可能。(一般的なケースでは、Proposer=Authorizer)

GasFeeもFLOWというネイティブトークンが必要だが、
Transaction Fee: 0.000001 FLOWと現時点では結構安い。
https://docs.onflow.org/flow-token/concepts#fees

ResouceObject

Flowのスマートコントラクトではトークン数量のようなアセットとなるものをリソースとして定義する。リソースは中央のコントラクトで管理されるのではなく、個々のアカウントのストレージに保有分のリソースが保持。
また、リソースはその時に唯一の場所に存在し、コピーや消失が起こるようなプログラムが書けないように保護されている。例えば、一般的なトークンでは下記のような情報・機能をもつリソースを、初期化するときに自分のアカウントに設置することが必要となる、
残高情報
引き出し機能
預け入れ機能

Flowの理解を助けるためのDemo

Market Demo

http://kitty-items-flow-testnet.herokuapp.com
https://github.com/onflow/kitty-items

このdemoでは、kibbleというサービストークンを用いて、ItemをmintしたりSellしたりする操作を試すことができます。
Contractはこの辺りを参照。
https://github.com/onflow/kitty-items/blob/master/cadence/transactions/kittyItemsMarket/sell_market_item.cdc
https://medium.com/flow-japan/cadence-tutorial2-3a7958ba00fb

non-fungeble Token

https://docs.onflow.org/cadence/tutorial/04-non-fungible-tokens/
https://medium.com/pinata/how-to-create-nfts-like-nba-top-shot-with-flow-and-ipfs-701296944bf

Dappsを作る

https://medium.com/flow-japan/flow-dapps-dev-3f042ba3b8d2
https://medium.com/pinata/how-to-display-your-nft-collection-like-nba-top-shot-with-flow-and-ipfs-6ba75048bf8a

リンク集