Verify a treasury yourself
You never have to trust this interface. Everything BALLAST shows is computed from public on-chain data you can read yourself. Here is how to reproduce the backing figure from scratch.
1. Find the treasury
From a project page, take the project token address and read its immutable
treasury() value on the explorer, or resolve it from the BallastFactory
creation event. Confirm the treasury's projectToken() points back to the
same token — this two-way check defeats a spoofed treasury.
2. List what it holds
On the ProjectTreasury contract, read:
assets()— every asset the treasury has heldlockedBalance(asset)— permanently locked amountcreatorWithdrawable(asset)— amount the creator could withdraw
The amount that counts toward backing is lockedBalance + creatorWithdrawable.
Pending (proposed-but-not-accepted) deposits sit in escrow and are not counted.
3. Price each asset
For each asset, read its Chainlink feed:
(, int256 answer, , uint256 updatedAt, ) = feed.latestRoundData();
uint8 feedDecimals = feed.decimals(); // read it — do not assume 8
- Reject a zero or negative
answer. - Check
updatedAt. Off-hours the price rests with no new heartbeat — that is expected, not an error. Note the age; do not discard the price for being rested. - On an L2, check the Chainlink sequencer uptime feed first. During a sequencer outage feeds go stale while contracts still respond.
- Do not multiply by the token's
uiMultiplier(). The feed price already includes it; applying it again double-counts.
4. Do the arithmetic
value(asset) = balance × price ÷ 10^(assetDecimals + feedDecimals)
total value = Σ value(asset)
backing per token = total value ÷ (totalSupply ÷ 10^tokenDecimals)
Compare it to what the project page shows. They should match. If the chain and this interface ever disagree, the chain is the source of truth.
5. Watch for withdrawals
Read pendingWithdrawal() on the treasury to see any announced withdrawal — its
asset, amount, and unlock time — without asking anyone's permission. Because
only one withdrawal can be active at a time and the notice period is immutable, the
countdown you see is the real one.
Reproduce it in code
The BackingLens contract does all of the above in a
single batched call, and its source is verified on the explorer. You can call it
directly, or copy its logic — it is the same math described here.
Verify the launch itself
Every token page also shows a live verification panel — one row per check, no narrative. Here is what each row actually means and how to reproduce it.
Source verified — is the contract's source code published and matching its
bytecode on the block explorer, for both the token and its treasury? Read via
Blockscout's public API (/api/v2/smart-contracts/{address}, is_verified).
Mint authority — BallastToken.sol has no mint() function anywhere in its
source. Every Ballast launch shares the same creation bytecode, so this is a
structural fact, not a per-token guess — confirmed by checking the token is a
genuine BallastFactory launch (launchIdOf(token) > 0) rather than trusting
it in isolation.
Mutable params — the only value a creator can change after launch is the
project metadata (name, logo, links) via setMetadataURI, and every change is
logged in a MetadataUpdated event forever. noticePeriod, the treasury
pointer, and the creator address are all immutable.
Liquidity locked — once graduated, BallastSeeder holds the pool position
permanently; it has no function that could remove it. Checked live by reading
the pool's liquidity via StateView.getLiquidity(poolId) — zero would mean
something is wrong, not that the lock failed.
Creator allocation — BallastToken's full supply mints to the factory at
launch, never the creator, so the launch itself grants 0%. A nonzero live
balance just means the creator later bought on the open market like anyone
else — worth showing, not worth treating as a red flag.
Backing assets — each asset the treasury actually holds, checked against
the same on-chain AssetRegistry the create flow itself reads, by address —
never by ticker. An address that isn't registered renders as unrecognized; an
address that isn't registered but claims a ticker that collides with a real
asset's renders as hostile. See why ticker matching alone isn't
safe — a real impostor pool with a fabricated
multi-billion-dollar reserve exists on this chain today under a different
address than the real asset it impersonates.
Sell simulation — a real V4Quoter.quoteExactInputSingle call through the
token's actual pool and hook, for a small test amount, run at request time. A
nonzero quote is live proof a sell path exists right now — not a cached
assumption from whenever the pool graduated.