เขียน Solidity ..ชีวิตดีได้ด้วย Hardhat!
ชีวิตของ Solidity Developer ที่อยู่ได้ด้วย Remix นั้น ถึงจุด ๆ หนึ่งอาจจะนึกขึ้นมาว่า มันมีสิ่งที่ดีกว่านี้รอเราอยู่ไหมนะ? …บทความนี้จะมาตอบเลยว่า มี!
จากบทความ Web 3.0 นั้น ผมได้มีการพูดถึง Smart Contract ไว้ว่าเป็นหนึ่งในอาวุธสำคัญที่จะทำให้เราเข้าใกล้โลก Web 3.0 เข้าไปอีกก้าว และเป็นฟีเจอร์หลักของ Ethereum เลยก็ว่าได้ ซึ่งสามารถเขียนโดยภาษา Solidity ผ่าน Tools ต่าง ๆ เช่น Remix หรือ Hardhat
Disclaimer: บทความนี้จะขอข้ามรายละเอียดทางเทคนิคหลายอย่างนะครับ โดยจะพยายามใส่ลิ้งก์ที่เกี่ยวข้องเผื่อศึกษาเพิ่มเติมให้ครบ
แล้วก็รวมถึง Technical Details ของ Solidity ที่ขอละไว้ ซึ่งตรงนี้คิดว่าหากอยากลงลึก ลองค้นหาคอร์สเรียน หรือถามอาจารย์ YouTube ดู น่าจะได้ความรู้ครบถ้วนกว่าเป็นบทความครับผม 🥰
ก่อนจะ Hardhat รู้จัก Remix สักนิด
Hardhat นั้นทำให้การสร้าง Smart Contract นั้น Developer ทำได้อย่างสะดวกสบายมากขึ้น
“สบายขึ้นนี่ คุณน้องเทียบกับอะไรฮะ?” — คำถามในใจผู้อ่าน
จริง ๆ แล้ว Ethereum.org เองเนี่ย ก็มี IDE สำหรับเขียน Solidity อยู่แล้วนะครับ ซึ่งก็คือ Remix
หากสนใจ Remix ลองเล่นดูโดยอ่าน Guide ละเอียดๆได้ที่นี่ แต่สำหรับบทความนี้ขอมูฟออนไปยัง Hardhat ดีกว่า
ทำไมถึงดีกว่า? ขอเล่าในหัวข้อถัดไปครับ
Hardhat!?
เป็น Development Environment ที่ทำให้เราสามารถ Compile, Deploy, Test, Debug ตัว Smart Contract ได้ ซึ่งมาพร้อมกับ Local Ethereum Network ทำให้เราสามารถทดสอบกับ Blockchain จริง ๆ ได้เลย
ข้อดีหลัก ๆ ของ Hardhat มีดังนี้:
- Debug ได้ง่าย Error Message ชัดเจน
- Fork Network ของจริงมาทดสอบได้ ทำให้ Test ได้แบบสมจริง
- ดู Stack Trace ได้
- สำหรับเหล่า JS Dev ทั้งหลาย; มี
console.log()
ด้วย! 😳 - เป็น JS Ecosystem ทำให้หลายๆคนอาจจะรู้สึกคุ้นเคยเหมือนคนเคยคุย
- มี Plugins ให้เลือกมาเสริมมากมาย
Outline บทความ
- ขึ้น Project Hardhat
- เขียน Smart Contract
- Automated Test ผ่าน Hardhat
- Deploy ด้วย Hardhat Script
- Verify Code บน Blockchain Explorer (ด้วย Hardhat Script)
1. ขึ้นของแบบไม่ของขึ้น
ไม่ของขึ้นเพราะง่ายมาก?
เปล่า เพราะงง! ผ่าม!
มาลุยขึ้นโปรเจ็กต์กันครับ!
เริ่มด้วยการขึ้น npm มาก่อน
npm init
จากนั้นลง hardhat
npm install --save-dev hardhat
แล้วสร้างโปรเจ็กต์ด้วย Hardhat — เลือกเป็น TypeScript ครับ (ผม TypeScript FC ❣️)
npx hardhat
จากนั้นจะได้โครงสร้าง Project มาประมาณนี้
อธิบายความหมายไฟล์สำคัญๆ
hardhat.config.ts
Configuration สำหรับ hardhat ทั้งหมด อ่านเพิ่มcontracts/
Directory รวมโค้ด Smart Contractscripts/
Directory รวมไฟล์ Scripts ต่างๆ เช่น Script สำหรับ Deploytest/
Directory รวมไฟล์ Automated Test
แก้คำสั่ง test
ใน scripts
ของ package.json
ให้เป็นตามนี้
"scripts": {
"test": "hardhat test"
}
กำหนด Environment
ก๊อปปี้ไฟล์ .env.example
ออกมา เป็น .env
แก้ค่าใน .env ให้เป็นค่าจริง ซึ่งสำหรับบทความนี้จะทำอยู่บน Ethereum Ropsten Testnet นะครับ
โดยแต่ละค่าก็คือ:
ETHERSCAN_API_KEY
API Key ของ etherscan.io (อันนี้ใช้ใน Step 5: Verify Contract เพื่อ Verify Code ผ่าน Script เท่านั้นครับ ข้ามไปแล้ว Verify แบบ Manual เอาก็ได้เหมือนกัน)ROPSTEN_URL
RPC URL ของ Ropsten ซึ่งเจ้า Hardhat generate ตัวอย่างมาให้เป็นของ Alchemy แต่ใช้ของ public ใน rpc.info ก็ได้ (เช่น ตอนที่ผมดูจะเป็น https://ropsten.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161)PRIVATE_KEY
Private Key ของ Wallet ที่จะใช้ Deploy Contract (กดดูใน Metamask ได้ — 🚨อย่าทำ Key หลุดนะครับบบ🚨)
บทความนี้เขียนตอน Ropsten ยังมีชีวิตอยู่ แต่มันกำลังจะ Deprecated ในอนาคตอันใกล้นี้นะครับ หากตอนอ่านบทความนี้มันสิ้นชีพไปแล้ว ลอง Testnet อื่นดูครับ
อ่านเพิ่มเติม
🕵🏻♂️ Checkpoint 1: Project Structure
ถึงจังหวะนี้จะได้ Directory พร้อมโค้ดตัวอย่างมาแล้วครับ ลองทดสอบดูได้โดยการ
npm run test
2. เขียน Contract กัน!
โจทย์เป็น Contract ที่จะทำหน้าที่ง่าย ๆ คือ ให้เราสามารถส่งจดหมายรัก (พร้อมเงินเปย์) ให้คนปลายทางสามารถรับไปได้
โดยในจดหมายจะมีข้อความเป็น String ติดไปด้วย — ซึ่งสำหรับ Scope งานนี้ จะเป็นใครก็อ่านได้ไปก่อน ยังไม่ต้องทำระบบกันไม่ให้คนอื่นอ่าน
แชร์ความสวยงามของความรักให้แก่ชาวโลกกันเถอะ 🌝
ย้ำว่าไม่ลง Technical Details นะครับ และโค้ดนี้ยังไม่ได้ผ่านการ Optimize Gas ใดๆ เน้นความอ่านง่ายเป็นหลัก
วางตำแหน่งกล่องจดหมาย
สร้างไฟล์ LoveLetter.sol
ไว้ใน Directory contracts/
⚠️ Challenge1: ก่อนอ่านโค้ดด้านล่าง ดูตัวอย่างจาก
contracts/Greeter.sol
อ่านโจทย์ แล้วเขียนเองดูครับ
ขึ้นโครงกล่องจดหมาย
ขึ้นโค้ด Contract มาพร้อมกับประกาศ Data Structure ที่ต้องใช้ให้ครบ
totalLetters
บอกว่าตอนนี้มีจดหมายอยู่กี่อันmapping(uint256 => address) public senders
เก็บว่า Letter ID นี้ ใครเป็นคนส่งmapping(uint256 => address) public receivers
เก็บว่า Letter ID นี้ ใครเป็นคนรับmapping(uint256 => Letter) public letters
เก็บข้อมูลภายใน Letter ซึ่งประกอบไปด้วย: ข้อความ, ปริมาณเงินเปย์, สถานะว่าถูกเปิดหรือยังstruct Letter
คือ Data Structure ของจดหมายconstructor
คือฟังก์ชั่นที่จะทำงานตอนเรา Deploy Contract ครั้งแรก อันนี้มีหน้าที่เพียงเซ็ตค่าtotalLetters
ให้เริ่มต้นที่0
เท่านั้นevent
คืออีเว้นต์ที่เราสามารถดักจับออกมาดูได้ ถ้า Transaction นั้นผ่าน ซึ่งจะมี 2 อีเว้นต์ที่เราต้องการ คือเมื่อมีคนส่งจดหมาย (Send
) กับเปิดจดหมาย (Open
)
Solidity มี Default Value อยู่แล้ว ซึ่งสำหรับ
uint256
ก็เป็น0
นั่นแหละ ดังนั้นจริงๆไม่ต้องเซ็ตก็ได้ แต่เขียนไว้เพื่อความชัดเจน
ช่องส่งจดหมาย
พอมีกล่องจดหมายเปล่า ๆ มาแล้ว ขั้นต่อไปก็คือฟังก์ชั่นสำหรับส่งจดหมาย
ช่องรับจดหมาย
มีคนส่งก็ต้องมีคนรับ ต่อไปคือฟังก์ชั่นรับจดหมาย ที่เรียกได้เฉพาะคนเป็น Receiver ของจดหมายนั้นๆ
ช่องแอบส่อง
ส่งได้รับได้ แต่เรายังไม่มีช่องเอาไว้ส่องของข้างในเลย ดังนั้นมาทำฟังก์ชั่นดูค่าต่าง ๆ ในจดหมายกันต่อ
🕵🏻♂️ Checkpoint 2: LoveLetter.sol
ถึงจังหวะนี้ จะได้ Contract LoveLetter
มาแล้วครับ อยู่ใน Directory contracts
โดยโค้ดทั้งหมดของ LoveLetter
เป็นดังนี้
เช็คความถูกต้องได้โดยรันคำสั่ง
npx hardhat compile
ซึ่งผลลัพธ์ควรจะต้องออกมาประมาณนี้ครับ ไม่มี Errors
ขั้นถัดไป มาใช้ความสามารถสุดเทพอย่างหนึ่งของ Hardhat กัน! นั่นก็คือ Automated Test
3. เทสแรกไม่เป็นไร เทสถัดไป.. แตก!
เขียนโค้ดเสร็จแล้ว มานั่งเทสมือมันเหนื่อย เรามองลองเขียน Automated Test ครอบกันดีกว่า
สร้างไฟล์ Test
สร้างไฟล์ loveletter.ts
ไว้ใน Directory test/
เทสอะไรบ้างดี
ลองเขียนเทสกันเบื้องต้นใน Usecase สำคัญ ๆ
- ทดสอบว่า Deploy สำเร็จ และ Initiate ค่าได้ถูกต้อง
- ทดสอบว่าสามารถส่งจดหมาย ผ่านฟังก์ชั่น
send
ได้สำเร็จ และ มีค่าในจดหมายที่ถูกต้อง - ทดสอบว่าจดหมายต้องถูกเปิดโดยคนที่ไม่ใช่ผู้รับไม่ได้
- ทดสอบว่าจดหมายถูกเปิดโดยฟังก์ชั่น
open
ได้โดยคนที่เป็นผู้รับ และได้เงินเปย์ปริมาณที่ถูกต้อง
⚠️ Challenge2: ก่อนอ่านโค้ดด้านล่าง อยากให้ลองเขียนเทสดูเองก่อน โดยดูตัวอย่างจากไฟล์
test/index.ts
ครับ
ฮัลโหล เทส เทส
รันคำสั่งเทสได้โดย
npm run test
ซึ่งผลลัพธ์ก็จะออกมาหน้าตาประมาณนี้ครับ
[send] 0 1000000000000000000 คืออะไร๊!?
สิ่งนั้นก็คือออออ…
console.log()
ภายใน Solidity ครับ!
log ตัวนี้ อยู่ภายในในฟังก์ชั่น send
ครับ ลองเล่น log นู่นนี่นั่นออกมาดูได้ อ่าน docs กดที่นี่
🕵🏻♂️ Checkpoint 3: ทดสอบความรัก
จังหวะนี้ก็จะได้ไฟล์ Automated Test เพื่อเสริมสร้างความมั่นใจก่อนเอา Contract ขึ้น Blockchain จริง ๆ แล้วนะครับ
⚠️ Challenge3: อยากให้ลองเพิ่ม Test Case เองครับ โดยเป็นสองเคสต่อไปนี้:
ทดสอบว่า หากเปิดจดหมายเดิมซ้ำ Transaction ควรจะถูก Revert
ทดสอบว่า ส่งจดหมายถัดไป จะเป็นจดหมาย ID
1
และเปิดได้โดยผู้รับเหมือนปกติ
เทสของโจทย์สองข้อนี้ที่ผมเขียน จะอยู่ใน GitHub Repository ที่ผมแปะไว้ท้ายบทความนะครับ — แอบดูตีมือ ☝️
4. -3–2–1 Blast off! Deploy!
โค้ดก็เสร็จแล้ว เทสก็ทำแล้ว คงไม่เหลืออะไรให้ทำอีกนอกจากเอาโค้ดขึ้นละ
ก่อนจะไปท่า Hardhat ขอกลับไปใช้ Remix กันก่อนนะครับ ถือเป็นการ Deploy แบบ Manual โดยไม่ต้องเขียน Script อะไรกันก่อน แบบว่าลำบากก่อนสบายทีหลัง 🌝
Remix สาย(ฉบับ)ย่อ
เริ่มจากสร้างไฟล์ใหม่ใน Remix ตั้งชื่อว่า LoveLetter.sol แล้วก๊อปโค้ดจากไฟล์ contracts/LoveLetter.sol
ใน Project เราไปวางครับ
จากนั้นเลือกไปที่หัวข้อ Compile แล้ว Config ตามนี้ครับ
- Compiler เลือกเป็นอะไรก็ได้ใน
0.8.*
(ง่าย ๆ ก็เอาตัวที่ใหม่สุดไป) เพราะจะเห็นว่าในโค้ดเราเขียนไว้ว่าเป็นpragma solidity ^0.8.0
- ติ๊ก Auto compile เพื่อชีวิตที่ดีกว่า (มันจะ Compile ทุกครั้งที่เราแก้โค้ด ทำให้เห็นได้เลยว่ามี Compilation Error อะไรหรือเปล่า)
- เลือก Contract เป็น LoveLetter.sol
- กด Compile!
สุดท้าย ก็คือไปที่หัวข้อ Deploy ครับ ซึ่ง Config ตามนี้
จะข้ามวิธีใช้ Metamask ไปนะครับ
- เลือก Environment เป็น Injected Web3 ซึ่งจะทำให้มัน Deploy ไปยัง Network ที่ Metamask นั้นเลือกอยู่ ดังนั้นให้ชัวร์ว่า Metamask เลือกเป็น Ropsten อยู่
- Account จะเป็นกระเป๋าที่ใช้ Deploy ให้ชัวร์ว่ามี Gas อยู่ใน Ropsten — ถ้าไม่มีไปกดจาก Faucet ได้ (ตอนผมกดต้องรออยู่สิบกว่านาทีถึงจะเข้า)
- เลือก Contract ให้เป็น
LoveLetter.sol
จากนั้น กด Deploy เลย!
หลังจาก Confirm ใน Metamask เสร็จก็นั่งจิบชารอ Transaction เสร็จซักแป๊ปครับ
ซึ่งหากเรียบร้อยจะขึ้นมาอยู่ใน Deployed Contracts แบบนี้
ถ้าลองก๊อปปี้ Address ไปดูใน ropsten.etherscan.io ได้ จะเห็น Transaction ที่ Deploy อยู่ครับ เยยยย้
⚠️ Challenge4: ลองเล่นใน Remix ทั้ง
send
,open
และฟังก์ชั่นอื่น ๆ ดูครับ แบบให้ receiver เป็นกระเป๋าอื่น แล้ว open ให้ได้
Deploy ยุ่งยาก ไม่ทำและ เลิกอ่านละบทความ
หยุดก่อนเถิดท่านผู้ใจร้อนรุ่ม
ถัดไปมาลองทำท่าที่ง่ายกว่ากัน!
Deploy ด้วย Script Hardhat
สร้างไฟล์ Script ไว้ที่ scripts/deploy-loveletter.ts
ได้เลยครับ (ก๊อปปี้มาจาก scripts/deploy.ts
เลยก็ได้)
⚠️ Challenge5: ลองเอา
scripts/deploy.ts
มาแก้เองดูครับ (แก้น้อยมากกก)
จากนั้นรัน Command ด้านล่างนี้เพื่อ Deploy ครับ
npx hardhat run scripts/deploy-loveletter.ts --network ropsten
🕵🏻♂️ Checkpoint 4: ปลดปล่อยความรักสู่โลก
ถึงจุดนี้ก็จะได้ Smart Contract ขึ้นไปอยู่บน Blockchain แล้วนะครับ
ลองก๊อปปี้ Address จากผลลัพธ์ใน Terminal ไปดูที่ https://ropsten.etherscan.io/ ได้เลย
ใครลองทำตามแล้วเทียบผลลัพธ์ใน Etherscan ดู อาจจะพบว่า ตรง Contract ของคุณไม่มีติ๊กสีเขียวอยู่
ซึ่งสิ่งนั้นจะเกิดขึ้นได้ เราต้อง “Verify” โค้ด Smart Contract เราก่อน เพราะปกติแล้วเราจะอ่านโค้ดของ Contract ที่ยังไม่ Verify ไม่ได้ จะเห็นเป็น Bytecode ยาว ๆ
Verify ยังไง? ตามมาดูกัน! หัวข้อสุดท้ายแล้ววว
5: ยืนยันว่ารักยังยั่งยืนอยู่ยั้งยืนยง
เนื่องจาก Blockchain นั้น ใคร ๆ ก็สามารถ Deploy Contract ขึ้นไปได้ (Permissionless) และใครก็สามารถดูโค้ดได้ด้วย
…แต่โค้ดที่เห็นนั้นก็จะเป็น ByteCode ที่มนุษย์เรา ๆ อ่านไม่รู้เรื่อง
ดังนั้น ถ้านักพัฒนาอยากให้ผู้ใช้สามารถมาตรวจสอบโค้ด(แบบที่มนุษย์อ่านออก)ด้วยตัวเอง ก็ต้องยืนยัน(Verify)โค้ดกันเสียก่อน ว่าโค้ดต้นฉบับก่อนที่จะกลายเป็น ByteCode นั้น หน้าตาเป็นยังไง
ซึ่งด้วยพลังแห่ง Hardhat นั้น ก็ทำได้ง่ายมาก โดยรันแค่นี้เลย! (ต้อง Config .env
ตัว ETHERSCAN_API_KEY
ก่อนนะครับ)
npx hardhat verify --network ropsten 0x8b53e7A4aBa76b8e2cdA11b0A3d7cBf5847C5aB6
ทีนี้ไม่ว่าใคร ก็สามารถดูโค้ดของเรา และตรวจสอบด้วยตัวเองได้แล้วครับ!
⚠️ Challenge6: ลอง Verify ในเว็บ Etherscan ดู ได้เหมือนกันครับ
ซึ่งถ้า Deploy โค้ดเดิมเป๊ะไป มันจะมองว่าเราเคย Verify ไปแล้ว ทำให้เราลอง Verify ใหม่ไม่ได้ เลยขอ Challenge ให้แก้โค้ด โดยการเอา
console.log
ออกครับ เพราะอย่างไรconsole.log
ใช้ได้เฉพาะในตอนเราเทสด้วย Hardhat เท่านั้นอยู่แล้ว ขึ้น Blockchain ไปไม่มีประโยชน์อะไร รังแต่ทำให้ Contract เราใหญ่ขึ้น
🕵🏻♂️ Checkpoint 5: ความรักจะยั่งยืนตลอดไป
ถึงจุดนี้ เราก็จะได้ Smart Contract ที่ผ่านการทดสอบอย่างหนักหน่วง(?) ขึ้นไปอยู่บน Blockchain จริง ๆ แล้วนะครับ
แถมการ Deploy & Verify ก็ยังง่ายดาย เพียงแค่รัน Command บรรทัดเดียวเท่านั้นเอง!
⚠️ Challenge7: Deploy & Verify บน Mainnet ดูครับ (ถ้า Ethereum แก๊สอาจจะแพงกระเป๋าตังขาดกระจุย ลองดู Chain อื่น ๆ เช่น Binance Smart Chain, Polygon ดูได้ครับ)
หากอยากลองยิง Smart Contract ส่งจดหมายรักบน Blockchain จริงดู ขอแนะนำให้รู้จักกับบทความสุดเทพ by Earth ชาว Cleverse อีกคนนึงครับ จะได้ลองใช้ JavaScript ในการ Interact กับ Smart Contract ผ่าน Ethers.js ซึ่งง่ายมากกก
🏁 Goal! ถึงจุดหมายแล้ว
ยินดีกับทุกท่านด้วย ที่มาถึงจุดนี้ได้ครับ ขอปรบมือให้สองที 👏
แปะ แปะ
ขอ Recap อีกสักทีว่าเราได้ทำอะไรไปบ้างในบทความนี้นะครับ
- ขึ้น Project Hardhat
- เขียน Smart Contract
- Automated Test ผ่าน Hardhat
- Deploy ด้วย Hardhat Script
- Verify Code บน Blockchain Explorer (ด้วย Hardhat Script)
ใช่ครับ ผมก๊อปข้างบนมา 🌝
ทั้งนี้ทั้งนั้น หนทางสู่การเป็นเซียน Solidity นั้นยังอีกยาวไกล เรายังเพิ่งเริ่มก้าวแรกไปด้วยกันเท่านั้น ยังมีเส้นทางสูงชันกว่าจะไปถึงยอดเขาครับ
ดังนั้นติดตามรอดูบทความ Advanced ยิ่งขึ้นจาก Cleverse ได้เลยครับ (เขียนเมื่อไหร่อีกเรื่อง 🎅🏼)