Skip to main content

Try it Live

Run Uint examples in the interactive playground
Conceptual Guide - For API reference and method documentation, see Uint API.
Unsigned integers are non-negative whole numbers used throughout Ethereum for amounts, balances, timestamps, and more. This guide teaches uint fundamentals using Tevm.

What Are Unsigned Integers?

An unsigned integer is a whole number (0, 1, 2, 3, …) with no negative values. The EVM uses unsigned integers for:
  • Token amounts and balances
  • Gas prices and limits
  • Block numbers and timestamps
  • Nonces and counters
  • Storage slot indices

EVM Integer Types

The EVM supports unsigned integers from 8 to 256 bits in 8-bit increments:
import { Uint } from 'tevm';

// Tevm provides uint256 (256-bit) by default
const value = Uint(100n);  // uint256

// Other sizes require masking:
// uint8:   0 to 2^8-1   (255)
// uint16:  0 to 2^16-1  (65535)
// uint32:  0 to 2^32-1  (4294967295)
// uint64:  0 to 2^64-1  (18446744073709551615)
// uint128: 0 to 2^128-1 (340282366920938463463374607431768211455)
// uint256: 0 to 2^256-1 (largest - 115792089237316195423570985008687907853269984665640564039457584007913129639935)

Why uint256?

Most Ethereum operations use uint256 (32 bytes) because:
  • EVM stack operates on 256-bit words
  • Efficient for large numbers (token amounts, wei)
  • Storage slots are 32 bytes
  • Cryptographic operations use 256-bit values

Creating Uints

import { Uint } from 'tevm';

// Direct construction (recommended for known values)
const amount = Uint(1000000000000000000n);  // 1 ETH in wei
const small = Uint(42n);
const zero = Uint.ZERO;  // Constant for 0
const one = Uint.ONE;    // Constant for 1

// Constructor (same as from)
const value = Uint(100n);

Big-Endian Encoding

The EVM stores unsigned integers in big-endian format: most significant byte first.
import { Uint } from 'tevm';

// Example: 255 (0xff) in big-endian
const value = Uint(255n);
const bytes = value.toBytes();

console.log(bytes.length);  // 32 bytes
console.log(bytes[0]);      // 0 (most significant)
console.log(bytes[31]);     // 255 (least significant)

// Hex representation shows big-endian
console.log(value.toHex());
// "0x00000000000000000000000000000000000000000000000000000000000000ff"
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ zeros
//                                                                    ^^ value
This matches EVM memory/storage layout and ABI encoding.

Arithmetic Operations

All arithmetic wraps on overflow (mod 2^256), matching EVM behavior:
import { Uint } from 'tevm';

const a = Uint(100n);
const b = Uint(50n);

// Addition
const sum = a.plus(b);                    // 150
console.log(sum.toBigInt());              // 150n

// Subtraction
const diff = a.minus(b);                  // 50
console.log(diff.toBigInt());             // 50n

// Multiplication
const product = a.times(b);               // 5000
console.log(product.toBigInt());          // 5000n

// Division (throws on divide by zero)
const quotient = a.dividedBy(b);          // 2
console.log(quotient.toBigInt());         // 2n

// Modulo
const remainder = a.modulo(b);            // 0
console.log(remainder.toBigInt());        // 0n

// Exponentiation
const power = Uint(2n).toPower(Uint(8n));  // 256
console.log(power.toBigInt());            // 256n

Comparisons

import { Uint } from 'tevm';

const a = Uint(100n);
const b = Uint(100n);
const c = Uint(200n);

// Equality
console.log(a.equals(b));         // true
console.log(a.notEquals(c));      // true

// Special checks
console.log(Uint.ZERO.isZero());  // true
console.log(a.isZero());          // false

Bitwise Operations

import { Uint } from 'tevm';

const a = Uint("0xff00");  // 0xff00
const b = Uint("0x00ff");  // 0x00ff

// Bitwise AND
const and = a.bitwiseAnd(b);
console.log(and.toHex(false));  // "0x0"

// Bitwise OR
const or = a.bitwiseOr(b);
console.log(or.toHex(false));   // "0xffff"

// Bitwise XOR
const xor = a.bitwiseXor(b);
console.log(xor.toHex(false));  // "0xffff"

// Bitwise NOT
const not = a.bitwiseNot();
console.log(not.toHex().slice(0, 10));  // "0x000000..." (inverted bits)

Size Variants and Padding

While Tevm focuses on uint256, you can work with smaller sizes:
import { Uint } from 'tevm';

// Simulate uint8 (0-255)
function toUint8(value: Uint): Uint {
  const mask = Uint(0xFFn);
  return value.bitwiseAnd(mask);
}

// Simulate uint16 (0-65535)
function toUint16(value: Uint): Uint {
  const mask = Uint(0xFFFFn);
  return value.bitwiseAnd(mask);
}

// Usage
const large = Uint(1000n);
const u8 = toUint8(large);
console.log(u8.toBigInt());  // 232n (1000 mod 256)

const u16 = toUint16(large);
console.log(u16.toBigInt()); // 1000n (fits in uint16)

Common Use Cases

import { Uint } from 'tevm';

// ERC20 token with 18 decimals
const tokenDecimals = 18n;
const amount = Uint(1000n * 10n ** tokenDecimals);  // 1000 tokens

// Convert to display value
function toDisplayValue(amount: Uint, decimals: bigint): string {
  const value = amount.toBigInt();
  const divisor = 10n ** decimals;
  const whole = value / divisor;
  const fraction = value % divisor;
  return `${whole}.${fraction.toString().padStart(Number(decimals), '0')}`;
}

console.log(toDisplayValue(amount, tokenDecimals));  // "1000.000000000000000000"

JavaScript Number Limitations

CRITICAL: JavaScript numbers are 64-bit floats, only precise up to 2^53-1 (9,007,199,254,740,991).
import { Uint } from 'tevm';

// ❌ WRONG: Precision loss with large numbers
const wrong = Uint(1e20);
console.log(wrong.toBigInt());  // Incorrect value (precision lost)

// ✅ CORRECT: Use bigint for large values
const correct = Uint(10n ** 20n);
console.log(correct.toBigInt());  // Exact value

// Safe conversions
const safeNumber = Uint(1000n).toNumber();  // 1000 (safe)

// Throws if exceeds safe integer range
try {
  const large = Uint(2n ** 100n);
  large.toNumber();  // Throws: value exceeds Number.MAX_SAFE_INTEGER
} catch (e) {
  console.error('Value too large for JavaScript number');
}

// Always use toBigInt() for large values
const large = Uint(2n ** 100n);
const bigint = large.toBigInt();  // Safe

Conversions

import { Uint } from 'tevm';

const value = Uint(100n);

// Padded (32 bytes, 64 hex chars + 0x prefix)
console.log(value.toHex());
// "0x0000000000000000000000000000000000000000000000000000000000000064"

// Unpadded (minimal hex)
console.log(value.toHex(false));
// "0x64"

// Always includes 0x prefix

Resources

Next Steps