Sausage Erectos X Polygon NFTスマートコントラクト——ERC-721とメタデータ設計
Polygon NFTスマートコントラクト──ERC-721の設計と実装
Sausage ErectoのカスタムフィギュアをNFTとして発行する仕組みを、Polygon(旧Matic)上に構築した。設計判断、実装、デプロイ、そしてテストの全過程を記録する。
ERC-721 vs ERC-1155の設計判断
NFTの規格には主にERC-721とERC-1155がある。
ERC-721: 各トークンが一意。1つのコントラクトで1種類のNFT。
ERC-1155: 複数種類のトークンを1つのコントラクトで管理。
同一デザインの複数発行にも対応。
Sausage Erectosでは、各フィギュアが完全にユニークなカスタムデザインであるため、ERC-721を選択した。ERC-1155の「同一トークンの複数発行」機能は不要であり、ERC-721のほうがマーケットプレイス(OpenSea等)との互換性が高い。
OpenZeppelinのコントラクト継承構造
セキュリティを最優先に、OpenZeppelinのライブラリを利用した。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract SausageErectos is ERC721, ERC721URIStorage, Ownable {
uint256 private _nextTokenId;
constructor() ERC721("SausageErectos", "SEREC") Ownable(msg.sender) {}
function mint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
// Override required by Solidity
function tokenURI(uint256 tokenId)
public view override(ERC721, ERC721URIStorage) returns (string memory) {
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public view override(ERC721, ERC721URIStorage) returns (bool) {
return super.supportsInterface(interfaceId);
}
}
Ownableにより、mintは管理者(コントラクトオーナー)のみが実行可能。ユーザーが注文し決済が完了した後、サーバーサイドからmintを実行する設計だ。
Hardhatでのデプロイスクリプト
// scripts/deploy.js
const { ethers } = require("hardhat");
async function main() {
const SausageErectos = await ethers.getContractFactory("SausageErectos");
const contract = await SausageErectos.deploy();
await contract.waitForDeployment();
console.log("Deployed to:", await contract.getAddress());
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Amoyテストネットでのテスト
Polygon Amoyテストネットでのテスト方法:
hardhat.config.jsにAmoyネットワークを追加- Alchemy/InfuraでAmoy用のRPC URLを取得
- テスト用POLをfaucetから取得
npx hardhat run scripts/deploy.js --network amoyでデプロイ- Polygonscan(Amoy)でコントラクトを検証
// hardhat.config.js
module.exports = {
networks: {
amoy: {
url: process.env.AMOY_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
},
},
};
メタデータのIPFS保存
NFTのメタデータはブロックチェーン上ではなく、IPFSに保存する。Pinataを利用してピン留め(永続化)する。
const pinataSDK = require('@pinata/sdk');
const pinata = new pinataSDK(process.env.PINATA_KEY, process.env.PINATA_SECRET);
async function uploadMetadata(figureData) {
// 画像をIPFSにアップロード
const imgResult = await pinata.pinFileToIPFS(figureData.image);
// メタデータJSON
const metadata = {
name: `Sausage Erectos #${figureData.id}`,
description: figureData.description,
image: `ipfs://${imgResult.IpfsHash}`,
attributes: [
{ trait_type: "Height", value: figureData.height },
{ trait_type: "Pose", value: figureData.poseName },
{ trait_type: "Color", value: figureData.color },
],
};
const metaResult = await pinata.pinJSONToIPFS(metadata);
return `ipfs://${metaResult.IpfsHash}`;
}
ガス代の実測値
Polygon上でのガス代は非常に安価だ。実際の測定結果:
コントラクトデプロイ: 約0.01 POL(約¥1)
1回のmint: 約0.002 POL(約¥0.2)
メタデータ更新: 約0.001 POL(約¥0.1)
Ethereumメインネットでは同じ操作に数千円かかることを考えると、Polygonの低コストは個人プロジェクトにとって決定的な利点だ。
NFTの技術的価値は、トークンそのものではなく、「デジタルな所有権」という概念を実装可能にしたことにある。Sausage Erectosの各フィギュアが、ブロックチェーン上で唯一無二であると証明されること。それは、創作物への敬意の技術的表現だ。