Skip to main content
Generating random numbers is a common task in smart contracts for applications such as gaming, NFT trait generation, and decentralized lotteries. TON provides built-in functions like random(), but their results can be predicted by validators unless additional techniques are used.
Single-block randomness (such as randomize_lt()) is not secure against validators who can influence or predict values. For stronger guarantees, use multi-phase schemes such as commit-reveal.

Why on-chain randomness is challenging

Computers cannot generate truly random information because they strictly follow instructions. Instead, pseudorandom number generators (PRNGs) rely on a seed value to produce a sequence of pseudorandom numbers. Running the same program with the same seed will always produce identical results. In TON, the seed varies for each block and is generated by validators. While this prevents regular users from predicting random values before block production, validators themselves can influence randomness in two ways:
  1. Generating different seeds when creating blocks
  2. Choosing which blocks to include external messages in
This fundamental limitation means all approaches to on-chain randomness involve trade-offs between speed, security, and decentralization guarantees.

Approach 1: Single-block randomness with randomize\_lt()

Mechanism

The randomize_lt() function mixes the transaction’s logical time into the random seed before generating random numbers. This provides basic entropy from blockchain state.

How it works

Call randomize_lt() once before using random() or rand() functions. The logical time adds variability to the seed, making it harder for observers to predict the outcome without executing the transaction.
randomize_lt();
// Generates values from 0 to 99
int random_number = rand(100);

Security model

  • ✅ Safe against regular users who cannot predict logical time
  • ⚠️ Vulnerable to colluding validators who can generate seeds or choose message inclusion blocks

Speed

Fast (single-block operation)

Use cases

  • Non-critical applications (gaming, cosmetic features)
  • NFT trait randomization
  • Scenarios where validator trust is assumed
Validators can predict single-block randomness. Do not use it for high-value applications such as financial lotteries or significant asset distribution.

Approach 2: Block skipping

Mechanism

Instead of using randomness from the current block, the contract waits for several blocks to pass before using their entropy. This approach sends messages across multiple blocks, making it harder for a single validator to control the final outcome.

How it works

The contract initiates an operation, then waits for responses that arrive several blocks later. The random seed from future blocks—which the initiating validator does not control—determines the result.

Security model

  • ✅ Resistant to regular users
  • ✅ More resistant to single validators than randomize_lt()
  • ⚠️ Still vulnerable to determined validators who can coordinate timing across multiple blocks they generate

Speed

Slow (requires multiple blocks to finalize)

Use cases

  • Medium-stakes applications
  • Lottery systems with moderate value
  • Scenarios requiring better security than single-block randomness
While block skipping improves security over single-block randomness, it only delays the threat. A validator representing 1/250 of the network can still choose optimal timing to influence outcomes in blocks they generate.

Approach 3: Commit-reveal scheme

Mechanism

Participants commit to secret values by publishing hashes, then reveal those values in a later phase. The final randomness is derived from combining all revealed values, ensuring no single party can determine the outcome alone. When properly implemented, this approach provides cryptographically secure randomness suitable for high-value applications.

How it works

  1. Commit phase: Each participant generates a random number off-chain and submits its hash to the contract.
  2. Reveal phase: After all commitments are received, participants disclose their original numbers.
  3. Combination: The contract combines the revealed numbers (e.g., by XOR or sum) to produce the final random value.

Security model

  • ✅ Cryptographically secure when properly implemented
  • ✅ Resistant to both user and validator manipulation; no single party can predict or influence the outcome
  • ⚠️ Requires incentives and penalties to prevent participants from refusing to reveal
  • ⚠️ Validators can influence timing or censor messages, but cannot determine the random value

Speed

Very slow (multi-phase, multi-block process)

Implementation requirements

  • On-chain verification of commitments
  • Penalty mechanisms for non-reveals or invalid reveals
  • Timeout handling for missing participants

Use cases

  • High-value applications (significant lotteries, auctions)
  • Decentralized gaming with financial stakes
  • Systems requiring Byzantine fault tolerance
Commit-reveal schemes require careful incentive design. Participants may refuse to reveal if the outcome is unfavorable. Use penalties or collateral to enforce honest behavior.

Comparison of approaches

Factorrandomize_lt()Block skippingCommit-reveal
SpeedFastSlowVery slow
Implementation complexityLowMediumHigh
Resistance to user manipulationHighHighCryptographically secure
Resistance to validator manipulationLowMediumHigh (validators cannot predict)
Cost (gas + storage)LowMediumHigh
Suitable for high-value applications❌ No⚠️ Use with caution✅ Yes (recommended for critical use)
When choosing an approach, consider the value at risk and required time-to-finality. For high-stakes applications such as lotteries with significant funds, use commit-reveal. Audit implementations through formal verification when possible.

Best practices

  • Avoid standalone random() calls. Validators controlling the block seed can predict the output.
  • Keep randomness out of external message receivers. External messages remain vulnerable even with randomize_lt().
  • Use hybrid or off-chain entropy for critical applications. Combine on-chain randomness with off-chain entropy or external randomness oracles when significant value is at risk.
  • Test randomness behavior across different blocks. Verify that the contract behaves correctly when randomness is manipulated within validator capabilities.

How block random seeds work

Understanding the underlying mechanism helps evaluate security trade-offs.

Seed generation by validators

Each block’s random seed is generated by the validator (or collator) creating that block. The validator node code generates 32 random bytes using cryptographically secure primitives:
prng::rand_gen().strong_rand_bytes(rand_seed->data(), 32);
A single validator generates this seed when creating a block, giving them control over the seed value. This is why single-block randomness is vulnerable to validator manipulation.

Per-contract randomization

The block seed is not used directly in contracts. Instead, it is hashed with the contract address to produce a per-contract seed:
contract_seed = SHA256(block_seed || contract_address)
This ensures different contracts in the same block receive different random seeds, preventing cross-contract randomness correlation.

Random number generation

The RANDU256 TVM instruction implements the actual random number generation. When called:
  1. Take the current seed r (32 bytes)
  2. Compute SHA512(r)
  3. Use the first 32 bytes as the new seed
  4. Return the remaining 32 bytes as the random number
Subsequent calls continue this chain, producing a deterministic sequence from the initial seed.
Because the random sequence is deterministic once the initial seed is known, anyone who knows both the block seed and contract address can predict all random values generated during a transaction. Regular users cannot predict these values before block production, but validators generating the block can, since they control the block seed.