NFT ブームが発生し、現在も続いています (この記事を書いている 2022 年 7 月現在)。 Etherscan には便利な検索ユーティリティがあり、その便利な検証および逆コンパイル機能とともに、多くの ERC721 のコードを覗いて比較することができます。多くの適切に設計された契約に加えて、多くの人が同じ過ちを何度も犯していることもわかります.この記事では、イーサスキャンで NFT コントラクトを表示するときによく気付く、NFT の最も一般的な「設計の失敗」の 4 つについて、私の意見を述べます。
この記事は主に EVM 互換のブロックチェーンを念頭に置いて書かれていることに注意してください。ただし、多くのポイントは、他のネットワークにも適用可能であるか、類似または同等のものがあります。
私が「設計の失敗」と呼んでいるものを犯したとしても、侮辱されないでください。これらは私の意見であり、さらに開発者として、ガス代のかかるネットワークの混雑した時代にガス代を節約する必要性を十分に理解しています.フリーランスのコンサルタントとしての私の見解を考えてみてください。開発支援に数千ドルを費やすことができるクライアントは、卓越性を追求するために、デプロイメントにさらに 100 ドルを費やすことができます。
これはもちろん、この記事の執筆時点で最も高価な Ethereum チェーンの話です。 Polygon はそうではなく、Solana (非 EVM) のような他のチェーンはさらにそうではありません。私が言いたいのは、資金が利用可能であれば、より質の高い実装の利点は、追加コストに見合う価値があるかもしれないということです.
これは非常に一般的ですが、これを見ると、私の目には、アマチュア的に行われた契約にフラグが立てられます。公平を期すために、有効で理解できる動機があります。 1 つには、多くのネットワークで契約を展開して管理することは非常に高価になり、それらのコストを節約するために多大な努力が払われてきました。そして、簡単にするために、契約自体にミントと販売のロジックを入れてみませんか?
しかし、これは本当に良い考えではありません。コントラクト自体は、ロジック ネットワークの不変の中心であるべきですが、お金を直接処理することで自身の手を汚してはなりません。これには、ERC721 実装と同じコントラクト コードで直接、販売、販売時間、ホワイトリスト登録などが含まれます。販売ロジックとコア ロジックは密接に結合されています。
すべてのロジックを 1 つのコントラクトに詰め込む理由としては、ガスのコストを節約することが最善かつ最も理解できる理由かもしれませんが、すべてのことを考慮すると、この設計のショートカットを実装しないほうがよい理由があると思います。核となるコントラクト ロジックだけが確定されている必要があり、ほとんどの場合、非常に標準的な方法で標準を実装します。多くのクローンは、お互いのほぼクローンです (またはその可能性があります)。鋳造戦略、価格設定 (ミントを販売している場合) — これらの種類のものは分離する必要があります。これにより、ユーザーの信頼を損なわないように契約を柔軟にすることができます。分離された設計と単一責任の原則。補足: 管理者の役割を持つ誰かが変更できる限り、ERC721 コントラクト自体で供給 (つまり maxSupply) を制限することは理にかなっていると思います。
トークン コントラクトには、許可されたアドレスのみが使用できる機能 (ミントや供給パラメーターに対する何らかの処理など) があるため、何らかのアクセス制御が必要です。これを実現する最も簡単な方法は、Ownable モデルを使用することです (通常、このような基本的なニーズのために車輪を再発明する理由から、OpenZeppelin の Ownable コントラクトを使用します)。ただし、次の理由から、代わりにロールベースのアクセス制御を使用することを強くお勧めします。 Ownable (またはそれに類するもの) を使用する動機は、おそらく単純さ (およびガス コストの節約) であり、表面上は問題ありません。また、あなた(またはあなたのクライアント)が「常に」契約を管理する唯一の人であることを「知っている」かもしれません。コストが低い場合は、将来の保証が望ましいです。また、ロールベースのセキュリティ (OpenZeppelin の IAccessControl など) の複雑さは、Ownable モデルと比較すると、正直なところ、ほんの少しだけ複雑 (かつ高価) です。ガスのコストがまだ問題である場合は、ロールベースのセキュリティ コード (OpenZeppelin であろうと独自のものであろうと) をいつでも必要なものだけに絞り込むことができます。しかし、役割ベースを使用するより重要な理由は、ERC721 コントラクト自体から機能を分離できることです (前のポイント、販売および価格設定情報など)。完全な管理者権限を許可せずに、「マイナー」ロールを割り当てることにより、別のコントラクトをミンターとして指定できます。一方、管理者 (または、おそらく契約ではなく人間である管理者) には、より高いレベルのアクセス許可 (アクセス許可の削除や追加など) がまだあります。ミンターが (たとえば) ニーズを満たさなくなった場合、そのミンティング権を取り消し、新しいミンティング戦略を実装する新しいコントラクトにマイニング権を割り当てることで、単純にそのミンターを廃止します。モジュール式で、便利で安全です。プロジェクトの特定のユースケースに基づいて、マイニング以外の他のアクティビティを同じ方法で処理できます。
多くのトークン (またはコントラクト全般) は、ERC-165 を実装していないか、最適に実装していません。私の見解では、ERC-165 は相互運用性に関するものです。それはあなたの契約を将来に適合させ、取引所はあなたのNFTのロイヤルティ構造について(例えば)調べるためにそれを呼び出すかもしれません.これがまったく実装されていないか、最適に実装されていないことがよくあります。
これを正しく実装するための経験則は次のとおりです。
|| type(ISomeInterface).interfaceId == _interfaceId
例:
function supportsInterface(bytes4 _interfaceId) public view override(ERC721, ERC721Enumerable) returns (bool) { return super.supportsInterface(_interfaceId) || _interfaceId == type(IERC2981).interfaceId; }
コードに ERC-165 を実装する親クラスがない場合、次のような 2 番目のタイプのみを表す必要があります。
function supportsInterface(bytes4 _interfaceId) public view override returns (bool) { return _interfaceId == type(IERC721).interfaceId || _interfaceId == type(IERC2981).interfaceId || _interfaceId == type(IAccessControl).interfaceId; }
コードが、親クラスの ERC-165 の実装によって処理されるもの以外のインターフェイスを実装しない場合、2 番目のタイプは必要ありません。そのような:
function supportsInterface(bytes4 _interfaceId) public view override(ERC721, ERC721Enumerable) //just make sure this list is complete returns (bool) { return super.supportsInterface(_interfaceId); }
ERC-165 を正しく実装することはオプションですが、重要です。トークンは、まだ実装されていない将来のシステムを含め、できるだけ多くの他のシステム (取引所など) と互換性がある必要があります。 ERC-165 規格は、時間が経ち、業界が成熟するにつれて、より使用され、重要になる可能性があります。
あなたの ERC721 トークンは非常に標準的で、ほとんどカスタマイズされていないすべてのサードパーティの親クラスとライブラリを使用している可能性があり、サードパーティのコードは十分にテストされ、安全であることで有名です。しかし、コードを徹底的にテストする必要があるのは、コードをメインネットにデプロイする直前に 1 回しか入手できず、永遠に栄光か恥かのどちらかをもたらすからです。
まず、もちろん単体テストです。私の意見では、どのテスト フレームワークを使用するかは重要ではありません。私はエーテルとモカでヘルメットを使用しています。私にとって唯一重要な点は、テスト カバレッジと、ハッピー パス ケース、例外ケース、エッジ ケースのカバレッジが広くて深いということです。すでに十分にテストされていることで有名なコード (OpenZeppelin など) をテストしている場合でも、(a) カスタム コードがこれらのケースの一部を壊している可能性があるため、再テストする必要があります。(b) OpenZeppelin には以前にバグがありました。彼らは将来再びかもしれません。時間を節約するために、プロジェクト間で再利用できるすべての ERC721 トークン、すべての ERC20 トークン、すべての ERC1155 トークンなどの標準的なテスト スイートを用意することができます。これはいい。次に、各プロジェクトにケースを追加して、標準へのカスタマイズをカバーできます。これは時間を節約します。単体テストでは、アクセス制御、基本機能 (作成や転送など)、一時停止可能性 (コントラクトが一時停止可能な場合)、ERC165 標準の実装などをカバーする必要があります。 solidity-coverage (nodejs パッケージ) を使用してカバレッジをテストできます。
最後に、自動化されたツールは、テストで非常に役立ちます。 Slither 、 Manticore 、およびMythrilは業界標準であり、通常、Consensys や Certik などのセキュリティ監査の主要な名前で使用されています。 Solidity -coverage (nodejs パッケージ) は、単体テストが提供するカバレッジの推定パーセンテージを示します (経験則として非常に便利です)。 Solgraphは、コントラクト コードの関係と接続を確認するのに役立つツールです。テスト計画に役立ちます。エキドナも便利です。ファジング テスト ツールです。個人的には、該当する場合はテスト優先の方法論を使用します。これにより、適切なテスト カバレッジが保証され、テスト スイートはプロジェクトの仕様に似たものになります。私はいくつかの良いテストカバレッジが大好きです。
> pip3 install slither-analyzer > pip3 install mythril > npm install solidity-coverage
要約すると、次のようになります。
etherscan でのコントラクト コードの検証は優れた機能です。 [Contract] タブに緑色のチェック マークが表示され、「完全一致」タグが付いたコード自体が表示されることで、信頼と説明責任が生まれます。誰かがあなたの契約を初めて見て、使用または投資の相対的なリスクを評価しようとしているとき、これは役に立ちます。そして、これは一般に、NFT だけでなく、すべてのタイプのすべてのコントラクトに当てはまります。