แดชบอร์ด QA ตรวจสอบสถานะสมาร์ทคอนแทรct  โพสต์ก่อนหน้านี้ได้อธิบายการใช้งานแบบครบวงจร: สัญญาโทเค็นขั้นต่ำ การบันทึกสถานะนอกเชนแดชบอร์ด QA ตรวจสอบสถานะสมาร์ทคอนแทรct  โพสต์ก่อนหน้านี้ได้อธิบายการใช้งานแบบครบวงจร: สัญญาโทเค็นขั้นต่ำ การบันทึกสถานะนอกเชน

สถานะบัญชี Ethereum: QA Pipeline สำหรับ Token ขั้นต่ำ

2026/04/09 13:48
5 นาทีในการอ่าน
หากมีข้อเสนอแนะหรือข้อกังวลเกี่ยวกับเนื้อหานี้ โปรดติดต่อเราได้ที่ [email protected]
แดชบอร์ด QA ที่ติดตามสถานะของสมาร์ทคอนแทรกต์

โพสต์ก่อนหน้านี้ได้อธิบายการทำงานแบบ end-to-end: สัญญาโทเค็นขั้นต่ำ, การสร้างสถานะ off-chain ใหม่, และ frontend React — ตั้งแต่ `mint()` ไปจนถึง MetaMask โพสต์นี้จะต่อยอดจากที่ทิ้งไว้: คุณจะทำ QA สิ่งแบบนี้ได้อย่างไร?

ผมไม่ใช่วิศวกรบล็อกเชน (ยัง) แต่รูปแบบ QA สามารถนำไปใช้ได้ดีในหลายๆ โดเมน และการยืมสิ่งที่ใช้ได้ผลจากที่อื่นคือวิธีที่ผมเรียนรู้เร็วที่สุด

สัญญาทำเพียงสามอย่าง: `mint`, `transfer`, และ `burn` แต่นั่นก็เพียงพอที่จะฝึกใช้เครื่องมือ QA ทั้งหมด: static analysis, mutation testing, gas profiling, formal verification

โค้ดอยู่ใน `egpivo/ethereum-account-state`

พีระมิด QA ของบล็อกเชน: จาก static analysis ที่ฐานไปจนถึง formal verification ที่ด้านบน

สิ่งที่เราเริ่มต้นด้วย

ก่อนที่จะเพิ่มสิ่งใหม่ๆ โปรเจกต์มีอยู่แล้ว:

  • 21 Foundry unit tests ครอบคลุมการเปลี่ยนสถานะแต่ละอย่าง (สำเร็จ, revert เมื่อได้รับ input ที่ผิดกฎหมาย, การปล่อย event)
  • 3 invariant tests ผ่าน `TokenHandler` ที่รันลำดับสุ่มของ `mint`/`transfer`/`burn` บน 10 actors (128k calls แต่ละตัว)
  • Fuzz tests ตรวจสอบ `sum(balances) == totalSupply` สำหรับจำนวนสุ่ม
  • TypeScript domain tests (Vitest) สะท้อน state machine บน on-chain
  • CI: compile, test, lint (Prettier + solhint)

ทุกการทดสอบผ่าน Coverage ดูดี แล้วทำไมต้องทำมากกว่านี้?

เพราะ "การทดสอบทั้งหมดผ่าน" ไม่ได้หมายความว่า "จับบั๊กทั้งหมดได้" 100% line coverage ยังคงพลาดบั๊กจริงได้หากไม่มี assertion ตรวจสอบสิ่งที่ถูกต้อง

Phase 1: Smart contract static analysis และ coverage

Slither

Slither(Trail of Bits) จับปัญหาที่มองไม่เห็นในการทดสอบ: reentrancy, unchecked return values, interface mismatches

./scripts/run-qa.sh slither

ผลลัพธ์: 1 Medium finding: `erc20-interface`: `transfer()` ไม่ return `bool`

นี่เป็นสิ่งที่คาดหวัง สัญญาตั้งใจไม่ให้เป็น ERC20 แบบเต็มรูปแบบ: มันเป็น state machine เพื่อการศึกษา แต่การค้นพบนี้ไม่ใช่เพียงทางวิชาการ:

หากมีคนนำโทเค็นนี้ไปใช้ในโปรโตคอลที่คาดหวัง ERC20 ในภายหลัง interface mismatch จะล้มเหลวอย่างเงียบๆ Slither ทำเครื่องหมายไว้ตอนนี้เพื่อให้การตัดสินใจเป็นไปอย่างรู้ตัว

Coverage

./scripts/run-qa.sh coverageผลลัพธ์ Coverage

ฟังก์ชันหนึ่งที่ไม่ได้ครอบคลุม: `BalanceLib.gt()` เราจะกลับมาที่นี่

ผลลัพธ์ forge coverage: ผ่านการทดสอบ 24 ครั้ง, ตาราง coverage ของ Token.sol

Gas snapshots

./scripts/run-qa.sh gas

ต้นทุน gas พื้นฐานสำหรับการดำเนินการทั้งสาม:

Gas ในแง่ของการดำเนินการ

ในการรันครั้งต่อไป `forge snapshot — diff` เปรียบเทียบกับพื้นฐาน การถดถอย gas 20% ใน `transfer()` เป็นต้นทุนจริงสำหรับผู้ใช้ทุกคน — การจับก่อน merge นั้นถูก

Phase 2: Mutation testing และ formal verification

Mutation testing (Gambit)

นี่คือจุดที่สิ่งต่างๆ เริ่มน่าสนใจ Gambit(Certora) สร้าง mutants: สำเนาของ `Token.sol` ที่มีบั๊กเล็กๆ ที่ตั้งใจ (`+=` เป็น `-=`, `>=` เป็น `>`, เงื่อนไขถูกกลับ) pipeline รันชุดทดสอบทั้งหมดกับ mutant แต่ละตัว หาก mutant รอด (การทดสอบทั้งหมดยังผ่าน) นั่นคือช่องว่างการทดสอบที่เป็นรูปธรรม

./scripts/run-qa.sh mutation

ผลลัพธ์: 97.0% mutation score — ฆ่า 32, รอด 1 จาก 33 mutants

log ผลลัพธ์ของ Gambit แสดง mutant แต่ละตัวและสิ่งที่เปลี่ยนแปลง ตัวอย่างบางส่วน:

Generated mutant #7: BinaryOpMutation — Token.sol:168
totalSupply = totalSupply.add(amountBalance) → totalSupply = totalSupply.sub(amountBalance)
KILLED by test_Mint_Success
Generated mutant #19: RelationalOpMutation — Token.sol:196
if (!fromBalance.gte(amountBalance)) → if (fromBalance.gte(amountBalance))
KILLED by test_Transfer_Success
Generated mutant #28: SwapArgumentsMutation — Token.sol:81
return Balance.unwrap(a) > Balance.unwrap(b) → return Balance.unwrap(b) > Balance.unwrap(a)
SURVIVED ← ไม่มีการทดสอบจับได้Gambit mutation testing: ฆ่า 32, รอด 1, mutation score 97.0%

mutant ที่รอดสลับ `a > b` เป็น `b > a` ใน `BalanceLib.gt()` ไม่มีการทดสอบจับได้เพราะ `gt()` เป็น dead code ไม่มีการเรียกใช้ที่ไหนเลยใน `Token.sol`

Coverage ทำเครื่องหมาย 91.67% functions แต่ไม่สามารถอธิบายช่องว่างได้ Mutation testing ทำได้: `gt()` เป็น dead code ไม่มีอะไรเรียกมัน และไม่มีใครสังเกตเห็นหากมันผิด

โค้ดที่ตายหรือไม่มีการปกป้องในสมาร์ทคอนแทรกต์มีตัวอย่างจริง

ฟังก์ชันนั้นไม่ได้ตั้งใจให้เรียกใช้ได้ แต่ไม่มีใครทดสอบข้อสมมติฐานนั้น `gt()` ของเราไม่เป็นอันตรายเมื่อเทียบ แต่รูปแบบเหมือนกัน: โค้ดที่มีอยู่แต่ไม่เคยถูกใช้งานคือโค้ดที่ไม่มีใครเฝ้าดู

Formal verification (Halmos)

Halmos(a16z) ใช้เหตุผลเกี่ยวกับ input ที่เป็นไปได้ทั้งหมด แบบ symbolic ในขณะที่ fuzz tests สุ่มค่าและหวังว่าจะตี edge cases Halmos พิสูจน์คุณสมบัติอย่างละเอียดถี่ถ้วน

./scripts/run-qa.sh halmos

ผลลัพธ์: 9/9 symbolic tests ผ่าน — พิสูจน์คุณสมบัติทั้งหมดสำหรับ input ทั้งหมด

คุณสมบัติที่ตรวจสอบแล้ว:

คุณสมบัติที่ได้รับการตรวจสอบ

หมายเหตุปฏิบัติหนึ่ง: Halmos 0.3.3 ไม่รองรับ `vm.expectRevert()` ดังนั้นผมจึงไม่สามารถเขียน revert tests แบบ Foundry ปกติได้ วิธีแก้คือรูปแบบ try/catch — หากการเรียกสำเร็จเมื่อควร revert `assert(false)` ทำให้การพิสูจน์ล้มเหลว:

function check_mint_reverts_on_zero_address(uint256 amount) public {
vm.assume(amount > 0);
try token.mint(address(0), amount) {
assert(false); // ไม่ควรมาถึงที่นี่
} catch {
// คาดว่าจะ revert - Halmos พิสูจน์ว่าเส้นทางนี้ถูกเลือกเสมอ
}
}

ไม่สวยงามที่สุด แต่มันใช้งานได้ — Halmos ยังคงพิสูจน์คุณสมบัติสำหรับ input ทั้งหมด นี่คือสิ่งที่คุณจะพบได้ก็ต่อเมื่อรันเครื่องมือจริงๆ

สำหรับบริบทว่าทำไม formal verification ถึงสำคัญ:

ช่องโหว่อยู่ในโค้ด ทุกคนสามารถตรวจสอบได้ แต่ไม่มีเครื่องมือหรือการทดสอบจับได้ก่อนการติดตั้งใช้งาน Symbolic provers เช่น Halmos มีอยู่เพื่อปิดช่องว่างนั้น — พวกมันไม่สุ่มตัวอย่าง พวกมันตรวจสอบ input space อย่างละเอียด

ผลลัพธ์ Halmos: ผ่านการทดสอบ 9 ครั้ง, ล้มเหลว 0, ผลลัพธ์การทดสอบ symbolic

ไฟล์ทดสอบคือ `contracts/test/Token.halmos.t.sol`

Phase 3: Cross-layer property testing

สถาปัตยกรรมของโพสต์แรกมี TypeScript domain layer ที่สะท้อน state machine บน on-chain เฟสนี้ทดสอบว่าทั้งสองตกลงกันจริงหรือไม่

Property-based testing ด้วย fast-check

ผมเพิ่ม fast-check property tests สำหรับ TypeScript domain layer สะท้อนสิ่งที่ fuzzer ของ Foundry ทำสำหรับ Solidity:

npm test - tests/unit/property.test.ts

ผลลัพธ์: 9/9 property tests ผ่าน หลังจากแก้ไขบั๊กจริง

คุณสมบัติที่ทดสอบ:

  • `Balance`: commutativity, associativity, identity, inverse, comparison consistency
  • `Token`: invariant `sum(balances) == totalSupply` ภายใต้ลำดับการดำเนินการสุ่ม (200 runs, 50 ops แต่ละครั้ง)
  • `Token`: `totalSupply` ไม่เป็นค่าลบหลังลำดับสุ่ม
  • `mint` สำเร็จเสมอสำหรับ input ที่ถูกต้อง
  • `transfer` รักษา `totalSupply`

บั๊กที่ fast-check พบ

fast-check พบบั๊ก cross-layer consistency จริงใน `Token.ts` `transfer()` ตัวอย่างตรงกันข้ามที่ย่อมาชัดเจนทันที:

Property failed after 3 tests
Shrunk 2 time(s)
Counterexample: transfer(from=0xaaa…, to=0xaaa…, amount=1n)
→ from == to (self-transfer)
→ verifyInvariant() returned false

Self-transfer (`from == to`) ทำลาย invariant `sum(balances) == totalSupply` `toBalance` ถูกอ่านก่อน`fromBalance` ถูกอัปเดต ดังนั้นเมื่อ `from == to` ค่าเก่าเขียนทับการหัก:

// Before (buggy)
const fromBalance = this.getBalance(from);
const toBalance = this.getBalance(to); // ← เก่าเมื่อ from == to
this.accounts.set(from.getValue(), fromBalance.subtract(amount));
this.accounts.set(to.getValue(), toBalance.add(amount)); // ← เขียนทับการหัก

แก้ไข: อ่าน `toBalance` หลังจากเขียน `fromBalance` ให้ตรงกับ storage semantics ของ Solidity:

// After (fixed)
const fromBalance = this.getBalance(from);
this.accounts.set(from.getValue(), fromBalance.subtract(amount));
const toBalance = this.getBalance(to); // ← ตอนนี้อ่านค่าที่อัปเดตแล้ว
this.accounts.set(to.getValue(), toBalance.add(amount));

สัญญา Solidity ไม่ได้รับผลกระทบ: มันอ่าน storage ใหม่หลังจากแต่ละการเขียน แต่ TypeScript mirror มีการพึ่งพาลำดับที่ละเอียดอ่อนที่ไม่มี unit test ที่มีอยู่ครอบคลุม

Cross-layer mismatches ในขนาดใหญ่กว่าเคยเป็นหายนะ

บั๊ก self-transfer ของเราจะไม่ทำให้ใครเสียเงิน แต่โหมดความล้มเหลวมีโครงสร้างเหมือนกัน: สองชั้นที่ควรตกลงกัน ไม่ตกลงกัน

อุปสรรคที่เจอตามทาง

การรันเครื่องมือ QA บนโปรเจกต์ที่มีอยู่ไม่เคยเป็นแค่ "ติดตั้งและรัน" สิ่งบางอย่างพังก่อนที่จะใช้งานได้:

  • 0% coverage เพราะ `foundry.toml` ไม่มี test path: การรัน `forge coverage` ครั้งแรกคืนค่า 0% ทั่วทั้งบอร์ด ปรากฎว่า `foundry.toml` ไม่ได้ระบุ `test = "contracts/test"` หรือ `script = "contracts/script"` ดังนั้น Forge จึงไม่พบการทดสอบใดๆ คำสั่ง coverage สำเร็จอย่างเงียบๆ — มันแค่ไม่มีอะไรให้ครอบคลุม นี่คือความล้มเหลวที่ทำให้เข้าใจผิดมากที่สุด: การรันสีเขียวโดยไม่มีผลลัพธ์ที่เป็นประโยชน์
  • `InvariantTest` import หายไปใน forge-std v1.14.0: `Invariant.t.sol` import `InvariantTest` จาก `forge-std` ซึ่งถูกลบออกในรุ่นล่าสุด การคอมไพล์ล้มเหลวด้วยข้อผิดพลาด "symbol not found" ที่ไม่ชัดเจน การแก้ไขคือการลบ import — `Test` เพียงอย่างเดียวก็เพียงพอสำหรับ invariant testing ของ Foundry ตอนนี้
  • `uint256(token.totalSupply())` vs `Balance.unwrap()`: การทดสอบใช้การแคสต์โดยชัดแจ้งเพื่อดึง `uint256` ที่อยู่ภายใต้จากประเภท `Balance` ที่ผู้ใช้กำหนด มันคอมไพล์ แต่มันเป็น idiom ที่ผิด — `Balance.unwrap(token.totalSupply())` คือสิ่งที่ระบบ UDVT ออกแบบมา ใช้กับ `Token.t.sol`, `Invariant.t.sol`, และ `DeploySepolia.s.sol`

การออกแบบ Pipeline

ทุกอย่างรันผ่านสองสคริปต์:

  • scripts/setup-qa-tools.sh`: ติดตั้ง Slither, Halmos, Gambit (idempotent)
  • `scripts/run-qa.sh`: รันการตรวจสอบ บันทึกผลลัพธ์พร้อมประทับเวลาไปที่ `qa-results/`

./scripts/run-qa.sh slither gas # เฉพาะ static analysis + gas
./scripts/run-qa.sh mutation # เฉพาะ mutation testing
./scripts/run-qa.sh all # ทุกอย่าง

ไม่ใช่ทุกการตรวจสอบจะเร็ว Slither และ coverage รันทุกการ commit Mutation testing และ Halmos ช้ากว่า — เหมาะสำหรับการรันรายสัปดาห์หรือก่อนการเผยแพร่มากกว่า

สรุป

Blockchain QA Toolchain: สิ่งที่แต่ละชั้นจับได้ — จาก static analysis ไปจนถึง cross-layer property testing

ห้าชั้น QA แต่ละชั้นจับปัญหาประเภทที่ต่างกัน

คำอธิบายชั้น

Gambit และ fast-check ให้ผลลัพธ์ที่นำไปใช้ได้มากที่สุดในรอบนี้

CI pipeline

การตรวจสอบ QA ตอนนี้เชื่อมต่อเข้ากับ GitHub Actions เป็น pipeline หกขั้นตอน:

CI Pipeline: Build & Lint กระจายไปที่ Test, Coverage, Gas, Slither, และ Audit stages

GitHub Actions pipeline: Build & Lint กั้นขั้นตอนทั้งหมดที่อยู่ด้านล่าง

คำอธิบาย Stage

References

  • Ethereum Account State source: [github.com/egpivo/ethereum-account-state](https://github.com/egpivo/ethereum-account-state)
  • Previous post: Ethereum Account State
  • Slither: github.com/crytic/slither
  • Gambit: github.com/Certora/gambit
  • Halmos: github.com/a16z/halmos
  • fast-check: github.com/dubzzz/fast-check
  • Foundry: getfoundry.sh

Notes

  • โพสต์นี้ดัดแปลงมาจากโพสต์บล็อกต้นฉบับของผม

Ethereum Account State: QA Pipeline for a Minimal Token เผยแพร่ครั้งแรกใน Coinmonks บน Medium ซึ่งผู้คนกำลังดำเนินการสนทนาต่อโดยการเน้นและตอบกลับเรื่องราวนี้

ข้อจำกัดความรับผิดชอบ: บทความที่โพสต์ซ้ำในไซต์นี้มาจากแพลตฟอร์มสาธารณะและมีไว้เพื่อจุดประสงค์ในการให้ข้อมูลเท่านั้น ซึ่งไม่ได้สะท้อนถึงมุมมองของ MEXC แต่อย่างใด ลิขสิทธิ์ทั้งหมดยังคงเป็นของผู้เขียนดั้งเดิม หากคุณเชื่อว่าเนื้อหาใดละเมิดสิทธิของบุคคลที่สาม โปรดติดต่อ [email protected] เพื่อลบออก MEXC ไม่รับประกันความถูกต้อง ความสมบูรณ์ หรือความทันเวลาของเนื้อหาใดๆ และไม่รับผิดชอบต่อการดำเนินการใดๆ ที่เกิดขึ้นตามข้อมูลที่ให้มา เนื้อหานี้ไม่ถือเป็นคำแนะนำทางการเงิน กฎหมาย หรือคำแนะนำจากผู้เชี่ยวชาญอื่นๆ และไม่ถือว่าเป็นคำแนะนำหรือการรับรองจาก MEXC

คุณอาจชอบเช่นกัน

หุ้น Netflix (NFLX) ปรับตัวขึ้นจากการอัพเกรดของ Goldman และกระแสการซื้อที่หนุนความเชื่อมั่น

หุ้น Netflix (NFLX) ปรับตัวขึ้นจากการอัพเกรดของ Goldman และกระแสการซื้อที่หนุนความเชื่อมั่น

TLDRs; หุ้น Netflix เพิ่มขึ้นเล็กน้อยขณะที่นักลงทุนสถาบันเพิ่มการถือหุ้นก่อนการประกาศผลประกอบการไตรมาสที่ 1 และการอัปเกรดจาก Goldman ช่วยเพิ่มความเชื่อมั่น Goldman Sachs ซื้อ
แชร์
Coincentral2026/04/09 15:40
FormBlends เปิดตัวแพลตฟอร์ม GLP-1 และ Peptide ชั้นนำพร้อมผู้ให้บริการ 7,282 รายและรายงานทางคลินิก 100 ฉบับ

FormBlends เปิดตัวแพลตฟอร์ม GLP-1 และ Peptide ชั้นนำพร้อมผู้ให้บริการ 7,282 รายและรายงานทางคลินิก 100 ฉบับ

ซานฟรานซิสโก รัฐแคลิฟอร์เนีย วันที่ 9 เมษายน 2569 – FormBlends แพลตฟอร์มเทคโนโลยีด้านสุขภาพที่มุ่งเน้นผลลัพธ์การลดน้ำหนักด้วย GLP-1 และรายชื่อผู้ให้บริการจัดการน้ำหนัก
แชร์
Techbullion2026/04/09 15:26
USD/INR: ความเสี่ยงของช่วงราคาและความไวต่อราคาน้ำมัน – MUFG

USD/INR: ความเสี่ยงของช่วงราคาและความไวต่อราคาน้ำมัน – MUFG

โพสต์ USD/INR: ความเสี่ยงในช่วงและความอ่อนไหวต่อน้ำมัน – MUFG ปรากฏบน BitcoinEthereumNews.com นักวิเคราะห์อาวุโสด้านสกุลเงินของ MUFG Michael Wan คาดว่า USD/INR จะยังคง
แชร์
BitcoinEthereumNews2026/04/09 15:38

ข่าวสดตลอด 24/7

มากกว่า

PRL $30,000 + 15,000 USDT

PRL $30,000 + 15,000 USDTPRL $30,000 + 15,000 USDT

ฝาก & เทรด PRL เพื่อเพิ่มรางวัลของคุณ!