【NFT】独自コントラクトでNFTを発行してみよう:NFTコントラクト実装の基礎 - Access Control編
今回の記事では、導入編、深掘り編と説明を進めてきた、NFTコントラクトのオーナシップについてどのように実装に落とし込んでいくのか、をOpenZeppelinの提供するContracts Wizardを使いながら説明していきます。
OpenZeppelinとは
OpenZeppelinはスマートコントラクトの開発言語であるSolidityのライブラリ群を提供しています。NFTはERC721という規格を使って実装されることが多いのですが、OpenZeppelinの提供するERC721のライブラリを利用することでセキュアにNFTを実装することができる、とされています。
Contract Wizardとは
Contract Wizardは、上記のライブラリをUI上で組み合わせることで、NFTを実装することができるサービスです。
Contract Wizardの使い方はこちらをご参照ください。
このシリーズでは、Contract Wizardの機能カテゴリにある
・Access Control
・Upgradeability
・Features
上記の3項目に分けて、オーナーシップがどのように実装されていくのかを掘り下げていきます。
今回の記事では主にAccess Controlについて掘り下げます。
Access Control: OwnableとRole
Access Controlはスマートコントラクトの機能を実行する権限をどのウォレットアドレスに持たせるかを制御する機能です。
Ownableでは、ownerという単一のウォレットアドレスに全ての権限を持たせるのに対して、Roleではそれぞれの機能ごとに、また複数のウォレットアドレスに権限を持たせることが可能です。
Contract Wizardを動かしながら確認してみましょう!
FeaturesのMintableとPausable、Access ControlのOwnableにチェックを入れると、下記のようなコードが生成されます。
Ownableを利用した場合は、コントラクトをデプロイしたウォレットアドレスがownerに設定され、下記のような管理機能には、onlyOwnerという修飾子が設定され、Ownerのみ実行できます。
// ownerはコントラクトの作成時に、デプロイを実行したウォレットアドレスが設定されます。
// public address owner = msg.sender
// NFTアセットの移転を制限する機能です。
// onlyOwner = ownerのみ実行可能です。
function pause() public onlyOwner {
_pause();
}
// NFTアセットの移転の制限を解除する機能です。
// onlyOwner = ownerのみ実行可能です。
function unpause() public onlyOwner {
_unpause();
}
// NFTアセットの新規発行をする機能です。
// onlyOwner = ownerのみ実行可能です。
function safeMint(address to, uint256 tokenId) public onlyOwner {
_safeMint(to, tokenId);
}
それでは、次にRoleの挙動を見てみましょう。
FeaturesのMintableとPausableのチェックはそのままで、Access ControlのRoleにチェックを入れた場合は、下記のようなコードが生成されます。
Roleを利用した場合は、それぞれの役割ごとに任意の単一・複数のアドレスを設定することができ、下記のような管理機能には、それぞれの役割ごとに実行できる機能を設定することができます。
// constructorはコントラクトの新規作成時に実行される初期化処理です。
// DEFAULT_ADMIN_ROLE、PAUSER_ROLE、MINTER_ROLEと役割ごとにウォレットアドレスを設定することができます。
constructor() ERC721("MyToken", "MTK") {
// DEFAULT_ADMIN_ROLEは他の権限の管理をする権限です。
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
// PAUSER_ROLEはNFTの移転を停止する権限です。
_grantRole(PAUSER_ROLE, msg.sender);
// MINTER_ROLEはNFTアセットの発行を行う権限です。
_grantRole(MINTER_ROLE, msg.sender);
}
// NFTアセットの移転を制限する機能です。
// onlyRole(PAUSER_ROLE) = Pause権限を持つウォレットアドレスのみ実行可能です。
function pause() public onlyRole(PAUSER_ROLE) {
_pause();
}
// NFTアセットの移転の制限を解除する機能です。
// onlyRole(PAUSER_ROLE) = Pause権限を持つウォレットアドレスのみ実行可能です。
function unpause() public onlyRole(PAUSER_ROLE) {
_unpause();
}
// NFTアセットの新規発行をする機能です。
// onlyRole(MINTER_ROLE) = Mint権限を持つウォレットアドレスのみ実行可能です。
function safeMint(address to, uint256 tokenId) public onlyRole(MINTER_ROLE) {
_safeMint(to, tokenId);
}
Access Controlに対する個人的な見解
僕はOwnableを採用することが多いです。Roleの柔軟性の高さは魅力的ですが、複雑な権限管理は実装と運用を難しくするので、Ownableのシンプルな権限管理で運用できるように設計をすることが望ましいと考えています。
加えて、OpenseaはOwnableに設定しているウォレットアドレスにCollectionの編集権限を持たせる仕組みになっているため、Ownableを採用した方がメリットが高いと考えています。
また、複数のウォレットアドレスで管理したいとなった場合も、Gnosis Safeなどのマルチシグに対応したコントラクトウォレットで対応することが可能です。
このコントラクトウォレットの詳細については、別途記事化予定です。
この記事では主にコントラクトのAccess Controlを取り上げましたが、次はAccess Controlによって管理される、コントラクトのアップグレード、各種機能を詳しく解説していく予定です。次回もお楽しみに!
この記事が気に入ったらサポートをしてみませんか?