- ウィザードを使って契約を開発
https://docs.openzeppelin.com/contracts/4.x/wizard
Mintable : NFT をミント
Auto increment Ids : id 自動増加
Burnable : NFT を焼却
Pausable : NFT の転送を一時停止
Enumerable : 総発行量を確認
URI storage : NFT の URI
Ownable: 所有権管理、1 人の管理者
Roles: 所有権管理、複数の管理者
Transparent: 透明なプロキシ
UUPS: プロキシ契約の衝突問題を解決
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/draft-ERC721VotesUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
/// @custom:security-contact [email protected]
contract Dragon is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable, PausableUpgradeable, OwnableUpgradeable, ERC721BurnableUpgradeable, EIP712Upgradeable, ERC721VotesUpgradeable {
using CountersUpgradeable for CountersUpgradeable.Counter;
CountersUpgradeable.Counter private _tokenIdCounter;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() initializer public {
__ERC721_init("Dragon", "Drag");
__ERC721Enumerable_init();
__ERC721URIStorage_init();
__Pausable_init();
__Ownable_init();
__ERC721Burnable_init();
__EIP712_init("Dragon", "1");
__ERC721Votes_init();
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
internal
whenNotPaused
override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
{
super._beforeTokenTransfer(from, to, tokenId, batchSize);
}
// 以下の関数はSolidityによって必要なオーバーライドです。
function _afterTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
internal
override(ERC721Upgradeable, ERC721VotesUpgradeable)
{
super._afterTokenTransfer(from, to, tokenId, batchSize);
}
function _burn(uint256 tokenId)
internal
override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
{
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
- remix https://remix.ethereum.org/
- safeMint の onlyOwner を削除し、誰でもミントできるようにする
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
function safeMint(address to, string memory uri) public {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
- 最大発行量を定義する
uint256 MAX = 1000;
function safeMint(address to, string memory uri) public {
require(_tokenIdCounter.current() <= MAX, "exceed");
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
- メタデータを IPFS にアップロード
https://console.filebase.com/
- メタデータの形式は JSON
{
"description": "ドラゴン",
"external_url": "https://openseacreatures.io/3",
"image": "https://ipfs.filebase.io/ipfs/QmewcWVm6zmnCEwDzZSanrzdFNdzCF8X4Tfo5Qi87Qdhrf",
"name": "ドラゴン",
"attributes": [
{
"trait_type": "ベース",
"value": "ヒトデ"
},
{
"trait_type": "目",
"value": "大きい"
},
{
"trait_type": "口",
"value": "驚いた"
},
{
"trait_type": "レベル",
"value": 5
}
]
}
7. コードを修正
string uri="ipfs://QmPHcbAwnaGkGm939xRzhwXuYVe12iqCKezDccJcmqf8p5";
function safeMint(address to) public {
require(_tokenIdCounter.current() <= MAX, "exceed");
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
- Alchemy アカウントを作成し、Sepolia テストネットを選択
https://dashboard.alchemy.com/
Sepolia ファ aucet https://sepoliafaucet.com/):
- MetaMask に Alchemy ノードを追加
- コンパイルして最適化を有効にする
- デプロイ
- テスト OpenSea で確認する https://testnets.opensea.io/