やっと初めてのDAppを公開するまでに至ったので、躓いたところを含めて記録しておこうかなと思います。ERC721を使ってみたので、それにもちょっと触れたいと思います。
Dapp概要
自分の所有物にIDをつけてブロックチェーン上に記録するアプリです。
仮に盗まれても所有者と所有物が結びついてないので、足がつくとかそんな感じです。
組み込んだ機能は以下です。
- プロダクトの登録(名前とプロダクトカテゴリー)
- プロダクトの削除
- プロダクトの受け渡し
- プロダクトの受け入れ
RopstenにデプロイしているのでMetamaskでRopstenネットワークに繋げて使ってみてください。メインネットのアカウントからでは繋がらないとは思うのですが、この辺はよく分からないので気をつけてください。メインのアカウントからEther送って消えてしまう可能性もあるかもしれないので。
URL: https://akira-19.github.io/ProductOwnership/#
Github: https://github.com/akira-19/ProductOwnership
使ったツール
一般的にEthereumのDApps開発で使うツールを使いました。
- Solidity version:0.5.0(開発言語)
- Truffle version:5.0.1(Solidity用フレームワーク)
- Web3.js version:1.0.0(EthereumのJavaScript API)
- Open Zeppelin(安全なスマートコントラクトを作るためのライブラリ)
- Github pages(サイトの公開)
- Remix(ブラウザIDE、コントラクトのデバッグに使いました)
コントラクト
ERC721を使って、各プロダクトをNFTとして登録しました。
基本的にはERC721のファンクションを使ってますが、いくつか付け足しました。
struct product {
uint productId;
string productName;
string productCategory;
}
とりあえず、TokenのIDとしてproductIdとプロダクト名とカテゴリーだけ構造体の中身に入れました。本当は他の要素も色々入れたかったのですが、結局やることはあまり変わらないのでシンプルにしました。
IDもシンプルにプロダクトのインクリメントのIDをハッシュ化したものにしました。
function _generateRondomNumber(uint _num) private pure returns (uint) {
return uint(keccak256(abi.encode(_num)));
}
あとは、これが主流か分かりませんが、ERC721.sol自体に直接ファンクションを追加しました。
ネットで調べたら他のERC721のファンクションを破壊する様なものでなければファンクション追加しても別に構へんでーみたいな投稿を見つけたので。
// 元々ERC721に組み込まれてるfunction
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _tokenOwner[tokenId];
require(owner != address(0));
return owner;
}
// 追加したfunction
function ownerOfProduct(uint tokenId) public view returns (address) {
address owner = _tokenOwner[tokenId];
return owner;
}
上の例ではownerのアドレスがaddress(0)
でも返して欲しかったので、新しいfunction作りました。
_tokenOwner[tokenId]
というトークンのIDを入れるオーナーのアドレスを返すマッピングがprivateでERC721.sol内部からしかアクセスできないので、仕方なくこうしました。新しく自分で定義するのも無駄な気がしたので。
デプロイ
これは色々情報が調べれば出てくるので特に説明しません。
const HDWalletProvider = require("truffle-hdwallet-provider");
const mnemonic ="Metamaskの自分のアカウントのニーモニック(自分の場合はGanacheの使いました)。英単語10語くらいで構成されるパスワードを忘れた時に入力するやつです。";
const infura_url = "INFURAで新しいプロジェクトを登録して、ENDPOINTをRopstenにした時に出てくるURLみたいなもの"
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// for more about customizing your Truffle configuration!
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*", // Match any network id
gas: 4500000
},
ropsten:{
provider: function(){
return new HDWalletProvider(mnemonic, infura_url)
},
network_id:3,
gas: 4700000
}
}
};
フォルダ構成
Github pagesでサイトを公開するのにdocsというフォルダに必要なhtml等を入れなければいけないっぽいうので下記の様なフォルダ、ファイル構成になっています。
docs/
├ css/
├ images/
├ js/
├ index.html
├ ProductOwnerhisp.json
ProductOwnerhisp.jsonはコントラクトをコンパイルした後にできるbuildフォルダからコピーして貼り付けました。
これ以外に関してはTruffleチュートリアルのペットショップをアンボックスしたものを使いましたので、フォルダ構成はそれと同じです。
課題
- 使ってないイベントとかコントラクトに入っててデプロイ時に無駄なお金がかかってしまった。
- プロダクトをGiveした後にGiveボタンとDeleteボタンを無効化したけど、トランザクションが処理されるまでは無効化されないので、修正したい。
- テストを書いてないので、書きたい。
- Javascriptの勉強不足で非同期処理の部分のネストが深くなったりしてたので、綺麗なコードをかける様にしたい。
- 何がTruffleのメソッドで何がWeb3.jsのメソッドでみたいな感じでよくわかってないままコピペしたのも結構あるので、その辺をちゃんと理解したい。
まとめ
実際作ると分からないところが色々出てくるのと、更新が異常に早い分野なので、調べても去年の情報で既に古いみたいのがとても出てきて、結局自分でどうにかしなければならないことも結構あったので勉強になりました。こうした方が良いとかあったら教えていただけるととても嬉しいです。