2024-03-29


Ubuntu Server 21.10でイーサリアムブロックチェーン【その1】 Ubuntu Server 21.10でイーサリアムブロックチェーン【その2】 Ubuntu Server 21.10でイーサリアムブロックチェーン【その3】 Ubuntu Server 21.10でイーサリアムブロックチェーン【その4】 Ubuntu Server 21.10でイーサリアムブロックチェーン【その5】
続きです。前回はブロックチェーン上で実行できる任意プログラム=コントラクトを作成しましたが、今回は決まった規格にのっとった=決まった変数・関数を実装した=コントラクトを作成してみます。
規格は沢山あって、今最も注目されているのがERC-721という規格です。この規格を満たしたコントラクトが発行したトークンがいわゆるNFTと言われているものです。
仮想通貨がいわゆる「お金そのもの」であるのに対して、トークンとは「商品引換券」であると言えます。ユーザーは仮想通貨を受け取れば換金できるし、トークンを受け取れば商品と交換できるということです。
ブロックチェーンにはこの仮想通貨とトークンが混在して流通しています。ERC-721を満たしたコントラクトを作成することで、非代替性のトークンを自動販売機のように自動で発行させられます。
1、テスト用のNFTデータを作って設置しておく
・NFTのメタ情報を書いたJSON
| JSON | nft_test_token_1.json | GitHub Source |
{
"description": "This is NFT token JSON for test only.",
"external_url": "https://www.servernote.net/NFT_Test/contents/nft_test_token.html",
"image": "https://www.servernote.net/NFT_Test/contents/nft_test_token_1.jpg",
"name": "nft_test_token"
}
external_urlはとりあえず紹介ページ、imageはNFTを写した写真などの紹介画像。
tokenURI = NFTのベースURI+トークンIDのJSONであるから、ここではトークンID番号1のNFT URIは
https://www.servernote.net/NFT_Test/contents/nft_test_token_1.json
という感じで用意した。
2、開発環境のインストール
ERC-721コントラクトを作成する用に既にSolidityで書かれたクラスライブラリがOpenZeppelinで提供されているので、通常それを継承して使います。Node.js、Truffle、Web3と組み合わせて作成します。
※2022/3/29追記※truffle developではdeployはできてもその後のmintでsendSignedTransactionがTransactionRevertedWithoutReasonErrorを吐いて強制ロールバックされる現象が当方環境で発生中
Node.jsのインストール
curl -L git.io/nodebrew | perl - setup export PATH=$HOME/.nodebrew/current/bin:$PATH nodebrew install-binary v16.7.0 nodebrew use v16.7.0
Truffle、Web3、OpenZeppelinのインストール
truffle, truffle-export-abiはコマンドとして使うので-gをつけてインストール。
truffle-export-abiは、分割コンパイルされた大量のSOL JSONオブジェクト群からコントラクト生成に必要なABI JSONを抜き出してくれるツール。
npm install -g truffle truffle-export-abi
ほかはライブラリとしてインストール。
npm install truffle-hdwallet-provider web3 openzeppelin-solidity
3、OpenZeppelinのクラスを継承してNFTコントラクトのソースを書く
Truffleプロジェクトを作成する。
mkdir NFT_Test cd NFT_Test truffle init
Migrationsは要らないので削除する。
rm contracts/Migrations.sol rm migrations/1_initial_migration.js
ソースコードcontracts/NFT_Test.solを作成する。
以下のような感じ。
| Shell | NFT_Test.sol | GitHub Source |
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol";
import "openzeppelin-solidity/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "openzeppelin-solidity/contracts/utils/Counters.sol";
contract NFT_Test is ERC721URIStorage {
using Strings for uint256;
using Counters for Counters.Counter;
Counters.Counter private _counter;
uint private _selling_price = 0.01 ether; // 販売価格
uint private _purchase_price = 0.005 ether; //買取価格
constructor () ERC721 ("NFT_Test", "NFT_TEST") {}
//msg = https://web3js.readthedocs.io/en/v1.7.1/web3-eth.html#sendtransaction
function mint () public payable returns (uint256) {
require(msg.value == _selling_price); //金銭受領確認
_counter.increment();
uint256 tokenId = _counter.current();
_mint(msg.sender, tokenId);
_setTokenURI(tokenId, string(abi.encodePacked("https://raw.githubusercontent.com/servernote/NFT_Test/master/contents/nft_test_token_", tokenId.toString(), ".json")));
return tokenId;
}
function burn (uint256 tokenId) public {
require(ownerOf(tokenId) == msg.sender); //所有者か確認
//NULL Address https://etherscan.io/address/0x000000000000000000000000000000000000dead
_transfer(msg.sender, 0x000000000000000000000000000000000000dEaD, tokenId);
address payable receiver = payable(msg.sender);
receiver.transfer(_purchase_price); //買取処理
}
}
このサンプルは、新規NFTトークン発行時に支払ってもらう価格を0.01ETHと定めて売り(発行し)、
もしそのトークンを返却するなら、0.005ETH(半額)で買い取ります(焼却します)というもの。
お客さんがmintをコールしてきたとき引数のmsg構造体のvalueで金銭0.01ether払っているかを確認。
OKならカウンタ変数をトークンIDとして、さきのベースURIと連結してtokenURIをセットしている。
買ったお客さんがこんなのいらんから返す=burnをコールしてきた場合、トークンをNULL Addressに捨てて、
お客さんに半額の0.005ETHをtransferして返している。
NULL Addressは以下に説明あり。
https://etherscan.io/address/0x000000000000000000000000000000000000dead
4、コンパイルしてABI JSONを作成する
プロジェクトトップディレクトリに戻ってコンパイルする
cd .. truffle compile
コンパイルが成功したら、ABI JSONを作成する。
truffle-export-abi notice: ABI extracted and output file wrote to: build/ABI.json
build/ABI.jsonにABI情報がまとめられる。このABIとコントラクトのアドレス情報があれば、外部ユーザーがコントラクトを生成できるのは【その5】の通りである。
5、デプロイ(ネットワーク登録)用のmigrateファイルを作成する
migrations/1_deploy_nft_test.jsを作成する。
| JavaScript | 1_deploy_nft_test.js | GitHub Source |
const NFT_Test = artifacts.require("NFT_Test");
module.exports = function(deployer) {
deployer.deploy(NFT_Test);
};
ただ単純にデプロイするだけのもの。
6、Truffle Developテストネットでデプロイする
ここからはTruffleのコンソールでコマンドライン作業をする。truffle developでテストネットワークを起動する。
truffle develop Truffle Develop started at http://127.0.0.1:9545/ Accounts: (0) 0xcf60f2f433767159cdeed84ebda8d0015740f07a (1) 0x20358b38df839b400ec1832a9d40f8f467f317f3 (2) 0xa75e537bc51fca0e5d79f1136e30e7520f6ace17 (3) 0xcd0f6b804ee1cdbcb2f724623670b26e71e36447 (4) 0x8bbced392bc97d0374adbeb411c5794183693064 (5) 0x5dc0231d58d36383ef5d2ffe81cca8fadf80bf12 (6) 0x14c69b7731d024a065cf8f5bd54fb1b43937c1fb (7) 0xd8026e8de5155d2ce381fc6d6fb4f1cf930acb54 (8) 0xc14622e385583ed974f65e2a9ff557e04c981d54 (9) 0x71ba3a6701ef479abb754456918067f9a8c202f5 Private Keys: (0) 41babfe2dbe7f29e09909f30b1dce1653766ca4863bbe918db4995d15fc778bd (1) c10301199c24066ad558f33a82e0f2fd3d6104d84a3bf90eebedb0af91d498f4 (2) f5f342aebe1b4c1c04884ff2f958a7726077beb1278adb664a701d869e792a5a (3) eb30e92f017d0213ca66a3b8b41999f29f036d935724a45a2c57748b7bdfe4d6 (4) 01489b2b16675af429fbf676398b073a0aafb64e37c7918968ca120d14a38368 (5) f53a9d00950b6fa4f3a9d7ab1f164c7e94fd54c8cf8dd1cc46aa6a93ba291a78 (6) 69373797c0a44b32b0a082048e3c2257e3829d936ee556eb7589b238d97b7fa2 (7) 792c76d94545b4b0ae8968722db5277e9f8ea138e52e1e5609c97b0183d85444 (8) 394b48ca297cc836d1206c57dae73b3cd585171429131e719e6612d39b8bfaaa (9) c157cdb6a7b951cd3078cc4f69593ee9474b047035b9e8b6958dbc8feafb783f Mnemonic: already kitten jacket child oil muffin remember nuclear claim nothing scorpion caught ?? Important ?? : This mnemonic was created for you by Truffle. It is not secure. Ensure you do not use it on production blockchains, or else you risk losing funds.
デプロイする
truffle(develop)> migrate Compiling your contracts... =========================== > Everything is up to date, there is nothing to compile. Starting migrations... ====================== > Network name: 'develop' > Network id: 5777 > Block gas limit: 6721975 (0x6691b7) 1_deploy_nft_test.js ==================== Deploying 'NFT_Test' -------------------- > transaction hash: 0x7a3e99ec1858b37e16ba10a726d67589862647a5e008c7f004ad2f4117422073 > Blocks: 0 Seconds: 0 > contract address: 0x985Bfa426BD58F5596878A32b6Fdf3E8adA01E4e > block number: 1 > block timestamp: 1648540572 > account: 0xcf60F2f433767159CdEed84ebdA8D0015740F07a > balance: 99.99108248725 > gas used: 2642226 (0x285132) > gas price: 3.375 gwei > value sent: 0 ETH > total cost: 0.00891751275 ETH > Saving artifacts ------------------------------------- > Total cost: 0.00891751275 ETH Summary ======= > Total deployments: 1 > Final cost: 0.00891751275 ETH
NFT_Testコントラクトがテストネットワークに無事登録された模様。
このコントラクトアドレスとさきのABI JSONがあればどこからでも生成できる。
アカウント0が生成したようなので、残り保有ETH数を見てみる。
web3.eth.getBalance("0xcf60f2f433767159cdeed84ebda8d0015740f07a");
'99991082487250000000'
7、Truffle Developテストネットでmintトランザクションを発行する
ではこのNFTを0.01ETH支払って買ってみる。そのままtruffle developコマンドラインで打っていく。
アカウント0がNFTをデプロイしたわけなので、別アカウントの5から購入を試みる。
CONTRACT_ABI = JSON.parse(fs.readFileSync("./build/ABI.json").toString());
CONTRACT_ADDRESS = "0x985Bfa426BD58F5596878A32b6Fdf3E8adA01E4e";
PUBLIC_KEY = "0x5dc0231d58d36383ef5d2ffe81cca8fadf80bf12";
PRIVATE_KEY = "f53a9d00950b6fa4f3a9d7ab1f164c7e94fd54c8cf8dd1cc46aa6a93ba291a78";
コントラクトをロードし、関数群の確認をする
contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS); contract.methods contract.methods.name().call()
mintをコールするためのトランザクションに渡す構造体を定義する。パラメータは以下に説明がある。
https://web3js.readthedocs.io/en/v1.7.1/web3-eth.html#sendtransaction
0.01ETH払いたい=10000000000000000Wei払いたい、になるのでvalueをweiで指定する。
web3.utils.toWei('0.01', 'ether');
'10000000000000000'
web3.utils.fromWei('10000000000000000', 'ether');
'0.01'
nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest");
msg = {from: PUBLIC_KEY, to: CONTRACT_ADDRESS, nonce: "1", gas: 53000, gasLimit: 53000, maxPriorityFeePerGas: 1000000000, maxFeePerGas: 1000000000, value: 10000000000000000, data: contract.methods.mint().encodeABI(),};
{
from: '0x5dc0231d58d36383ef5d2ffe81cca8fadf80bf12',
to: '0x985Bfa426BD58F5596878A32b6Fdf3E8adA01E4e',
nonce: '1',
gas: 53000,
gasLimit: 53000,
maxPriorityFeePerGas: 1000000000,
maxFeePerGas: 1000000000,
value: 10000000000000000,
data: '0x1249c58b'
}
トランザクションに署名する
signed_msg = await web3.eth.signTransaction(msg, PRIVATE_KEY); '0x02f87782053901843b9aca00843b9aca0082cf0894985bfa426bd58f5596878a32b6fdf3e8ada01e4e87038d7ea4c68000841249c58bc001a0df0ec23baf7bfc9bedfb68bf0c8d5faf8e6c67200abeccd046bb6c055f43dffea0697d01f3ca5282c053196694ded240504536aecb6406af269691ac614b18d8f2'
署名つきトランザクションを送信する
receipt = await web3.eth.sendSignedTransaction(signed_msg);
Uncaught Error: Transaction has been reverted by the EVM:
{
"transactionHash": "0x8d877970e1fe649df5490c3f96481650b477b26c2928459fdb7fc6e36568a6b7",
"transactionIndex": 0,
"blockNumber": 3,
"blockHash": "0x4dda0a1f262df9867f1d93148f0502047b73b9b6da2970078fda17a8d6d0a7db",
"from": "0x5dc0231d58d36383ef5d2ffe81cca8fadf80bf12",
"to": "0x985bfa426bd58f5596878a32b6fdf3e8ada01e4e",
"cumulativeGasUsed": 23350,
"gasUsed": 23350,
"contractAddress": null,
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": false,
"effectiveGasPrice": "0x3b9aca00",
"type": "0x2"
}
at Object.TransactionRevertedWithoutReasonError (/home/hogeuser/.nodebrew/node/v16.7.0/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-helpers/lib/errors.js:98:1)
at Object.TransactionError (/home/hogeuser/.nodebrew/node/v16.7.0/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-helpers/lib/errors.js:87:1) {
receipt: {
transactionHash: '0x8d877970e1fe649df5490c3f96481650b477b26c2928459fdb7fc6e36568a6b7',
transactionIndex: 0,
blockNumber: 3,
blockHash: '0x4dda0a1f262df9867f1d93148f0502047b73b9b6da2970078fda17a8d6d0a7db',
from: '0x5dc0231d58d36383ef5d2ffe81cca8fadf80bf12',
to: '0x985bfa426bd58f5596878a32b6fdf3e8ada01e4e',
cumulativeGasUsed: 23350,
gasUsed: 23350,
contractAddress: null,
logs: [],
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
status: false,
effectiveGasPrice: '0x3b9aca00',
type: '0x2'
}
}
自分の環境ではなにやらエラーが出て解決しないので、次回以降、truffleはいったんやめてオンラインIDEのRemixでやり直そうと思います。。
Ubuntu Server 21.10でイーサリアムブロックチェーン【その7】
※本記事内容の無断転載を禁じます。
ご連絡は以下アドレスまでお願いします★
Wav2Lipのオープンソース版を改造して外部から呼べるAPI化する
Wav2Lipのオープンソース版で静止画の口元のみを動かして喋らせる
【iOS】アプリアイコン・ロゴ画像の作成・設定方法
オープンソースリップシンクエンジンSadTalkerをAPI化してアプリから呼ぶ【2】
オープンソースリップシンクエンジンSadTalkerをAPI化してアプリから呼ぶ【1】
【Xcode】iPhone is not available because it is unpairedの対処法
【Let's Encrypt】Failed authorization procedure 503の対処法
【Debian】古いバージョンでapt updateしたら404 not foundでエラーになる場合
ファイアウォール内部のWindows11 PCにmacOS Sequoiaからリモートデスクトップする
進研ゼミチャレンジタッチをAndroid端末化する
Windows11+WSL2でUbuntuを使う【2】ブリッジ接続+固定IPの設定
VirtualBoxの仮想マシンをWindows起動時に自動起動し終了時に自動サスペンドする
【C/C++】小数点以下の切り捨て・切り上げ・四捨五入
GitLabにHTTPS経由でリポジトリをクローン&読み書きを行う
タスクスケジューラで変更を適用できません。ユーザーアカウントが不明であるか、パスワードが正しくないか、またはユーザーアカウントにタスクを変更する許可がありません。と出た
Intel MacBookAir2020にWindows10→Windows11をインストールする
Linuxでtar.xz形式のファイルを解凍する
Windows11のコマンドプロンプトでテキストをコピーする