TICK is a fixed-supply SPL token on Solana. Its total supply is the number of seconds between launch and 2100-01-01T00:00:00Z — minted once, in a single transaction, with the mint authority revoked in that same transaction.
Those tokens are then given away for free, at 1 TICK per second, released in completed one-minute batches. Anyone may claim. There is no sale, no allowlist, no fee, no cooldown, and no per-claimant state.
Status: LIVE on Mainnet since 2026-08-12. The program is deployed and immutable — its upgrade authority is
noneand cannot be restored.
program 5b8oDRPP6iGMP4hLeip9nr5Wtsfejuwy6aqkk3Msh9hPmint SuHS5YeFjHDX2a56HEktzikGZ1ACuUAfLgw3FKm2TxHsupply 2,315,907,000 TICK — mint and freeze authority both nullgenesis 1786537800(2026-08-12T12:30:00Z); first claim at genesis + 60
--finallanded at block time1786537214, 586 seconds before genesis, so no claim was possible while the program could still be rewritten. A stranger can check that from chain state alone: compare that transaction's block time againstdistributor.genesis_ts.
One on-chain program, tick_distributor, with three instructions:
| Instruction | Who | When |
|---|---|---|
initialize |
INIT_AUTHORITY, once per mint |
at launch |
fund_reserve |
anyone (once) | at launch |
claim |
anyone | for ever after |
At launch a single transaction creates the mint, mints the entire supply,
revokes the mint authority, and initializes the distributor. Then
fund_reserve moves the whole supply into a vault owned by a program-derived
address. From that moment the only way a token leaves the vault is claim, and
the amount is pure clock arithmetic:
released = whole minutes elapsed since genesis × 60 TICK
payable = released − already claimed
A claim takes the entire outstanding backlog. If two claims race, one takes the
backlog and the other gets NothingToClaim — never a double payout.
The program is deployed to Mainnet and its upgrade authority has been revoked. Whatever it does is what it does for the next 74 years.
The launch transaction has landed, so every line below is now a fact about Mainnet rather than an intention:
- Mint more. The mint authority was revoked in that same transaction. Supply is fixed for ever.
- Change the rules. The program is finalized; there is no upgrade path.
- Take the tokens. No withdraw path exists. Nothing leaves the vault except
through
claim. - Get special treatment. Over the TICK distributor,
INIT_AUTHORITYhas exactly the power any other address has: none. It cannot mint, pause, withdraw, upgrade, or re-initialize it —initializeuses Anchoriniton a PDA keyed by the mint, so it can never run twice for the same mint. - Stop it. No pause, no admin, no crank. Accrual is the chain's clock.
One thing, and it is worth stating plainly. Distributors are keyed per mint
(seeds = [b"distributor", tick_mint]), so INIT_AUTHORITY — and only
INIT_AUTHORITY — can call initialize again for a different mint, creating
another distributor under the same immutable program id, at any time, for ever.
Such a distributor could never reach TICK's vault or supply: its state, vault authority and vault are all derived from its own mint, and funds cannot cross between distributors. But it would run identical code at an identical program id, and could be presented as though it were TICK.
So the program id is not what identifies TICK. The mint address is. Verify the mint first; the distributor, vault authority, vault and metadata accounts are all derived from it.
The launch mint is SuHS5YeFjHDX2a56HEktzikGZ1ACuUAfLgw3FKm2TxH. Any token
whose mint is not that address is not TICK, whatever it is called and whatever
program it runs. Two earlier launch attempts were aborted before funding and
left inert mints on Mainnet (ACqXdjJm…, jiT2CseU…); they can never emit a
token — their distributors were never funded, and claim is impossible until
fund_reserve latches — but an explorer will show them, so they are named here
rather than left to be discovered.
| Decimals | 9 |
| Batch | 60 seconds |
| End | 2100-01-01T00:00:00Z (Unix 4102444800) |
| Supply | END_TIMESTAMP − genesis whole TICK, fixed at launch |
| Emission | 1 TICK/second, released per completed minute |
Start at docs/README.md.
docs/spec/TOKEN.md— the tokendocs/spec/DISTRIBUTOR.md— the faucetdocs/public/WHITEPAPER.md— the long formdocs/public/RISK_DISCLOSURES.md— read thisSECURITY.md— reporting a vulnerability
If prose anywhere disagrees with programs/tick_distributor/src, the source
wins.
Toolchain is pinned deliberately: Anchor 1.1.2, Agave/Solana 3.1.10,
platform-tools v1.52, host Rust 1.97.1. The last of those only builds the IDL
generator — platform-tools is what compiles the bytecode, which is why
Anchor.toml pins solana_version as well.
pnpm install
anchor build
cargo test --workspace --all-targets
pnpm testThe frozen artifact is target/deploy/tick_distributor.so, 214,088 bytes,
built from commit f8eaea5 for program id
5b8oDRPP6iGMP4hLeip9nr5Wtsfejuwy6aqkk3Msh9hP.
| Measured by | Value |
|---|---|
shasum -a 256 / sha256sum on the file |
568f06ed057a1c29b020cf4f7fcf9aa50b90a1a64edf8f5b9fdeb80e969fdc6d |
solana-verify (get-executable-hash, get-program-hash) |
f16e47501ce77a61d217792918a19b15c77b7188a8f5f70fdf20629924ffce4c |
solana-verify build in the pinned container — a rebuild, and it does not reproduce this artifact |
5d470ad370c3a08dde93ad61dab3832e424772ebea82bd276bdc2bf3d920f920 |
shasum -a 256 on the IDL target/idl/tick_distributor.json |
4b616622d184ca0ad86be8286ff2787c06046b2d0c8da4babcd6b9c07303af5a |
Those first two figures are one artifact measured two ways, not two
artifacts. solana-verify strips trailing zero bytes before hashing; this
file ends in 15 of them, so it hashes the leading 214,073 bytes and reports
f16e4750…. A plain sha256sum hashes all 214,088 and reports 568f06ed….
Neither is wrong, and a difference between them is not a failed
reproduction. The third figure, unlike the first two, is a different
binary: get-executable-hash and get-program-hash only hash bytes that
already exist, while build re-compiles the source, and the container's
compiler does not emit the deployed bytes — see "Reproducing it" below.
Compare like with like:
shasum -a 256 target/deploy/tick_distributor.so # 568f06ed…
solana-verify get-executable-hash target/deploy/tick_distributor.so # f16e4750…
# and the equivalence itself, if you want to see it:
python3 -c "import hashlib,sys; d=open(sys.argv[1],'rb').read(); \
print(hashlib.sha256(d).hexdigest()); print(hashlib.sha256(d.rstrip(b'\0')).hexdigest())" \
target/deploy/tick_distributor.soNative rebuild against the pinned local toolchain:
scripts/verify-build.sh # rebuild, compare .so and IDL hashes
scripts/verify-build.sh --rpc <endpoint> # ...and compare against deployed bytesThe --rpc step identifies the cluster by asking it for its genesis hash
rather than reading its hostname, and prints what it found; add
--expect-cluster mainnet-beta to turn that into an assertion. It reads bytes
and signs nothing, so it is safe to point at Mainnet.
Container rebuild, which needs nothing from this machine except Docker — and which does not reproduce the deployed artifact:
solana-verify build --library-name tick_distributor \
--base-image solanafoundation/solana-verifiable-build:3.1.10Expect 5d470ad370c3a08dde93ad61dab3832e424772ebea82bd276bdc2bf3d920f920 — a
different binary. An earlier version of this README said to expect
f16e4750… here; that was wrong. The cargo build-sbf this container runs
and the anchor build that produced the deployed artifact do not emit the
same bytes, so the container is not a route to reproducing this program. The
route that does reproduce it is scripts/verify-build.sh above — the native
rebuild against the pinned toolchain, which yields 568f06ed… — subject to
the caveat in the next section: that rebuild has so far been demonstrated only
on the author's machine.
The
--base-imagepin is required — do not drop it. Left to itself,solana-verifyinfers a base image from the dependency tree and picks3.0.1, whose Cargo 1.84 cannot parse theedition2024manifest ofblock-buffer 0.12.1. It then hard-fails with "failed to cargo build". That is a stale inference, not a broken project.
Run it from a clean clone: solana-verify searches the working tree for a
program to build, and a target/ directory left over from local testing can
contain source trees with different declare_id! values. The image is
linux/amd64; on an Apple-silicon host it needs an emulating Docker runtime
(this was measured under Colima started with --vm-type vz --vz-rosetta).
The artifact reproduces natively on the author's host: nine builds across
eight varied environment dimensions (paths, cleared CARGO_HOME, target and
temp directories, locale, timezone, umask) all produced the same hash. It does
not reproduce in the pinned public container. An earlier version of this
section claimed that it did; that claim was wrong. solana-verify build
compiles with the container's cargo build-sbf, the deployed artifact came
from anchor build, and the two do not emit the same bytes: the container
yields 5d470ad3… (measured 2026-08-11 on a clean clone), and OtterSec's
remote verifier — infrastructure this project does not control — produced the
same 5d470ad3…. The container build is reproducible across environments;
what it reproduces is a different binary.
What the native reproduction is not: independent. Every build that has
matched the deployed program ran on the author's machine, and no third party
has reproduced the hash. The one third-party build on record — the remote
verifier's — used the container path and got 5d470ad3…. Until someone else
assembles the pinned toolchain, runs scripts/verify-build.sh and reports
568f06ed…, the claim rests on one operator's word — and checking it now
costs a stranger the full toolchain setup rather than one Docker pull, which
is a real regression in how cheaply this program can be checked.
An on-chain Verified Build badge (solana-verify verify-from-repo) rides on
that same container build, so it is closed to this program: the registry's
remote verifier rebuilds in the container, gets 5d470ad3…, and reports a
mismatch. Do not expect a verified-build badge for this program. Its
absence is this section, not evidence about the deployed bytes — read a build
you can rerun yourself.
On a fresh clone
anchor buildprints a "Program ID mismatch" warning and suggestsanchor keys sync. Do not run it — it rewritesdeclare_id!to a randomly generated local key and invalidates the published hash. The build is already correct. Seedocs/operations/DEVELOPMENT.md.
Local end-to-end demo on a throwaway validator:
pnpm distributor:demo:localStart from the mint address. Every other address is derivable from it, so
you never have to trust a record in this repository — and the mint is the only
thing that tells TICK apart from another distributor running the same code at
the same program id. See
docs/operations/DEVNET.md.
A rehearsal faucet is live on Devnet: mint
FbzcDjaHYhGqVi9vHWpzHQfQUxiedcFYJKR7zyWZQGZ3, program
6bBr6ggWg8CY9HAFWwyvehaUpMnBsoJ6WLcwBHfVaSte. These check it against the
chain and derive every address independently:
pnpm distributor:status:devnet:test
pnpm verify:supply:devnet:testverify:supply re-derives the distributor PDA, vault authority and vault from
the mint, and asserts the supply is exactly the whole seconds from the recorded
genesis to 2100-01-01T00:00:00Z. It warns while the rehearsal program remains
upgradeable; pass --require-immutable to treat that as a failure, which is
what you would use against the Mainnet launch.
The un-suffixed
pnpm distributor:status:devnet/pnpm verify:supply:devnetare pinned to the frozen launch id5b8oDRPP…, which is deployed on Mainnet and nowhere else — its Devnet rehearsal copy was closed on 2026-07-27. Run against Devnet they still fail closed, by design; point them at Mainnet to check the live launch.If you check that address on Devnet yourself, expect to find an account.
solana program closeleaves the program stub behind — it still reportsexecutable: trueand is still owned by the BPF upgradeable loader — while deleting the programdata account that holds the bytecode. Verified 2026-07-30: programdataG9uKL9kaQGYeb3xFf1v4ssxZtUymGWRVCGQk2F7k6ud8is absent, so the program cannot be invoked. An executable flag is not evidence that a program exists; resolve the programdata account, which is whatverify-build.shreads.
programs/tick_distributor/ the on-chain program — the only thing that is trustless
scripts/ launch ceremony, verification, and localnet harnesses
tests/ offline TypeScript tests
docs/ specifications, operations, audit record
docs/archive/ superseded designs — never implementation guidance
TICK does nothing except count. It has no yield, no governance, no utility, no treasury, and no roadmap of features. Its supply is a countdown and its distribution is a clock. That is the whole idea.
Please read
docs/public/RISK_DISCLOSURES.md before
forming any expectation about it.