Skip to main content
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.

Overview

Opcode: 0x50 Introduced: Frontier (EVM genesis) POP removes the top item from the stack without using its value. Used to discard unneeded computation results or clean up the stack.

Specification

Stack Input:
value (any uint256)
Stack Output:
[]
Gas Cost: 2 (GasQuickStep) Operation:
stack.pop()

Behavior

POP discards the top stack item. The value is not returned or used - it simply removes one item from the stack depth. Key characteristics:
  • Requires stack depth ≥ 1
  • Does not return the popped value
  • Decreases stack depth by 1
  • Cannot fail on empty stack (reverts with StackUnderflow)

Examples

Basic Usage

import { handler_0x50_POP } from '@tevm/voltaire/evm/stack/handlers';
import { createFrame } from '@tevm/voltaire/evm/Frame';

// Discard top value
const frame = createFrame({
  stack: [42n, 100n],
  gasRemaining: 1000n
});

const err = handler_0x50_POP(frame);

console.log(frame.stack); // [42n] - top value removed
console.log(frame.gasRemaining); // 998n

Solidity Compilation

contract Example {
    function discard() public pure returns (uint256) {
        uint256 a = 10;
        uint256 b = 20;
        // b not used - compiler inserts POP
        return a;
    }
}

// Generated bytecode:
// PUSH1 0x0a   (push 10)
// PUSH1 0x14   (push 20)
// POP          (discard b)
// ...          (return a)

Assembly Usage

assembly {
    // Clean up unused return values
    let x := 100
    let y := add(x, 50)  // Stack: [150]
    pop                   // Discard result
    // Stack: []
}

Gas Cost

Cost: 2 gas (GasQuickStep) POP is one of the cheapest stack operations, same tier as:
  • PUSH0 (0x5f): 2 gas
Cheaper than:
  • PUSH1-32 (0x60-0x7f): 3 gas
  • DUP1-16 (0x80-0x8f): 3 gas
  • SWAP1-16 (0x90-0x9f): 3 gas

Common Usage

Discarding Return Values

contract TokenSwap {
    IERC20 token;

    function swap() public {
        // Transfer returns bool, but we don't check it
        assembly {
            // CALL returns success on stack
            // If we don't need it:
            pop  // Discard return value
        }
    }
}

Stack Cleanup

assembly {
    // After complex computation
    let a := add(1, 2)   // Stack: [3]
    let b := mul(a, 4)   // Stack: [3, 12]
    let c := div(b, 2)   // Stack: [3, 12, 6]

    // Only need final result
    swap2                 // Stack: [6, 12, 3]
    pop                   // Stack: [6, 12]
    pop                   // Stack: [6]
}

Optimizing Storage Reads

function getBalance(address user) public view returns (uint256) {
    assembly {
        // Load storage slot
        mstore(0, user)
        mstore(32, 0)
        let slot := keccak256(0, 64)
        let balance := sload(slot)

        // Clean up memory (optional optimization)
        mstore(0, 0)  // Stack: [0]
        pop           // Clean stack
        mstore(32, 0) // Stack: [0]
        pop           // Clean stack

        // Return balance
        mstore(0, balance)
        return(0, 32)
    }
}

Security

Stack Underflow Protection

// UNSAFE: Assumes stack has value
assembly {
    pop  // Reverts if stack empty!
}
// SAFE: Check before popping
function safePop() public pure {
    assembly {
        // Solidity ensures stack safety
        let x := 100
        pop  // Safe, x on stack
    }
}

Double Spending Prevention

// DANGEROUS: Forgetting to pop
contract Vulnerable {
    mapping(address => uint256) balances;

    function withdraw() public {
        assembly {
            // Load balance
            let bal := sload(balances.slot)
            // Transfer (leaves success on stack)
            // Forgot to POP success value!
            // Next operation uses wrong value!
        }
    }
}

Gas Waste

// INEFFICIENT: Unnecessary computation
function waste() public pure returns (uint256) {
    uint256 x = expensiveComputation();  // Gas spent
    pop(x);  // Result discarded!
    return 42;
}

// EFFICIENT: Don't compute if not needed
function efficient() public pure returns (uint256) {
    return 42;
}

Optimization

Avoid Unnecessary Pushes

// BAD: Push then immediately pop
assembly {
    push1 0x42
    pop
}

// GOOD: Don't push at all
assembly {
    // Nothing
}

Reorder to Minimize POPs

// INEFFICIENT: Many POPs
assembly {
    let a := 1
    let b := 2
    let c := 3
    pop  // Discard c
    pop  // Discard b
    // Use a
}

// EFFICIENT: Avoid intermediate variables
assembly {
    let a := 1
    // Use a directly
}

Implementation

import { consumeGas } from "../../Frame/consumeGas.js";
import { popStack } from "../../Frame/popStack.js";
import { FastestStep } from "../../../primitives/GasConstants/BrandedGasConstants/constants.js";

/**
 * POP opcode (0x50) - Remove top item from stack
 *
 * Stack: [value] => []
 * Gas: 2 (GasQuickStep)
 */
export function handler_0x50_POP(frame: FrameType): EvmError | null {
  const gasErr = consumeGas(frame, FastestStep);
  if (gasErr) return gasErr;

  const { error } = popStack(frame);
  if (error) return error;

  frame.pc += 1;
  return null;
}

Edge Cases

Empty Stack

// Stack underflow
const frame = createFrame({ stack: [] });
const err = handler_0x50_POP(frame);

console.log(err); // { type: "StackUnderflow" }

Single Item Stack

// Success case
const frame = createFrame({ stack: [100n] });
const err = handler_0x50_POP(frame);

console.log(err); // null
console.log(frame.stack); // []

Out of Gas

// Insufficient gas
const frame = createFrame({
  stack: [42n],
  gasRemaining: 1n
});

const err = handler_0x50_POP(frame);
console.log(err); // { type: "OutOfGas" }

Maximum Stack Depth

// Works at any depth
const frame = createFrame({
  stack: new Array(1024).fill(0n)
});

const err = handler_0x50_POP(frame);

console.log(err); // null
console.log(frame.stack.length); // 1023

References