Technology Apr 17, 2026 · 2 min read

Time and Deadlines in Compact: Block Time, Counters & the Uint<16> Problem

Time and Deadlines in Compact: Block Time, Counters &amp; the Uint&lt;16&gt; Problem A practical guide to writing time-based smart contracts on Midnight Network Why This Matters Most smart contracts need time. Auctions end at a deadline. Escrows expire if unclaimed. On traditi...

DE
DEV Community
by Wilson
Time and Deadlines in Compact: Block Time, Counters & the Uint<16> Problem

Time and Deadlines in Compact: Block Time, Counters & the Uint<16> Problem

A practical guide to writing time-based smart contracts on Midnight Network

Why This Matters

Most smart contracts need time. Auctions end at a deadline. Escrows expire if unclaimed. On traditional chains, you read block.timestamp. On Midnight, privacy changes everything — you can't simply read the timestamp because that would leak information.

Midnight's Compact language provides block time comparison circuits that enforce deadlines without revealing the actual time.

The Block Time API

export circuit isBefore(deadline: Uint<64>): [] {
  return assert(blockTimeLt(disclose(deadline)), "Deadline has passed");
}

export circuit isAtOrAfter(start: Uint<64>): [] {
  return assert(blockTimeGte(disclose(start)), "Start time not yet reached");
}
Function Returns true when
blockTimeLt(t) block_time < t
blockTimeGte(t) block_time >= t
blockTimeGt(t) block_time > t
blockTimeLte(t) block_time <= t

The Uint<16> Problem

Type Max Value Can Hold Unix Timestamp?
Uint<16> 65,535 No
Uint<32> 4,294,967,295 Yes (until 2106)
Uint<64> 18 quintillion Yes

Always use Uint<64> for timestamps. Current Unix timestamp is ~1.7 billion, far exceeding Uint<16>'s limit.

Practical Pattern: Timed Auction

export circuit placeBid(amount: Uint<64>, deadline: Uint<64>): [] {
  assert(disclose(auctionActive), "Auction not active");
  assert(blockTimeLt(disclose(deadline)), "Auction has ended");
  assert(disclose(amount > highestBid), "Bid too low");
  highestBid = disclose(amount);
}

export circuit endAuction(deadline: Uint<64>): [] {
  assert(blockTimeGte(disclose(deadline)), "Auction not yet ended");
  auctionActive = disclose(false);
}

Workaround: Storing Deadlines On-Chain

Since ledger state is public, split the timestamp:

export ledger deadline_hi: Uint<16>;
export ledger deadline_lo: Uint<16>;

export circuit isBeforeDeadline(): [] {
  let fullTimestamp: Uint<64> = disclose(deadline_hi) * 65536 + disclose(deadline_lo);
  assert(blockTimeLt(disclose(fullTimestamp)), "Deadline passed");
}

Quick Reference

Rule Why
Use Uint<64> for timestamps Uint<16> max is 65,535 — can't hold Unix timestamp
Always disclose() values Mandatory since Compact v0.16
Use Counter for phases Tracks progression, not wall-clock time
Pass deadlines as parameters Avoids leaking timing information on-chain

Full tutorial: https://gist.github.com/wilsonhoe/a59c6eb387f1193ca4d7f6c06a7b0c64

Midnight Network tutorial series. Bounty #306.

DE
Source

This article was originally published by DEV Community and written by Wilson.

Read original article on DEV Community
Back to Discover

Reading List