[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-1-comment-evm-execute-code-opcodes-pile-gas":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-000000000101","a0000000-0000-0000-0000-000000000062","Deep EVM #1 : Comment l'EVM exécute votre code — Opcodes, pile et gas","deep-evm-1-comment-evm-execute-code-opcodes-pile-gas","Un parcours technique de la machine virtuelle Ethereum : comment les opcodes manipulent la pile, comment fonctionne le comptage du gas, et ce qui se passe réellement quand votre transaction s'exécute.","## L'EVM est une machine à pile\n\nLa machine virtuelle Ethereum (EVM) n'est pas comme le processeur x86 de votre ordinateur portable. Elle n'a pas de registres. C'est une **machine à pile** — chaque calcul empile ou dépile des éléments d'une pile de 1024 éléments où chacun est un mot de 256 bits (32 octets).\n\nQuand vous appelez un smart contract, l'EVM reçoit le bytecode du contrat — une séquence plate d'opcodes d'un octet — et commence l'exécution à l'octet 0. Il n'y a pas de table de fonctions, pas d'en-tête ELF, pas d'étape d'édition de liens. Le bytecode est le programme.\n\n```\n\u002F\u002F Solidity :\n\u002F\u002F uint256 result = 2 + 3;\n\n\u002F\u002F Compile en bytecode :\n\u002F\u002F PUSH1 0x02  PUSH1 0x03  ADD\n\n\u002F\u002F Trace de pile :\n\u002F\u002F []           -> PUSH1 0x02 -> [2]\n\u002F\u002F [2]          -> PUSH1 0x03 -> [2, 3]\n\u002F\u002F [2, 3]       -> ADD        -> [5]\n```\n\nChaque opcode consomme ses opérandes du sommet de la pile et repousse son résultat. L'opcode ADD dépile deux valeurs, les additionne et empile la somme. C'est fondamentalement différent des architectures à registres où vous spécifiez des registres source et destination.\n\n## Catégories d'opcodes\n\nL'EVM définit environ 140 opcodes, regroupés par catégories fonctionnelles :\n\n### Arithmétique et comparaison\n- **ADD, SUB, MUL, DIV, MOD** — Arithmétique entière 256 bits de base. Tous coûtent 3 gas (niveau G_verylow).\n- **SDIV, SMOD** — Division et modulo signés en complément à deux.\n- **ADDMOD, MULMOD** — Arithmétique modulaire : `(a + b) % N` et `(a * b) % N` en un seul opcode. Essentiels pour les opérations sur courbes elliptiques, coût de 8 gas.\n- **EXP** — Exponentiation. Coûte 10 gas + 50 par octet de l'exposant, ce qui en fait l'un des opcodes arithmétiques les plus chers.\n- **LT, GT, SLT, SGT, EQ, ISZERO** — Opcodes de comparaison qui empilent 1 (vrai) ou 0 (faux).\n\n### Opérations bit à bit\n- **AND, OR, XOR, NOT** — Logique bit à bit, 3 gas chacune.\n- **SHL, SHR, SAR** — Décalage à gauche, décalage logique à droite, décalage arithmétique à droite (ajoutés dans Constantinople, EIP-145). Avant leur existence, les décalages nécessitaient MUL\u002FDIV par des puissances de 2.\n- **BYTE** — Extrait un seul octet d'un mot de 32 octets. `BYTE(0, x)` retourne l'octet le plus significatif.\n\n### Manipulation de la pile\n- **POP** — Supprime l'élément du sommet.\n- **PUSH1 à PUSH32** — Empile de 1 à 32 octets de données immédiates. PUSH1 est l'opcode le plus fréquent dans le bytecode déployé.\n- **DUP1 à DUP16** — Duplique le N-ième élément de la pile au sommet.\n- **SWAP1 à SWAP16** — Échange l'élément du sommet avec le N-ième élément en dessous.\n\n### Environnement et informations de bloc\n- **CALLER** (msg.sender), **CALLVALUE** (msg.value), **CALLDATALOAD**, **CALLDATASIZE**, **CALLDATACOPY** — Accès au contexte de la transaction.\n- **NUMBER**, **TIMESTAMP**, **BASEFEE**, **CHAINID** — Informations au niveau du bloc.\n- **BALANCE**, **EXTCODESIZE**, **EXTCODECOPY** — Interrogation d'autres comptes.\n\n## Le barème du gas\n\nChaque opcode a un coût en gas. Le gas sert deux objectifs : il empêche les boucles infinies (problème de l'arrêt) et il tarife équitablement les ressources de calcul.\n\nLes coûts de gas se répartissent en niveaux :\n\n| Niveau | Gas | Exemples |\n|--------|-----|----------|\n| Zéro | 0 | STOP, RETURN, REVERT |\n| Base | 2 | ADDRESS, ORIGIN, CALLER |\n| Très bas | 3 | ADD, SUB, LT, GT, AND, OR, POP |\n| Bas | 5 | MUL, DIV, MOD |\n| Moyen | 8 | ADDMOD, MULMOD, JUMP |\n| Élevé | 10 | JUMPI |\n| Spécial | variable | SLOAD, SSTORE, CALL, CREATE |\n\nLes opcodes les plus chers sont ceux qui touchent à l'état :\n\n```\n\u002F\u002F Coûts de gas pour l'accès à l'état (après EIP-2929) :\n\u002F\u002F SLOAD (froid) :   2100 gas\n\u002F\u002F SLOAD (chaud) :    100 gas\n\u002F\u002F SSTORE (froid, 0->non-zéro) : 22100 gas\n\u002F\u002F SSTORE (chaud) :   100 gas (+ 20000 si 0->non-zéro)\n\u002F\u002F CALL (froid) :    2600 gas\n\u002F\u002F CALL (chaud) :     100 gas\n\u002F\u002F BALANCE (froid) : 2600 gas\n\u002F\u002F BALANCE (chaud) :  100 gas\n```\n\n## Accès froid vs chaud (EIP-2929)\n\nL'EIP-2929 (mise à jour Berlin, avril 2021) a introduit le concept de **liste d'accès** — un ensemble par transaction d'adresses et de slots de stockage déjà consultés.\n\nLa première fois que vous accédez à un slot de stockage ou à une adresse externe dans une transaction, il est « froid » et coûte du gas supplémentaire. Les accès suivants sont « chauds » et bon marché. C'est pourquoi l'ordre dans lequel vous lisez les slots de stockage est important pour l'optimisation du gas.\n\n```solidity\n\u002F\u002F En Solidity, ce pattern est coûteux :\nfunction bad() external view returns (uint256) {\n    \u002F\u002F Première lecture du slot : 2100 gas (froid)\n    uint256 a = myStorage;\n    \u002F\u002F ... logique ...\n    \u002F\u002F Deuxième lecture : 100 gas (chaud)\n    uint256 b = myStorage;\n    return a + b;\n}\n\n\u002F\u002F Mise en cache mémoire :\nfunction good() external view returns (uint256) {\n    uint256 cached = myStorage; \u002F\u002F 2100 gas (froid), une seule fois\n    return cached + cached;     \u002F\u002F 6 gas (ADD + DUP)\n}\n```\n\n## Flux d'exécution : que se passe-t-il dans une transaction\n\nQuand vous envoyez une transaction qui appelle un contrat, voici la séquence complète :\n\n1. **Validation de la transaction** — Vérification du nonce, solde >= valeur + gas * gasPrice, vérification de la signature.\n2. **Déduction du gas intrinsèque** — 21 000 gas pour la transaction elle-même, plus 16 gas par octet de calldata non nul et 4 par octet nul.\n3. **Configuration du contexte** — L'EVM crée un contexte d'exécution : code, calldata, appelant, valeur, gas restant.\n4. **Le compteur de programme commence à 0** — L'EVM lit l'opcode à la position 0 et l'exécute.\n5. **Exécution séquentielle** — Chaque opcode est exécuté, le gas est déduit. JUMP et JUMPI permettent un flux de contrôle non linéaire, mais uniquement vers des positions marquées par JUMPDEST.\n6. **Terminaison** — L'exécution se termine par STOP (succès, pas de données de retour), RETURN (succès, avec données de retour), REVERT (échec, état annulé) ou épuisement du gas.\n7. **Validation ou annulation de l'état** — En cas de succès, tous les changements d'état sont validés. En cas d'annulation, tous les changements dans ce contexte d'appel sont annulés.\n\n## Le compteur de programme et JUMP\n\nLe compteur de programme (PC) est un registre implicite qui suit la position actuelle dans le bytecode. La plupart des opcodes avancent le PC de 1 (ou de 1 + N pour les opcodes PUSH). Deux opcodes modifient directement le PC :\n\n- **JUMP** — Dépile une destination de la pile, définit le PC à cette valeur. La destination doit contenir un opcode JUMPDEST sinon la transaction échoue.\n- **JUMPI** — Saut conditionnel. Dépile la destination et la condition. Si la condition est non nulle, saut ; sinon continuation séquentielle.\n\nC'est ainsi que l'EVM implémente if\u002Felse, les boucles et la dispatch de fonctions. Le compilateur Solidity génère un sélecteur de fonction qui charge les 4 premiers octets du calldata, compare avec les signatures connues et exécute JUMPI vers le bloc de code correspondant.\n\n## Sous-appels : CALL, STATICCALL, DELEGATECALL\n\nLes contrats peuvent invoquer d'autres contrats via trois opcodes d'appel :\n\n- **CALL** — Appel standard. Crée un nouveau contexte d'exécution avec sa propre pile et mémoire. L'appelé s'exécute indépendamment ; s'il reverte, seuls ses changements sont annulés.\n- **STATICCALL** — Appel en lecture seule (EIP-214). Tout opcode modifiant l'état (SSTORE, CREATE, LOG, SELFDESTRUCT) dans l'appelé provoque un revert immédiat.\n- **DELEGATECALL** — Exécute le code de l'appelé dans le contexte de stockage de l'appelant. msg.sender et msg.value sont préservés de l'appel original. C'est ainsi que fonctionnent les patterns proxy et les bibliothèques.\n\n## Implications pratiques pour le MEV\n\nSi vous construisez des bots MEV, comprendre l'EVM au niveau des opcodes n'est pas optionnel — c'est une exigence compétitive. Chaque unité de gas économisée dans l'exécution de votre bot est une marge bénéficiaire. Points clés :\n\n- **Simulez avant de soumettre** — Utilisez `eth_call` ou un EVM local (revm, EVMONE) pour tracer l'exécution et connaître le coût exact en gas.\n- **Minimisez les accès froids** — Préchauffez les slots de stockage via les listes d'accès (EIP-2930).\n- **Utilisez STATICCALL pour les lectures** — Légèrement moins cher et garantit l'absence de mutation d'état.\n- **Connaissez vos coûts d'opcodes** — Un seul SLOAD mal placé peut coûter 2100 gas ; dans un environnement MEV compétitif, c'est la différence entre profit et perte.\n\n## Conclusion\n\nL'EVM est élégante dans sa simplicité : une machine à pile avec des mots de 256 bits, un format de bytecode plat et un système de comptage de gas qui tarife chaque opération. Comprendre ce fondement — opcodes, pile et gas — est le prérequis pour tout ce qui suit dans cette série : disposition mémoire, optimisation du stockage, primitives de sécurité, et enfin l'écriture de Yul et Huff bruts.\n\nDans le prochain article, nous explorerons les quatre emplacements de données de l'EVM : pile, mémoire, stockage et calldata — et pourquoi choisir le bon détermine si votre contrat coûte 0,50 $ ou 50 $ à exécuter.","\u003Ch2 id=\"l-evm-est-une-machine-pile\">L’EVM est une machine à pile\u003C\u002Fh2>\n\u003Cp>La machine virtuelle Ethereum (EVM) n’est pas comme le processeur x86 de votre ordinateur portable. Elle n’a pas de registres. C’est une \u003Cstrong>machine à pile\u003C\u002Fstrong> — chaque calcul empile ou dépile des éléments d’une pile de 1024 éléments où chacun est un mot de 256 bits (32 octets).\u003C\u002Fp>\n\u003Cp>Quand vous appelez un smart contract, l’EVM reçoit le bytecode du contrat — une séquence plate d’opcodes d’un octet — et commence l’exécution à l’octet 0. Il n’y a pas de table de fonctions, pas d’en-tête ELF, pas d’étape d’édition de liens. Le bytecode est le programme.\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\u002F\u002F Solidity :\n\u002F\u002F uint256 result = 2 + 3;\n\n\u002F\u002F Compile en bytecode :\n\u002F\u002F PUSH1 0x02  PUSH1 0x03  ADD\n\n\u002F\u002F Trace de pile :\n\u002F\u002F []           -&gt; PUSH1 0x02 -&gt; [2]\n\u002F\u002F [2]          -&gt; PUSH1 0x03 -&gt; [2, 3]\n\u002F\u002F [2, 3]       -&gt; ADD        -&gt; [5]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Chaque opcode consomme ses opérandes du sommet de la pile et repousse son résultat. L’opcode ADD dépile deux valeurs, les additionne et empile la somme. C’est fondamentalement différent des architectures à registres où vous spécifiez des registres source et destination.\u003C\u002Fp>\n\u003Ch2 id=\"cat-gories-d-opcodes\">Catégories d’opcodes\u003C\u002Fh2>\n\u003Cp>L’EVM définit environ 140 opcodes, regroupés par catégories fonctionnelles :\u003C\u002Fp>\n\u003Ch3>Arithmétique et comparaison\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>ADD, SUB, MUL, DIV, MOD\u003C\u002Fstrong> — Arithmétique entière 256 bits de base. Tous coûtent 3 gas (niveau G_verylow).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>SDIV, SMOD\u003C\u002Fstrong> — Division et modulo signés en complément à deux.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>ADDMOD, MULMOD\u003C\u002Fstrong> — Arithmétique modulaire : \u003Ccode>(a + b) % N\u003C\u002Fcode> et \u003Ccode>(a * b) % N\u003C\u002Fcode> en un seul opcode. Essentiels pour les opérations sur courbes elliptiques, coût de 8 gas.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>EXP\u003C\u002Fstrong> — Exponentiation. Coûte 10 gas + 50 par octet de l’exposant, ce qui en fait l’un des opcodes arithmétiques les plus chers.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>LT, GT, SLT, SGT, EQ, ISZERO\u003C\u002Fstrong> — Opcodes de comparaison qui empilent 1 (vrai) ou 0 (faux).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>Opérations bit à bit\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>AND, OR, XOR, NOT\u003C\u002Fstrong> — Logique bit à bit, 3 gas chacune.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>SHL, SHR, SAR\u003C\u002Fstrong> — Décalage à gauche, décalage logique à droite, décalage arithmétique à droite (ajoutés dans Constantinople, EIP-145). Avant leur existence, les décalages nécessitaient MUL\u002FDIV par des puissances de 2.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>BYTE\u003C\u002Fstrong> — Extrait un seul octet d’un mot de 32 octets. \u003Ccode>BYTE(0, x)\u003C\u002Fcode> retourne l’octet le plus significatif.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>Manipulation de la pile\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>POP\u003C\u002Fstrong> — Supprime l’élément du sommet.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>PUSH1 à PUSH32\u003C\u002Fstrong> — Empile de 1 à 32 octets de données immédiates. PUSH1 est l’opcode le plus fréquent dans le bytecode déployé.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>DUP1 à DUP16\u003C\u002Fstrong> — Duplique le N-ième élément de la pile au sommet.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>SWAP1 à SWAP16\u003C\u002Fstrong> — Échange l’élément du sommet avec le N-ième élément en dessous.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>Environnement et informations de bloc\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>CALLER\u003C\u002Fstrong> (msg.sender), \u003Cstrong>CALLVALUE\u003C\u002Fstrong> (msg.value), \u003Cstrong>CALLDATALOAD\u003C\u002Fstrong>, \u003Cstrong>CALLDATASIZE\u003C\u002Fstrong>, \u003Cstrong>CALLDATACOPY\u003C\u002Fstrong> — Accès au contexte de la transaction.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>NUMBER\u003C\u002Fstrong>, \u003Cstrong>TIMESTAMP\u003C\u002Fstrong>, \u003Cstrong>BASEFEE\u003C\u002Fstrong>, \u003Cstrong>CHAINID\u003C\u002Fstrong> — Informations au niveau du bloc.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>BALANCE\u003C\u002Fstrong>, \u003Cstrong>EXTCODESIZE\u003C\u002Fstrong>, \u003Cstrong>EXTCODECOPY\u003C\u002Fstrong> — Interrogation d’autres comptes.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"le-bar-me-du-gas\">Le barème du gas\u003C\u002Fh2>\n\u003Cp>Chaque opcode a un coût en gas. Le gas sert deux objectifs : il empêche les boucles infinies (problème de l’arrêt) et il tarife équitablement les ressources de calcul.\u003C\u002Fp>\n\u003Cp>Les coûts de gas se répartissent en niveaux :\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Niveau\u003C\u002Fth>\u003Cth>Gas\u003C\u002Fth>\u003Cth>Exemples\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>Zéro\u003C\u002Ftd>\u003Ctd>0\u003C\u002Ftd>\u003Ctd>STOP, RETURN, REVERT\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Base\u003C\u002Ftd>\u003Ctd>2\u003C\u002Ftd>\u003Ctd>ADDRESS, ORIGIN, CALLER\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Très bas\u003C\u002Ftd>\u003Ctd>3\u003C\u002Ftd>\u003Ctd>ADD, SUB, LT, GT, AND, OR, POP\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Bas\u003C\u002Ftd>\u003Ctd>5\u003C\u002Ftd>\u003Ctd>MUL, DIV, MOD\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Moyen\u003C\u002Ftd>\u003Ctd>8\u003C\u002Ftd>\u003Ctd>ADDMOD, MULMOD, JUMP\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Élevé\u003C\u002Ftd>\u003Ctd>10\u003C\u002Ftd>\u003Ctd>JUMPI\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Spécial\u003C\u002Ftd>\u003Ctd>variable\u003C\u002Ftd>\u003Ctd>SLOAD, SSTORE, CALL, CREATE\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>Les opcodes les plus chers sont ceux qui touchent à l’état :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\u002F\u002F Coûts de gas pour l'accès à l'état (après EIP-2929) :\n\u002F\u002F SLOAD (froid) :   2100 gas\n\u002F\u002F SLOAD (chaud) :    100 gas\n\u002F\u002F SSTORE (froid, 0-&gt;non-zéro) : 22100 gas\n\u002F\u002F SSTORE (chaud) :   100 gas (+ 20000 si 0-&gt;non-zéro)\n\u002F\u002F CALL (froid) :    2600 gas\n\u002F\u002F CALL (chaud) :     100 gas\n\u002F\u002F BALANCE (froid) : 2600 gas\n\u002F\u002F BALANCE (chaud) :  100 gas\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"acc-s-froid-vs-chaud-eip-2929\">Accès froid vs chaud (EIP-2929)\u003C\u002Fh2>\n\u003Cp>L’EIP-2929 (mise à jour Berlin, avril 2021) a introduit le concept de \u003Cstrong>liste d’accès\u003C\u002Fstrong> — un ensemble par transaction d’adresses et de slots de stockage déjà consultés.\u003C\u002Fp>\n\u003Cp>La première fois que vous accédez à un slot de stockage ou à une adresse externe dans une transaction, il est « froid » et coûte du gas supplémentaire. Les accès suivants sont « chauds » et bon marché. C’est pourquoi l’ordre dans lequel vous lisez les slots de stockage est important pour l’optimisation du gas.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-solidity\">\u002F\u002F En Solidity, ce pattern est coûteux :\nfunction bad() external view returns (uint256) {\n    \u002F\u002F Première lecture du slot : 2100 gas (froid)\n    uint256 a = myStorage;\n    \u002F\u002F ... logique ...\n    \u002F\u002F Deuxième lecture : 100 gas (chaud)\n    uint256 b = myStorage;\n    return a + b;\n}\n\n\u002F\u002F Mise en cache mémoire :\nfunction good() external view returns (uint256) {\n    uint256 cached = myStorage; \u002F\u002F 2100 gas (froid), une seule fois\n    return cached + cached;     \u002F\u002F 6 gas (ADD + DUP)\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"flux-d-ex-cution-que-se-passe-t-il-dans-une-transaction\">Flux d’exécution : que se passe-t-il dans une transaction\u003C\u002Fh2>\n\u003Cp>Quand vous envoyez une transaction qui appelle un contrat, voici la séquence complète :\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Validation de la transaction\u003C\u002Fstrong> — Vérification du nonce, solde &gt;= valeur + gas * gasPrice, vérification de la signature.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Déduction du gas intrinsèque\u003C\u002Fstrong> — 21 000 gas pour la transaction elle-même, plus 16 gas par octet de calldata non nul et 4 par octet nul.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Configuration du contexte\u003C\u002Fstrong> — L’EVM crée un contexte d’exécution : code, calldata, appelant, valeur, gas restant.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Le compteur de programme commence à 0\u003C\u002Fstrong> — L’EVM lit l’opcode à la position 0 et l’exécute.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Exécution séquentielle\u003C\u002Fstrong> — Chaque opcode est exécuté, le gas est déduit. JUMP et JUMPI permettent un flux de contrôle non linéaire, mais uniquement vers des positions marquées par JUMPDEST.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Terminaison\u003C\u002Fstrong> — L’exécution se termine par STOP (succès, pas de données de retour), RETURN (succès, avec données de retour), REVERT (échec, état annulé) ou épuisement du gas.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Validation ou annulation de l’état\u003C\u002Fstrong> — En cas de succès, tous les changements d’état sont validés. En cas d’annulation, tous les changements dans ce contexte d’appel sont annulés.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"le-compteur-de-programme-et-jump\">Le compteur de programme et JUMP\u003C\u002Fh2>\n\u003Cp>Le compteur de programme (PC) est un registre implicite qui suit la position actuelle dans le bytecode. La plupart des opcodes avancent le PC de 1 (ou de 1 + N pour les opcodes PUSH). Deux opcodes modifient directement le PC :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>JUMP\u003C\u002Fstrong> — Dépile une destination de la pile, définit le PC à cette valeur. La destination doit contenir un opcode JUMPDEST sinon la transaction échoue.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>JUMPI\u003C\u002Fstrong> — Saut conditionnel. Dépile la destination et la condition. Si la condition est non nulle, saut ; sinon continuation séquentielle.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>C’est ainsi que l’EVM implémente if\u002Felse, les boucles et la dispatch de fonctions. Le compilateur Solidity génère un sélecteur de fonction qui charge les 4 premiers octets du calldata, compare avec les signatures connues et exécute JUMPI vers le bloc de code correspondant.\u003C\u002Fp>\n\u003Ch2 id=\"sous-appels-call-staticcall-delegatecall\">Sous-appels : CALL, STATICCALL, DELEGATECALL\u003C\u002Fh2>\n\u003Cp>Les contrats peuvent invoquer d’autres contrats via trois opcodes d’appel :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>CALL\u003C\u002Fstrong> — Appel standard. Crée un nouveau contexte d’exécution avec sa propre pile et mémoire. L’appelé s’exécute indépendamment ; s’il reverte, seuls ses changements sont annulés.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>STATICCALL\u003C\u002Fstrong> — Appel en lecture seule (EIP-214). Tout opcode modifiant l’état (SSTORE, CREATE, LOG, SELFDESTRUCT) dans l’appelé provoque un revert immédiat.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>DELEGATECALL\u003C\u002Fstrong> — Exécute le code de l’appelé dans le contexte de stockage de l’appelant. msg.sender et msg.value sont préservés de l’appel original. C’est ainsi que fonctionnent les patterns proxy et les bibliothèques.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"implications-pratiques-pour-le-mev\">Implications pratiques pour le MEV\u003C\u002Fh2>\n\u003Cp>Si vous construisez des bots MEV, comprendre l’EVM au niveau des opcodes n’est pas optionnel — c’est une exigence compétitive. Chaque unité de gas économisée dans l’exécution de votre bot est une marge bénéficiaire. Points clés :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Simulez avant de soumettre\u003C\u002Fstrong> — Utilisez \u003Ccode>eth_call\u003C\u002Fcode> ou un EVM local (revm, EVMONE) pour tracer l’exécution et connaître le coût exact en gas.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Minimisez les accès froids\u003C\u002Fstrong> — Préchauffez les slots de stockage via les listes d’accès (EIP-2930).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Utilisez STATICCALL pour les lectures\u003C\u002Fstrong> — Légèrement moins cher et garantit l’absence de mutation d’état.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Connaissez vos coûts d’opcodes\u003C\u002Fstrong> — Un seul SLOAD mal placé peut coûter 2100 gas ; dans un environnement MEV compétitif, c’est la différence entre profit et perte.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"conclusion\">Conclusion\u003C\u002Fh2>\n\u003Cp>L’EVM est élégante dans sa simplicité : une machine à pile avec des mots de 256 bits, un format de bytecode plat et un système de comptage de gas qui tarife chaque opération. Comprendre ce fondement — opcodes, pile et gas — est le prérequis pour tout ce qui suit dans cette série : disposition mémoire, optimisation du stockage, primitives de sécurité, et enfin l’écriture de Yul et Huff bruts.\u003C\u002Fp>\n\u003Cp>Dans le prochain article, nous explorerons les quatre emplacements de données de l’EVM : pile, mémoire, stockage et calldata — et pourquoi choisir le bon détermine si votre contrat coûte 0,50 $ ou 50 $ à exécuter.\u003C\u002Fp>\n","fr","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:28.835975Z","Parcours technique de la machine virtuelle Ethereum : opcodes, opérations sur la pile, comptage du gas, accès froid et chaud expliqués.","EVM opcodes",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-000000000014","Solidity","solidity","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"]