奇跡体験!アンビリバボーbotのアレをフルオンチェーンNFTとして再現する
前置き
この記事は特定のNFTや仮想通貨の購買を促進する記事ではありません。
奇跡体験!アンビリバボーbotのアレとは?
こういうアレですね。似たようなのでファルコン・パンチとか。
この1つ1つの乱数生成結果をフルオンチェーンNFTとして作ったらなんか面白いんじゃないか?と思ったので今回実装しました。
実装
環境
- hardhat
- solidity 0.8.0系
フルオンチェーンとは
フルオンチェーンNFTの概要と作る簡単な方法についてはこちらがわかりやすくまとまっています。
今回はこの生成を自動化する部分も作ります。
環境構築
hardhatのget-startedに書かれている手順に沿ってプロジェクトを作ります。
npm install --save-dev hardhat
npx hardhat
openzeppelinのライブラリもインストールします
(ERC721と便利クラス用)
npm i @openzeppelin/contracts
Base64クラスのライブラリをインストール
(フルオンチェーン化するために必要)
npm i base64-sol@1.0.1
// Kisekitaiken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "base64-sol/base64.sol";
contract Kisekitaiken is ERC721 {
using Counters for Counters.Counter;
Counters.Counter private _tokenCounter;
struct KisekitaikenToken {
string prefixText;
string suffixText;
}
KisekitaikenToken[] public tokens;
// 日本語の文字列を埋め込むには unicode"" としなければコンパイルエラーになる
string[] private _prefixWord = [
unicode"奇",
unicode"跡",
unicode"体",
unicode"験"
];
string[] private _suffixWord = [
unicode"ア",
unicode"ン",
unicode"ビ",
unicode"リ",
unicode"バ",
unicode"ボ"
];
// 疑似乱数用の使い捨て変数
uint256 randNonce;
constructor() ERC721("Kisekitaiken", "KSK") {}
function mint() public {
_tokenCounter.increment();
uint256 newItemId = _tokenCounter.current();
_safeMint(msg.sender, newItemId);
// 奇跡体験 と アンビリバボ をランダム生成する ---->>>>
string memory prefixText;
uint256 prefixTextLength = 4;
for (uint256 i = 0; i < prefixTextLength; i++) {
uint256 random = randMod(prefixTextLength);
prefixText = string(
abi.encodePacked(prefixText, _prefixWord[random])
);
}
string memory suffixText;
uint256 suffixTextLength = 6;
for (uint256 i = 0; i < suffixTextLength; i++) {
uint256 random = randMod(suffixTextLength);
suffixText = string(
abi.encodePacked(suffixText, _suffixWord[random])
);
}
// <<<<----
tokens.push(KisekitaikenToken(prefixText, suffixText));
}
function mintBatch(uint256 _mintCount) public {
for (uint256 i = 0; i < _mintCount; i++) {
mint();
}
}
// 乱数生成用
function randMod(uint256 _modulus) internal returns (uint256) {
randNonce++;
return
uint256(
keccak256(
abi.encodePacked(block.timestamp, msg.sender, randNonce)
)
) % _modulus;
}
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
require(
_exists(tokenId),
"ERC721Metadata: URI query for nonexistent token"
);
KisekitaikenToken memory token = tokens[tokenId - 1];
string memory svg = getSVG(token);
bytes memory json = abi.encodePacked(
'{"name": "',
token.prefixText,
unicode"!",
token.suffixText,
unicode"ー",
'", "description": "kisekitaiken", "image": "data:image/svg+xml;base64,',
Base64.encode(bytes(svg)),
'"}'
);
string memory _tokenURI = string(
abi.encodePacked(
"data:application/json;base64,",
Base64.encode(json)
)
);
return _tokenURI;
}
// OpenSea等で表示される実体
function getSVG(KisekitaikenToken memory token)
private
pure
returns (string memory)
{
return
string(
abi.encodePacked(
'<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350">',
"<style>text{fill:black;font-family:serif;}</style>",
'<rect width="100%" height="100%" fill="#a9ceec" />',
'<text x="10%" y="45%" font-size="50px">',
token.prefixText,
unicode"!",
"</text>",
'<text x="10%" y="65%" font-size="40px">',
token.suffixText,
unicode"ー",
"</text>",
"</svg>"
)
);
}
}
技術的に難しいことはしていなく、ランダム生成用のプールからランダムに文字列をピックし、保存。
読み出すときはsvgを生成してbase64エンコードして返すような流れです。
デプロイ用のスクリプト作成
- deploy.jsを作成する
// scripts/deploy.js
const hre = require("hardhat");
async function main() {
const KisekitaikenFactory = await hre.ethers.getContractFactory(
"Kisekitaiken"
);
const kisekitaiken = await KisekitaikenFactory.deploy();
await kisekitaiken.deployed();
// gasLimitがデフォルトだとガスリミットエラーによりトランザクションが失敗してしまうため、上限をあげています。
await kisekitaiken.mintBatch(50, { gasLimit: 10000000 });
console.log("kisekitaiken deployed to:", kisekitaiken.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
デプロイを実行する
npx hardhat run scripts/deploy.js --network rinkeby
デプロイのやり方↓
確認
今回のサンプル↓
無事svgが表示されているのを確認できました
まとめ
- 奇跡体験アンビリバボーbotのようなランダム文字列生成フルオンチェーンNFTを作った
- hardhatを用いてrinkebyテストネットにデプロイした
最後に
今回はネタ寄りの記事でしたが、アイデア次第ではいままでにないNFTを作れるかも?
また、スマートコントラクトでの乱数生成は脆弱性を生じやすいです。
今回使った乱数生成を実際のプロダクトで流用するかどうかは慎重に判断してください。
参考リンク
今回のフルオンチェーンのソースコードは こちらのサイトを参考に改変しました。ありがとうございます🙏
etc
Solidityについてワイワイ学ぶコミュニティ「solidity-jp」を作りました!
いまから学んでみたい/学習中だけどの日本語の情報が少ない/古くて時間がかかっているという方、一緒に学びましょう〜!!
また、TwitterにてSolidityについて技術的な部分を発信しています。良ければフォローお願いします!
Discussion