[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-10-huff-stack-management-dup-swap":3},{"article":4,"author":55},{"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":16,"meta_description":17,"focus_keyword":18,"og_image":19,"canonical_url":19,"robots_meta":20,"created_at":15,"updated_at":15,"tags":21,"category_name":35,"related_articles":36},"d0000000-0000-0000-0000-000000000110","a0000000-0000-0000-0000-000000000002","Deep EVM #10: Huff Stack Management — takes(), returns(), and the Art of dup\u002Fswap","deep-evm-10-huff-stack-management-dup-swap","Master the EVM stack machine: visualize stack state, use dup1-16 and swap1-16 effectively, and build patterns for keeping values, rotating, and cleaning up.","## The Stack Machine Mental Model\n\nThe EVM is a stack machine. There are no registers, no named variables — just a last-in-first-out stack of 32-byte words, 1024 slots deep. Every opcode either pushes, pops, or rearranges items on this stack. If you cannot hold the current stack state in your head, you will produce buggy bytecode. This article is about building that mental model.\n\n### Notation Convention\n\nThroughout this article (and in Huff comments), we represent stack state with brackets where the leftmost item is the top of the stack:\n\n```\n\u002F\u002F [top, second, third, ..., bottom]\n0x01  \u002F\u002F [1]\n0x02  \u002F\u002F [2, 1]\nadd   \u002F\u002F [3]\n```\n\nEvery Huff macro should have a stack comment after each opcode. This is not optional — it is the only way to audit correctness.\n\n## DUP: Duplicating Stack Items\n\nThe EVM provides `DUP1` through `DUP16`. `DUPn` copies the n-th item from the top and pushes it onto the stack. The stack grows by 1.\n\n```huff\n\u002F\u002F Stack: [a, b, c, d]\ndup1   \u002F\u002F [a, a, b, c, d]       — copy top\ndup3   \u002F\u002F [c, a, a, b, c, d]    — copy 3rd from top\n```\n\nGas cost: 3 gas for any DUPn. This is one of the cheapest operations in the EVM.\n\n### When to DUP\n\nDUP is your tool for **non-destructive reads**. Many opcodes consume their arguments (ADD pops two, pushes one), so if you need a value again later, DUP it before feeding it to a consuming opcode.\n\n```huff\n#define macro SAFE_SUB() = takes(2) returns(1) {\n    \u002F\u002F takes: [a, b] — compute a - b, revert if b > a\n    dup2 dup2       \u002F\u002F [a, b, a, b]\n    lt              \u002F\u002F [a \u003C b?, a, b]\n    revert_underflow jumpi  \u002F\u002F [a, b]\n    sub             \u002F\u002F [a - b]\n    done jump\n    revert_underflow:\n        0x00 0x00 revert\n    done:\n}\n```\n\nNotice the `dup2 dup2` — we duplicate both `a` and `b` because `lt` will consume them, but we still need the originals for the `sub`.\n\n## SWAP: Rearranging the Stack\n\nThe EVM provides `SWAP1` through `SWAP16`. `SWAPn` exchanges the top item with the (n+1)-th item. The stack size stays the same.\n\n```huff\n\u002F\u002F Stack: [a, b, c, d]\nswap1  \u002F\u002F [b, a, c, d]          — swap top with 2nd\nswap3  \u002F\u002F [d, a, c, b]          — swap top with 4th\n```\n\nGas cost: 3 gas for any SWAPn.\n\n### When to SWAP\n\nSWAP reorders arguments for opcodes that expect a specific ordering. For example, `SUB` computes `stack[0] - stack[1]`. If your values are in the wrong order:\n\n```huff\n\u002F\u002F Stack: [b, a]  — but we want a - b\nswap1   \u002F\u002F [a, b]\nsub     \u002F\u002F [a - b]\n```\n\n## The Depth-16 Limitation\n\nDUP and SWAP only reach 16 deep. If a value is at position 17 or deeper, you cannot access it with a single opcode. This is a hard EVM constraint.\n\n**Strategies for deep stacks:**\n\n1. **Restructure your logic** to keep needed values near the top. This is the best approach.\n2. **Use memory as scratch space.** Store a value with `MSTORE`, retrieve it later with `MLOAD`. Costs 3+3=6 gas vs 3 gas for DUP, but breaks the depth barrier.\n3. **Break the macro into smaller macros** that each operate on fewer stack items.\n\n```huff\n#define macro STASH_TO_MEMORY() = takes(1) returns(0) {\n    \u002F\u002F takes: [value]\n    0x80 mstore     \u002F\u002F []  — stash at 0x80 (scratch space)\n}\n\n#define macro RECALL_FROM_MEMORY() = takes(0) returns(1) {\n    0x80 mload      \u002F\u002F [value]\n}\n```\n\nIn MEV contracts we often reserve `0x80..0xc0` as a scratch area for values that would otherwise push the stack past 16.\n\n## Common Patterns\n\n### Pattern 1: Keeping a Value Through a Consuming Operation\n\nYou have `[x]` and need to call an opcode that consumes `x` but you still need `x` afterward.\n\n```huff\n\u002F\u002F Want: compute hash of x, but keep x\n\u002F\u002F Stack: [x]\ndup1        \u002F\u002F [x, x]\n0x00 mstore \u002F\u002F [x]  — memory[0] = x\n0x20 0x00   \u002F\u002F [0, 32, x]\nkeccak256   \u002F\u002F [hash, x]\n```\n\n### Pattern 2: Rotating Three Items\n\nYou have `[a, b, c]` and need `[c, a, b]`:\n\n```huff\nswap2       \u002F\u002F [c, b, a]\nswap1       \u002F\u002F [c, a, b]\n```\n\n2 opcodes, 6 gas. There is no single-opcode rotation in the EVM.\n\nYou have `[a, b, c]` and need `[b, c, a]`:\n\n```huff\nswap1       \u002F\u002F [b, a, c]\nswap2       \u002F\u002F [b, c, a]\n```\n\n### Pattern 3: Cleaning Up Unwanted Stack Items\n\nAfter a computation you may have extra items. Use `pop` (2 gas) to discard:\n\n```huff\n\u002F\u002F Stack: [result, garbage1, garbage2]\nswap2       \u002F\u002F [garbage2, garbage1, result]\npop         \u002F\u002F [garbage1, result]\npop         \u002F\u002F [result]\n```\n\nOr more efficiently:\n\n```huff\n\u002F\u002F Stack: [result, garbage1, garbage2]\nswap1 pop   \u002F\u002F [result, garbage2]\nswap1 pop   \u002F\u002F [result]\n```\n\n### Pattern 4: Duplicating a Pair\n\nYou need to copy the top two items:\n\n```huff\n\u002F\u002F Stack: [a, b]\ndup2        \u002F\u002F [b, a, b]\ndup2        \u002F\u002F [a, b, a, b]\n```\n\nNotice you DUP in reverse order. `dup2` first copies `b` (which is at position 2), then `dup2` copies `a` (now at position 2 because we grew the stack). This pattern appears constantly in comparison-before-arithmetic code.\n\n## Stack Visualization Discipline\n\nWhen writing Huff, adopt this discipline:\n\n1. **Comment every line** with the stack state after execution.\n2. **Verify takes\u002Freturns** — count stack items at entry and exit.\n3. **Trace every branch** — at each JUMPI, both the taken and not-taken paths must leave the stack in a valid state.\n4. **Watch for stack drift** — if a loop body does not perfectly balance pushes and pops, the stack will grow or shrink on each iteration.\n\n```huff\n#define macro TRANSFER() = takes(3) returns(0) {\n    \u002F\u002F takes: [amount, from, to]\n\n    \u002F\u002F Load sender balance\n    dup2                    \u002F\u002F [from, amount, from, to]\n    sload                   \u002F\u002F [bal_from, amount, from, to]\n\n    \u002F\u002F Check sufficient balance\n    dup1 dup3               \u002F\u002F [amount, bal_from, bal_from, amount, from, to]\n    gt                      \u002F\u002F [amount > bal_from?, bal_from, amount, from, to]\n    insufficient jumpi      \u002F\u002F [bal_from, amount, from, to]\n\n    \u002F\u002F Deduct from sender\n    dup2                    \u002F\u002F [amount, bal_from, amount, from, to]\n    swap1 sub               \u002F\u002F [bal_from - amount, amount, from, to]\n    dup3                    \u002F\u002F [from, new_bal, amount, from, to]\n    sstore                  \u002F\u002F [amount, from, to]\n\n    \u002F\u002F Add to receiver\n    dup3                    \u002F\u002F [to, amount, from, to]\n    sload                   \u002F\u002F [bal_to, amount, from, to]\n    add                     \u002F\u002F [new_bal_to, from, to]\n    swap2                   \u002F\u002F [to, from, new_bal_to]\n    sstore                  \u002F\u002F [from]\n    pop                     \u002F\u002F []\n    done jump\n\n    insufficient:\n        0x00 0x00 revert\n    done:\n}\n```\n\nEvery line has a stack comment. Every branch terminates cleanly. This is the only way to write correct Huff.\n\n## Debugging Stack Errors\n\nThe most common bugs in Huff:\n\n1. **Stack underflow** — Popping from an empty stack. The EVM reverts at runtime. Cause: miscounted `takes` or missing a DUP.\n2. **Stack imbalance at JUMP** — A JUMPDEST reached from two different paths expects different stack states. The compiler will not catch this.\n3. **Off-by-one in DUP\u002FSWAP** — `dup3` vs `dup4` when you added an extra push earlier. This is why stack comments are mandatory.\n\n`huffc` has a `--stack-check` flag that performs basic stack analysis:\n\n```bash\nhuffc src\u002FContract.huff -r --stack-check\n```\n\nIt catches obvious underflows but cannot trace all dynamic jump paths. For complex contracts, trace execution manually with `forge debug` or `evm-trace`.\n\n## Advanced: The Stack as a Register File\n\nExperienced Huff developers think of the top ~8 stack positions as a register file:\n\n```\nPosition 1 (top):    Working register — current computation\nPosition 2-3:        Argument registers — inputs to next operation\nPosition 4-6:        Local variable registers — values needed soon\nPosition 7-8:        Context registers — loop counters, base pointers\nPosition 9+:         Spill area — rarely accessed, consider memory\n```\n\nThis mental model helps you decide when to SWAP a value to the top vs. when to DUP it, and when to spill to memory.\n\n## Summary\n\nStack management is the core skill for Huff development. DUP for non-destructive reads, SWAP for reordering, and memory for values beyond depth 16. Comment every line with the stack state. Verify every branch. In the next article, we will use these skills to build an O(1) function dispatcher with packed jump tables — where precise stack management directly translates to gas savings.","\u003Ch2 id=\"the-stack-machine-mental-model\">The Stack Machine Mental Model\u003C\u002Fh2>\n\u003Cp>The EVM is a stack machine. There are no registers, no named variables — just a last-in-first-out stack of 32-byte words, 1024 slots deep. Every opcode either pushes, pops, or rearranges items on this stack. If you cannot hold the current stack state in your head, you will produce buggy bytecode. This article is about building that mental model.\u003C\u002Fp>\n\u003Ch3>Notation Convention\u003C\u002Fh3>\n\u003Cp>Throughout this article (and in Huff comments), we represent stack state with brackets where the leftmost item is the top of the stack:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\u002F\u002F [top, second, third, ..., bottom]\n0x01  \u002F\u002F [1]\n0x02  \u002F\u002F [2, 1]\nadd   \u002F\u002F [3]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Every Huff macro should have a stack comment after each opcode. This is not optional — it is the only way to audit correctness.\u003C\u002Fp>\n\u003Ch2 id=\"dup-duplicating-stack-items\">DUP: Duplicating Stack Items\u003C\u002Fh2>\n\u003Cp>The EVM provides \u003Ccode>DUP1\u003C\u002Fcode> through \u003Ccode>DUP16\u003C\u002Fcode>. \u003Ccode>DUPn\u003C\u002Fcode> copies the n-th item from the top and pushes it onto the stack. The stack grows by 1.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F Stack: [a, b, c, d]\ndup1   \u002F\u002F [a, a, b, c, d]       — copy top\ndup3   \u002F\u002F [c, a, a, b, c, d]    — copy 3rd from top\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Gas cost: 3 gas for any DUPn. This is one of the cheapest operations in the EVM.\u003C\u002Fp>\n\u003Ch3>When to DUP\u003C\u002Fh3>\n\u003Cp>DUP is your tool for \u003Cstrong>non-destructive reads\u003C\u002Fstrong>. Many opcodes consume their arguments (ADD pops two, pushes one), so if you need a value again later, DUP it before feeding it to a consuming opcode.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro SAFE_SUB() = takes(2) returns(1) {\n    \u002F\u002F takes: [a, b] — compute a - b, revert if b &gt; a\n    dup2 dup2       \u002F\u002F [a, b, a, b]\n    lt              \u002F\u002F [a &lt; b?, a, b]\n    revert_underflow jumpi  \u002F\u002F [a, b]\n    sub             \u002F\u002F [a - b]\n    done jump\n    revert_underflow:\n        0x00 0x00 revert\n    done:\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Notice the \u003Ccode>dup2 dup2\u003C\u002Fcode> — we duplicate both \u003Ccode>a\u003C\u002Fcode> and \u003Ccode>b\u003C\u002Fcode> because \u003Ccode>lt\u003C\u002Fcode> will consume them, but we still need the originals for the \u003Ccode>sub\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch2 id=\"swap-rearranging-the-stack\">SWAP: Rearranging the Stack\u003C\u002Fh2>\n\u003Cp>The EVM provides \u003Ccode>SWAP1\u003C\u002Fcode> through \u003Ccode>SWAP16\u003C\u002Fcode>. \u003Ccode>SWAPn\u003C\u002Fcode> exchanges the top item with the (n+1)-th item. The stack size stays the same.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F Stack: [a, b, c, d]\nswap1  \u002F\u002F [b, a, c, d]          — swap top with 2nd\nswap3  \u002F\u002F [d, a, c, b]          — swap top with 4th\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Gas cost: 3 gas for any SWAPn.\u003C\u002Fp>\n\u003Ch3>When to SWAP\u003C\u002Fh3>\n\u003Cp>SWAP reorders arguments for opcodes that expect a specific ordering. For example, \u003Ccode>SUB\u003C\u002Fcode> computes \u003Ccode>stack[0] - stack[1]\u003C\u002Fcode>. If your values are in the wrong order:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F Stack: [b, a]  — but we want a - b\nswap1   \u002F\u002F [a, b]\nsub     \u002F\u002F [a - b]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"the-depth-16-limitation\">The Depth-16 Limitation\u003C\u002Fh2>\n\u003Cp>DUP and SWAP only reach 16 deep. If a value is at position 17 or deeper, you cannot access it with a single opcode. This is a hard EVM constraint.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Strategies for deep stacks:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Restructure your logic\u003C\u002Fstrong> to keep needed values near the top. This is the best approach.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Use memory as scratch space.\u003C\u002Fstrong> Store a value with \u003Ccode>MSTORE\u003C\u002Fcode>, retrieve it later with \u003Ccode>MLOAD\u003C\u002Fcode>. Costs 3+3=6 gas vs 3 gas for DUP, but breaks the depth barrier.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Break the macro into smaller macros\u003C\u002Fstrong> that each operate on fewer stack items.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro STASH_TO_MEMORY() = takes(1) returns(0) {\n    \u002F\u002F takes: [value]\n    0x80 mstore     \u002F\u002F []  — stash at 0x80 (scratch space)\n}\n\n#define macro RECALL_FROM_MEMORY() = takes(0) returns(1) {\n    0x80 mload      \u002F\u002F [value]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>In MEV contracts we often reserve \u003Ccode>0x80..0xc0\u003C\u002Fcode> as a scratch area for values that would otherwise push the stack past 16.\u003C\u002Fp>\n\u003Ch2 id=\"common-patterns\">Common Patterns\u003C\u002Fh2>\n\u003Ch3>Pattern 1: Keeping a Value Through a Consuming Operation\u003C\u002Fh3>\n\u003Cp>You have \u003Ccode>[x]\u003C\u002Fcode> and need to call an opcode that consumes \u003Ccode>x\u003C\u002Fcode> but you still need \u003Ccode>x\u003C\u002Fcode> afterward.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F Want: compute hash of x, but keep x\n\u002F\u002F Stack: [x]\ndup1        \u002F\u002F [x, x]\n0x00 mstore \u002F\u002F [x]  — memory[0] = x\n0x20 0x00   \u002F\u002F [0, 32, x]\nkeccak256   \u002F\u002F [hash, x]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Pattern 2: Rotating Three Items\u003C\u002Fh3>\n\u003Cp>You have \u003Ccode>[a, b, c]\u003C\u002Fcode> and need \u003Ccode>[c, a, b]\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">swap2       \u002F\u002F [c, b, a]\nswap1       \u002F\u002F [c, a, b]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>2 opcodes, 6 gas. There is no single-opcode rotation in the EVM.\u003C\u002Fp>\n\u003Cp>You have \u003Ccode>[a, b, c]\u003C\u002Fcode> and need \u003Ccode>[b, c, a]\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">swap1       \u002F\u002F [b, a, c]\nswap2       \u002F\u002F [b, c, a]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Pattern 3: Cleaning Up Unwanted Stack Items\u003C\u002Fh3>\n\u003Cp>After a computation you may have extra items. Use \u003Ccode>pop\u003C\u002Fcode> (2 gas) to discard:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F Stack: [result, garbage1, garbage2]\nswap2       \u002F\u002F [garbage2, garbage1, result]\npop         \u002F\u002F [garbage1, result]\npop         \u002F\u002F [result]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Or more efficiently:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F Stack: [result, garbage1, garbage2]\nswap1 pop   \u002F\u002F [result, garbage2]\nswap1 pop   \u002F\u002F [result]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Pattern 4: Duplicating a Pair\u003C\u002Fh3>\n\u003Cp>You need to copy the top two items:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F Stack: [a, b]\ndup2        \u002F\u002F [b, a, b]\ndup2        \u002F\u002F [a, b, a, b]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Notice you DUP in reverse order. \u003Ccode>dup2\u003C\u002Fcode> first copies \u003Ccode>b\u003C\u002Fcode> (which is at position 2), then \u003Ccode>dup2\u003C\u002Fcode> copies \u003Ccode>a\u003C\u002Fcode> (now at position 2 because we grew the stack). This pattern appears constantly in comparison-before-arithmetic code.\u003C\u002Fp>\n\u003Ch2 id=\"stack-visualization-discipline\">Stack Visualization Discipline\u003C\u002Fh2>\n\u003Cp>When writing Huff, adopt this discipline:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Comment every line\u003C\u002Fstrong> with the stack state after execution.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Verify takes\u002Freturns\u003C\u002Fstrong> — count stack items at entry and exit.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Trace every branch\u003C\u002Fstrong> — at each JUMPI, both the taken and not-taken paths must leave the stack in a valid state.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Watch for stack drift\u003C\u002Fstrong> — if a loop body does not perfectly balance pushes and pops, the stack will grow or shrink on each iteration.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro TRANSFER() = takes(3) returns(0) {\n    \u002F\u002F takes: [amount, from, to]\n\n    \u002F\u002F Load sender balance\n    dup2                    \u002F\u002F [from, amount, from, to]\n    sload                   \u002F\u002F [bal_from, amount, from, to]\n\n    \u002F\u002F Check sufficient balance\n    dup1 dup3               \u002F\u002F [amount, bal_from, bal_from, amount, from, to]\n    gt                      \u002F\u002F [amount &gt; bal_from?, bal_from, amount, from, to]\n    insufficient jumpi      \u002F\u002F [bal_from, amount, from, to]\n\n    \u002F\u002F Deduct from sender\n    dup2                    \u002F\u002F [amount, bal_from, amount, from, to]\n    swap1 sub               \u002F\u002F [bal_from - amount, amount, from, to]\n    dup3                    \u002F\u002F [from, new_bal, amount, from, to]\n    sstore                  \u002F\u002F [amount, from, to]\n\n    \u002F\u002F Add to receiver\n    dup3                    \u002F\u002F [to, amount, from, to]\n    sload                   \u002F\u002F [bal_to, amount, from, to]\n    add                     \u002F\u002F [new_bal_to, from, to]\n    swap2                   \u002F\u002F [to, from, new_bal_to]\n    sstore                  \u002F\u002F [from]\n    pop                     \u002F\u002F []\n    done jump\n\n    insufficient:\n        0x00 0x00 revert\n    done:\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Every line has a stack comment. Every branch terminates cleanly. This is the only way to write correct Huff.\u003C\u002Fp>\n\u003Ch2 id=\"debugging-stack-errors\">Debugging Stack Errors\u003C\u002Fh2>\n\u003Cp>The most common bugs in Huff:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Stack underflow\u003C\u002Fstrong> — Popping from an empty stack. The EVM reverts at runtime. Cause: miscounted \u003Ccode>takes\u003C\u002Fcode> or missing a DUP.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Stack imbalance at JUMP\u003C\u002Fstrong> — A JUMPDEST reached from two different paths expects different stack states. The compiler will not catch this.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Off-by-one in DUP\u002FSWAP\u003C\u002Fstrong> — \u003Ccode>dup3\u003C\u002Fcode> vs \u003Ccode>dup4\u003C\u002Fcode> when you added an extra push earlier. This is why stack comments are mandatory.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Ccode>huffc\u003C\u002Fcode> has a \u003Ccode>--stack-check\u003C\u002Fcode> flag that performs basic stack analysis:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">huffc src\u002FContract.huff -r --stack-check\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>It catches obvious underflows but cannot trace all dynamic jump paths. For complex contracts, trace execution manually with \u003Ccode>forge debug\u003C\u002Fcode> or \u003Ccode>evm-trace\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch2 id=\"advanced-the-stack-as-a-register-file\">Advanced: The Stack as a Register File\u003C\u002Fh2>\n\u003Cp>Experienced Huff developers think of the top ~8 stack positions as a register file:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>Position 1 (top):    Working register — current computation\nPosition 2-3:        Argument registers — inputs to next operation\nPosition 4-6:        Local variable registers — values needed soon\nPosition 7-8:        Context registers — loop counters, base pointers\nPosition 9+:         Spill area — rarely accessed, consider memory\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>This mental model helps you decide when to SWAP a value to the top vs. when to DUP it, and when to spill to memory.\u003C\u002Fp>\n\u003Ch2 id=\"summary\">Summary\u003C\u002Fh2>\n\u003Cp>Stack management is the core skill for Huff development. DUP for non-destructive reads, SWAP for reordering, and memory for values beyond depth 16. Comment every line with the stack state. Verify every branch. In the next article, we will use these skills to build an O(1) function dispatcher with packed jump tables — where precise stack management directly translates to gas savings.\u003C\u002Fp>\n","en","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:22.887766Z","Deep EVM #10: Huff Stack Management — dup, swap, and Stack Discipline","Master EVM stack management in Huff: dup1-16, swap1-16, the depth-16 limitation, common patterns, and debugging stack errors.","huff stack management evm",null,"index, follow",[22,27,31],{"id":23,"name":24,"slug":25,"created_at":26},"c0000000-0000-0000-0000-000000000016","EVM","evm","2026-03-28T10:44:21.513630Z",{"id":28,"name":29,"slug":30,"created_at":26},"c0000000-0000-0000-0000-000000000020","Gas Optimization","gas-optimization",{"id":32,"name":33,"slug":34,"created_at":26},"c0000000-0000-0000-0000-000000000017","Huff","huff","Blockchain",[37,43,49],{"id":38,"title":39,"slug":40,"excerpt":41,"locale":12,"category_name":35,"published_at":42},"de000000-0000-0000-0000-000000000003","The Ethereum Interoperability Layer: How 55+ L2s Become One Chain","ethereum-interoperability-layer-how-55-l2s-become-one-chain","Ethereum has 55+ Layer 2 rollups, fragmenting liquidity and user experience. The Ethereum Interoperability Layer — combining cross-rollup messaging, shared sequencers, and based rollups — aims to unify them into a single composable network.","2026-03-28T10:44:35.632478Z",{"id":44,"title":45,"slug":46,"excerpt":47,"locale":12,"category_name":35,"published_at":48},"de000000-0000-0000-0000-000000000002","ZK Proofs Beyond Rollups: Verifiable AI Inference on Ethereum","zk-proofs-beyond-rollups-verifiable-ai-inference-ethereum","Zero-knowledge proofs are no longer just a scaling tool. In 2026, zkML enables verifiable AI inference on-chain, ZK coprocessors move heavy computation off-chain with on-chain verification, and new proving systems like SP1 and Jolt make it practical.","2026-03-28T10:44:35.618408Z",{"id":50,"title":51,"slug":52,"excerpt":53,"locale":12,"category_name":35,"published_at":54},"dd000000-0000-0000-0000-000000000003","EIP-7702 in Practice: Building Smart Account Flows After Pectra","eip-7702-in-practice-building-smart-account-flows-after-pectra","EIP-7702 lets any Ethereum EOA temporarily act as a smart contract within a single transaction. Here is how to implement batch transactions, gas sponsorship, and social recovery using the new account abstraction primitive.","2026-03-28T10:44:35.031290Z",{"id":13,"name":56,"slug":57,"bio":58,"photo_url":19,"linkedin":19,"role":59,"created_at":60,"updated_at":60},"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"]