Aller au contenu principal
BlockchainMar 28, 2026

Deep EVM #17 : Test des contrats Huff — Tests fork Foundry et assertions de gas

OS
Open Soft Team

Engineering Team

Pourquoi tester est critique en Huff

Huff est un langage d’assemblage EVM de bas niveau qui vous donne un contrôle direct sur la pile, la mémoire et le stockage. Cette puissance a un coût : pas de compilateur pour attraper les erreurs de type, pas de SafeMath, pas de vérification automatique des limites. Chaque opcode que vous écrivez est exactement ce qui est déployé. Cela rend le test non seulement important mais absolument critique.

Configuration de l’environnement de test

Foundry est le framework de test idéal pour Huff grâce à foundry-huff :

# foundry.toml
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
ffi = true

[profile.default.fuzz]
runs = 10000
// test/HuffContract.t.sol
import {HuffDeployer} from "foundry-huff/HuffDeployer.sol";

contract HuffTest is Test {
    address public huffContract;

    function setUp() public {
        huffContract = HuffDeployer.deploy("src/Contract");
    }

    function test_getValue() public {
        (bool success, bytes memory data) = huffContract.staticcall(
            abi.encodeWithSignature("getValue()")
        );
        assertTrue(success);
        assertEq(abi.decode(data, (uint256)), 42);
    }
}

Test différentiel

La technique la plus puissante pour les contrats Huff : écrire la même logique en Solidity et vérifier que les deux implémentations produisent des résultats identiques pour toutes les entrées.

contract DifferentialTest is Test {
    address huffImpl;
    SolidityReference solImpl;

    function setUp() public {
        huffImpl = HuffDeployer.deploy("src/Token");
        solImpl = new SolidityReference();
    }

    function testFuzz_transfer(address to, uint256 amount) public {
        // Exécuter les deux implémentations
        (bool huffOk, bytes memory huffResult) = huffImpl.call(
            abi.encodeWithSignature("transfer(address,uint256)", to, amount)
        );
        (bool solOk, bytes memory solResult) = address(solImpl).call(
            abi.encodeWithSignature("transfer(address,uint256)", to, amount)
        );

        // Vérifier que les résultats correspondent
        assertEq(huffOk, solOk, "Success mismatch");
        if (huffOk) {
            assertEq(huffResult, solResult, "Return data mismatch");
        }
    }
}

Tests fork

Les tests fork exécutent vos tests contre un fork de la blockchain réelle :

forge test --fork-url https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY -vvv

Cela permet de tester votre contrat Huff contre les vrais pools Uniswap, les vrais tokens ERC-20, et le vrai état on-chain.

function test_swapOnMainnet() public {
    // Fork au bloc spécifique pour la reproductibilité
    vm.createSelectFork("mainnet", 18_000_000);

    // Déployer le contrat Huff
    address swap = HuffDeployer.deploy("src/Swap");

    // Tester contre le vrai pool Uniswap
    deal(WETH, swap, 1 ether);
    (bool success,) = swap.call(
        abi.encodeWithSignature("executeSwap()")
    );
    assertTrue(success);
}

Snapshots et assertions de gas

Les snapshots de gas permettent de détecter les régressions de performance :

function test_gasUsage() public {
    uint256 gasBefore = gasleft();
    (bool success,) = huffContract.call(
        abi.encodeWithSignature("transfer(address,uint256)", bob, 100)
    );
    uint256 gasUsed = gasBefore - gasleft();

    assertTrue(success);
    assertLt(gasUsed, 30_000, "Gas regression detected");
}

Ou avec forge snapshot :

forge snapshot
# Crée .gas-snapshot avec les coûts de gas de chaque test

Stratégies de test pour Huff

  1. Test de chaque opcode — Vérifiez chaque chemin de branchement dans vos macros
  2. Tests aux limites — Testez avec 0, 1, MAX_UINT256, et les valeurs de bordure
  3. Test de réentrance — Vérifiez que votre contrat résiste aux appels de réentrance
  4. Test de sous-débordement de pile — Envoyez des calldata malformés
  5. Test différentiel systématique — Comparez avec Solidity pour chaque fonction

Conclusion

Tester les contrats Huff nécessite plus de rigueur que Solidity car il n’y a pas de filet de sécurité du compilateur. Le test différentiel, les tests fork et les assertions de gas constituent la base d’une suite de tests robuste.