Timing Windows¶
TIMLG rounds are slot-bounded: each phase is gated by Solana slot numbers recorded on-chain.
The timing model is designed to enforce the “Hawking Wall” principle: commitments must be made before the target randomness pulse is knowable.
Round parameters (MVP)¶
A round is created with parameters that define its timing and target:
| Parameter | Type | Meaning |
|---|---|---|
target_pulse_index |
u64 | Which public randomness pulse is targeted (e.g., NIST index) |
commit_open_slot |
u64 | Slot when the commit window opens |
commit_deadline_slot |
u64 | Last slot where commits are accepted |
reveal_deadline_slot |
u64 | Last slot where reveals are accepted |
claim_grace_slots |
u64 | Grace period after reveal deadline before sweep closes claims |
[!TIP] These protocol constants are defined in the on-chain program's constants.rs source file.
Notes:
- Slots are the source of truth for enforcement. Wall-clock displays are approximate.
target_pulse_indexis informational until the oracle posts the pulse; the on-chain checks rely onpulse_setand slot gating.claim_grace_slotsis configured at the protocol level (inConfig) and applied by the operator.
Phase gating (what can happen when)¶
| Phase | Gate condition | Allowed actions | Notes |
|---|---|---|---|
| Commit | slot <= commit_deadline_slot and pulse_set == false |
Create tickets (commit_ticket, batches) |
Commit stops at commit_deadline_slot. The pulse must not be set during commit.
|
| Pulse posting | slot >= commit_deadline_slot and pulse_set == false |
Oracle posts pulse (set_pulse_signed) |
The pulse must be posted after commits close. Posting is allowed from the boundary onward (`slot >= commit_deadline_slot`). Note that in the *boundary slot*, ordering matters: once `pulse_set = true`, commits are rejected even if `slot == commit_deadline_slot`. |
| Reveal | pulse_set == true and slot <= reveal_deadline_slot |
Reveal tickets (reveal_ticket) |
Reveals are only valid once a pulse exists. |
| Finalize | slot > reveal_deadline_slot and pulse_set == true |
Finalize round (finalize_round) |
Locks the round for settlement. |
| Token settlement | slot > reveal_deadline_slot and round finalized |
Settle (settle_round_tokens) → enable claims |
Burns losers and burns NO-REVEAL stake. Winners become claimable. |
| Claim | after token settlement and swept == false |
Users claim rewards (claim_reward) |
Claim refunds stake + mints reward. Claims are blocked once swept. |
| Ticket cleanup |
If round is alive: ticket.processed == true and (if ticket.win) ticket.claimed == trueIf round is archived: round.lamports() == 0
|
Close ticket and reclaim its SOL rent deposit (close_ticket) |
close_ticket is user-signed and returns the ticket account’s lamports to the user (close = user). It is not part of token rewards.
|
| Sweep (SOL + SPL) | slot > reveal_deadline_slot + claim_grace_slots and round finalized |
Sweep SOL and tokens (sweep_unclaimed) |
MVP sweep transfers lamports to the SOL treasury and remaining tokens to the SPL treasury, then marks the round swept (closing claims). |
The target pulse and “knowability”¶
The protocol assumes a public randomness source that is:
- Unpredictable before it is published (e.g., NIST beacon pulses)
- Publicly verifiable after publication
The “Hawking Wall” principle is enforced by:
- requiring commits to happen before the pulse is posted (
pulse_set == false) - only allowing reveals after the pulse exists (
pulse_set == true) - binding each ticket to a bit index derived from ticket inputs (so users can’t choose the bit after seeing the pulse)
Edge cases and rules of thumb¶
1) Pulse arrives early or late (relative to expectations)¶
The oracle may be delayed or the network may vary. The protocol remains safe as long as:
- the oracle does not post the pulse while commits are still allowed
- users cannot reveal before the pulse is posted
Oracle Liveness Check (Safety Buffer)¶
To protect the Refund availability, the protocol enforces a strict Liveness Check on pulose posting:
If the Oracle attempts to post a pulse when there are less than 50 slots (~20s) remaining before the reveal_deadline_slot, the transaction is rejected with PulseTooLate.
This guarantees that: 1. If a pulse is accepted, users always have at least ~20s to submit reveals. 2. If the Oracle is too late, the round remains in the "Pulse Not Set" state, ensuring users can claim a Refund after the timeout.
2) Users commit at the boundary slot¶
The boundary is defined in slots. If a commit lands at exactly commit_deadline_slot, it is still within the window.
- If a reveal lands after
reveal_deadline_slot, it is rejected (the ticket becomes NO-REVEAL).
3) Slot-time vs wall-clock time¶
Slots are the only timing source the program can rely on. User-facing tooling may display approximate wall-clock times, but correctness must be enforced using slots.
Deployment phases: Devnet vs Mainnet¶
- Devnet: Used for end-to-end testing and UI/UX validation. Operator automation may be more permissive.
- Mainnet: Requires hardened oracle operations, rate limiting, and stricter monitoring.
Gasless UX: the relayer role (TBD)¶
The protocol supports “gasless” variants where:
- users sign messages (ed25519) instead of transactions
- a relayer pays fees and submits the on-chain transaction
Current status:
- Gasless commit: Implemented via
commit_batch_signed. - Gasless reveal: Implemented via
reveal_batch_signed. - State: Operational details (spam prevention, per-user quotas) are still to be defined.
- Goal: Users should only need the stake token to participate.
Recommended parameter sizing (public guidance)¶
For devnet/localnet demos:
- Choose commit/reveal windows with enough slots to absorb normal network variance.
- Prefer conservative spacing: commit closes well before the targeted pulse can be knowable.
- Keep
claim_grace_slotslong enough for users in different time zones.
Exact numbers are deployment- and network-dependent, so the docs avoid hardcoding constants.