Opcode Summary
| Property | Value |
|---|---|
| Opcode | 0x50 |
| Mnemonic | POP |
| Gas | 2 |
| Stack Input | a (any 32-byte value on top of the stack) |
| Stack Output | (none — the top value is removed and discarded) |
| Behavior | Removes the topmost item from the stack and discards it permanently. The value is not written to memory, storage, or returndata — it simply ceases to exist. POP is one of the simplest opcodes in the EVM: it decrements the stack pointer by one. If the stack is empty when POP executes, the EVM triggers a stack underflow exception and the entire execution context reverts, consuming all forwarded gas. |
Threat Surface
POP itself is mechanically trivial — it removes one word from the stack. It has no memory side effects, no storage interaction, and no external calls. Its 2-gas cost is among the lowest in the EVM. Yet POP sits at the center of one of the most consequential bug classes in smart contract security: silently discarded return values.
The threat surface centers on three properties:
-
POP is how the EVM discards values the programmer chose not to use. When a Solidity developer calls
address.send(amount)without capturing the boolean return value, the compiler emits aCALL(which pushes a success/failure flag onto the stack) followed immediately byPOPto discard that flag. The contract proceeds as if the call succeeded regardless of the actual outcome. This compiler-level pattern —CALLthenPOP— is the bytecode signature of the unchecked external call vulnerability (SWC-104), one of the most exploited bug classes in Ethereum’s history. -
Stack underflow on an empty stack is an immediate revert. If POP executes when the stack is empty, the EVM halts with an exception. All gas forwarded to the current call context is consumed, and all state changes within that context are rolled back. While the EVM’s runtime stack validation prevents undefined behavior, an attacker who can force a code path that triggers an unexpected POP on an empty stack causes a denial-of-service: the transaction reverts, and the caller loses gas. Under EOF (EIP-5450), stack underflow is validated at deploy time, eliminating this class of runtime failure for EOF-validated contracts.
-
Inline assembly POP is unchecked and compiler-invisible. In Yul/inline assembly blocks, developers can emit raw
popinstructions to discard values from function calls likecall(),staticcall(), ordelegatecall(). Unlike high-level Solidity, the compiler does not warn when assemblypopdiscards a return value. This makes assembly blocks a blind spot for static analysis tools and a frequent source of unchecked call vulnerabilities in hand-optimized contracts.
Smart Contract Threats
T1: Discarded CALL Return Values — Unchecked External Calls (Critical)
When a contract makes an external call using low-level functions (.send(), .call(), .delegatecall(), .staticcall()), the EVM pushes a boolean success flag onto the stack. If the developer does not check this flag, the Solidity compiler emits POP to discard it. The contract continues execution as though the call succeeded, even if it failed. This is classified as SWC-104 (Unchecked Call Return Value) and appears in approximately 30% of Solidity audit findings.
The consequences include:
-
Lost ETH transfers treated as successful. A contract calls
recipient.send(amount)without checking the return value. The send fails (recipient’s fallback reverts, or recipient is a contract requiring >2300 gas), but the contract marks the payment as complete. The ETH remains in the contract, and the recipient never receives it. A second party can then claim those funds or the contract enters an inconsistent state. -
ERC-20 transfers that silently fail. Some ERC-20 tokens (notably USDT/Tether) do not return a boolean from
transfer()andtransferFrom(). When called via low-level.call(), the return data is empty, and if not handled withabi.decode, the contract assumes success. POP discards whatever was (or wasn’t) on the stack, and the token transfer is treated as complete when zero tokens moved. -
State corruption from partial execution. When a multi-step operation (e.g., swap then distribute proceeds) has an unchecked call midway, the later steps execute with incorrect assumptions about earlier steps’ success. State variables are updated based on phantom transfers, creating exploitable inconsistencies.
Why it matters: Unchecked external calls are a top-5 smart contract vulnerability by frequency and by dollar losses. The bytecode pattern CALL ... POP (without a conditional jump on the success flag) is the direct manifestation of this bug at the EVM level.
T2: Stack Underflow — Denial of Service (Medium)
POP on an empty stack causes an immediate exception. The EVM halts the current execution context, consumes all remaining gas forwarded to that context, and reverts all state changes. Attack vectors include:
-
Malformed bytecode execution. If a contract’s bytecode contains a code path where POP executes with insufficient stack depth (e.g., due to a compiler bug or hand-written bytecode error), any transaction hitting that path reverts unconditionally. This is a permanent denial-of-service for that code path.
-
Griefing via forced revert paths. In contracts that use assembly and depend on certain stack states, an attacker who can influence which code path executes may steer execution toward a path that underflows. The transaction reverts, wasting the caller’s gas.
-
EOF mitigation. EIP-5450 (EOF Stack Validation) moves stack underflow/overflow checks to deploy time. Under EOF, bytecode that could underflow at any reachable instruction is rejected during deployment. This eliminates runtime stack underflow for EOF contracts, though legacy (non-EOF) contracts remain vulnerable.
Why it matters: Stack underflow from POP doesn’t enable fund theft directly, but it causes gas loss and denial-of-service. In contracts where a revert blocks a critical state transition (e.g., a payment queue), underflow can freeze funds indefinitely.
T3: Compiler Optimization Discarding Needed Values (Medium)
The Solidity compiler uses POP when it determines a return value is unused. Compiler optimizations can interact with this in unexpected ways:
-
Dead code elimination removing safety checks. If the optimizer determines that a variable holding a call’s return value is never read, it may elide the variable and emit POP directly after the CALL. A developer who intended to check the return value in a later statement that the optimizer removed (e.g., due to an unreachable code path) silently loses the check.
-
Optimizer bugs affecting inline assembly. The Solidity optimizer (versions >=0.8.13) had a bug where it incorrectly removed memory writes in assembly blocks when the block didn’t reference surrounding Solidity variables. While not directly a POP issue, optimizer interactions with stack management in assembly can lead to values being discarded or overwritten unexpectedly.
-
ABI decoding edge cases. When calling contracts that return unexpected data lengths (e.g., a token that returns nothing vs. a boolean), the compiler’s generated code may POP values that were expected to be decoded, or fail to POP extra stack items, leading to stack imbalance in subsequent operations.
Why it matters: Developers trust the compiler to faithfully translate their intent. When optimizations discard values the developer assumed were retained, the resulting bytecode has different security properties than the source code suggests.
T4: Inline Assembly POP Misuse (High)
In Yul and inline assembly, pop is explicitly used to discard return values from low-level calls. Unlike high-level Solidity (where the compiler warns about unused return values), assembly pop is silent:
-
Explicit suppression of call results. A common assembly pattern is
pop(call(gas(), target, value, 0, 0, 0, 0))— thepopdiscards the success flag. The developer may intend a fire-and-forget call, but if the call’s success matters to subsequent logic, this creates the same vulnerability as SWC-104 at the assembly level, invisible to Solidity-level static analyzers. -
Stack balancing errors. Assembly blocks must leave the stack in the same state they found it (or the expected output state). Incorrect use of
popto “fix” a stack imbalance can mask deeper logic errors — the developer pops a value to make the compiler happy, but that value was a critical return code or pointer. -
Memory pointer corruption. When assembly code interacts with the free memory pointer (0x40) and uses
popto discard intermediate values, a misplacedpopcan cause subsequent memory operations to read from or write to incorrect offsets, leading to data corruption that propagates through the contract.
Why it matters: Assembly is used in gas-critical code paths (DEX routers, bridges, MEV bots) where correctness is paramount. Assembly pop is the most direct way to silently ignore a call failure, and it bypasses all compiler-level safety checks.
T5: Silent Value Destruction in Complex Stack Operations (Low)
POP permanently destroys information. In complex contracts with deep stack operations, an incorrectly placed POP can silently destroy values that are needed later:
-
Discarding DUP’d values prematurely. A developer DUPs a value for later use, then inadvertently POPs the copy before it’s consumed. The subsequent opcode that expected the value reads a different stack item, causing logic corruption.
-
Off-by-one stack depth errors in hand-written bytecode. Contracts written in raw bytecode or generated by non-Solidity compilers (Vyper, Huff, Fe) may have stack depth miscalculations where POP removes the wrong item from the stack. Since POP always removes the top item, any miscalculation in the number of items pushed/popped before it changes which value gets destroyed.
-
CREATE/CREATE2 return value discard.
CREATEandCREATE2push the deployed contract’s address (or 0 on failure) onto the stack. If this value is POPped without checking for zero, the contract doesn’t know whether deployment succeeded. Subsequent interactions with the “deployed” contract target address(0), which either reverts or burns ETH.
Why it matters: While primarily a correctness issue rather than a direct attack vector, silent value destruction in production bytecode has caused contract failures where funds become permanently locked.
Protocol-Level Threats
P1: No Gas Griefing Vector (Low)
POP costs a fixed 2 gas with no dynamic component. It reads and discards one stack item in a single cycle. It cannot be used for gas amplification attacks, and its cost is too low to be meaningful in gas metering games. No protocol-level vulnerability has ever been attributed to POP’s gas cost.
P2: Consensus Safety (Low)
POP is deterministic — it removes the top stack element unconditionally. All EVM client implementations agree on its behavior. No consensus bugs have been attributed to POP. The only implementation variance is in error handling for stack underflow: all clients must revert with an out-of-gas exception, and all conforming clients do.
P3: EOF Stack Validation Transition — EIP-5450 (Low)
EIP-5450 introduces deploy-time stack validation for EOF-formatted contracts. Under EOF, the validator statically verifies that no instruction can execute with insufficient stack depth, which means POP on an empty stack is caught before the contract is deployed. This eliminates the T2 (stack underflow) threat for EOF contracts. However, legacy (non-EOF) contracts will coexist on the network indefinitely, so the runtime underflow risk persists for older deployed code.
Edge Cases
| Edge Case | Behavior | Security Implication |
|---|---|---|
| POP on empty stack | EVM exception: execution halts, all gas consumed, state reverted | Denial-of-service for that code path; transaction sender loses gas |
| POP after failed CALL | POP removes the 0 (failure) flag from the stack; execution continues | Contract proceeds as if the call succeeded — classic unchecked call bug (SWC-104) |
| POP after successful CALL | POP removes the 1 (success) flag; execution continues | No immediate bug, but the success confirmation is lost — later error paths cannot distinguish success from failure |
| POP in STATICCALL context | Behaves identically to POP in any context; no state-change restriction applies to POP itself | Discarding a STATICCALL return value is the same unchecked-call pattern in a read-only context |
| POP of CREATE/CREATE2 result | Discards the deployed address (or 0 on failure) | Contract cannot verify deployment succeeded; may interact with address(0) or a non-existent contract |
POP in inline assembly (pop(call(...))) | Explicitly discards the low-level call’s success boolean | Bypasses Solidity compiler warnings about unchecked returns; invisible to most static analyzers |
| Multiple consecutive POPs | Each POP removes one stack item; N POPs remove N items | Stack underflow if fewer than N items remain; common in hand-written bytecode cleanup sequences |
| POP under EOF (EIP-5450) | Stack underflow is validated at deploy time; POP on empty stack prevents deployment | Eliminates runtime underflow for EOF contracts; no effect on legacy contracts |
| POP of high-value data (private keys, seeds) | Value is removed from the stack; no memory/storage trace | POP does not zero memory — the value was never in memory. Stack values exist only in the execution context and are not recoverable after POP. |
Real-World Exploits
Exploit 1: King of the Ether Throne — Unchecked send() Return Value (February 2016)
Root cause: The contract used .send() to transfer ETH compensation to dethroned monarchs but did not check the boolean return value. The compiler emitted CALL followed by POP, discarding the success flag.
Details: King of the Ether Throne was an early Ethereum game where players paid increasing amounts of ETH to claim the “throne.” When a new monarch took over, the contract sent the claim price to the previous monarch via .send(). The Mist Ethereum Wallet created contract-based wallets for users, and these contract wallets required more than the 2300 gas stipend that .send() provides. When sending ETH to these contract wallets, the .send() call failed and returned false, but the contract executed POP on that false value and continued as if payment succeeded.
The contract marked the compensation as “sent” in its internal accounting regardless of the transfer outcome. The ETH remained locked in the King of the Ether contract, and dethroned monarchs never received their compensation. The contract had no withdrawal mechanism for failed payments.
POP’s role: At the bytecode level, the vulnerability is a CALL (which pushes 0 for failure) immediately followed by POP (which discards that 0). Without a conditional jump checking the success flag between CALL and POP, the failure is invisible to the contract’s logic.
Impact: Multiple users lost ETH compensation payments. While the total losses were modest by modern standards (< 100 ETH), the exploit became the canonical example of the unchecked return value vulnerability and directly influenced the design of Solidity’s transfer() function, which auto-reverts on failure.
References:
- King of the Ether Throne Post-Mortem
- Crytic/not-so-smart-contracts: Unchecked External Call
- King of the Ether Contract Safety Checklist
Exploit 2: Aperture Finance — Unchecked Low-Level Call Enabling $3.67M Drain (January 2026)
Root cause: Insufficient input validation on parameters passed to low-level .call(), combined with failure to check return values, allowed attackers to redirect execution to arbitrary contracts.
Details: On January 25, 2026, Aperture Finance was exploited across Ethereum, Arbitrum, and Base chains for over $3.67 million. The attacker exploited an arbitrary-call vulnerability where user-controlled parameters were passed directly into low-level call() invocations without proper validation or return value checking. The attacker crafted call parameters that targeted ERC-20 token contracts, invoking transferFrom() to drain assets by abusing existing token approvals that users had granted to the Aperture contracts.
The core issue was twofold: (1) the contract did not validate the target address or calldata of the low-level call, and (2) the return value of the call was not checked, so even if the call had partially failed or returned unexpected data, execution would have continued.
POP’s role: The unchecked call pattern at the bytecode level follows the same CALL ... POP structure. The success flag was discarded, and the contract could not distinguish between a successful token transfer and a failed one. While the primary vulnerability was the arbitrary call target (not the unchecked return), the POP of the success flag eliminated a potential defense layer.
Impact: $3.67M drained across three chains. The exploit demonstrated that unchecked low-level calls remain a live vulnerability class in production DeFi, even in 2026.
References:
Exploit 3: Unchecked send() in Early DeFi — Recurring Pattern (2016-2019)
Root cause: Widespread use of .send() without return value checking across early Ethereum contracts, compiling to the CALL POP bytecode pattern.
Details: Between 2016 and 2019, dozens of contracts used the pattern recipient.send(amount) (or the even earlier recipient.call.value(amount)()) without checking the return value. The SWC (Smart Contract Weakness Classification) registry cataloged this as SWC-104, and empirical studies found the pattern in thousands of deployed contracts. Common failure modes included:
- Lottery and gambling contracts that marked winners as “paid” regardless of whether the ETH transfer succeeded. Failed payments left ETH stuck in the contract with no withdrawal path.
- Multi-party payment splitters that iterated over a list of recipients, calling
.send()in a loop without checking returns. One failed send (e.g., to a contract wallet) silently skipped that recipient while continuing to pay others. - Crowdfunding contracts where refund mechanisms used unchecked
.send(). When refunds failed (recipient reverted), the contract marked refunds as complete, permanently locking contributor funds.
The Solidity team introduced .transfer() (which auto-reverts on failure) specifically to combat this pattern. Later, the community shifted to the checks-effects-interactions pattern with explicit return value checking via require(success) after .call{value: amount}("").
POP’s role: In every instance, the compiled bytecode shows the same structure: CALL pushes a success flag, then POP immediately discards it. The POP instruction is the bytecode-level mechanism that makes the vulnerability possible — it is the point where the contract’s ability to detect failure is permanently destroyed.
Impact: Cumulative losses across many contracts totaling hundreds to thousands of ETH. More importantly, the pattern established the unchecked return value as one of the “classic” smart contract vulnerability classes, leading to SWC-104, static analyzer rules in Slither/Mythril/Securify, and the OWASP Smart Contract Top 10 (SC06).
References:
- SWC-104: Unchecked Call Return Value
- OWASP SC06: Unchecked External Calls
- BailSec: Unchecked Return Values — A Silent Threat
Attack Scenarios
Scenario A: Unchecked send() in a Payment Contract
contract VulnerablePayment {
mapping(address => uint256) public balances;
mapping(address => bool) public paid;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function payOut(address payable recipient) external {
require(!paid[recipient], "already paid");
uint256 amount = balances[recipient];
require(amount > 0, "no balance");
// VULNERABLE: .send() returns false if recipient reverts,
// but the return value is POPped by the compiler.
// The contract marks payment as complete regardless.
recipient.send(amount);
// State updated even if send() failed -- funds are stuck
paid[recipient] = true;
balances[recipient] = 0;
}
// Attack: Recipient is a contract whose receive() reverts.
// .send() returns false, POP discards it, paid[recipient] = true.
// The ETH stays in this contract, and recipient can never claim it.
// If a second mechanism relies on paid[recipient] == true,
// the attacker can exploit the inconsistent state.
}Scenario B: Assembly pop(call(…)) Hiding a Failed Transfer
contract VulnerableDEXRouter {
function swapAndForward(
address tokenIn,
address tokenOut,
uint256 amountIn,
address recipient
) external {
// Perform swap (simplified)
uint256 amountOut = _executeSwap(tokenIn, tokenOut, amountIn);
// VULNERABLE: Assembly pop discards transfer success flag.
// If the ERC-20 transfer fails (e.g., recipient is blacklisted),
// the router believes tokens were forwarded.
assembly {
let ptr := mload(0x40)
mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 4), recipient)
mstore(add(ptr, 36), amountOut)
// pop discards the success boolean -- transfer failure is invisible
pop(call(gas(), tokenOut, 0, ptr, 68, 0, 0))
}
// Contract proceeds as if recipient received tokens.
// Tokens remain in the router; attacker can claim them later.
emit SwapCompleted(tokenIn, tokenOut, amountIn, amountOut, recipient);
}
function _executeSwap(address, address, uint256) internal pure returns (uint256) {
return 1000; // simplified
}
event SwapCompleted(address, address, uint256, uint256, address);
}Scenario C: Stack Underflow DoS via Malformed Bytecode
// Hand-crafted bytecode that underflows on a specific path
//
// Normal path: PUSH1 0x01 PUSH1 0x02 ADD POP STOP
// Stack: [1] -> [1,2] -> [3] -> [] -> halt
//
// Malicious path: JUMPDEST POP STOP
// Stack: [] -> EXCEPTION (underflow)
//
// If an attacker can force a JUMP to the JUMPDEST at the start
// of the malicious path when the stack is empty, POP triggers
// a stack underflow exception. The transaction reverts, consuming
// all gas forwarded to this context.
//
// In a real contract, this manifests when a rarely-tested code path
// (e.g., error handling branch) has a stack depth mismatch.
// The contract works for normal operations but reverts for edge cases,
// potentially blocking withdrawals or liquidations.
Scenario D: Discarded CREATE2 Return Value
contract VulnerableFactory {
function deployAndFund(bytes32 salt, bytes memory bytecode) external payable {
address deployed;
assembly {
deployed := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
// VULNERABLE: Does not check if deployed == address(0)
// (deployment failure). If create2 fails (e.g., contract already
// exists at that address, or init code reverts), deployed is 0.
// The ETH is sent to address(0), which burns it permanently.
(bool success,) = deployed.call{value: msg.value}("");
require(success, "funding failed");
// Even if we checked success here, sending ETH to address(0)
// succeeds (it's a valid transfer to the zero address).
// The real bug is discarding the create2 result without
// checking for zero -- conceptually the same as POP on a
// critical return value.
}
}Mitigations
| Threat | Mitigation | Implementation |
|---|---|---|
| T1: Unchecked external call return | Always check the boolean return value of low-level calls | (bool success,) = target.call{value: amount}(""); require(success, "call failed"); |
| T1: Unchecked send() | Use transfer() (auto-reverts) or checked call() | Replace recipient.send(amount) with (bool ok,) = recipient.call{value: amount}(""); require(ok); |
| T1: Non-standard ERC-20 returns | Use OpenZeppelin’s SafeERC20 library | SafeERC20.safeTransfer(token, recipient, amount) handles tokens that return nothing, false, or revert |
| T2: Stack underflow DoS | Use high-level Solidity instead of raw bytecode; test all code paths | Comprehensive test coverage including error/edge paths; formal verification for critical contracts |
| T2: EOF stack validation | Deploy as EOF-formatted contracts when available | EOF (EIP-5450) catches stack underflow at deploy time; no runtime risk for validated contracts |
| T3: Compiler optimization discarding values | Pin compiler versions; audit compiled bytecode for critical contracts | Compare source-level intent against emitted bytecode; use forge inspect to review compiler output |
| T4: Assembly pop misuse | Avoid assembly for external calls; when unavoidable, always check success | Replace pop(call(...)) with let ok := call(...) if iszero(ok) { revert(0, 0) } |
| T4: Static analysis blind spots | Run Slither, Mythril, or Securify on compiled bytecode | Slither’s unchecked-lowlevel and unchecked-send detectors specifically flag CALL POP patterns |
| T5: Discarded CREATE/CREATE2 return | Always check for address(0) after deployment | require(deployed != address(0), "deployment failed") |
| General: Withdrawal pattern | Use pull-over-push for ETH distribution | Instead of pushing ETH to recipients (where send can fail), let recipients withdraw via a claim() function |
Compiler/EIP-Based Protections
- Solidity
transfer()(>= 0.4.13): Introduced as a safe alternative to.send().transfer()automatically reverts on failure, eliminating theCALL POPpattern for ETH transfers. Limited to 2300 gas, which prevents reentrancy but can fail for contract recipients that need more gas. - Solidity >= 0.8.0 compiler warnings: The compiler emits a warning when the return value of
.send()or low-level.call()is not used, directly flagging the pattern that compiles toCALL POP. - SafeERC20 (OpenZeppelin): Wraps ERC-20
transfer,transferFrom, andapprovecalls with return value checking and handles non-compliant tokens (no return value, false return). Eliminates the POP-based discard of token transfer results. - EIP-5450 (EOF Stack Validation): Validates stack depth at deploy time for EOF contracts. POP on an empty stack prevents contract deployment, eliminating runtime stack underflow entirely.
- Slither unchecked-lowlevel / unchecked-send detectors: Static analysis rules that detect the
CALL ... POPpattern in bytecode and flag unchecked return values at the source level.
Severity Summary
| Threat ID | Category | Severity | Likelihood | Real-World Precedent |
|---|---|---|---|---|
| T1 | Smart Contract | Critical | High | King of Ether Throne (2016), Aperture Finance ($3.67M, 2026), SWC-104 in thousands of contracts |
| T2 | Smart Contract | Medium | Low | No major exploit solely from POP underflow; mitigated by compiler-generated stack checks |
| T3 | Smart Contract | Medium | Low | Solidity optimizer bugs (2022); no direct exploit from POP-related optimization |
| T4 | Smart Contract | High | Medium | Recurring pattern in MEV bots and DEX routers using assembly; Aperture Finance |
| T5 | Smart Contract | Low | Low | Contract deployment failures from discarded CREATE return values; funds locked but no major named exploit |
| P1 | Protocol | Low | N/A | — |
| P2 | Protocol | Low | N/A | — |
| P3 | Protocol | Low | N/A | EIP-5450 pending; eliminates T2 for EOF contracts |
Related Opcodes
| Opcode | Relationship |
|---|---|
| ISZERO (0x15) | The safe alternative to POP after a CALL. ISZERO checks whether the CALL’s return value is 0 (failure) and is used with JUMPI to branch on call failure. The pattern CALL ISZERO PUSH jumpdest JUMPI is the checked-call equivalent of the vulnerable CALL POP. |
| CALL (0xF1) | Pushes a success flag (0 or 1) onto the stack. When followed by POP instead of a conditional check, this creates the unchecked external call vulnerability (SWC-104). CALL is the primary opcode whose return value POP dangerously discards. |
| DUP1 (0x80) | Duplicates the top stack item. Used to preserve a value before POP consumes it. The pattern CALL DUP1 ... POP allows checking the call result while still cleaning up the stack. DUP1 before POP is a defensive pattern to retain values that might be needed. |
| DELEGATECALL (0xF4) | Like CALL, pushes a success flag. Unchecked DELEGATECALL (followed by POP) is especially dangerous because a failed delegatecall can leave the calling contract in an inconsistent state with its storage partially modified. |
| STATICCALL (0xFA) | Read-only external call that pushes a success flag. Discarding this via POP means the contract cannot distinguish between “the call returned valid data” and “the call reverted.” |
| CREATE (0xF0) / CREATE2 (0xF5) | Push the deployed contract address (or 0 on failure). POP discarding this value means the contract cannot verify deployment succeeded, potentially sending funds to address(0). |
| SWAP1 (0x90) | Swaps top two stack items. Used in conjunction with POP to discard a specific stack item that is not on top. SWAP1 POP discards the second-from-top item. |
| JUMPI (0x57) | Conditional jump that consumes the top stack item as the condition. The safe pattern after CALL is ISZERO PUSH dest JUMPI (jump to error handler if call failed). Replacing this with POP eliminates the safety branch entirely. |