MetaMaskとWeb3.js、スマートコントラクト(solidity)とReact.jsから構築されています。
土台部分については、下記コマンドにより生成
truffle unbox react
solcのバージョン情報等については、truffle-config.jsを参照ください。
- 資金調達関連機能: 実装済み
- NFT関連機能: NFTコントラクトは実装ずみ(Collection表示機能はバグがあるので今後修正)
- マルチシグ関連機能: マルチシグウォレット生成&署名機能実装済み
- ERC020規格のトークン発行関連機能
- ゲーム関連機能
https://mashharuki.github.io/fundraiser-dapp/
名称 | 内容 |
---|---|
truffle | スマートコントラクト開発用のフレームワークとして使用している。テストやデプロイを行う。 |
React | フロントエンド側の開発をするために使用している。 |
Material-UI(MUI) | React向けのUIコンポーネントライブラリ |
Open Zeppelin | solidity用のフレームワーク |
root/
┣ client/ : フロントエンド側のディレクトリ
| ┣ src/ :App.jsなどのアプリを構成するファイルを格納するディレクトリ
| | ┣ contracts/ : コンパイル済みのコントラクトjsonファイルを格納する
| | ┣ fundraiser/ : 資金調達機能関連のコンポーネント用ディレクトリ
| | ┣ mytoken/ : ERC20トークン関連のコンポーネント用ディレクトリ
| | ┣ nft/ : NFT関連のコンポーネント用ディレクトリ
| | └ wallet/ : ウォレット関連のコンポーネント用ディレクトリ
| ┣ public/ : CSSファイルなど全体を通して使用するファイルを格納するディレクトリ
| ┣ node_modules/ : npmによってインストールするモジュール群を格納するディレクトリ
| ┣ package.json: npm用の設定ファイル
| └ package-lock.json: npm installによってインストールされたモジュールの情報を記載したファイル
┣ contracts/ : スマートコントラクト(バックエンド側)のディレクトリ
| ┣ common/: 共通で使用するスマートコントラクトを格納したディレクトリ
| ┣ ERC20/: ERC20トークン関連のコントラクトを格納したディレクトリ
| ┣ safeContracts/: マルチシグウォレット関連のコントラクトを格納したディレクトリ
| | ┣ base/ : ベースとなるコントラクトを格納したディレクトリ
| | ┣ common/ : 共通機能コントラクトを格納したディレクトリ
| | ┣ external/ : 上限値チェック用のコントラクトを格納したディレクトリ
| | ┣ interfaces/ : インターフェース関連のコントラクト格納ディレクトリ
| | └ proxies/ : proxyコントラクト関連の格納ディレクトリ
| └ oracles/ : オラクル処理関連のコントラクト格納ディレクトリ
┣ develop/ : 実装途中のファイルを格納するディレクトリ
┣ migrations/ : デプロイ用のJsファイルを格納するディレクトリ
┣ node_modules/ : npmによってインストールするモジュール群を格納するディレクトリ
┣ test/ : スマートコントラクトのテストコードを格納するディレクトリ
┣ img/ : 画像データを格納するディレクトリ
┣ truffle-config.js: truffle用の設定ファイル
┣ README.md: リポジトリの各種説明を記載
┣ README2.md: オラクル処理の各種説明を記載
┣ LICENSE: ライセンス情報を記載
└ package-lock.json: npm installによってインストールされたモジュールの情報を記載したファイル
呼び出したコントラクトに存在しない関数を指定した場合に呼ばれる関数
proxy patternではこの関数をうまく利用する。
コード内にアセンブリ言語を使用できるインラインアセンブリがある。
データサイズを意識することでガスを節約することが可能になります。
assembly {
let result := add(x, y)
mstore(0x0, result)
return(0x0, 32)
}
スマートコントラクトは一度デプロイすると変更できないが、
上記のfallback関数の考え方を応用し、コントラクトを
proxyコントラクトとlogicコントラクトに分ける開発方法のこと。
これにより一度デプロイした後でも呼び出すコントラクトを更新することが
可能となる。
solidityではコントラクトから他のコントラクトの関数を実行する際に、Method IDをFunction Selectorに渡して実行する。
関数名と引数の型の文字列をkeccak256でハッシュ化し、頭の4byteを取ったものがMethod IDになります。
算出例:
bytes4(keccak256("setNum(uint256)") = 0xcd16ecbf
callまたはdelegatecallで別のコントラクトを呼び出した時に使用するデータ領域です。
calldataは、Method IDと引数(32bytes)を合わせたデータのこと。
スマートコントラクトは、基本的には一度デプロイしたら後から内容を変更することはできない。
(proxy patternの用に擬似的に変更することは可能。)
そのため、デプロイ前にテストや専門企業に監査を実施してもらうことがで非常に重要となる。
また、OpenZeppelinが発表しているような安全なライブラリを使って開発することが重要となる。
<a href="https://ecouffes.github.io/smart-contract-best-practices/security_tools/">Ethereum Smart Contract Best Practices</a>などのベストプラクティスなどを
参考にして開発を進めると良いと考えている。
ABIとは、バイナリーファイル(実行形式ファイル)へのアクセスに対して互換性を与えるもの。
スマートコントラクトは、EVM上で実行可能なバイナリーファイルとなっており、コンパイル時に生成されるJSONファイルにはReact.jsなどを利用して構築されたクライアントアプリからWeb3.jsなどのライブラリをTransactionを介してやり取りを行う方法について互換性を与えるための定義が記述されている。この中身は、コントラクトの機能を外部から呼び出すための標準化された方法が記述されており、ether.jsやweb3.jsなどのライブラリがABIに準拠したデータ構造で呼び出しを行うことで。ブロックチェーン上にあるコントラクトの任意の機能にアクセスすることができる。
solidityなどの高級言語で記述されたソースコードをイーサリアム上でも実行できるバイトコードに変換する翻訳機のこと。
コントラクトコードはこのEVMを介して実行される。(逆にEVM互換性があればイーサリアムでなくともsolidityで記述したスマートコントラクトをマイグレーションすることができる。Astar Network等)
npm i
npm install -g mocha
npm install -g chai
npm install @openzeppelin/contracts
npm install --save react-router-dom
npm i @chainlink/contracts
npm update react@17.0.2 react-dom@17.0.2 react-router-dom@6.0.1
truffle test
うまくいけば下記の様に全てのテスト項目がpassされる。(2022年5月7日時点)
※ テスト項目については、不足している箇所がある。
Contract: FundraiserFactory: deployment
✓ has been deployde (64ms)
Contract: FundraiserFactory: createfundraiser
✓ increments the fundraisersCount (203ms)
✓ emits the FundraiserCreated event (148ms)
Contract: FundraiserFactory: fundraisers
when fundraisers collection is empty
✓ returns an empty collection (63ms)
varying limits
✓ returns 10 results when limit requested is 10
- returns 20 results when limit requested is 20
- returns 20 results when limit requested is 30
varying offset
✓ contains the fundraiser with the appropriate offset
- contains the fundraiser with the appropriate offset
boundary conditions
✓ raises out of bounds error (156ms)
✓ adjusts return size to prevent out of bounds error
Contract: Fundraiser
initialization
✓ gets the beneficiary name
✓ gets the beneficiary url
✓ gets the beneficiary imageURL
✓ gets the beneficiary description
✓ gets the beneficiary description
✓ gets the owner
setBeneficiary
✓ updated beneficiary when called by owner account (46ms)
✓ throws an error when called from a non-owner account
making donations
✓ increases myDonationsCount (57ms)
✓ include donation in myDonations (43ms)
✓ increase the totalDonations amount (69ms)
✓ increase donationsCount (69ms)
✓ emit the DonationReceived event (48ms)
withdrawing funds
✓ transfers balance to beneficiary (46ms)
✓ emit Withdraw event
withdrawing funds
access controls
✓ throws an error when called from a non-owner account
✓ permits the owner to call the function
fallback function
✓ increase the totalDonations amount
✓ increase donationsCount (57ms)
Contract: MultiSigFactory: deployment
✓ has been deployde (64ms)
Contract: MultiSigFactory: create MultiSigWallet
✓ increments the mutliSigWalletsCount
Contract: MyTokenFactory: deployment
✓ has been deployde (63ms)
Contract: MyTokenFactory: create MyToken
✓ increments the MyTokenCount (225ms)
Contract: MyTokenFactory: MyTokens
when MyTokens collection is empty
✓ returns an empty collection (175ms)
varying limits
✓ returns 10 results when limit requested is 10
✓ returns 20 results when limit requested is 20
✓ returns 20 results when limit requested is 30
varying offset
✓ contains MyToken with the appropriate offset
✓ contains MyToken with the appropriate offset (40ms)
boundary conditions
✓ raises out of bounds error
✓ adjusts return size to prevent out of bounds error
Contract: NFTFactory: deployment
✓ has been deployde (67ms)
Contract: NFTFactory: create NFT
✓ increments the NFTCount (325ms)
Contract: NFTFactory: nfts
when NFTs collection is empty
✓ returns an empty collection (620ms)
varying limits
✓ returns 10 results when limit requested is 10
- returns 20 results when limit requested is 20
- returns 20 results when limit requested is 30
varying offset
✓ contains NFT with the appropriate offset (268ms)
- contains NFT with the appropriate offset
boundary conditions
✓ raises out of bounds error
- adjusts return size to prevent out of bounds error
Contract: NFT test
initialization
✓ gets the NFT name
✓ gets the NFT symbol (163ms)
✓ gets the NFT imageURL
mint test
✓ mint NFT (568ms)
indexing
NFT lists: [
'data:application/json;base64,eyJuYW1lIjoiTWFzaCIsImRlc2NyaXB0aW9uIjoiVGhpcyBORlQgaXMgYSB0ZXN0ISEiLCJVUkwiOiJodHRwczovL3BsYWNla2l0dGVuLmNvbS82MDAvMzUwIn0=',
'data:application/json;base64,eyJuYW1lIjoiTWFzaCIsImRlc2NyaXB0aW9uIjoiVGhpcyBORlQgaXMgYSB0ZXN0ISEiLCJVUkwiOiJodHRwczovL3BsYWNla2l0dGVuLmNvbS82MDAvMzUwIn0=',
'data:application/json;base64,eyJuYW1lIjoiTWFzaCIsImRlc2NyaXB0aW9uIjoiVGhpcyBORlQgaXMgYSB0ZXN0ISEiLCJVUkwiOiJodHRwczovL3BsYWNla2l0dGVuLmNvbS82MDAvMzUwIn0='
]
✓ lists NFT (1050ms)
50 passing (44s)
7 pending
truffle compile
truffle migrate --network develop
なお、マイグレーションしたいファイルを指定する場合は下記のように打ち込む
※ M1 チップ搭載のMacBookで実行する場合は、sudoをつけて実行すること
truffle migrate --f 2 --to 3
(client/contracts/ 配下に「コントラクト名.json」ができていれば成功。)
truffle compile --network rinkeby
truffle migrate --network rinkeby
(client/contracts/ 配下に「コントラクト名.json」ができていれば成功。)
- ニーモニックコード用の環境変数を.envファイルに追記する。
MNEMONIC="<自分のMetaMaskのニーモニックフレーズ>"
- INFURAのプロジェクトID用の環境変数を.envファイルに追記する。
INFURA_PROJECT_ID=<自分のINFURAプロジェクトID>
- truffle-config.jsにrinkeby用のネットワーク設定を追加する。
// 必要なモジュールをインポート
require('dotenv').config();
const HDWalletProvider = require('truffle-hdwallet-provider');
// Rinkeby用
rinkeby: {
provider: () => {
const mnenonic = process.env.MNEMONIC;
const project_id = process.env.INFURA_PROJECT_ID;
return new HDWalletProvider(
mnenonic,
`https://rinkeby.infura.io/v3/${project_id}`
);
},
network_id: 4,
},
@openzeppelin/contracts配下の「ERC20.sol」ファイルに少し修正が必要。
修正点1
// 変数を追加
uint8 private _decimals;
修正点2
// コンストラクターの定義を変更する。
constructor(string memory name_, string memory symbol_, uint8 decimals_) {
_name = name_;
_symbol = symbol_;
// 桁数を設定する。
_setupDecimal(decimals_);
}
修正点3
// 桁数を変更するためのメソッドを追加する。
/**
* 小数点桁数を設定するための関数
* @param value 設定する桁数
*/
function _setupDecimal(uint8 value) internal {
_decimals = value;
}
「backend/hardhat」配下で下記コマンドを実行
npx hardhat compile
npx hardhat run scripts/deploy.js --network rinkeby
デプロイに成功する下記のようなものがコンソール上に表示される
FundraiserFactory deployed to: 0x174387193854254F0d7bB7808EF36D6FeeffCdbf
- FundraiserFactory: 0x174387193854254F0d7bB7808EF36D6FeeffCdbf
- node.jsをインストールしておくこと
- ganacheをインストールして事前に起動しておくこと
- ganacheを使ってプライベートネット上にスマートコントラクトをデプロイすること
- デプロイしたコントラクトの情報が記載されているJSONファイルをclient/contractsフォルダ内にコピペする。(※重要)
- MetaMaskをインストールしておくこと
- プライベートネットの秘密鍵をMetaMaskにインポートしておくこと
準備ができたら、clientフォルダで下記コマンドを実行する。
npm run start
http:localhost:3000/ にアクセスすると最初のページが表示されている。
準備ができたら、backendディレクトリ直下で下記コマンドを実行する。
node server.js
http:localhost:3001/ にアクセスすると最初のページが表示されている。
buildしたい場合は、次のコマンドを打つこと!
npm run build
うまくいっていれば、client/buildディレクトリ配下にビルドの成果物が出力されている。
-
Error: VM Exception while processing transaction: revert
コントラクトファイルなどのrequire()文の条件に反しているなどが考えられるため、入力したアドレスなどを見直す。もしくは、require()文の条件を見直すこと。 -
Error: VM Exception while processing transaction: invalid opcode
存在しない関数などを呼びだそうとしている時に発生するため、スペルや大文字小文字を確認する。 -
Error: VM Exception while processing transaction: out of gas
gasが足りない時に発生するため、設定を見直すこと。send()メソッドを呼び出すときに、明示的にgasの量を指定すると治る。
※SafeContractのエラー詳細についてはこちらを参照。
Error: Could not find artifacts for Migrations from any sources
コントラクトデプロイ時にコントラクトが見つからないと行っている・・
2022年4月19日現在、rinkebyとgoerliにデプロイしようとするとこのエラーが出る。
ローカルのブロックチェーンにデプロイする時には問題なくできるが原因はわからず。。
9_deploy_myToken_factory.js
===========================
Deploying 'MyTokenFactory'
--------------------------
> transaction hash: 0x07f51fc1acc33c549076a841e090e5b7f9fa137a7864db028aa09e244e69b651
> Blocks: 0 Seconds: 5
> contract address: 0x569e692993BfB66304Edb5aCB5B4e18c8A77Bcb4
> block number: 10433154
> block timestamp: 1648887649
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 144.757575357593868517
> gas used: 2781469 (0x2a711d)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.05562938 ETH
2_deploy_fundraiser_factory.js
==============================
Deploying 'FundraiserFactory'
-----------------------------
> transaction hash: 0x0416925ce57bff0d948c3f01506602e9e6f46999ae87736bedec65f5e0a618f6
> Blocks: 2 Seconds: 21
> contract address: 0xA2828D0Ea000B7098a6D1b6Ff31A97a3421464E1
> block number: 10433170
> block timestamp: 1648887889
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 144.732332497593868517
> gas used: 1262143 (0x13423f)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.02524286 ETH
全てのコントラクトのデプロイ記録
Starting migrations...
======================
> Network name: 'rinkeby'
> Network id: 4
> Block gas limit: 29970705 (0x1c95111)
1_deploy_DEX.js
===============
Deploying 'DEX'
---------------
> transaction hash: 0xafc59e3ff5fc0d53f11b5dc4b9e14b3361e3a6b69a7dce66a044896c3307ab1f
> Blocks: 0 Seconds: 9
> contract address: 0x2A2f5591FaF06EAC30Cf46A78D16Cce2d97B4dD7
> block number: 11273645
> block timestamp: 1661571867
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 155.094078864174473726
> gas used: 3048419 (0x2e83e3)
> gas price: 1.500000012 gwei
> value sent: 0 ETH
> total cost: 0.004572628536581028 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 11273646)
> confirmation number: 2 (block: 11273647)
tokenList: [
'0x06Dc2032695B30D0166E6f1f21C74Fe804F52553',
'0x8dde86fCe1FBE467ec067eF49B2b018AA0D6624d'
]
> Saving artifacts
-------------------------------------
> Total cost: 0.004572628536581028 ETH
1_deploy_fundraiser_factory.js
==============================
Deploying 'FundraiserFactory'
-----------------------------
> transaction hash: 0x6cec4893962cf9383c5d5b11cfcd6050290f0429a780532f185f0d360bcad9f1
> Blocks: 0 Seconds: 9
> contract address: 0xd201E969068Fc246C6cDD6DaeEacC57018C258c5
> block number: 11273648
> block timestamp: 1661571912
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 155.092185631659327866
> gas used: 1262155 (0x13424b)
> gas price: 1.500000012 gwei
> value sent: 0 ETH
> total cost: 0.00189323251514586 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 11273649)
> confirmation number: 2 (block: 11273650)
> Saving artifacts
-------------------------------------
> Total cost: 0.00189323251514586 ETH
2_deploy_NFT_factory.js
=======================
Deploying 'NFTFactory'
----------------------
> transaction hash: 0xcc8a6e0cdb3875c8dfd7f91d5780973bb07ad54b75c120507e2983fa002cc28d
> Blocks: 0 Seconds: 9
> contract address: 0x68eBAd847A016bB830B3607e0eEeA516A09EA5e6
> block number: 11273651
> block timestamp: 1661571957
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 155.086798959119825601
> gas used: 3591115 (0x36cbcb)
> gas price: 1.500000011 gwei
> value sent: 0 ETH
> total cost: 0.005386672539502265 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 11273652)
> confirmation number: 2 (block: 11273653)
> Saving artifacts
-------------------------------------
> Total cost: 0.005386672539502265 ETH
3_deploy_multiSig_factory.js
============================
Deploying 'MultiSigFactory'
---------------------------
> transaction hash: 0x1303c171755bbfb51aaff7aaea5d086334ef32b47f5063e971b2282eea175113
> Blocks: 1 Seconds: 21
> contract address: 0xe6230b8D99491dAd48e1de70156b4fd8b7b66b6f
> block number: 11273655
> block timestamp: 1661572017
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 155.08513326760761053
> gas used: 1110461 (0x10f1bd)
> gas price: 1.500000011 gwei
> value sent: 0 ETH
> total cost: 0.001665691512215071 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 11273656)
> confirmation number: 2 (block: 11273657)
> Saving artifacts
-------------------------------------
> Total cost: 0.001665691512215071 ETH
4_deploy_safeContract_factory.js
================================
Deploying 'SafeContractFactory'
-------------------------------
> transaction hash: 0x6bd5bd12e5254440ef62025e21b7d7fbc86bf19470acaf81fa3e1126da8f0287
> Blocks: 1 Seconds: 13
> contract address: 0x58C6d8c3AD7087475664C3b31294Fbc575E360CF
> block number: 11273658
> block timestamp: 1661572062
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 155.080180024571286748
> gas used: 3302162 (0x326312)
> gas price: 1.500000011 gwei
> value sent: 0 ETH
> total cost: 0.004953243036323782 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 11273659)
> confirmation number: 2 (block: 11273660)
> Saving artifacts
-------------------------------------
> Total cost: 0.004953243036323782 ETH
5_deploy_safeContractProxy_factory.js
=====================================
Deploying 'GnosisSafeProxyFactory'
----------------------------------
> transaction hash: 0x87b311dedbb5684172856795a119ae03950f6c71166458d8f067769379b53500
> Blocks: 1 Seconds: 13
> contract address: 0xeffCBA09787e47e3Ad4B5fEB9886a267c5952e58
> block number: 11273661
> block timestamp: 1661572107
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 155.079001548063430238
> gas used: 785651 (0xbfcf3)
> gas price: 1.50000001 gwei
> value sent: 0 ETH
> total cost: 0.00117847650785651 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 11273662)
> confirmation number: 2 (block: 11273663)
> Saving artifacts
-------------------------------------
> Total cost: 0.00117847650785651 ETH
6_deploy_myToken_factory.js
===========================
Deploying 'MyTokenFactory'
--------------------------
> transaction hash: 0x13a016c2023a1fafcfb317006791aa6bd1359ff8229e9fb6683813047c2d4d50
> Blocks: 2 Seconds: 21
> contract address: 0x5a61d594351d4754D6496b5B51237b3eCb8d1E34
> block number: 11273665
> block timestamp: 1661572167
> account: 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072
> balance: 155.074829344532834079
> gas used: 2781469 (0x2a711d)
> gas price: 1.500000011 gwei
> value sent: 0 ETH
> total cost: 0.004172203530596159 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 11273666)
> confirmation number: 2 (block: 11273667)
> Saving artifacts
-------------------------------------
> Total cost: 0.004172203530596159 ETH
Summary
=======
> Total deployments: 7
> Final cost: 0.023822148178220675 ETH
=======
brew services start postgresql
brew services stop postgresql
- CodeQL
- Node.js CI
ブロックチェーン dapp&ゲーム開発入門 Solidityによるイーサリアム分散アプリプログラミング
いちばんやさしいブロックチェーンの教本 人気講師が教えるビットコインを支える仕組み
図解即戦力 ブロックチェーンのしくみと開発がこれ1冊でしっかりわかる教科書
Ethereum Smart Contract Best Practices(和訳)
画像ファイル保管庫(200/300などをつけて検索すると猫の画像がヒットします。)
ERC20トークンでマイトークン作成・実行までまるっと解説!
【React.js】CRUD作成 Jsonファイルにデータを登録する方法