การใช้ Custom Errors ในภาษา Solidity
![การใช้ Custom Errors ในภาษา Solidity](/_vercel/image?url=%2Fimages%2Fcovers%2Fsolidity.png&w=828&q=80)
การทำ Error Handling น่าจะเป็น common ที่มีในทุกๆภาษา เพื่อจัดการกับ error case และ error message ซึ่ง Solidity ก็มีเช่นกัน ใน Solidity เรามักจะใช้ revert
กับ require
โดยวันนี้จะมาพูดถึงเรื่อง Custom Error ซึ่งจะใช้ในการทำ error ที่มันมากกว่าการใช้แค่ string เวลาเราจะใช้ revert('Error')
วิธีการทำ Custom Errors ในภาษา Solidity นั้นมีมาตั้งแต่ Solidity v0.8.4 ข้อดีคือลดค่า gas และก็ error message สื่อความหมาย ว่า error เพราะอะไร ข้อดีของ Custom Error คือมันค่อนข้าง dynamic เราสามารถใช้ type อื่นๆได้นอกเหนือจาก string
ตัวอย่าง
การใช้งาน custom error ง่ายๆ แค่ประกาศและตั้งชื่อเป็น error แบบนี้
1error Unauthorized();
หรืออยากใช้ Error ร่วมกับ Parameters ก็สามารถกำหนดได้เหมือนการสร้าง function
1error InsufficientBalance(uint256 available, uint256 required);
ทดลองสร้างโปรเจ็ค
ลองสร้างโปรเจ็คด้วย Hardhat ขึ้นมาแบบง่ายๆ เพื่อลองเทส Custom Error
1# สร้างโฟลเดอร์ชื่อ custom-error2mkdir custom-error & cd custom-error3
4yarn init -y5yarn add hardhat -D6
7yarn hardhat8# หรือ npx hardhat
โดยผมเลือกเป็น Sample project ธรรมดาเลย
1👷 Welcome to Hardhat v2.10.1 👷2
3✔ What do you want to do? · Create a JavaScript project4✔ Hardhat project root: · /Users/chai/my-folder5✔ Do you want to add a .gitignore? (Y/n) · y6✔ Do you want to install this sample project's dependencies with yarn (...)? (Y/n) · y
สร้างไฟล์ CustomError.sol
ขึ้นมา มี function ง่ายๆ คือ hello()
และ hi(uint256 amount)
ที่จะ revert custom error
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.9;3
4contract CustomError {5 uint256 public unlockTime;6 address payable public owner;7
8 error Unauthorized();9 error InvalidAmount(uint256 amount, address _address);10
11 function hello() public pure {12 revert Unauthorized();13 }14
15 function hi(uint256 amount) public view returns (bool) {16 // 10 wei17 if (amount > 10) {18 revert InvalidAmount(amount, msg.sender);19 }20 return true;21 }22}
สร้างไฟล์ test/custom-error.test.js
ขึ้นมา
1const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers')2const { expect } = require('chai')3
4describe('CustomError', function () {5 async function deployContractFixture() {6 const [owner] = await ethers.getSigners()7
8 const CustomErrorContract = await ethers.getContractFactory('CustomError')9 const contract = await CustomErrorContract.deploy()10 return { contract, owner }11 }12
13 it('should revert with custom error "Unauthorized"', async () => {14 const { contract } = await loadFixture(deployContractFixture)15
16 await expect(contract.hello()).to.be.revertedWithCustomError(contract, 'Unauthorized')17 })18
19 it('should revert with custom error "InvalidAmount"', async () => {20 const { contract, owner } = await loadFixture(deployContractFixture)21
22 const TWENTY_WEI = 2023
24 await expect(contract.hi(TWENTY_WEI))25 .to.be.revertedWithCustomError(contract, 'InvalidAmount')26 .withArgs(TWENTY_WEI, owner.address)27 })28
29 it('should say hi and return TRUE', async () => {30 const { contract } = await loadFixture(deployContractFixture)31
32 const FIVE_WEI = 533
34 const hi = await contract.hi(FIVE_WEI)35 await expect(hi).to.be.true36 })37})
จากไฟล์เทสคือ
- เราใช้
loadFixture
ซึ่งเป็น helper ของ hardhat จัดการเรื่อง fixture ให้เรา - ใช้
revertedWithCustomError
ซึ่งปกติ ถ้า revert เราใช้revertedWith()
1await expect(contract.hello()).to.be.revertedWithCustomError(contract, 'Unauthorized')
- และสำหรับ Custom Error ที่มีหลาย parameter ก็เลย เทสด้วย
.withArgs
เพื่อเช็คว่า argument ที่ส่งไป Custom Error ถูกต้องหรือไม่
1await expect(contract.hi(TWENTY_WEI))2 .to.be.revertedWithCustomError(contract, 'InvalidAmount')3 .withArgs(TWENTY_WEI, owner.address)
ส่วน Test สุดท้าย ไม่มีอะไรมาก call hi()
ถ้าส่ง wei ไปไม่ถึง 10 ก็ ไม่โดน revert ได้ return true กลับมา ทดลองรันไฟล์เทส
yarn hardhat test test/custom-error.test.js
# หรือ npx hardhat test test/custom-error.test.js
จะได้ผลลัพธ์
CustomError ✔ should revert with custom error "Unauthorized" (519ms) ✔ should revert with custom error "InvalidAmount" ✔ should say hi and return TRUE
3 passing (540ms)
เป็นอันเรียบร้อย 🎉
สรุป
บทความนี้ก็เป็นการแนะนำ Custom Error แบบคร่าวๆ ว่าการใช้งานทำยังไง และเราจะ design custom error แบบไหน ให้มันสื่อหรือเข้าใจง่าย รวมถึงตัวอย่างการเทส เงื่อนไขการ revert แบบคร่าวๆ โดยใช้ revertedWithCustomError
หวังว่าบทความนี้จะมีประโยชน์ไม่มากก็น้อย
Reference
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust