เริ่มต้นเขียน Solidity ด้วย Hardhat

ช่วงนี้ผมค่อนข้างอินกับ Smart Contract เพราะกำลังอยู่ในช่วงหัดเขียน หัดลองครับ ก็เลยลองเขียนเป็นบทความเผื่อใครหลายๆ คนที่สนใจเหมือนๆกัน ตัวผมเองก็ยังเป็นมือใหม่มากๆ
บทความนี้จะพาไปลองส่องตัวโปรเจ็ค hardhat ว่าตัว sample project ของ hardhat มีอะไรบ้าง และรู้จักกับ solidity คร่าวๆ วิธีการ deploy วิธีการเรียกใช้ hardhat
ซึ่งทั้งหมดยังเป็น local network นะครับ ยังไม่ได้ติดต่อ testnet หรือ mainnet แต่อย่างใด
Step 1 - สร้างโปรเจ็ค
ทำการสร้างโปรเจ็คขึ้นมา ผมตั้งชื่อว่า hello-solidity
mkdir hello-soliditycd hello-sollidity
จากนั้น init project เพื่อให้มีไฟล์ package.json
:
npm init -y# หรือ yarnyarn init -y
ติดตั้ง hardhat
npm install hardhat --save-dev# Yarnyarn add hardhat -D
เมื่อติดตั้ง hardhat เรียบร้อยแล้ว ก็ทำการสร้างโปรเจ็คด้วยคำสั่งของ hardhat
npx hardhat
# yarnyarn hardhat
- เลือก Create a basic sample project
- Hardhat project root ?
<enter>
ถ้าไม่ต้องการเปลี่ยนตัวโปรเจ็คก็สร้างที่ root folder. - Do you want to add a .gitignore? (Y/n) › y - เพื่อ generate ไฟล์
.gitignore
- Do you want to install this sample project’s dependencies with yarn… - y เพื่อติดตั้ง dependencies ให้เลย ไม่งั้น ก็ต้องมา
npm install
ทีหลังอยู่ดี
ผลลัพธ์จะได้แบบนี้
yarn hardhat
yarn run v1.22.18$ /Users/chai/dev/hello-solidity/node_modules/.bin/hardhat888 888 888 888 888888 888 888 888 888888 888 888 888 8888888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888888 888 "88b 888P" d88" 888 888 "88b "88b 888888 888 .d888888 888 888 888 888 888 .d888888 888888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
👷 Welcome to Hardhat v2.9.2 👷
✔ What do you want to do? · Create a basic sample project✔ Hardhat project root: · /Users/chai/dev/hello-solidity✔ Do you want to add a .gitignore? (Y/n) · y✔ Do you want to install this sample project's dependencies with yarn (@nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers)? (Y/n) · y......
✨ Project created ✨See the README.md file for some example tasks you can run.✨ Done in 48.59s.
Step 2 - Hardhat
มาลองสำรวจกันดีกว่าว่า Hardhat ให้อะไรเรามาบ้าง
จะเห็นว่า Hardhat ได้ทำการ generate ไฟล์และโฟลเดอร์ต่างๆ ให้เรา ดังนี้
tree -L 2 -I node_modules
├── README.md├── contracts│ └── Greeter.sol├── hardhat.config.js├── package.json├── scripts│ └── sample-script.js├── test│ └── sample-test.js└── yarn.lock
contracts
- เป็นไฟล์ Smart Contract ของเรานั่นเองscripts
- เป็นไฟล์ javascript ที่เอาไว้ interact กับ smart contract (จริงๆ ชื่อ folder ไรก็ได้)test
- ไฟล์ testhardhat.config.js
- เป็นไฟล์ config หลัก ของ Hardhat เช่นกำหนด network, url หรือ account ต่างๆ
ลองดูไฟล์ contracts/Greeter.sol
//SPDX-License-Identifier: Unlicensepragma solidity ^0.8.0;
import "hardhat/console.sol";
contract Greeter { string private greeting;
constructor(string memory _greeting) { console.log("Deploying a Greeter with greeting:", _greeting); greeting = _greeting; }
function greet() public view returns (string memory) { return greeting; }
function setGreeting(string memory _greeting) public { console.log("Changing greeting from '%s' to '%s'", greeting, _greeting); greeting = _greeting; }}
บรรทัดแรก เป็นการกำหนด License ให้กับไฟล์ เช่น MIT, Unlicense
// SPDX-License-Identifier: Unlicense
ต่อมา
pragma solidity ^0.8.0;
pragma directive เพื่อบอกให้ compiler รู้ว่าจะ compile solidity เวอร์ชั่นอะไร ตัวเลข เป็นแบบ semver
contract Greeter {
}
ตัว contract ถ้าสังเกต จะเหมือนการสร้าง class เลย และจริงๆ ก็มอง contract เป็น class ก็ได้
constructor(string memory _greeting) { console.log("Deploying a Greeter with greeting:", _greeting); greeting = _greeting;}
มี constructor
เหมือนภาษา programming อื่นๆ ซึ่งตัว contructor จะถูกเรียกครั้งเดียว ในปกติ เวลาสร้าง class ตัว constructor จะถูกเรียกตอน new Class()
ส่วนใน contract ตัว constructor จะถูกเรียกตอน deploy contract.
import "hardhat/console.sol";console.log("");
เราสามารถใช้ console.log()
ในภาษา solidity ได้ เพราะเรา import ตัว console.sol
จาก hardhat มานั่นเอง
function greet() public view returns (string memory) { return greeting;}
ฟังค์ชั่น greet()
ทำการ return string กลับไป ก็เหมือน function ในภาษาอื่นๆ ต่างกันที่ syntax นิดหน่อย คือ
public
- คือกำหนดให้เป็น public function ใครก็เรียกใช้ function นี้ได้view
- เพื่อบอกว่า function นี้จะไม่เปลี่ยนแปลงค่าstate
นะ (read only)- returns (string memory)
- การ return จะเขียนในรูปแบบนี้ ซึ่ง
memory` คือรูปแบบการเก็บข้อมูล
function setGreeting(string memory _greeting) public { console.log("Changing greeting from '%s' to '%s'", greeting, _greeting); greeting = _greeting;}
function setGreeting
รับค่า string จากนั้นทำการ set ค่า ให้ greeting
ที่เป็น state variable ที่เรากำหนดไว้ใน contract.
string private greeting;
เมื่อไหร่ก็ตามที่ state variable มีการเปลี่ยนค่า คือการ write data ลงบน blockchain ก็จะเกิด transaction ขึ้น สังเกต เราเข้าถึง greeting
ได้เลย ถ้าอย่าง JavaScript อาจต้องใช้ this.greeting
ไรงี้
ต่อมา สังเกตไฟล์ scripts/sample-scripts.js
const hre = require('hardhat')
async function main() { // Hardhat always runs the compile task when running scripts with its command // line interface. // // If this script is run directly using `node` you may want to call compile // manually to make sure everything is compiled // await hre.run('compile');
// We get the contract to deploy const Greeter = await hre.ethers.getContractFactory('Greeter') const greeter = await Greeter.deploy('Hello, Hardhat!')
await greeter.deployed()
console.log('Greeter deployed to:', greeter.address)}
สิ่งที่ได้รู้จากตัวอย่าง sample-scripts
คือ
- hardhat จะรัน
hardhat compile
ให้เราอัตโนมัติ ถ้าเราใช้คำสั่งhardhat run
(task) hre
(Hardhat Runtime Environment) ไม่จำเป็นต้อง import ถ้าเราใช้คำสั่งnpx hardhat
ตัวhre
จะเป็น global scope เข้าถึงได้เลย
ทดลอง compile โดยการเปิด Terminal ขึ้นมา
npx hardhat compile
ตัว Hardhat จะทำการ compile และ generate โฟลเดอร์ artifacts
ขึ้นมา
├── artifacts│ ├── build-info│ ├── contracts│ └── hardhat
กลับไปดู sample-scripts
อีกครั้ง
//1. บรรทัดนี้ เป็นคำสั่งเพื่อสร้าง contract factory จาก contract ที่ชื่อ `Greeter`const Greeter = await hre.ethers.getContractFactory('Greeter')
// 2. เมื่อได้ factory ก็ใช้คำสั่ง deploy จะเห็นว่า "Hello, Hardhat!" คือค่า argument ที่ถูกส่งไปใน contructor นั่นเองconst greeter = await Greeter.deploy('Hello, Hardhat!')
// 3. เรียก `deployed()` เพื่อรอ transaction confirm เพราะข้อมูลที่ deploy มันคือการเซฟลง blockchainawait greeter.deployed()
สังเกตว่า ตัว syntax ในไฟล์นี้มันก็คือ JavaScript นี่แหละ หากใครไม่คุ้นชิน promise ลองอ่านเรื่อง async/await เพิ่มเติมครับ
สุดท้าย ทดสอบรัน sample-scripts ดูผลลัพธ์ดีกว่า
npx hardhat run scripts/sample-scripts.js
จะได้ผลลัพธ์เป็นแบบนี้ (ตัว address ไม่เหมือนกันนะ)
Deploying a Greeter with greeting: Hello, Hardhat!Greeter deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Step 3 - Interact with Contract
หลังจากที่เราพอรู้ขั้นตอน deploy แล้ว ต่อมาลองเรียก greet()
จากไฟล์ sample-scripts
ทำได้แบบนี้เลย
const Greeter = await hre.ethers.getContractFactory('Greeter')const greeter = await Greeter.deploy('Hello, Hardhat!')
await greeter.deployed()
const greet = await greeter.greet()console.log('greeting : ', greet)
ผลลัพธ์
Deploying a Greeter with greeting: Hello, Hardhat!Greeter deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3greeting : Hello, Hardhat!
ลอง setGreeting()
ดู ซึ่งการเปลี่ยน state หรือการเขียนข้อมูลลง blockchain มันต้องรอ transaction confirm ครับ ซึ่ง ตัว js ก็จะเป็นแบบนี้
const Greeter = await hre.ethers.getContractFactory('Greeter')const greeter = await Greeter.deploy('Hello, Hardhat!')
await greeter.deployed()
console.log('Greeter deployed to:', greeter.address)
const greet = await greeter.greet()console.log('greeting : ', greet)
const tx = await greeter.setGreeting('This is new greeting.')tx.wait()
const newGreet = await greeter.greet()console.log('greeting (new) : ', newGreet)
สังเกตว่า awaitreeter.setGreeting()
จะได้เป็น transaction ที่ยังไม่ confirm ครับ ต้องใช้ tx.wait()
เพื่อรอ transaction confirm นั่นเอง
ผลลัพธ์
Deploying a Greeter with greeting: Hello, Hardhat!Greeter deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3greeting : Hello, Hardhat!Changing greeting from 'Hello, Hardhat!' to 'This is new greeting.'greeting (new) : This is new greeting.
สรุป
สำหรับบทความนี้ ก็เป็นพื้นฐาน เป็น Ovewview คร่าวๆ สำหรับคนที่สนใจ Solidity / Smart Contract นะครับ เรียกได้ว่าเขียนโค๊ดไม่กี่บรรทัดเอง ส่วนใหญ่เน้นทำความเข้าใจกับมันก่อน และการใช้ Hardhat ที่ค่อนข้างสะดวกและง่ายมากๆ ยังไง ก็ลองไปเล่น ลองฝึกเพิ่มเติมกันดูนะครับ เพราะอ่านอย่างเดียวก็ไม่ได้อะไร ต้องไปลองหัดทำ หัดประยุกต์ดูด้วยตัวเองครับ
Happy Coding
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust