見出し画像

NFT交換のお試しプログラミング

自分の好きなMetaaniや注目しているIsekai BattleなどでNFTが発行されているが、Metaaniではメタバース空間に配置できるItemのNFT、Isekai Battleではゲーム内で使用するアイテムが多く存在しています。
現在マーケットで取引自体はできるが、自分のNFTを売りに出したり、誰かのNFTを買ったりすることがメインなので、色違いを交換したい。被っているItemを交換したい。と思うことがあるのではないかな。と、考えていたので、自分の勉強のためにもコントラクト作成、Reactで交換の手続きを行うサイトを検証のためにローカルで作ってみました。


最初に

交換をしたければ、相手と連絡をとってお互いにNFTを転送したらいいのですが、ネットの世界なので相手にNFTを転送したとして、相手からもNFTが転送されるとは限らないので、交換の場合は相手を信用することが必要になります。
メルカリのようなサービスがなかった時代のネットの世界でも、ネットの知り合いに物をやり取りする場合、送ったあとに交換対象が送られてこない。ことが発生したりするので本当に自分が信頼する人としかこのようなやり取りはできませんでした。
そこでメルカリのようなサービスが出てきて、今では気軽に個人間の取引ができるようになりました。
※ここでは、相手を信用することなくユーザーはメルカリを信用する形になります。

このような仕組みをNFTでも試してみたい。SolidityとReactの実装をしたことがないので、手を動かして理解を深めたい。ということからチャレンジしてみました。
すでにこういう転送のサービスがあって、そのコントラクトを見るのも勉強になりますが、自分でこんなことできるのかな。って考えて実装して細かく試してみたほうが理解は深まるはず。

仕組み

用意したコントラクト

登録

メインで作成したのは、このトレードコントラクトです。
このトレードコントラクトに、トレードしたいNFTのコントラクトのアドレスを登録します。
このアドレス登録は、オーナーのアドレスのみ実行できます。

mint

mint

ユーザーは各々NFTをmintします。
自分がmintしたNFTや他の人がmintしたNFTをみて、交換したいNFTをマーケット等で見つけたとします。

トレードリクエスト

トレードリクエスト

例えば、ユーザAがNFT(TokenID = 1)、ユーザBがNFT(TokenID = 2)を保有しているとして、ユーザAが自分の保有しているNFT1とユーザBのNFT2を交換したい場合、トレードコントラクトに自分のNFT1のTokenIDと交換したいNFTを保有しているユーザのアドレス、交換したいTokenIDをトレードコントラクトに登録します。
このとき、トレードコントラクトにNFT1のTokenIDのapproveの権限を渡して、トレードコントラクトにNFT1を預けます。

交換

交換

ユーザーBは、その交換を受ける場合は、トレードコントラクトに対して交換を実行します。
このときにトレードコントラクトにNFT2のTokenIDのapproveの権限を渡して、ユーザーBのNFT2をユーザーAに転送。そして、トレードコントラクトに預けられているNFT1をユーザーBに転送して交換完了となります。

今回の交換の流れは以上になります。

実践

検証用のプログラムですし、私がReactなどのWeb系のFE開発が初めてなので、細かい見た目やレイアウトの勘所がなく、コントラクトの操作ができて動きを検証できることを最優先で進めました。


まずは、ローカルのnodeを起動して今回作成したトレードコントラクトとNFTの交換が必要なので、ERC721のコントラクトをdeployします。
ローカル環境にあるアカウントのアドレスを2つMetamaskに読み込ませて、準備完了です。

ユーザーA: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
ユーザーB: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
ERC721コントラクト:  0x5FbDB2315678afecb367f032d93F642f64180aa3
トレードコントラクト: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
ユーザーAはコントラクトをdeployしたオーナーのアドレスでもあります。

Wallet Connect

まずは、Wallet Connectを行います。
接続後は、コントラクト操作に必要な最低限の画面が表示されます。
一番上に表示されているアドレスが、Wallet Connectのアドレスになるので、現在ユーザーAで接続しています。

取引したいNFTのアドレスを登録

トレードコントラクトに取引を行いたいNFTのコントラクトアドレスを登録するために、NFTのコントラクトアドレスを入力し、「set Trade NFT」をクリックします。
クリックすると、Metamaskが立ち上がり署名してトランザクションを通します。
この作業はdeploy後に最初に一回オーナーが行うイメージです。

ユーザーがNFTをmint

取引するために、ユーザーがNFTを所有しておく必要があるので、ユーザーAでmintするために、「mint」ボタンをクリックしてトランザクションを通します。
トランザクションが通ったのを確認して、本当にNFTを所有しているか確認のために、ユーザーAのアドレスを入力して、「balanceOf」をクリックしてユーザーAが所有している数が分かります。
今回ユーザーAが最初のmintなので、Token ID1の所有者がユーザーAなのかを確認するために、Token IDに「1」を入力し「ownerOf」「tokenURI」をクリックして確認します。

balanceOfの部分で、ユーザーAが1つのNFTを所有していることが確認でき、「ownerOf」「tokenURI」の左に「1」を入力し、ボタンをクリックすると横にユーザーAのアドレス、所有しているNFTの画像が表示されています。
今回、画像ファイル自体に意味はないので、フルオンチェーンの形にして、TokenIDを真ん中に表示した画像にしています。


続いて、交換対象のためにユーザーBに切り替えて同様にmintして所有の確認を行います。

ここでユーザーAがToken ID1のNFT、ユーザーBがToken ID2のNFTを所有している状態になりました。

交換のリクエスト

ユーザーAの所有しているToken ID1とユーザーBの所有しているToken ID2を交換したい。ということで、交換のリクエストをユーザーAから行います。
ユーザーAから操作するので、接続をユーザーAに切り替えて、自分のToken IDとユーザーBのアドレス、交換したいToken IDを入力し、「trade request」をクリックして、トランザクションを通します。
ユーザーAの所有するToken ID1のNFTの転送許可をトレードコントラクトに渡さないといけないので、権限設定のトランザクションを通し、そのままトレードコントラクトに交換リクエストのトランザクションを通します。

トランザクションの承認を確認できたら、ユーザーAのToken ID1のNFTはトレードコントラクトに転送されているので、所有の確認をしてみます。

ユーザーAのアドレスでbalanceOfを確認すると「0」になっており、Token ID1の所有者は「0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512」に変わっており、このアドレスはトレードコントラクトのアドレスになっています。

交換

続いて、交換を行うためにユーザーBに切り変えて、交換を行っていきます。
まずは、自分(ユーザーB)にどのような交換リクエストが来ているのか確認するために、「get exchange token ids」をクリックします。

すると、Requested AddressにユーザーAのアドレスと、Token ID1をToken ID2に交換したい。という情報が表示されます。
これを確認し、実際に交換を行うために、ユーザーBの所有しているToken ID、相手のアドレス、相手のToken IDを入力して、「execute exchange」をクリックします。
最初にユーザーBのToken ID2の転送許可をトレードコントラクトに権限設定のトランザクションを通して、トレードコントラクトに交換のトランザクションを通します。

交換の確認

トランザクションが通れば交換の完了となるので、Token ID1とToken ID2の所有者を確認します。
1はユーザーBの0x70997970C51812dc3A010C7d01b50e0d17dc79C8、2はユーザーAの0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266になっているはずです。

1の所有者確認
2の所有者確認

確かに、1のownerOfがユーザーBのアドレス、2のownerOfがユーザーAのアドレスに変わっています。
以上で、交換の実際の動きの検証が完了です。

まとめ

実際に交換自体は成功しました。
今回は試しの実装なので正常系のみですが、UnitTestでは異常系の確認も行っています。
例えば、相手が所有していないToken IDを指定して交換リクエストを行う。など、色々とパターンがあります。
それ以外にも、どこまで制約をつけたほうが良いのか、リクエストのキャンセルでNFTの所有が戻ってくるのか。など安全に使えるためにコントラクト側の機能追加はまだまだ必要です。

コントラクトは基本的にdeployした後には変更不可なので、実装・テスト
が重要ですし、色々なパターンを想定する必要があるのでリリースできるまで相当時間がかかるイメージです。

今回の転送のコントラクトのように、NFTの転送の権限を渡したりするものは、このコントラクトの実装が悪ければ、預けたNFTを悪意のあるユーザーが奪っていく。交換がうまくいかなくて誰も取り出すことができない。など絶対に発生してはいけません。
何かあったときのために、オーナー権限で取り出せる機能を実装しておくこともできるとは思いますが、そのようなコントラクトだと利用する側はオーナー権限を持った人を信用することになってしまいます。
オーナー権限所有者が勝手に操作はしないだろう。っていう信頼のもと使うのも個人の判断ですが、そのオーナー権限を持った人自体がハッキングにあい秘密鍵を奪われる。など発生してしまうとどうしようもありません。

今回のコントラクトでは、setApprovalForAllは使用しませんでしたが、多く交換をしたいな。と思った場合は、ガス代節約のためにのほうが便利なのかもしれないですが、その場合はこのコントラクトをどこまで信頼するのかの個人判断になります。


ちゃんとした機能を持ったコントラクトの開発をサンプルとはいえ初めて実装してみましたが、世の中に安心してリリースする場合はオーナー側の権限設計。何かあったときのためにアップデート可能にするのか。最終的にはコントラクトの監査をしてくれるところに依頼して安全性の確認、オーナー権限の秘密鍵の管理。など相当ハードルが高いイメージです。

もしこの機能を私のような一個人がリリースしたとして、NFTの交換にこのコントラクトを使ってやりましょう。と相手に依頼したとして誰が私を信頼してくれるのでしょうかw
コントラクトのコードを見て信頼してください。と言ったとしてその判断をできるユーザーがどれだけいるのでしょうか。コントラクトが安心だとしても交換するためのWebサイトの安全性は??など。。。

現段階ではNFTを発行しているプロジェクト側が便利なコントラクトを実装してリリースしないとユーザーは怖くて利用できない気がしますが、個人が開発したとしてもそのコントラクト、Webページを色々な人が確認・検証して安全なのが判断できれば使用する様に今後はなっていくのでしょう。

Web2時代では提供されているAPIなどを利用して機能を追加することができましたが、Web3ではコントラクトにある機能は、誰もがアクセスでき便利な機能を実装することができるWeb3はワクワクする事が多いですね。

その分、組織や人を信頼するのではなくコントラクトの仕組みや、コントラクトを実行するWebページの実装の理解できることが重要になりそうです。Web2ではそのサービスで問題が起きればサービス側が何かしら助けてくれたり、保証してくれることもありますが、Web3だと自己責任になることが多いですし。。。

個人的には実装を経験して知識をつけて、やれること。やれないことが理解できるので、Web3の防御力を上げる力にもなるような気がしています。


この記事が気に入ったらサポートをしてみませんか?