Deep EVM #12 : Huff avancé — Exécution adaptative et calcul on-chain
Engineering Team
Au-delà des bases
Les trois articles précédents ont couvert les fondamentaux de Huff : macros, gestion de pile et tables de saut. Cet article explore les techniques avancées que les développeurs de bots MEV et de protocoles DeFi utilisent en production.
Exécution adaptative
L’exécution adaptative signifie que le contrat ajuste son comportement en fonction de l’état actuel de la blockchain — prix du gas, numéro de bloc, état des pools de liquidité — plutôt que de suivre un chemin d’exécution fixe.
Branchement basé sur le prix du gas
#define macro ADAPTIVE_EXECUTION() = takes(0) returns(0) {
gasprice // [gasPrice]
0x174876E800 // [100 gwei, gasPrice]
gt // [gasPrice > 100 gwei?]
expensive_gas jumpi
// Chemin gas normal — exécution complète
FULL_SWAP()
stop
expensive_gas:
// Chemin gas élevé — seulement les opérations rentables
MINIMAL_SWAP()
stop
}
Ce pattern est crucial pour les bots MEV : quand le gas est élevé, seules les opportunités les plus rentables valent la peine d’être poursuivies.
Vérification d’état on-chain
Avant d’exécuter un swap d’arbitrage, vérifiez que les réserves du pool n’ont pas changé depuis votre simulation :
#define macro CHECK_RESERVES() = takes(2) returns(0) {
// takes: [expected_reserve0, expected_reserve1]
// Appeler getReserves() sur le pool
0x0902f1ac // selector getReserves()
0x00 mstore
0x00 0x00 0x04 0x00 // retOffset, retSize, argsSize, argsOffset
[POOL] gas staticcall // [success]
iszero revert_path jumpi
// Comparer avec les réserves attendues
0x00 mload // [actual_reserve0]
dup3 // [expected_reserve0, actual_reserve0]
eq iszero revert_path jumpi
0x20 mload // [actual_reserve1]
dup3 // [expected_reserve1, actual_reserve1]
eq iszero revert_path jumpi
// Les réserves correspondent — continuer
pop pop // nettoyer la pile
continue jump
revert_path:
0x00 0x00 revert // Annuler — les conditions ont changé
continue:
}
Authentification multi-opérateur
Pour les bots MEV opérés par plusieurs adresses :
#define macro IS_AUTHORIZED() = takes(0) returns(0) {
caller // [caller]
dup1 [OPERATOR_1] eq // [isOp1?, caller]
authorized jumpi
dup1 [OPERATOR_2] eq // [isOp2?, caller]
authorized jumpi
[OPERATOR_3] eq // [isOp3?]
authorized jumpi
0x00 0x00 revert
authorized:
pop // nettoyer la pile
}
Chaque vérification supplémentaire coûte environ 15 gas (PUSH20 + EQ + JUMPI). Pour 3 opérateurs, c’est un overhead négligeable.
Astuces de disposition mémoire
Réutilisation de zones mémoire
Dans un contrat Huff, vous n’avez pas de pointeur de mémoire libre. Planifiez votre disposition mémoire comme un développeur embarqué :
// Zones mémoire du contrat
// 0x00-0x1f : espace scratch pour keccak256
// 0x20-0x3f : espace scratch pour les retours
// 0x40-0x7f : zone de calldata pour les appels externes
// 0x80-0xbf : zone de stockage temporaire pour les valeurs de pile profondes
En réutilisant les mêmes zones mémoire, vous évitez l’expansion mémoire et les coûts quadratiques associés.
Encodage compact des données
// Au lieu de stocker des adresses complètes de 32 octets :
// Utilisez les 20 octets réels et compactez 2 adresses dans 40 octets
// au lieu de 64 octets
mstore(0x00, shl(96, address1)) // Adresse 1 aux octets 0-19
mstore(0x14, shl(96, address2)) // Adresse 2 aux octets 20-39
Optimisation extrême : chaque octet compte
Remplacer les zéros par RETURNDATASIZE
Avant le premier CALL, RETURNDATASIZE retourne 0 et ne coûte que 2 gas au lieu des 3 gas de PUSH1 0x00 :
// Au lieu de :
0x00 // 3 gas, 2 octets
// Utilisez (avant le premier CALL) :
returndatasize // 2 gas, 1 octet
Économie : 1 gas + 1 octet de bytecode. Sur un contrat avec 50 utilisations de zéro, c’est 50 gas et 50 octets.
SELFBALANCE au lieu de BALANCE
// Au lieu de :
address balance // 2 opcodes, 2605 gas (froid)
// Utilisez :
selfbalance // 1 opcode, 5 gas
Conclusion
Les techniques avancées de Huff — exécution adaptative, disposition mémoire planifiée, et micro-optimisations au niveau de l’octet — séparent les contrats de production des exercices d’apprentissage. Dans les prochains articles, nous passerons au MEV : ce qu’il est, comment trouver des opportunités d’arbitrage, et comment construire un pipeline de simulation.