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: 0x7B
Introduced: Frontier (EVM genesis)
PUSH28 pushes a 28-byte immediate value from the bytecode onto the stack. The 28 bytes immediately following the opcode are read and zero-padded to 256 bits.
Specification
Stack Input:
Stack Output:
value (uint256, 28 bytes from bytecode)
Gas Cost: 3 (GasFastestStep)
Bytecode: 1 byte opcode + 28 bytes immediate data
Operation:
value = read_bytes(pc + 1, 28) // Big-endian
stack.push(value)
pc += 29
Behavior
PUSH28 reads 28 bytes from bytecode starting at position pc + 1, interprets them as a big-endian unsigned integer, and pushes the result onto the stack.
Key characteristics:
- Reads exactly 28 bytes following opcode
- Big-endian byte order (most significant byte first)
- Zero-padded to 256 bits if less than 32 bytes
- InvalidOpcode if insufficient bytecode remaining
- PC advances by 29 (opcode + data)
Examples
Basic Usage
import { handler_0x7B_PUSH28 } from '@tevm/voltaire/evm/stack/handlers';
import { createFrame } from '@tevm/voltaire/evm/Frame';
// Bytecode with PUSH28
const bytecode = new Uint8Array([
0x7B, // PUSH28
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c // 28 bytes: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c
]);
const frame = createFrame({
bytecode,
pc: 0,
stack: [],
gasRemaining: 1000n
});
const err = handler_0x7B_PUSH28(frame);
console.log(frame.stack); // [0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c00000000n]
console.log(frame.pc); // 29
console.log(frame.gasRemaining); // 997n (3 gas consumed)
Solidity Compilation
contract Example {
// 28-byte constant
uint224 constant VALUE = 2.695994666715064e+67;
// PUSH28 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff
}
Assembly Usage
assembly {
// Push 28-byte value
push28 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff
}
Gas Cost
Cost: 3 gas (GasFastestStep)
All PUSH1-32 instructions cost the same despite different data sizes. Bytecode size impact:
- PUSH28: 29 bytes (1 opcode + 28 data)
- PUSH32: 33 bytes (1 opcode + 32 data)
Comparison:
| Opcode | Gas | Bytes | Use Case |
|---|
| PUSH0 | 2 | 1 | Zero constant (Shanghai+) |
| PUSH1 | 3 | 2 | Small numbers (0-255) |
Common Usage
28-Byte Constants
assembly {
// 28-byte literal
push28 0xabababababababababababababababababababababababababababab
}
Big-Endian Encoding
// Bytecode: PUSH28 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c
// Reads as: 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c
// Most significant byte first
// Byte 0: 0x01 (highest significance)
// Byte 27: 0x1c (lowest significance)
Implementation
/**
* Read immediate data from bytecode for PUSH operations
*/
function readImmediate(bytecode: Uint8Array, pc: number, size: number): bigint | null {
if (pc + 1 + size > bytecode.length) {
return null;
}
let result = 0n;
for (let i = 0; i < size; i++) {
result = (result << 8n) | BigInt(bytecode[pc + 1 + i]);
}
return result;
}
/**
* PUSH28 opcode (0x7B) - Push 28 bytes onto stack
*
* Stack: [] => [value]
* Gas: 3 (GasFastestStep)
*/
export function handler_0x7B_PUSH28(frame: FrameType): EvmError | null {
const gasErr = consumeGas(frame, FastestStep);
if (gasErr) return gasErr;
const value = readImmediate(frame.bytecode, frame.pc, 28);
if (value === null) {
return { type: "InvalidOpcode" };
}
const pushErr = pushStack(frame, value);
if (pushErr) return pushErr;
frame.pc += 29;
return null;
}
Edge Cases
Insufficient Bytecode
// Bytecode ends before 28 bytes read
const bytecode = new Uint8Array([0x7B, 0x01]); // Only 1 byte instead of 28
const frame = createFrame({ bytecode, pc: 0 });
const err = handler_0x7B_PUSH28(frame);
console.log(err); // { type: "InvalidOpcode" }
Stack Overflow
// Stack at maximum capacity
const frame = createFrame({
stack: new Array(1024).fill(0n),
bytecode: new Uint8Array([0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
});
const err = handler_0x7B_PUSH28(frame);
console.log(err); // { type: "StackOverflow" }
Out of Gas
// Insufficient gas
const frame = createFrame({
gasRemaining: 2n, // Need 3 gas
bytecode: new Uint8Array([0x7B, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
});
const err = handler_0x7B_PUSH28(frame);
console.log(err); // { type: "OutOfGas" }
Maximum Value
// All bytes 0xFF
const bytecode = new Uint8Array([0x7B, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
const frame = createFrame({ bytecode, pc: 0 });
handler_0x7B_PUSH28(frame);
console.log(frame.stack[0]); // 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000n
References