[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-9-introduction-huff-macros-labels-opcodes":3},{"article":4,"author":54},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":7,"meta_description":16,"focus_keyword":17,"og_image":18,"canonical_url":18,"robots_meta":19,"created_at":15,"updated_at":15,"tags":20,"category_name":34,"related_articles":35},"d6000000-0000-0000-0000-000000000109","a0000000-0000-0000-0000-000000000062","Deep EVM #9 : Introduction à Huff — Macros, labels et opcodes bruts","deep-evm-9-introduction-huff-macros-labels-opcodes","Introduction pratique à Huff, le langage d'assemblage EVM de bas niveau qui vous donne un contrôle direct sur chaque opcode, chaque octet de bytecode et chaque unité de gas.","## Pourquoi Huff existe\n\nSolidity est une abstraction merveilleuse — jusqu'à ce qu'elle ne le soit plus. Quand vous avez besoin d'un contrat qui tient dans 100 octets de bytecode runtime, qui dispatche les fonctions en O(1) avec une table de saut compacte, ou qui économise 200 gas sur un chemin critique exécuté des millions de fois par jour, vous avez besoin de quelque chose plus proche du métal. Ce quelque chose, c'est **Huff**.\n\nHuff est un langage d'assemblage EVM de bas niveau avec un système de macros léger ajouté par-dessus. Il n'a pas de variables, pas de types, pas de compilateur qui optimise dans votre dos. Ce que vous écrivez est ce qui finit on-chain — opcode par opcode.\n\n## Installer Huff\n\nLe compilateur canonique est `huffc`, écrit en Rust :\n\n```bash\ncurl -L get.huff.sh | bash\nhuffup\nhuffc --version\n```\n\nCela installe `huffc` dans `~\u002F.huff\u002Fbin`. Ajoutez-le à votre PATH et vérifiez :\n\n```bash\n$ huffc --version\nhuffc 0.3.2\n```\n\nVous pouvez aussi utiliser Huff dans les projets Foundry avec `foundry-huff`, qui permet de déployer des fichiers `.huff` de la même manière que des fichiers `.sol`.\n\n## Hello World : un contrat minimal\n\nÉcrivons un contrat qui retourne le mot de 32 octets `0x01` à tout appel :\n\n```huff\n#define macro MAIN() = takes(0) returns(0) {\n    0x01            \u002F\u002F [0x01]\n    0x00            \u002F\u002F [0x00, 0x01]\n    mstore          \u002F\u002F []          — memory[0x00..0x20] = 0x01\n    0x20            \u002F\u002F [0x20]\n    0x00            \u002F\u002F [0x00, 0x20]\n    return          \u002F\u002F halt — retourner memory[0x00..0x20]\n}\n```\n\nCompilez :\n\n```bash\nhuffc src\u002FHelloWorld.huff -r\n```\n\nLe flag `-r` produit le bytecode runtime. Vous obtiendrez quelque chose comme `600160005260206000f3` — 10 octets. Un contrat Solidity retournant `1` compile en environ 200+ octets de bytecode runtime parce que solc émet un dispatcher complet, un hash de métadonnées, la configuration du pointeur de mémoire libre et un encodeur ABI.\n\n## Macros vs fonctions\n\nHuff a deux primitives de réutilisation de code : les **macros** et les **fonctions**.\n\n### Macros (`#define macro`)\n\nLes macros sont inlinées à chaque site d'appel. Pas d'overhead de JUMP, pas de gas supplémentaire — le compilateur copie littéralement les opcodes dans l'appelant. C'est le choix par défaut et préféré pour le code critique en gas.\n\n```huff\n#define macro REQUIRE_NOT_ZERO() = takes(1) returns(0) {\n    \u002F\u002F takes: [value]\n    continue        \u002F\u002F [continue_dest, value]\n    jumpi           \u002F\u002F []  — sauter si value != 0\n    0x00 0x00 revert\n    continue:\n}\n```\n\n### Fonctions (`#define fn`)\n\nLes fonctions génèrent une vraie paire JUMP\u002FJUMPDEST. Elles économisent la taille du bytecode au prix d'environ 22 gas supplémentaires par appel. Utilisez-les uniquement quand la taille du bytecode compte plus que le gas.\n\n## Labels et destinations de saut\n\nLes labels en Huff sont des emplacements JUMPDEST nommés. Le compilateur les résout en offsets concrets dans le bytecode à la compilation.\n\n```huff\n#define macro LOOP_EXAMPLE() = takes(1) returns(1) {\n    \u002F\u002F takes: [n]\n    0x00                \u002F\u002F [acc, n]\n    loop:\n        dup2            \u002F\u002F [n, acc, n]\n        iszero          \u002F\u002F [n==0?, acc, n]\n        done jumpi      \u002F\u002F [acc, n]\n        swap1           \u002F\u002F [n, acc]\n        0x01 swap1 sub  \u002F\u002F [n-1, acc]\n        swap1           \u002F\u002F [acc, n-1]\n        0x01 add        \u002F\u002F [acc+1, n-1]\n        loop jump\n    done:\n        swap1 pop       \u002F\u002F [acc]\n}\n```\n\nChaque label compile en un seul octet `JUMPDEST` (`0x5b`). Les références compilent en `PUSH2 \u003Coffset> JUMP` (ou `JUMPI`). C'est exactement ce que vous écririez à la main en assembleur EVM brut — Huff gère simplement la comptabilité des offsets.\n\n## takes() et returns()\n\nLes annotations `takes(n)` et `returns(m)` sur les macros et fonctions sont de la documentation et des indices pour le compilateur. Elles indiquent — au lecteur et au vérificateur de pile de Huff — combien d'éléments de pile le bloc s'attend à consommer et produire.\n\n```huff\n#define macro ADD_TWO() = takes(2) returns(1) {\n    add  \u002F\u002F consomme 2 éléments, en produit 1\n}\n```\n\n## Comparaison : Huff vs bytecode Solidity\n\nConsidérons une simple fonction vue `getValue()` qui retourne un slot de stockage :\n\n**Solidity :**\n```solidity\nfunction getValue() external view returns (uint256) {\n    return value;\n}\n```\n\nSolc génère environ 40 octets pour le dispatcher + encodage ABI.\n\n**Équivalent Huff :**\n```huff\n#define function getValue() view returns (uint256)\n\n#define macro GET_VALUE() = takes(0) returns(0) {\n    [VALUE_SLOT]    \u002F\u002F [slot]\n    sload           \u002F\u002F [value]\n    0x00 mstore     \u002F\u002F []  — stocker en mémoire\n    0x20 0x00 return\n}\n```\n\nLa version Huff fait 12 octets de bytecode pour le corps. Pas d'overhead d'encodage ABI, pas de pointeur de mémoire libre, pas de hash de métadonnées.\n\n## Constantes et slots de stockage\n\nLes constantes Huff sont des valeurs à la compilation qui sont inlinées comme instructions PUSH :\n\n```huff\n#define constant VALUE_SLOT = 0x00\n#define constant OWNER_SLOT = 0x01\n#define constant MAX_UINT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n```\n\nUtilisation : `[VALUE_SLOT]` empile `0x00`, `[MAX_UINT]` empile la valeur complète de 32 octets. Les constantes améliorent la lisibilité sans coûter de gas — elles sont purement syntaxiques.\n\n## Quand utiliser Huff\n\nHuff n'est pas un langage généraliste. Utilisez-le quand :\n\n1. **Le gas est la contrainte principale** — Contrats MEV où 100 gas détermine la rentabilité.\n2. **La taille du bytecode compte** — Contrats déployés par d'autres contrats (factories CREATE2) où un initcode plus petit = moins de gas de déploiement.\n3. **Vous avez besoin d'un dispatch personnalisé** — Tables de saut, sélecteurs compactés, ou encodage ABI non standard.\n4. **Vous apprenez l'EVM** — Rien n'enseigne mieux l'EVM que l'écriture d'opcodes bruts.\n\nPour tout le reste, écrivez en Solidity et lisez la sortie du compilateur avec `solc --asm`. Vous serez plus productif et moins sujet aux erreurs.\n\n## Résumé\n\nHuff vous donne une ligne directe vers le bytecode EVM avec juste assez d'abstraction pour rester sain d'esprit. Les macros inlinent le code pour une réutilisation à overhead nul. Les labels gèrent la comptabilité des offsets de saut. Les annotations `takes`\u002F`returns` attrapent les erreurs de pile tôt. Dans le prochain article, nous plongerons plus profondément dans la gestion de pile — l'art du `dup`, `swap`, et du maintien de votre modèle mental de la pile en synchronisation avec la réalité.","\u003Ch2 id=\"pourquoi-huff-existe\">Pourquoi Huff existe\u003C\u002Fh2>\n\u003Cp>Solidity est une abstraction merveilleuse — jusqu’à ce qu’elle ne le soit plus. Quand vous avez besoin d’un contrat qui tient dans 100 octets de bytecode runtime, qui dispatche les fonctions en O(1) avec une table de saut compacte, ou qui économise 200 gas sur un chemin critique exécuté des millions de fois par jour, vous avez besoin de quelque chose plus proche du métal. Ce quelque chose, c’est \u003Cstrong>Huff\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>Huff est un langage d’assemblage EVM de bas niveau avec un système de macros léger ajouté par-dessus. Il n’a pas de variables, pas de types, pas de compilateur qui optimise dans votre dos. Ce que vous écrivez est ce qui finit on-chain — opcode par opcode.\u003C\u002Fp>\n\u003Ch2 id=\"installer-huff\">Installer Huff\u003C\u002Fh2>\n\u003Cp>Le compilateur canonique est \u003Ccode>huffc\u003C\u002Fcode>, écrit en Rust :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">curl -L get.huff.sh | bash\nhuffup\nhuffc --version\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Cela installe \u003Ccode>huffc\u003C\u002Fcode> dans \u003Ccode>~\u002F.huff\u002Fbin\u003C\u002Fcode>. Ajoutez-le à votre PATH et vérifiez :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">$ huffc --version\nhuffc 0.3.2\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Vous pouvez aussi utiliser Huff dans les projets Foundry avec \u003Ccode>foundry-huff\u003C\u002Fcode>, qui permet de déployer des fichiers \u003Ccode>.huff\u003C\u002Fcode> de la même manière que des fichiers \u003Ccode>.sol\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch2 id=\"hello-world-un-contrat-minimal\">Hello World : un contrat minimal\u003C\u002Fh2>\n\u003Cp>Écrivons un contrat qui retourne le mot de 32 octets \u003Ccode>0x01\u003C\u002Fcode> à tout appel :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro MAIN() = takes(0) returns(0) {\n    0x01            \u002F\u002F [0x01]\n    0x00            \u002F\u002F [0x00, 0x01]\n    mstore          \u002F\u002F []          — memory[0x00..0x20] = 0x01\n    0x20            \u002F\u002F [0x20]\n    0x00            \u002F\u002F [0x00, 0x20]\n    return          \u002F\u002F halt — retourner memory[0x00..0x20]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Compilez :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">huffc src\u002FHelloWorld.huff -r\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Le flag \u003Ccode>-r\u003C\u002Fcode> produit le bytecode runtime. Vous obtiendrez quelque chose comme \u003Ccode>600160005260206000f3\u003C\u002Fcode> — 10 octets. Un contrat Solidity retournant \u003Ccode>1\u003C\u002Fcode> compile en environ 200+ octets de bytecode runtime parce que solc émet un dispatcher complet, un hash de métadonnées, la configuration du pointeur de mémoire libre et un encodeur ABI.\u003C\u002Fp>\n\u003Ch2 id=\"macros-vs-fonctions\">Macros vs fonctions\u003C\u002Fh2>\n\u003Cp>Huff a deux primitives de réutilisation de code : les \u003Cstrong>macros\u003C\u002Fstrong> et les \u003Cstrong>fonctions\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Ch3>Macros (\u003Ccode>#define macro\u003C\u002Fcode>)\u003C\u002Fh3>\n\u003Cp>Les macros sont inlinées à chaque site d’appel. Pas d’overhead de JUMP, pas de gas supplémentaire — le compilateur copie littéralement les opcodes dans l’appelant. C’est le choix par défaut et préféré pour le code critique en gas.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro REQUIRE_NOT_ZERO() = takes(1) returns(0) {\n    \u002F\u002F takes: [value]\n    continue        \u002F\u002F [continue_dest, value]\n    jumpi           \u002F\u002F []  — sauter si value != 0\n    0x00 0x00 revert\n    continue:\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Fonctions (\u003Ccode>#define fn\u003C\u002Fcode>)\u003C\u002Fh3>\n\u003Cp>Les fonctions génèrent une vraie paire JUMP\u002FJUMPDEST. Elles économisent la taille du bytecode au prix d’environ 22 gas supplémentaires par appel. Utilisez-les uniquement quand la taille du bytecode compte plus que le gas.\u003C\u002Fp>\n\u003Ch2 id=\"labels-et-destinations-de-saut\">Labels et destinations de saut\u003C\u002Fh2>\n\u003Cp>Les labels en Huff sont des emplacements JUMPDEST nommés. Le compilateur les résout en offsets concrets dans le bytecode à la compilation.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro LOOP_EXAMPLE() = takes(1) returns(1) {\n    \u002F\u002F takes: [n]\n    0x00                \u002F\u002F [acc, n]\n    loop:\n        dup2            \u002F\u002F [n, acc, n]\n        iszero          \u002F\u002F [n==0?, acc, n]\n        done jumpi      \u002F\u002F [acc, n]\n        swap1           \u002F\u002F [n, acc]\n        0x01 swap1 sub  \u002F\u002F [n-1, acc]\n        swap1           \u002F\u002F [acc, n-1]\n        0x01 add        \u002F\u002F [acc+1, n-1]\n        loop jump\n    done:\n        swap1 pop       \u002F\u002F [acc]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Chaque label compile en un seul octet \u003Ccode>JUMPDEST\u003C\u002Fcode> (\u003Ccode>0x5b\u003C\u002Fcode>). Les références compilent en \u003Ccode>PUSH2 &lt;offset&gt; JUMP\u003C\u002Fcode> (ou \u003Ccode>JUMPI\u003C\u002Fcode>). C’est exactement ce que vous écririez à la main en assembleur EVM brut — Huff gère simplement la comptabilité des offsets.\u003C\u002Fp>\n\u003Ch2 id=\"takes-et-returns\">takes() et returns()\u003C\u002Fh2>\n\u003Cp>Les annotations \u003Ccode>takes(n)\u003C\u002Fcode> et \u003Ccode>returns(m)\u003C\u002Fcode> sur les macros et fonctions sont de la documentation et des indices pour le compilateur. Elles indiquent — au lecteur et au vérificateur de pile de Huff — combien d’éléments de pile le bloc s’attend à consommer et produire.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro ADD_TWO() = takes(2) returns(1) {\n    add  \u002F\u002F consomme 2 éléments, en produit 1\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"comparaison-huff-vs-bytecode-solidity\">Comparaison : Huff vs bytecode Solidity\u003C\u002Fh2>\n\u003Cp>Considérons une simple fonction vue \u003Ccode>getValue()\u003C\u002Fcode> qui retourne un slot de stockage :\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Solidity :\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-solidity\">function getValue() external view returns (uint256) {\n    return value;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Solc génère environ 40 octets pour le dispatcher + encodage ABI.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Équivalent Huff :\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define function getValue() view returns (uint256)\n\n#define macro GET_VALUE() = takes(0) returns(0) {\n    [VALUE_SLOT]    \u002F\u002F [slot]\n    sload           \u002F\u002F [value]\n    0x00 mstore     \u002F\u002F []  — stocker en mémoire\n    0x20 0x00 return\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>La version Huff fait 12 octets de bytecode pour le corps. Pas d’overhead d’encodage ABI, pas de pointeur de mémoire libre, pas de hash de métadonnées.\u003C\u002Fp>\n\u003Ch2 id=\"constantes-et-slots-de-stockage\">Constantes et slots de stockage\u003C\u002Fh2>\n\u003Cp>Les constantes Huff sont des valeurs à la compilation qui sont inlinées comme instructions PUSH :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define constant VALUE_SLOT = 0x00\n#define constant OWNER_SLOT = 0x01\n#define constant MAX_UINT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Utilisation : \u003Ccode>[VALUE_SLOT]\u003C\u002Fcode> empile \u003Ccode>0x00\u003C\u002Fcode>, \u003Ccode>[MAX_UINT]\u003C\u002Fcode> empile la valeur complète de 32 octets. Les constantes améliorent la lisibilité sans coûter de gas — elles sont purement syntaxiques.\u003C\u002Fp>\n\u003Ch2 id=\"quand-utiliser-huff\">Quand utiliser Huff\u003C\u002Fh2>\n\u003Cp>Huff n’est pas un langage généraliste. Utilisez-le quand :\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Le gas est la contrainte principale\u003C\u002Fstrong> — Contrats MEV où 100 gas détermine la rentabilité.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>La taille du bytecode compte\u003C\u002Fstrong> — Contrats déployés par d’autres contrats (factories CREATE2) où un initcode plus petit = moins de gas de déploiement.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Vous avez besoin d’un dispatch personnalisé\u003C\u002Fstrong> — Tables de saut, sélecteurs compactés, ou encodage ABI non standard.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Vous apprenez l’EVM\u003C\u002Fstrong> — Rien n’enseigne mieux l’EVM que l’écriture d’opcodes bruts.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Pour tout le reste, écrivez en Solidity et lisez la sortie du compilateur avec \u003Ccode>solc --asm\u003C\u002Fcode>. Vous serez plus productif et moins sujet aux erreurs.\u003C\u002Fp>\n\u003Ch2 id=\"r-sum\">Résumé\u003C\u002Fh2>\n\u003Cp>Huff vous donne une ligne directe vers le bytecode EVM avec juste assez d’abstraction pour rester sain d’esprit. Les macros inlinent le code pour une réutilisation à overhead nul. Les labels gèrent la comptabilité des offsets de saut. Les annotations \u003Ccode>takes\u003C\u002Fcode>\u002F\u003Ccode>returns\u003C\u002Fcode> attrapent les erreurs de pile tôt. Dans le prochain article, nous plongerons plus profondément dans la gestion de pile — l’art du \u003Ccode>dup\u003C\u002Fcode>, \u003Ccode>swap\u003C\u002Fcode>, et du maintien de votre modèle mental de la pile en synchronisation avec la réalité.\u003C\u002Fp>\n","fr","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:29.075328Z","Introduction à Huff, le langage d'assemblage EVM de bas niveau. Apprenez les macros, labels, takes\u002Freturns et la comparaison avec Solidity.","huff langage evm",null,"index, follow",[21,26,30],{"id":22,"name":23,"slug":24,"created_at":25},"c0000000-0000-0000-0000-000000000016","EVM","evm","2026-03-28T10:44:21.513630Z",{"id":27,"name":28,"slug":29,"created_at":25},"c0000000-0000-0000-0000-000000000020","Gas Optimization","gas-optimization",{"id":31,"name":32,"slug":33,"created_at":25},"c0000000-0000-0000-0000-000000000017","Huff","huff","Blockchain",[36,42,48],{"id":37,"title":38,"slug":39,"excerpt":40,"locale":12,"category_name":34,"published_at":41},"d0000000-0000-0000-0000-000000000608","La couche d'interoperabilite Ethereum : comment 55+ L2 deviennent une seule chaine","couche-interoperabilite-ethereum-55-l2-deviennent-une-seule-chaine","Ethereum compte 55+ rollups Layer 2, fragmentant la liquidite et l'experience utilisateur. La couche d'interoperabilite Ethereum — combinant messagerie cross-rollup, sequenceurs partages et based rollups — vise a les unifier en un reseau composable unique.","2026-03-28T10:44:45.078068Z",{"id":43,"title":44,"slug":45,"excerpt":46,"locale":12,"category_name":34,"published_at":47},"d0000000-0000-0000-0000-000000000607","Les preuves ZK au-dela des rollups : l'inference IA verifiable sur Ethereum","preuves-zk-au-dela-des-rollups-inference-ia-verifiable-ethereum","Les preuves a connaissance nulle ne sont plus un simple outil de scalabilite. En 2026, zkML permet l'inference IA verifiable on-chain, les ZK coprocesseurs deplacent le calcul lourd hors chaine avec verification on-chain, et de nouveaux systemes de preuve comme SP1 et Jolt rendent tout cela pratique.","2026-03-28T10:44:45.071974Z",{"id":49,"title":50,"slug":51,"excerpt":52,"locale":12,"category_name":34,"published_at":53},"d0000000-0000-0000-0000-000000000584","EIP-7702 en pratique : construire des flux de comptes intelligents apres Pectra","eip-7702-en-pratique-construire-flux-comptes-intelligents-apres-pectra","EIP-7702 permet a tout EOA Ethereum d'agir temporairement comme un contrat intelligent dans une seule transaction. Voici comment implementer les transactions par lots, le parrainage de gas et la recuperation sociale avec la nouvelle primitive d'account abstraction.","2026-03-28T10:44:43.586053Z",{"id":13,"name":55,"slug":56,"bio":57,"photo_url":18,"linkedin":18,"role":58,"created_at":59,"updated_at":59},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]