Payable vault, withdrawal and CEI
01 — Bài toán
Thiết kế một Smart Contract có thể được nhiều địa chỉ gọi mà không cần tin cậy một backend quản lý state.
02 — Vấn đề
Code chạy trong EVM, mỗi state write tiêu tốn gas và giao dịch có thể bị gọi từ bất kỳ địa chỉ nào. Java private không tương đương với bí mật on-chain.
03 — Giải pháp
Áp dụng các primitive Solidity đúng ngữ cảnh, xác minh preconditions, cập nhật state nhất quán và kiểm thử trường hợp thất bại.
04 — Lý thuyết
Native coin transfers are external calls. Update internal state before calling a recipient (Checks–Effects–Interactions), and revert on failure. Contract balance can exceed liabilities via forced transfers, so equality is not guaranteed.
So sánh Java: contract giống một stateful service được nhân bản theo đồng thuận; msg.sender tương tự authenticated caller nhưng không tự đảm bảo user identity; storage tồn tại trên chain, không giống Java heap.
Dependency: M04 EVM; M05 bài trước nếu có.
05 — Hands-on lab: Payable vault, withdrawal and CEI
Chuẩn bị
- Foundry (forge, cast, anvil); Solidity 0.8.24
- Chạy hoàn toàn local, không dùng mainnet funds
Thực hiện
- Đọc
theory.mdvàsolution/src/Lab.sol, xác định state, invariants và external calls. - Tự triển khai
starter/src/Lab.soltrước khi xem solution. - Viết các test trong
starter/test/Lab.t.sol. cd solution && forge build && forge test -vvvforge test --gas-reportđể quan sát chi phí.forge test -vvvvnếu test fail, đọc call trace và revert reason.
Các test trong reference
testDepositWithdrawtestOverdrawRevertstestZeroDepositRevertstestReceiverFailureRollsBack
Expected output
forge build thành công, tất cả các test trên PASS. Đây là kết quả dự kiến, chưa được xác minh trong môi trường đóng gói vì không có forge/solc.
Troubleshooting
forge: command not found: cài Foundry chính thức.- Compiler download error: kiểm tra network và solc_version.
- Assertion failure: chạy
forge test -vvvvđể xem trace.
07 — Exercises
- Implement contract và test từ đầu trong
starter/, không copy solution. - Write an adversarial receiver contract that attempts reentry; verify it cannot withdraw more than its balance.
- Bổ sung ít nhất một negative test (revert) và một edge case.
- Viết giải thích vì sao state thay đổi hoặc revert; so sánh với Java service.
Acceptance criteria: forge build pass; toàn bộ test pass; không sử dụng tài sản mainnet; README ghi các invariants và assumptions.
08 — Interview
Q1: Vì sao contract cần kiểm tra msg.sender? Bởi mọi địa chỉ đều có thể gửi transaction; quyền phải được enforce on-chain, không thể chỉ dựa vào API gateway.
Q2: view và pure khác nhau thế nào? view có thể đọc state nhưng không sửa state; pure không đọc hoặc sửa state contract.
Q3: Revert ảnh hưởng gì? State updates trong phạm vi bị revert được rollback, nhưng gas đã tiêu thụ không được hoàn lại hoàn toàn.
Q4: Payable vault, withdrawal and CEI có rủi ro gì? Kiểm tra access control, arithmetic bounds, external calls, assumptions về người gọi và accounting invariants.