Open4

RustでSolanaのNFTマーケットプレイスを自作する道

あんはるあんはる

ぼちぼち

SolanaでNFTマーケットプレイスと自作してみようと思います。

Solanaとは

「イーサリアムキラー」と呼ばれているそうです。
Rustで書けます!!
この辺が詳しい↓
https://labs.septeni.co.jp/entry/2021/11/08/090000

技術スタック

Solanaプログラム(イーサリアムで言うスマートコントラクト)
・Solana
・Metaplex
・Anchor
フロント(Web)
まだ調べてないですがReactで書けたらReactで書く予定

↓この記事と同じ技術スタック
https://betterprogramming.pub/how-to-mint-nfts-on-solana-using-rust-and-metaplex-f66bac717cb8

NFTマーケットプレイスの概要

・Solana上にNFTを作成できる
・素材(画像など)はArweaveで永続化

なぜArweave?

Arweaveは分散型ストレージで、長期間データを保存できるらしい。
実際はNFTでもAWSやGCPのクラウドストレージが使われていて、結局中央集権になっちゃってるらしい。そうなると削除されたり変更されたりする可能性があるため、Arweaveなどの分散型ストレージを使った方がいいのではないかと言う感じだ。
↓詳しいこと
https://arweave.news/nft-404-japanese/

参考サイト

独自?のNFTマーケットを運営しているサイト Solana、Arweaveを用いている
https://market.solanamonkey.business
Solana Monkeyのメタデータ
https://explorer.solana.com/address/ESKj3E5XPCcFZ74iB3hWAnxZj4eU1GfXNHxGJCo1o9v5/metadata

音楽のNFTサイト、こちらもSolana、Arweaveを用いていて参考になる
https://pianity.com/ja
音楽の個別ページを見るとこんな表示がありArweaveが使われていることがわかる。

あんはるあんはる

SolanaでNFTを作る(Rust, Anchor, Metaplex)

まずは、プログラムからNFTを作るところから。
前回も紹介したこの記事がわかりやすいので、このコードを辿りながら動くか確認してみる。
https://betterprogramming.pub/how-to-mint-nfts-on-solana-using-rust-and-metaplex-f66bac717cb8

※ Macでやります

Solana周りの技術をインストール

とてもスムーズに導入できた

Rustをインストール

万が一、Rustをインストールしていなければ、こちらから。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

https://www.rust-lang.org/tools/install より

Solana CLIを導入

sh -c "$(curl -sSfL https://release.solana.com/v1.10.31/install)"

https://docs.solana.com/cli/install-solana-cli-tools より

Anchorを導入

cargo install --git https://github.com/project-serum/anchor avm --locked --force
avm install latest
avm use latest

https://hackmd.io/@ironaddicteddog/solana-anchor-escrow より

これで完了!

Solanaの設定

devnetに設定

devnetを用いて開発を行うので、devnetと設定する。

solana config set --url devnet

Wallet作成

solanaのwalletを入れるディレクトリに、JSONのwalletのkeypairを保存する。
このkeypairを指定してコマンドを打つとpubkeyがいつでも確認できる!

mkdir ~/my-solana-wallet
solana-keygen new --outfile ~/my-solana-wallet/my-keypair.json
solana-keygen pubkey ~/my-solana-wallet/my-keypair.json

公式がとても丁寧!
https://docs.solana.com/wallet-guide/file-system-wallet#generate-a-file-system-wallet-keypair

そしてsolanaの設定にkeypairの場所を入れておこう。

solana config set --keypiar ~/my-solana-wallet/my-keypair.json

設定が合っているか不安になったら、
こちらで簡単に確認できます!

solana config get

雛形を生成!

Solanaプログラムの雛形を簡単に作れるフレームワーク、Anchorで作っていく。

anchor init solana-nft-example

solana-nft-exampleの部分は好きなプロジェクト名をつける。

雛形ができたらAnchor.tomlをいじる。
providerの欄のclusterをdevnet、walletを先ほど作成したkeypairの場所に設定する。

[provider]
cluster = "devnet"
wallet = "/Users/anharu/my-solana-wallet/my-keypair.json"

次に必要なパッケージをインストールしていく。
programs/<project name>/Cargo.tomlを開く

最新のバージョンを設定しておく。

[dependencies]
anchor-lang = "0.25.0"
anchor-spl = "0.25.0"
mpl-token-metadata = {version = "1.3.2", features = ["no-entrypoint"]}

NFTを作れるようにする

programs/<project name>/src/lib.rsをいじる。

https://github.com/anoushk1234/metaplex-anchor-nft/blob/master/programs/metaplex-anchor-nft/src/lib.rs を使ってとりあえず動かしてみた。

mint_nftの関数はこのようになってる。creator_keyは自分のpubkey、uriはNFTの画像URL、titleはNFTのタイトルを意味してる。
これを設定することでJSから呼び出すことができる。

pub fn mint_nft(
        ctx: Context<MintNFT>,
        creator_key: Pubkey,
        uri: String,
        title: String,
    ) -> Result<()> {

テストからmint_nftを呼び出し

tests/<project name>.tsでテストを作ろう。

これも、 https://github.com/anoushk1234/metaplex-anchor-nft/blob/master/tests/metaplex-anchor-nft.ts を利用した。

TSからは、 program.methods.mintNftで先ほどの関数を実行できた。snake caseからcamel caseに変換されていることに注意してください。

このコード↓を利用すると、好きな画像を設定できません。
とりあえず、CLIでarweaveに画像をアップロードしてみよう!

    const tx = await program.methods.mintNft(
      mintKey.publicKey,
      "https://arweave.net/y5e5DJsiwH0s_ayfMwYk-SnrZtVZzHLQDSTZ5dNRUHA",
      "NFT Title",
    )
      .accounts({
        mintAuthority: wallet.publicKey,
        mint: mintKey.publicKey,
        tokenAccount: NftTokenAccount,
        tokenProgram: TOKEN_PROGRAM_ID,
        metadata: metadataAddress,
        tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
        payer: wallet.publicKey,
        systemProgram: SystemProgram.programId,
        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
        masterEdition: masterEdition,
      },
      )
      .rpc();

CLIでarweaveに画像アップロード

Arweaveのwalletを入手

公式サイトのCLAIM A TOKENから、walletを作り、ツイートをすると、ARがもらえる。

CLIをインストールして、

npm install -g arweave-deploy

deployコマンドで、Arweaveのkeyfileの場所と、画像のパスを指定するだけ!

 arweave deploy --key-file <arweave key file> <file path>

画像を作ったら次はNFTメタデータJSONを作る。

↓ここに書いてある形式に乗っ取って設定する
https://docs.metaplex.com/programs/token-metadata/token-standard

私はこのように設定してみた。devnetなので適当に。

{
    "name":"My tomie art",
    "symbol":"ANHARU",
    "description":"My tomie art",
    "seller_fee_basis_points":420,
    "external_url":"",
    "attributes":
    [
        {
            "trait_type": "name",
            "value": "My tomie art"
        }
    ],
    "properties": {
        "category":"image",
        "creators":
        [
            {"address":"GucSuCq7MvzPgwSQLkWi19wJThWpQ6uMy813DAEBUMym","share":100}
        ],
        "files":
        [
            {"uri":"https://arweave.net/e578yh89yejwPMXLfYMy_6GsqtlsEktKoYfIOYfsCBU?ext=jpg","type":"image/jpeg"}
        ]
    },
    "image":"https://arweave.net/e578yh89yejwPMXLfYMy_6GsqtlsEktKoYfIOYfsCBU"
}

これも先ほどと同じようにarweaveにアップロードしよう。

これでオリジナルなarweaveのファイルとタイトルを設定する。
最初間違えて、URLを画像リンクにしてしまったのですが、solscanで確認すると画像が表示されず。

正しくは、ここはNFTのメタデータJSONのリンクを貼る。

    const tx = await program.methods.mintNft(
      mintKey.publicKey,
      "https://arweave.net/wLQwK2ENbXVfQQ-n7tsVbGVREx61oVqSWJFQgoorzug",
      "My tomie art",
    )

準備ができたらanchor testでテストを実行!

> anchor test
BPF SDK: /Users/anharu/.local/share/solana/install/releases/1.10.31/solana-release/bin/sdk/bpf
cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: cargo +bpf build --target bpfel-unknown-unknown --release
    Finished release [optimized] target(s) in 0.70s
cargo-build-bpf child: /Users/anharu/.local/share/solana/install/releases/1.10.31/solana-release/bin/sdk/bpf/dependencies/bpf-tools/llvm/bin/llvm-readelf --dyn-symbols /Users/anharu/Projects/solana-nft-example/target/deploy/solana_nft_example.so

To deploy this program:
  $ solana program deploy /Users/anharu/Projects/solana-nft-example/target/deploy/solana_nft_example.so
The program address will default to this keypair (override with --program-id):
  /Users/anharu/Projects/solana-nft-example/target/deploy/solana_nft_example-keypair.json
Deploying workspace: https://api.devnet.solana.com
Upgrade authority: /Users/anharu/my-solana-wallet/my-keypair.json
Deploying program "solana-nft-example"...
Program path: /Users/anharu/Projects/solana-nft-example/target/deploy/solana_nft_example.so...
Program Id: 7mD7Xd7vLfmUb8TocxpJcj1c3EkfGDQfqVkAUyWhxsxc

Deploy success

Found a 'test' script in the Anchor.toml. Running it as a test suite!

Running test suite: "/Users/anharu/Projects/solana-nft-example/Anchor.toml"

yarn run v1.22.17
warning package.json: No license field
$ /Users/anharu/Projects/solana-nft-example/node_modules/.bin/ts-mocha -p ./tsconfig.json -t 1000000 'tests/**/*.ts'


  solana_nft_example
NFT Account:  377gZbV7sxmwEGMozWZ45mQRAksQwDRaAhr46QfS5Reg
{
  context: { apiVersion: '1.10.29', slot: 149686865 },
  value: {
    data: { parsed: [Object], program: 'spl-token', space: 82 },
    executable: false,
    lamports: 1461600,
    owner: PublicKey {
      _bn: <BN: 6ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9>
    },
    rentEpoch: 346
  }
}
Account:  5cwP9BdaMvBo2sSdWYiu5GauDAsGXgDwWNVrwKVdDATxHwiMuTa5mkHDLwYGfNEBsBeRzHLioU23VDSQ1JVridHT
Mint key:  Hk4usYxqgidgbsUt28ykULreQbPrsK2dEDwg44US7ATs
User:  GucSuCq7MvzPgwSQLkWi19wJThWpQ6uMy813DAEBUMym
Metadata address:  5bcBcLdthN5jC3JaLTSkwo2SKyYoRF8YYLzQku7Lxfaw
MasterEdition:  BqJmrALKAL3MwUGpoVKRd8Xb84hUyUFEV57JW3G3FtA8
Your transaction signature aZUQyKcomNAxT9UD1jSqmE1ww7aFgahYn6NTRDxWRaBi1NjaFyBoQfMYXLiaiUWCg6HWngtFoTHCnov6x9ate7x
    ✔ Is initialized! (3357ms)


  1 passing (3s)

✨  Done in 9.26s.

このように成功!早速solscanで見てみる

https://solscan.io にアクセスし、
devnetを設定。

ターミナルに表示されたmint key(Hk4usYxqgidgbsUt28ykULreQbPrsK2dEDwg44US7ATs)を検索欄に入れる。

そうすると、、正しく画像が表示されいい感じ!

もしNot foundになるときは、devnetになっているか確認し、なっていれば、根気よくretryする。

メタデータ欄を見るとarweaveのjsonリンクもちゃんと設定されていることがわかる。

今後

arweaveのjsのパッケージでプログラムからアップロードしたり、WebアプリからNFT作れるようにしたい。

追記

ここまでやったのを公開しました。
これからもこれをどんどんカスタマイズしていこうと思います。
https://github.com/anharu2394/solana-nft-example.git

あんはるあんはる

NFTマーケットのフロントについて

Anchorを使えば、Solanaのプログラムの操作ができるとわかったので、
フロントエンドのライブラリはなんでも良さそうです。

なので、Reactで行おうと思います。

あんはるあんはる

フロントよりも、学ぶことが多いバックエンドの方を優先的にやっていこうと思う。

NFTを作る機能はほぼ出来上がったので、売買する機能を実装していきたい。

ただ、多くのNFTマーケットプレイスには、オークション機能が備わっている。

オークションのプログラムをとりあえず動かしてみる

オークション機能つけてるいい例ないかなーとGithubを漁っていると発見!!

こちらのリポジトリ。

https://github.com/armaniferrante/auction-house

とりあえずうごがしてみる。

そうすると、こちらのエラーに遭遇
https://book.anchor-lang.com/anchor_in_depth/the_accounts_struct.html#safety-checks

ひたすらエラーが出たフィールドにひたすらこのコメントを追加

    /// CHECK: This is not dangerous because we don't read or write from this account

そしてanchor testをする前に、yarn installを忘れずに。
自動的にyarn run postinstallが走って、submoduleの方の設定をしてくれる。

そして、anchor testをすると、、テストが正常に走りました!

これベースでフロントをつけたらいい感じになるのかな???やってみる