Hẳn đối với những bạn mới bắt đầu tìm hiểu Ethereum nói riêng cũng như các nền tảng EVM-based nói chung đều biết đến Truffle
(một framework hỗ trợ compile, deploy smart contract). Tuy nhiên, khi cần phát triển các smart contract lớn, phức tạp hơn thì nó sẽ bộc lộ 1 số hạn chế như tốn nhiều gas, thời gian deploy lâu, … Với Hardhat
, framework đem lại cho chúng ta những tùy chọn, tính năng phần nào vượt trội hơn để làm với các hệ thống smart contract phức tạp hơn.
1. Giới thiệu tổng quan
Cũng như Truffle, Hardhat là một môi trường phát triển để biên dịch, triển khai, test và debug Dapp Ethereum.
Một số điểm tính năng nổi bật của Hardhat
- Tích hợp mạng local
hardhat
, dễ dàng chạy và debug code ngay trên local. - Debug dễ dàng hơn: Với Hardhat, chúng ta có thể debug code Solidity dễ dàng hơn khi có thể console.log ra các biến (Solidity vốn ko hỗ trợ console.log)
- Hệ thống plugin: Giúp developer có thể bổ sung chức năng, tùy vào từng dự án cụ thể
- Hỗ trợ TypeScript
2. Cài đặt
Chúng ta còn setup thử 1 project mẫu nho nhỏ với hardhat
. Chúng ta thực hiện các thao tác như dưới đây
1 2 3 4 5 | <span class="token function">mkdir</span> hardhat-tutorial <span class="token builtin class-name">cd</span> hardhat-tutorial <span class="token function">npm</span> init --yes <span class="token function">npm</span> <span class="token function">install</span> --save-dev hardhat |
1 2 | npx hardhat |
Chúng ta sẽ chọn lựa chọn số 1 (tạo ra 1 project mẫu)
Tiếp tục lựa chọn các option cần thiết
Cuối cùng, cấu trúc thư mục project mẫu của Hardhat sẽ như thế này các bạn ạ
Còn đây lấy cấu trúc tương ứng của Truffle Box
Cơ bản cấu trúc project của 2 công cụ là tương đối giống nhau
- Thư mục
contracts
chứa mã nguồn của các smart contract - Thư mục
scripts
vàmigrations
chứa các script viết bằng Javascript để biên dịch, deploy smart contract. - Thư mục
test
chứa unit test viết cho contract - 2 File
hardhat.config.js
vàtruffle-config.js
chứa các config như networks, địa chỉ ví, trình biên dịch …
3. Sử dụng
Greeter.sol
Chúng ta có file smart contract Greeter.sol
trong folder contracts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | pragma solidity <span class="token operator">^</span><span class="token number">0.7</span><span class="token number">.0</span><span class="token punctuation">;</span> <span class="token comment">// Thư viện giúp sử dụng console.log trong solidity để debug</span> <span class="token keyword">import</span> <span class="token string">"hardhat/console.sol"</span><span class="token punctuation">;</span> contract Greeter <span class="token punctuation">{</span> string greeting<span class="token punctuation">;</span> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">string memory _greeting</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Deploying a Greeter with greeting:"</span><span class="token punctuation">,</span> _greeting<span class="token punctuation">)</span><span class="token punctuation">;</span> greeting <span class="token operator">=</span> _greeting<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">greet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">public</span> view <span class="token function">returns</span> <span class="token punctuation">(</span><span class="token parameter">string memory</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> greeting<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">setGreeting</span><span class="token punctuation">(</span><span class="token parameter">string memory _greeting</span><span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Changing greeting from '%s' to '%s'"</span><span class="token punctuation">,</span> greeting<span class="token punctuation">,</span> _greeting<span class="token punctuation">)</span><span class="token punctuation">;</span> greeting <span class="token operator">=</span> _greeting<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
hardhat.config.js
File hardhat.config.js
là nơi chúng ta định nghĩa các networks, trình biên dịch, thư mục file build, …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'@nomiclabs/hardhat-waffle'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">task</span><span class="token punctuation">(</span><span class="token string">"accounts"</span><span class="token punctuation">,</span> <span class="token string">"Prints the list of accounts"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> accounts <span class="token operator">=</span> <span class="token keyword">await</span> ethers<span class="token punctuation">.</span><span class="token function">getSigners</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> account <span class="token keyword">of</span> accounts<span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>account<span class="token punctuation">.</span>address<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> solidity<span class="token operator">:</span> <span class="token string">'0.7.3'</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Một phiên bản hoàn thiện hơn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> defaultNetwork<span class="token operator">:</span> <span class="token string">'hardhat'</span><span class="token punctuation">,</span> networks<span class="token operator">:</span> <span class="token punctuation">{</span> hardhat<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> development<span class="token operator">:</span> <span class="token punctuation">{</span> url<span class="token operator">:</span> <span class="token string">'http://127.0.0.1:8545'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> quorum<span class="token operator">:</span> <span class="token punctuation">{</span> url<span class="token operator">:</span> <span class="token string">'http://127.0.0.1:22000'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> solidity<span class="token operator">:</span> <span class="token punctuation">{</span> version<span class="token operator">:</span> <span class="token string">'0.6.2'</span><span class="token punctuation">,</span> settings<span class="token operator">:</span> <span class="token punctuation">{</span> optimizer<span class="token operator">:</span> <span class="token punctuation">{</span> enabled<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> runs<span class="token operator">:</span> <span class="token number">200</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> paths<span class="token operator">:</span> <span class="token punctuation">{</span> sources<span class="token operator">:</span> <span class="token string">'./contracts'</span><span class="token punctuation">,</span> tests<span class="token operator">:</span> <span class="token string">'./test'</span><span class="token punctuation">,</span> cache<span class="token operator">:</span> <span class="token string">'./cache'</span><span class="token punctuation">,</span> artifacts<span class="token operator">:</span> <span class="token string">'./artifacts'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> mocha<span class="token operator">:</span> <span class="token punctuation">{</span> timeout<span class="token operator">:</span> <span class="token number">20000</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
scripts/sample-script.js
Khác với Truffle, Hardhat không cần deploy “đệm” thêm contract Migration.sol
, nên tiết kiệm được 1 lượng gas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">const</span> hre <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"hardhat"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// load contract Greeter</span> <span class="token keyword">const</span> Greeter <span class="token operator">=</span> <span class="token keyword">await</span> hre<span class="token punctuation">.</span>ethers<span class="token punctuation">.</span><span class="token function">getContractFactory</span><span class="token punctuation">(</span><span class="token string">"Greeter"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Truyền tham số cho hàm constructor</span> <span class="token keyword">const</span> greeter <span class="token operator">=</span> <span class="token keyword">await</span> Greeter<span class="token punctuation">.</span><span class="token function">deploy</span><span class="token punctuation">(</span><span class="token string">"Vui Tet Cung Viblo"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// deploy contract</span> <span class="token keyword">await</span> greeter<span class="token punctuation">.</span><span class="token function">deployed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Greeter deployed to:"</span><span class="token punctuation">,</span> greeter<span class="token punctuation">.</span>address<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> process<span class="token punctuation">.</span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token parameter">error</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> process<span class="token punctuation">.</span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Deploy
1 2 | npx hardhat run scripts/sample-script.js |
Nếu muốn deploy code contract ở các mạng khác thì chúng ta thêm lựa chọn --network
1 2 | npx hardhat run scripts/sample-script.js --network development |
Vậy đoạn console.log chúng ta đặt trong hàm constructor
đã được thực thi
4. Các tính năng nâng cao hơn
Biên dịch các contract ở nhiều phiên bản Solidity khác nhau
hardhat.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> solidity<span class="token operator">:</span> <span class="token punctuation">{</span> compilers<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> version<span class="token operator">:</span> <span class="token string">"0.5.5"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> version<span class="token operator">:</span> <span class="token string">"0.6.7"</span><span class="token punctuation">,</span> settings<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Hardhat trong trường hợp này sẽ dùng trình biên dịch 0.5.5
với các contract pragma solidity ^0.5.0
và 0.6.7
với các contract pragma solidity ^0.6.0
Trong TH contract định nghĩa trình biên dịch pragma solidity >=0.5.0
thì Hardhat sẽ lựa chọn trình biên dịch cao nhất 0.6.7
Hoặc chúng ta có thể config chi tiết cho contracs nào dùng trình biên dịch nào
1 2 3 4 5 6 7 8 9 10 11 12 | module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> solidity<span class="token operator">:</span> <span class="token punctuation">{</span> compilers<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">,</span> overrides<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"contracts/Foo.sol"</span><span class="token operator">:</span> <span class="token punctuation">{</span> version<span class="token operator">:</span> <span class="token string">"0.5.5"</span><span class="token punctuation">,</span> settings<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Chạy riêng 1 mạng blockchain local như ganache
Trước đây, mình hay dùng ganache để deploy smart contract trên local với truffle để thử nghiệm, tất nhiên là mình có thể dùng truffle develop
nhưng ganache có nhiều lựa chọn và cách cấu hình các thông số hơn. Với Hardhat, mình có thể dùng luôn Hardhat, nó hỗ trợ cả việc mô phỏng các phiên bản hardforks của Ethereum trên local.
1 2 | npx hardhat node |
Mặc định mạng sẽ chạy ở port 8545
, chúng ta có thể thêm các tham số để điều chỉnh lại theo ý muốn
1 2 3 4 5 | --fork --fork-block-number --hostname --port |
Kết luận
Nhìn chung, Hardhat là 1 công cụ rất mạnh, đầy đủ tính năng cần thiết giúp các developer có thể triển khai smart contract trên Ethereum. Cách dùng Hardhat cũng khá giống với Truffle, ngoài ra cũng khắc phục được 1 số điểm chưa tốt từ đối thủ.