Hardhatを使って、EtherWalletと対話してみる
今回は、Hardhatを利用して、Contractをデプロイをして、コンソールでいろいろ試してみたいと思います.
デプロイするコントラクトは、下記のリンクから引用したEtherWallet
です.
https://solidity-by-example.org/app/ether-wallet
送金とオーナーによる引き落とし機能を持つシンプルなウォレットです.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; contract EtherWallet { address payable public owner; constructor() { owner = payable(msg.sender); } receive() external payable {} function withdraw(uint _amount) external { require(msg.sender == owner, "caller is not owner"); payable(msg.sender).transfer(_amount); } function getBalance() external view returns (uint) { return address(this).balance; } }
(上記のリンクから引用)
Hardhatのボイラープレートをクローン
以下のリンクから、ボイラープレートをクローンします.
https://github.com/NomicFoundation/hardhat-boilerplate
contracts
の下に、EtherWallet.sol
を作成し、↑のソースコードをペーストします.
また、npm install
を実行して、依存関係のあるモジュールをインストールします.
Hardhat Console
事前に、先頭のアカウントを取得しておきます.
外部アカウントの取得
// 20個のアカウントが用意されている > const signers = await ethers.getSigners() > signers.length 20 // 先頭のアカウントを取得する > const [signer] = await ethers.getSigners() > signer.address '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
コントラクトのデプロイ
// ContractFactoryのインスタンスを取得して、デプロイする > const EtherWallet = await ethers.getContractFactory('EtherWallet') > const etherWallet = await EtherWallet.deploy() // デプロイには、時間がかかるので、テストコードを書くときは // 以下のように、デプロイが完了するまで待つ処理を入れる // > await etherWallet.deployed(); // コントクラトをデプロイした時に、コントラクトの作成者が // `getSigners()` の先頭のアカウントであることが確認できる > etherWallet.signer.address '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
コントラクトの owner
の取得
まず、EtherWallet
の owner
を取得してみます.
EtherWallet
の constructor
で、owne = msg.sender
の代入をしているので、コントラクトの作成者と一致するはずです.
> await etherWallet.owner()
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
実際に一致することが確認できました.
コントラクトの balance
(ETHの残高) の取得
次に、getBalance()
を実行して、コントラクトの残高を取得してみましょう.
コントラクト作成時に、ETHを送金していないので、ETHの残高は 0
です.
> await etherWallet.getBalance() BigNumber { value: "0" }
外部アカウントの残高の取得
外部アカウントの残高は、以下の方法で取得できます.
> await signer.getBalance() BigNumber { value: "9999999504861250000000" }
EtherWalletにETHを送金
コントラクトアカウントに送金するには、対象のコントラクトに receive
もしくは fallback
関数が定義されている必要があります.
EtherWallet
には、receive
関数が定義されているので、送金することができます.
// トランザクションのデータを作成する // to: 送金先のコントラクトのアドレス // value: 送金するETH (単位: wei) > const transaction = { to: etherWallet.address, value: ethers.utils.parseEther('1', 'ether') } // 先頭アカウントから、トランザクションを送信する > await signer.sendTransaction(transaction) { hash: '0x3ae7d8579786f95e5a1d3ca9f0663dcb632a9ade9cc9c76f4f5f07a7e265d9ae', type: 2, accessList: [], blockHash: '0x66d547369f44f4de2d7f73e60310cebc8c89d3df3730efc2b8b705f59c16e5a6', blockNumber: 2, transactionIndex: 0, confirmations: 1, from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', gasPrice: BigNumber { value: "1767550540" }, maxPriorityFeePerGas: BigNumber { value: "1000000000" }, maxFeePerGas: BigNumber { value: "2535101080" }, gasLimit: BigNumber { value: "21055" }, to: '0x5FbDB2315678afecb367f032d93F642f64180aa3', value: BigNumber { value: "1000000000000000000" }, nonce: 1, data: '0x', r: '0x7d46f030fd6e1ab1a2728ced6466a87a21176fc606236ad11f25e39b4bed890f', s: '0x0b133a974a772cc502fd72004dd4dff9e8ca45e3dfb97faf7de49bab9a36a4a3', v: 1, creates: null, chainId: 1337, wait: [Function (anonymous)] }
返り値は、トランザクションのデータです.
再度、EtherWallet
の残高を取得すると、1ETH
増えていることが確認できます.
> await etherWallet.getBalance() BigNumber { value: "1000000000000000000" }
また、トランザクション送信者の残高から 1ETH
引き落とされています.
> await signer.getBalance() BigNumber { value: "9998999467645473380300" }
EtherWalletからETHを引き落とす
先頭アカウントから、withdraw
を実行して、0.5ETH
を引き落としてみます.
> await etherWallet.withdraw(ethers.utils.parseEther('0.5', 'ether')) { hash: '0x1c203e8dbcb89e92b798cd7e26da9264ad3289cdfd04a68298e5d9bb082ff827', type: 2, accessList: [], blockHash: '0x1499403cfcc14f29ada2217dea9c2e946653eaac28844f5b1900bc9a07c9252d', blockNumber: 3, transactionIndex: 0, confirmations: 1, from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', gasPrice: BigNumber { value: "1671741396" }, maxPriorityFeePerGas: BigNumber { value: "1000000000" }, maxFeePerGas: BigNumber { value: "2343482792" }, gasLimit: BigNumber { value: "29021784" }, to: '0x5FbDB2315678afecb367f032d93F642f64180aa3', value: BigNumber { value: "0" }, nonce: 2, data: '0x2e1a7d4d00000000000000000000000000000000000000000000000006f05b59d3b20000', r: '0x9033c02bb41f731eead690e9dd3407da3e22777ed69aa975ef0653fd7aed932f', s: '0x0bf02b2be806aa9893d5fcd267fa0728152750ef592bbdd73e521ba24e875f26', v: 1, creates: null, chainId: 1337, wait: [Function (anonymous)] }
EtherWallet
の残高を取得すると、0.5
ETH に減っていることが確認できます.
> await etherWallet.getBalance() BigNumber { value: "500000000000000000" }
では、コントラクトの作成者以外で、withdraw
を実行したら、どうなるでしょうか?
まず、別の外部アカウントを取得します.
> let [_,signer2] = await ethers.getSigners() > signer2.address '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'
コントラクトのメソッドを実行する際に、トランザクションの送信者を別のアカウントにするには、<Contract>.connect
を使用します.
以下では、2番目のアカウント(singer2
) から、トランザクションを送信します.
EtherWallet
の withdraw
では、 require
を使って、引き落とし可能なアカウントをオーナーのみに制限しています.
実際に、試したところ以下のエラーが発生して、トランザクションに失敗しました.
> await etherWallet.connect(signer2).withdraw(ethers.utils.parseEther('0.5', 'ether')) Uncaught: Error: VM Exception while processing transaction: reverted with reason string 'caller is not owner' at EtherWallet.withdraw (contracts/EtherWallet.sol:14) at processTicksAndRejections (node:internal/process/task_queues:95:5) at runNextTicks (node:internal/process/task_queues:64:3) at listOnTimeout (node:internal/timers:533:9) at processTimers (node:internal/timers:507:7) at HardhatNode._mineBlockWithPendingTxs (/Users/mishimawataru/WebstormProjects/hardhat-boilerplate/node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:1805:23) at HardhatNode.mineBlock (/Users/mishimawataru/WebstormProjects/hardhat-boilerplate/node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:494:16) at EthModule._sendTransactionAndReturnHash (/Users/mishimawataru/WebstormProjects/hardhat-boilerplate/node_modules/hardhat/src/internal/hardhat-network/provider/modules/eth.ts:1522:18)
このページでは、Hardhat Consoleを使用して、EtherWalletの処理をいろいろと試してみました.
次回は、Hardhatを使用したテストコードについてみていきたいと思います.