Testing Kontrak Huff dengan Foundry Fork Test dan Asersi Gas
Engineering Team
Mengapa Testing Kontrak Huff Berbeda
Kontrak Huff beroperasi di tingkat opcode mentah — tanpa pemeriksaan overflow, tanpa revert message, tanpa guardrail Solidity. Ini berarti testing menjadi lebih kritis: satu kesalahan stack akan menyebabkan perilaku yang tidak terduga tanpa error message yang berguna.
Foundry adalah framework testing terbaik untuk kontrak Huff karena:
- foundry-huff — Plugin yang mengkompilasi dan men-deploy file
.huffdalam test Solidity - Fork testing — Simulasikan terhadap state mainnet nyata
- Gas reporting — Lacak biaya gas per fungsi
- Fuzzing — Temukan edge case dengan input acak
Setup foundry-huff
forge install huff-language/foundry-huff
Tambahkan ke foundry.toml:
[profile.default]
ffi = true # Diperlukan untuk kompilasi Huff
Contoh deployment di test:
import {HuffDeployer} from "foundry-huff/HuffDeployer.sol";
contract MyHuffTest is Test {
address public target;
function setUp() public {
target = HuffDeployer.deploy("src/MyContract");
}
function testBasicFunction() public {
(bool ok, bytes memory data) = target.call(
abi.encodeWithSignature("getValue()")
);
assertTrue(ok);
uint256 value = abi.decode(data, (uint256));
assertEq(value, 42);
}
}
Fork Testing Terhadap Mainnet
Fork test memungkinkan Anda menguji kontrak Huff terhadap state blockchain nyata — termasuk token balance, pool reserves, dan harga oracle.
contract ForkTest is Test {
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address constant UNISWAP_PAIR = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc;
address bot;
function setUp() public {
// Fork mainnet pada block tertentu
vm.createSelectFork("mainnet", 18_000_000);
bot = HuffDeployer.deploy("src/SwapBot");
// Berikan WETH ke bot
deal(WETH, bot, 10 ether);
}
function testSwapExactIn() public {
uint256 balanceBefore = IERC20(USDC).balanceOf(address(this));
// Panggil bot untuk swap WETH -> USDC
(bool ok,) = bot.call(
abi.encodePacked(
uint8(0x01), // opcode: swapExactIn
uint256(1 ether) // amount
)
);
assertTrue(ok, "Swap gagal");
uint256 balanceAfter = IERC20(USDC).balanceOf(address(this));
assertGt(balanceAfter, balanceBefore, "Tidak menerima USDC");
}
}
Keuntungan Fork Testing
- State nyata — Reserves pool, oracle price, token balance semuanya nyata
- Reprodusibilitas — Pin ke block tertentu untuk hasil deterministik
- Edge case — Temukan masalah dengan token non-standar (USDT, rebase token)
Asersi Gas
Untuk kontrak Huff, gas adalah metrik kualitas utama. Foundry menyediakan snapshot gas:
function testGasSwap() public {
uint256 gasBefore = gasleft();
(bool ok,) = bot.call(
abi.encodePacked(uint8(0x01), uint256(1 ether))
);
assertTrue(ok);
uint256 gasUsed = gasBefore - gasleft();
// Asersi batas gas
assertLt(gasUsed, 80_000, "Swap melebihi batas gas");
// Snapshot untuk tracking regresi
emit log_named_uint("Gas swap", gasUsed);
}
Gas Snapshot untuk CI/CD
# Buat snapshot awal
forge snapshot
# Bandingkan dengan snapshot sebelumnya
forge snapshot --diff
Output menunjukkan fungsi mana yang gas-nya berubah:
testGasSwap() (gas: -150) ✓
testGasTransfer() (gas: +30) ⚠
Fuzzing Kontrak Huff
Fuzzing sangat penting untuk kontrak Huff karena tidak ada pemeriksaan otomatis:
function testFuzz_SwapAmount(uint256 amount) public {
// Batasi input ke range yang masuk akal
amount = bound(amount, 0.001 ether, 100 ether);
deal(WETH, bot, amount);
(bool ok,) = bot.call(
abi.encodePacked(uint8(0x01), amount)
);
// Swap harus selalu berhasil untuk jumlah yang valid
assertTrue(ok, "Swap gagal untuk jumlah valid");
}
function testFuzz_InvalidCalldata(bytes calldata data) public {
// Kontrak harus menangani calldata invalid dengan baik
(bool ok,) = bot.call(data);
// Boleh gagal, tetapi tidak boleh hang atau berperilaku aneh
// Verifikasi state tidak berubah
}
Jalankan dengan iterasi tinggi:
forge test --match-test testFuzz -vvv --fuzz-runs 10000
Testing Asersi Stack
Untuk memverifikasi bahwa makro Huff menangani stack dengan benar:
function testStackBalance() public {
// Test bahwa kontrak mengembalikan nilai yang benar
// untuk berbagai input — jika stack tidak seimbang,
// hasilnya akan salah
for (uint256 i = 0; i < 100; i++) {
(bool ok, bytes memory data) = target.call(
abi.encodeWithSignature("compute(uint256)", i)
);
assertTrue(ok);
uint256 result = abi.decode(data, (uint256));
assertEq(result, expectedResult(i));
}
}
Testing Keamanan
function testReentrancy() public {
// Deploy kontrak penyerang yang mencoba reentrancy
Attacker attacker = new Attacker(bot);
// Berikan dana ke attacker
deal(WETH, address(attacker), 10 ether);
// Serangan harus gagal
vm.expectRevert();
attacker.attack();
}
function testUnauthorizedAccess() public {
// Panggil fungsi admin dari non-operator
vm.prank(address(0xdead));
(bool ok,) = bot.call(
abi.encodePacked(uint8(0x03)) // withdraw
);
assertFalse(ok, "Non-operator berhasil memanggil withdraw");
}
Best Practice Testing Huff
- 100% path coverage — Test setiap cabang JUMPI di kontrak
- Boundary testing — Test dengan 0, 1, MAX_UINT256, dan nilai batas lainnya
- Gas regression — Jalankan gas snapshot di CI untuk mendeteksi regresi
- Fork test — Selalu test terhadap state mainnet nyata
- Fuzzing — Minimal 10.000 iterasi per fungsi
- Negative testing — Verifikasi bahwa calldata invalid ditangani dengan benar
- Dokumentasi test — Setiap test harus menjelaskan skenario apa yang diverifikasi
Kesimpulan
Testing kontrak Huff memerlukan pendekatan yang lebih ketat dari testing Solidity karena tidak ada safety net. Foundry dengan foundry-huff memberikan toolchain lengkap: fork testing untuk akurasi state, asersi gas untuk regresi, dan fuzzing untuk edge case. Investasi dalam test suite yang komprehensif membayar dirinya sendiri dengan mencegah bug yang mahal di produksi.