Post
Share your knowledge.
Understanding the Sui Clock Module and Timestamp Usage
I'm building time-sensitive logic in Sui Move (e.g., token vesting, auction deadlines) but struggling with reliable timestamp handling. Key questions:
- Clock Object: Why does Sui need a shared Clock instead of block timestamps?
- Accuracy: How precise are these timestamps compared to block times?
- Usage Patterns: Best practices for time-locks, deadlines, and scheduling?
- Testing: How to mock time in unit tests?
Current Issues:
- Basic sui::clock usage works but feels restrictive
- Unsure how to handle time across different shards/validators
- Testing time-dependent logic is flaky
- Sui
Answers
21. Sui Clock Fundamentals
Why a Shared Clock?
Sui’s parallel execution model means:
- No guaranteed block order → can’t use block numbers/timestamps
Clock
is a shared object providing consensus-backed time
Core Structure:
module sui::clock {
struct Clock has key {
id: UID,
timestamp_ms: u64,
}
}
Key Properties:
- Updated every 2 seconds (configurable by validators)
- Milliseconds precision (vs. Ethereum’s ~12s blocks)
- Immutable – can’t be modified by contracts
2. Practical Usage Patterns
Basic Time Check
use sui::clock;
public entry fn execute_if_ready(
clock: &Clock,
lock: &mut LockedAsset
) {
assert!(clock.timestamp_ms >= lock.unlock_time, ELocked);
// Release assets
}
Deadline Enforcement
struct Auction has key {
id: UID,
end_time: u64, // Stored as Clock timestamp
highest_bid: u64
}
public entry fn bid(
auction: &mut Auction,
clock: &Clock,
bid_amount: u64
) {
assert!(clock.timestamp_ms < auction.end_time, EAuctionEnded);
// Process bid
}
Time-Locked Actions
struct VestingSchedule has key {
id: UID,
start_time: u64,
cliff_duration: u64 // In milliseconds
}
public fun claimable_amount(
vesting: &VestingSchedule,
clock: &Clock
): u64 {
let elapsed = clock.timestamp_ms - vesting.start_time;
// Linear vesting math
}
3. Advanced Considerations
Shard Consistency
- All shards see the same Clock timestamp within a transaction
- For cross-shard ops, pass
&Clock
as a shared object reference
Gas Optimization
- Store relative times (e.g.,
duration_ms: u64
) instead of absolute where possible - Reuse the same
Clock
reference across functions
Error Handling
const MAX_CLOCK_DRIFT_MS: u64 = 3000; // 3s tolerance
public fun validate_clock(clock: &Clock) {
assert!(tx_context::epoch(ctx) == clock.epoch(), EStaleClock);
assert!(clock.timestamp_ms <= tx_context::now_ms(ctx) + MAX_CLOCK_DRIFT_MS, EInvalidTime);
}
4. Testing Strategies
Mocking Clock in Tests
#[test_only]
fun mock_clock(timestamp_ms: u64): Clock {
Clock { id: test_scenario::new_object(ctx), timestamp_ms }
}
#[test]
fun test_vesting() {
let clock = mock_clock(1000);
let vesting = VestingSchedule { start_time: 0, ... };
// Test claimable amount at t=1000
}
Epoch Transition Tests
#[test]
fun test_clock_epoch_change() {
let mut scenario = test_scenario::begin(...);
// Advance epoch
test_scenario::next_epoch(&mut scenario);
let clock = test_scenario::take_shared<Clock>(scenario);
assert!(clock.epoch() == 1, EEpochError);
}
If you’re working on time-sensitive logic like auctions or vesting schedules in Sui Move, the sui::clock
module is your go-to way of tracking time. Sui doesn’t rely on block timestamps like Ethereum or Solana because it aims for deterministic execution and parallelism. That’s why it uses a shared Clock object, which updates once per checkpoint and gives you a canonical source of time.
You interact with time by reading from this shared Clock object (usually passed into entry functions) using clock.timestamp
. This value is updated roughly every second and gives you the current UNIX timestamp in seconds. While it’s not tied to every block like in EVM, it’s precise enough for practical use cases like token unlocks or bid expiries — just not microsecond-level precision.
When building time-based logic, always pass in the Clock object explicitly so that your Move function can read it and compare timestamps against your stored deadlines. To avoid rigid behavior, use grace periods or leeway in your comparisons (if current_time > unlock_time + buffer
), and avoid relying on exact seconds.
For testing, Sui allows you to mock the Clock in unit tests by creating your own instance of the shared object with a custom timestamp. That way, you can simulate future conditions without needing real time to pass. Just construct the Clock manually with a set value in your #[test]
functions.
If you feel restricted, it’s likely due to how Sui protects the Clock from mutation outside checkpoints. You’ll need to design your modules to expect timestamps as input and read them from the official shared object. Avoid creating your own time source, as it breaks determinism.
Do you know the answer?
Please log in and share it.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
- Why does BCS require exact field order for deserialization when Move structs have named fields?53
- Multiple Source Verification Errors" in Sui Move Module Publications - Automated Error Resolution43
- Sui Transaction Failing: Objects Reserved for Another Transaction25
- How do ability constraints interact with dynamic fields in heterogeneous collections?05