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: 0x11
Introduced: Frontier (EVM genesis)
GT performs unsigned greater than comparison on two 256-bit integers. Returns 1 if the first value is strictly greater than the second, 0 otherwise. All values are treated as unsigned integers in the range 0 to 2^256 - 1.
This operation complements LT for implementing range checks and conditional logic in smart contracts.
Specification
Stack Input:
Stack Output:
Gas Cost: 3 (GasFastestStep)
Operation:
Behavior
GT pops two values from the stack, compares them as unsigned 256-bit integers, and pushes 1 if a > b, otherwise 0:
- If
a > b: Result is 1 (true)
- If
a <= b: Result is 0 (false)
All comparisons are unsigned. Values with bit 255 set are treated as large positive numbers, not negative values.
Examples
Basic Comparison
import { gt } from '@tevm/voltaire/evm/comparison';
import { createFrame } from '@tevm/voltaire/evm/Frame';
// 10 > 5 = 1 (true)
const frame = createFrame({ stack: [10n, 5n] });
const err = gt(frame);
console.log(frame.stack); // [1n]
console.log(frame.gasRemaining); // Original - 3
Equal Values
// 20 > 20 = 0 (false)
const frame = createFrame({ stack: [20n, 20n] });
const err = gt(frame);
console.log(frame.stack); // [0n]
Lesser Value
// 20 > 30 = 0 (false)
const frame = createFrame({ stack: [20n, 30n] });
const err = gt(frame);
console.log(frame.stack); // [0n]
Zero Comparison
// 1 > 0 = 1 (true)
const frame = createFrame({ stack: [1n, 0n] });
gt(frame);
console.log(frame.stack); // [1n]
// 0 > 1 = 0 (false)
const frame2 = createFrame({ stack: [0n, 1n] });
gt(frame2);
console.log(frame2.stack); // [0n]
Maximum Values
// (2^256 - 1) > (2^256 - 2) = 1 (true)
const MAX = (1n << 256n) - 1n;
const frame = createFrame({ stack: [MAX, MAX - 1n] });
gt(frame);
console.log(frame.stack); // [1n]
Unsigned Treatment
// 2^255 is treated as large positive (not negative)
const SIGN_BIT = 1n << 255n;
// 2^255 > 1 = 1 (true, unsigned comparison)
const frame = createFrame({ stack: [SIGN_BIT, 1n] });
gt(frame);
console.log(frame.stack); // [1n]
// In signed comparison (SGT), this would be 0 because 2^255 = -2^255 < 1
Gas Cost
Cost: 3 gas (GasFastestStep)
GT shares the lowest gas tier with other comparison and basic operations:
- LT, GT, SLT, SGT, EQ (comparisons)
- ISZERO, NOT
- ADD, SUB
Comparison:
- LT/GT/EQ: 3 gas
- MUL/DIV: 5 gas
- ADDMOD: 8 gas
Edge Cases
Boundary Values
const MAX = (1n << 256n) - 1n;
// MAX > 0 = 1
gt(createFrame({ stack: [MAX, 0n] })); // [1n]
// 0 > MAX = 0
gt(createFrame({ stack: [0n, MAX] })); // [0n]
// MAX > MAX = 0
gt(createFrame({ stack: [MAX, MAX] })); // [0n]
Sign Bit Set
// Values with bit 255 set are large positive (unsigned)
const SIGN_BIT = 1n << 255n; // 2^255
// SIGN_BIT is treated as 2^255, not -2^255
// 2^255 > 1 = 1 (true, unsigned)
gt(createFrame({ stack: [SIGN_BIT, 1n] })); // [1n]
// Compare with SGT (signed):
// SGT would return 0 because 2^255 = -2^255 < 1 (signed)
Stack Underflow
// Not enough stack items
const frame = createFrame({ stack: [5n] });
const err = gt(frame);
console.log(err); // { type: "StackUnderflow" }
console.log(frame.stack); // [5n] (unchanged)
Out of Gas
// Insufficient gas
const frame = createFrame({ stack: [10n, 5n], gasRemaining: 2n });
const err = gt(frame);
console.log(err); // { type: "OutOfGas" }
console.log(frame.gasRemaining); // 0n
Large Values
// Arbitrary precision supported
const a = 987654321098765432109876543210n;
const b = 123456789012345678901234567890n;
const frame = createFrame({ stack: [a, b] });
gt(frame);
console.log(frame.stack); // [1n] (a > b)
Common Usage
Upper Bounds Checking
// require(value <= max) === require(!(value > max))
assembly {
if gt(value, max) {
revert(0, 0)
}
}
Range Validation
// Check if value > min
assembly {
let valid := gt(value, min)
if iszero(valid) {
revert(0, 0)
}
}
Maximum Value
// max(a, b)
assembly {
let maximum := a
if gt(b, a) {
maximum := b
}
}
Countdown Loop
// for (uint i = n; i > 0; i--)
assembly {
let i := n
for {} gt(i, 0) { i := sub(i, 1) } {
// Loop body
}
}
Balance Check
// require(balance > amount)
assembly {
if iszero(gt(balance, amount)) {
revert(0, 0)
}
}
Implementation
/**
* GT opcode (0x11) - Greater than comparison (unsigned)
*/
export function handle(frame: FrameType): EvmError | null {
// Consume gas (GasFastestStep = 3)
const gasErr = consumeGas(frame, FastestStep);
if (gasErr) return gasErr;
// Pop operands (b is top, a is second)
const bResult = popStack(frame);
if (bResult.error) return bResult.error;
const b = bResult.value;
const aResult = popStack(frame);
if (aResult.error) return aResult.error;
const a = aResult.value;
// Compare: a > b (unsigned)
const result = a > b ? 1n : 0n;
// Push result
const pushErr = pushStack(frame, result);
if (pushErr) return pushErr;
// Increment PC
frame.pc += 1;
return null;
}
Testing
Test Coverage
import { describe, it, expect } from 'vitest';
import { handle as GT } from './0x11_GT.js';
describe('GT (0x11)', () => {
it('returns 1 when a > b', () => {
const frame = createFrame([30n, 20n]);
expect(GT(frame)).toBeNull();
expect(frame.stack).toEqual([1n]);
expect(frame.pc).toBe(1);
expect(frame.gasRemaining).toBe(997n);
});
it('returns 0 when a <= b (equal)', () => {
const frame = createFrame([20n, 20n]);
expect(GT(frame)).toBeNull();
expect(frame.stack).toEqual([0n]);
});
it('returns 0 when a < b', () => {
const frame = createFrame([10n, 20n]);
expect(GT(frame)).toBeNull();
expect(frame.stack).toEqual([0n]);
});
it('handles 1 > 0', () => {
const frame = createFrame([1n, 0n]);
expect(GT(frame)).toBeNull();
expect(frame.stack).toEqual([1n]);
});
it('handles max uint256 values', () => {
const MAX = (1n << 256n) - 1n;
const frame = createFrame([MAX, MAX - 1n]);
expect(GT(frame)).toBeNull();
expect(frame.stack).toEqual([1n]);
});
it('treats all values as unsigned', () => {
// 2^255 is large positive as unsigned
const SIGN_BIT = 1n << 255n;
const frame = createFrame([SIGN_BIT, 1n]);
expect(GT(frame)).toBeNull();
expect(frame.stack).toEqual([1n]); // 2^255 > 1
});
it('returns StackUnderflow with insufficient stack', () => {
const frame = createFrame([10n]);
expect(GT(frame)).toEqual({ type: 'StackUnderflow' });
});
it('returns OutOfGas when insufficient gas', () => {
const frame = createFrame([30n, 20n], 2n);
expect(GT(frame)).toEqual({ type: 'OutOfGas' });
});
it('preserves stack below compared values', () => {
const frame = createFrame([100n, 200n, 30n, 20n]);
expect(GT(frame)).toBeNull();
expect(frame.stack).toEqual([100n, 200n, 1n]);
});
});
Edge Cases Tested
- Basic comparisons (a > b, a = b, a < b)
- Zero comparisons (1 > 0, 0 > 1)
- Maximum values (MAX > MAX-1)
- Unsigned treatment (sign bit set)
- Stack underflow (< 2 items)
- Out of gas (< 3 gas)
- Large arbitrary values
- Stack preservation
Security
Unsigned vs Signed Confusion
CRITICAL: GT treats all values as unsigned. Do not use for signed integer comparisons:
// VULNERABLE: Using GT for signed values
function isPositive(int256 value) returns (bool) {
// GT treats -1 as 2^256-1 (huge positive!)
assembly {
return(0, gt(value, 0)) // WRONG!
}
// Returns true for negative values!
}
// CORRECT: Use SGT for signed comparisons
function isPositive(int256 value) returns (bool) {
assembly {
return(0, sgt(value, 0)) // Correct
}
}
Boundary Conditions
// VULNERABLE: Wrong boundary check
require(value > max); // Should be >=
// CORRECT: Explicit boundary
require(value >= max); // Or use GT with adjusted value
Integer Overflow Before Comparison
// VULNERABLE: Overflow corrupts comparison
uint256 newValue = oldValue + delta; // May wrap
require(newValue > oldValue); // Check may fail incorrectly
// CORRECT: Check before operation
require(delta > 0 && oldValue <= type(uint256).max - delta);
uint256 newValue = oldValue + delta;
Type Confusion
// VULNERABLE: Mixing signed/unsigned
function withdrawLimit(int256 signedAmount) {
uint256 amount = uint256(signedAmount); // Unsafe cast!
require(balance > amount); // Negative becomes huge positive
}
// CORRECT: Validate before cast
function withdrawLimit(int256 signedAmount) {
require(signedAmount > 0, "negative amount");
uint256 amount = uint256(signedAmount);
require(balance > amount);
}
Optimizations
Relationship to LT
// These are equivalent:
// a > b === b < a
assembly {
let greater := gt(a, b)
// Same as:
let greater := lt(b, a)
}
// Choose based on stack layout for fewer swaps
Inversion Pattern
// These are equivalent:
// a > b === !(a <= b) === iszero(or(lt(a, b), eq(a, b)))
assembly {
// Direct (cheapest)
let greater := gt(a, b)
// Inverted (3 + 3 + 3 + 3 = 12 gas, avoid)
let greater := iszero(or(lt(a, b), eq(a, b)))
}
Constant Optimization
// Compiler optimizes constant comparisons
assembly {
if gt(value, 0) { // Common check: value > 0
// Optimized in EVM implementations
}
}
// Equivalent but potentially less optimized:
assembly {
if iszero(iszero(value)) { // !(value == 0)
// More operations
}
}
Benchmarks
GT performance matches LT (both are fastest-tier operations):
Execution time (relative):
- GT: 1.0x (baseline)
- LT/EQ: 1.0x
- ISZERO: 0.95x
- ADD: 1.0x
- MUL: 1.5x
Gas efficiency:
- 3 gas per comparison
- ~333,333 comparisons per million gas
- Highly optimized in all EVM implementations
References
- LT - Less than (unsigned)
- SLT - Signed less than
- SGT - Signed greater than
- EQ - Equality check
- ISZERO - Zero check