最近は猫も杓子も Non Fungible Token (NFT) の話をしていて、道ばたの猫もニャーンファンジブルトークンと鳴いていた。NFT ブームの火付け役は恐らく NBA Top Shot だろう。

NBA Top Shot は Flow ブロックチェーン 上にデプロイされたコントラクトによって実現されている。この記事では Flow メインネットから Flow JavaScript SDK を使ってデータを取得する方法について述べる。

データを取得する方法にも、ブロックやトランザクションを直接指定して記録されたデータを読む方法や、コントラクトが発火させるイベントを取得する方法などいくつかがあるが、ここでは Script を実行してデータを取得する。Script とは、Cadence 言語で書かれたコードのうち、ブロックチェーンの状態を変化させないものを指す。これを使うと、例えば コントラクトが公開する関数のうち、単に値を返却するようなものを実行し、その結果を取得できる [1]。ガス代もかからない。

Cadence の詳細については公式の Cadence チュートリアルに詳しいが、実際にメインネット上でどうやってスクリプトを実行するかが書かれた記事が少なく、一方で公式のFlow App Quickstart では Web アプリを1つチュートリアルとして作るということで大きすぎるため、本記事で Script を動かすという点に絞って説明する。

@onflow/fcl と、 @onflow/types の2つのパッケージが必要なのでインストールしておく。

$ npm install --save @onflow/fcl @onflow/types

まずは単純な例として、足し算を行う Script を実行する例を示す。

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

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

  const resp = await fcl.send([
    fcl.script(`
      pub fun main(): Int {
        return 1 + 2;
      }
    `)
  ]);
  
  const result = await fcl.decode(res);
  console.log(result)
})();

上記のコードを実行すると、

3

と表示されるはずだ。

見て分かるように、fcl.script() 内に実行したい Script を記述し、fcl.send() に配列の要素として渡せば良い。返り値は fcl.decode() でデコードする必要がある。これらの関数の使い方の例は、実は @onflow/types の使い方のページに一覧がある

重要なのは、fcl.config() 以下を通して設定する API のエンドポイントだ。 公式の Flow App Quickstart にはテストネットのための設定は書いてあるが、メインネットの設定は書かれていない。ググるとFlow Japan Community の記事が出てきて、https://access-mainnet-beta.onflow.org を指定すれば良いことが分かる。 [2]

main() に引数を渡したい場合は、fcl.send() に渡す配列の2つめに、fcl.args() で生成した引数オブジェクトを渡せば良い。各引数は fcl.arg(1, types.Int) のように型を指定してラップする必要がある。以下のような例となる。

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

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

  const resp = await fcl.send([
    fcl.script(`
      import TopShot from 0x0b2a3299cc857e29

      pub fun main(a: Int, b: Int): Int {
        return a + b;
      }
    `),
    fcl.args([
      fcl.arg(1, types.Int),
      fcl.arg(2, types.Int)
    ])
  ]);
  
  const result = await fcl.decode(resp);
  console.log(result);
})();

そろそろ、コントラクトと対話して実際にブロックチェーン上のデータを参照してみたい。今回は Top Shot のコントラクトに定義された getPlayMetaData(playID: UInt32) を呼び、指定された Play [3] が持つメタデータを取得してみたい。

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

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

  const resp = await fcl.send([
    fcl.script(`
      import TopShot from 0x0b2a3299cc857e29

      pub fun main(): {String: String}? {
        let metadata = TopShot.getPlayMetaData(playID: 42);
        return metadata;
      }
    `)
  ]);
  
  const result = await fcl.decode(resp);
  console.log(result);
})();

上記では、playID = 42 である Play のメタデータを取得している。結果は以下のようになった。

{
  FullName: 'Lonnie Walker',
  FirstName: 'Lonnie',
  LastName: 'Walker',
  Birthdate: '1998-12-14',
  Birthplace: 'Reading, PA, USA',
  JerseyNumber: '1',
  DraftTeam: 'San Antonio Spurs',
  DraftYear: '2018',
  DraftSelection: '18',
  DraftRound: '1',
  TeamAtMomentNBAID: '1610612759',
  TeamAtMoment: 'San Antonio Spurs',
  PrimaryPosition: 'SG',
  PlayerPosition: 'GF',
  Height: '77',
  Weight: '204',
  TotalYearsExperience: '1',
  NbaSeason: '2019-20',
  DateOfMoment: '2019-12-04 01:30:00 +0000 UTC',
  PlayCategory: '3 Pointer',
  PlayType: '3 Pointer',
  HomeTeamName: 'San Antonio Spurs',
  AwayTeamName: 'Houston Rockets',
  HomeTeamScore: '135',
  AwayTeamScore: '133'
}

結果から、Top Shot では一つ一つの Play をどのようにブロックチェーン上で表現しているかが分かる。画像や動画のハッシュはおろか、URL や パス、ファイル名すら書き込まれていない点は気にしておいても良いかもしれない。

なお、

import TopShot from 0x0b2a3299cc857e29

この行について、どうやって TopShot のコントラクトがデプロイされたアドレスをするかという点だが、Flowscan で適当にいくつかトランザクションを漁ると Top Shot 関連のトランザクションを見つけることができ、それらのトランザクションの Event 欄を見ると、Event を発行したコントラクトを知ることができた。コントラクトのページに飛べばコントラクト自体のソースを見ることもできる。なお、Top Shot の全てのコントラクトは GitHub 上で公開されている [4]


本記事では、Flow JavaScript SDK で Script を実行し、メインネットから情報を取得する方法について書いた。Flow には Go SDK と JavaScript SDK があるが、Go SDK については公式のドキュメントも充実しておりその他の情報も多く見つかるが、JavaScript SDK については情報が少なく少し苦労した。

@onflow/fcl はブラウザでも動き、これを使って Flow を使った Web アプリも作れるようだ(メインネットに対してトランザクションを投げるためには Dapper Labs からアクセス権をもらわないといけないようだが)。本記事の情報が参考になると嬉しい。


  1. Ethereum でいうところの、Solidity で view としてマークされた関数を呼び出すようなもの。ただし、Cadence ではコントラクト作成時に view のような指示をすることはなく、Script の実行時にブロックチェーンの状態が変わらなければ何でも Script として実行できるようだ。 ↩︎

  2. Blocto Wallet がホストする https://flow-access-mainnet.portto.io もあるようだが、僕の環境ではエラーになった。内部的に Permision Denied のエラーが出ているようだったので、承認された人しか使えなくなったのかもしれない。 ↩︎

  3. 基本的には Top Shot で公開されているそれぞれのモーメントの、シリアルナンバー非依存の情報と思ってもらえば良い。ただし、モーメントは、 Play に対してユニークではなく、Set (Base Set とか Cool Cats のような発売単位) と Play のペアに対してユニークなものとして識別されることに注意。同じ Play が複数の Set に含まれることがあり、その場合は同じモーメントでも異なる商品となる。 ↩︎

  4. デプロイされたものと同一であることは保証されないので注意。 ↩︎