はじめに(背景・課題)
ERC721規格のTokenを使ったシステムを開発していると、
「1回発行したTokenを全部リセットしたいなー」と考えることがある。
特にTokenの値をオフチェーンのDBと紐づけている場合は、「DBリセットするタイミングでTokenもリセットしたいなー。デプロイし直すとアドレス変わるから、アドレスはそのままでリセットできるといいなー。」と考える。
そこで、Reset関数を実装したERC721Resettableなるものを真剣に考えてみたので、その考察についてメモを残す。
結論
ERC721にリセット関数はいらない。
リセットしたいならコントラクトをデプロイし直すのがいい。
参照:https://gifmagazine.net/post_images/3014792
理由
色々書いておいて元も子もない結論だが、真剣に考察した上でそう思ったので、その理由を述べていく。
1.Eventの履歴は消せない
まず、Eventの履歴を消せないことが挙げられる。
Storageの値を全て消したとしても、Eventの履歴を消すことはできない。
ERC721は、mintやtransferといったタイミングでEventをemitしており、サーバーも絡んだシステムでは、これらの値を使ったシステムを組んでいることがほとんどである。
そのため、実際の実装としては、「Token側はリセット時点でResetイベントをemitするようにする。サーバー側ではResetイベントも監視し、最新のResetイベント以降のイベントしか見ないようにする。」といった工夫が必要になってくる。
開発しやすくするためのリセット機能のはずが、複雑な処理を生んでしまい、モヤモヤした気持ちになる。
2. 消すべきstorageがprivate
ERC721規格のTokenを開発する場合、OpenZeppelinのERC721を継承するのが慣例になっている。
これを継承していない場合、監査の段階で「OpenZeppelinのERC721を継承してください」と言われてしまう。(継承していないと、ERC721自体の実装にも監査が必要なのでコストがかさむ。)
そこで、OpenZeppelinのERC721を見ていくのだが、リセットしたいstorage変数がprivateで宣言されている(例えば_tokenOwner)。
このため、「継承した上でリセット関数を追加する」のではなく、「継承元のERC721自体にリセット関数を追加する」必要が出てくる。private変数をinternalに書き換える方法もあるが、どちらにせよERC721自体を書き換える必要が出てくる。
「開発で使うだけなんだから、別にいいんじゃない?」と思うかもしれないが、これだと、最新のOpenZeppelinによる変更が反映されないファイルを使い続けることになってしまい、ときどき手動でメンテナンスする必要が出てくるため、使い勝手が悪い。
3. mappingはdeleteできない
mapping型の変数は、それ自体をdeleteすることができない。
https://solidity.readthedocs.io/en/develop/types.html#delete
mappingに追加した値のkeyを保存しておけば、1つ1つ要素をdeleteしていけるが、大変。
大変が嫌で始めたので、モヤモヤする。
4. Truffleからのデプロイが改善されている
以前は、Truffle内部のWeb3.jsが0.x系だったため、globalなtestnet(rinkebyやropstenなど)に対する、transactionを含むコマンドが連続して実行できなかった。(awaitできないので、jsのスクリプトもshellスクリプトも連続的に回すのが難しく、工夫がいる状態だった。)
しかし、内部にweb3の1.x系が組み込まれ、この問題が解決した。スクリプトを書けば、複雑なデプロイ時の処理も1つのコマンドで実行できる。
まとめ
このように、
「ERC721にリセット関数があれば便利だなー」と考え、
実装を試みた結果、
「(開発効率を考えると)リセット関数はいらない」という結論に至った。
コントラクトをデプロイし直して、変更されたアドレスをサーバー側にも渡すほうが効率が良い。
同じように考えている人の参考になれば幸いです。