Skip to content

Verify the $CRANK fair launch from chain state

The $CRANK utility token fair launch runs through an on-chain escrow in the Crank program. On-chain state is the transparency mechanism: the launch terms, every pledge, the escrow balance and the settlement outcome all live in Solana accounts that anyone can read. Crank's REST endpoint and the /launch page only mirror that state for convenience.

This page is the recipe for checking the mirror against the chain yourself.

What lives on chain

Account Seeds (program 7TjAk4i7EtDYagevPCtTddXBurXxTEADeNwujtzyTbj6) Holds
LaunchConfig ["launch", launch_id as u64 little-endian] Terms, status, total_pledged, pledger_count, escrow account address
Pledge ["pledge", launch_id u64 LE, pledger pubkey] One wallet's cumulative pledge, tokens released, refunded flag
Pledge escrow token account ["escrow_ta", launch_id u64 LE] The pledged stablecoin, owned by the LaunchConfig PDA
Launch token vault ["launch_vault", launch_id u64 LE] The launch-mint tokens allocated to pledgers

Both account types use the standard Anchor encoding: an 8-byte discriminator (sha256("account:<Name>")[0..8]) followed by little-endian Borsh fields.

LaunchConfig (343 bytes):

Offset Field Type
10 status (0 pending, 1 active, 2 succeeded, 3 failed, 4 finalized) u8
11 launch_id u64
19 / 51 / 83 authority / launch_mint / pledge_mint pubkey
115 escrow_token_account pubkey
147 / 155 min_raise / max_raise (pledge-mint base units) u64
163 token_allocation (launch-mint base units) u64
171 total_pledged u64
179 pledger_count u32
183 / 191 start_ts / end_ts i64
199 / 207 / 211 liquidity release schedule: release_start_ts i64, release_cliff_seconds u32, release_duration_seconds u32
215 max_pledge_per_wallet (0 = uncapped) u64

Pledge (139 bytes): launch_id u64 at 10, pledger pubkey at 18, amount u64 at 50, tokens_released u64 at 58, refunded bool at 66, pledged_ts i64 at 67.

The settlement rules

Settlement is deterministic and the same for every participant:

  1. When the pledge window closes, finalize_launch sets the status to succeeded if total_pledged >= min_raise, otherwise failed.
  2. Succeeded: every pledger's allocation is floor(pledge_amount * token_allocation / total_pledged) (pledger_entitlement in the escrow program, computed in 128-bit integers). That is one uniform clearing rate -- total_pledged / token_allocation pledge units per launch token -- for everyone. Allocations unlock on the published liquidity release schedule (nothing before the cliff, then linear over the duration), and each pledger claims their own with a transaction they sign. Floor-division dust stays in the vault.
  3. Failed: no launch tokens are allocated. Every pledger can reclaim their full pledge with refund_pledge, a transaction they sign themselves.
  4. Finalized: a succeeded launch whose raise has been collected from the escrow; the escrow token account is then empty.

Because these rules use only LaunchConfig and Pledge fields, every figure the /launch page shows can be reproduced from the accounts above.

Run the verifier

verify-ilo-settlement.mjs is a read-only Node script (no keypair, no signing) that performs every check below against any RPC node you choose:

npm i @solana/web3.js@1
curl -O https://docs.crank.ing/assets/verify-ilo-settlement.mjs
node verify-ilo-settlement.mjs --launch-id 1 --cluster devnet \
  --api https://api.crank.ing

Options: --rpc <url> (your own node), --program-id <id>, --wallet <pubkey> (show one wallet's allocation or refund), --json (machine-readable report), --cluster localnet|devnet|mainnet-beta.

It reports each check as OK or FAIL and exits 0 only when all of them hold:

Check Meaning
sum_of_pledges_equals_total_pledged Every Pledge account for the launch adds up to LaunchConfig.total_pledged
pledge_account_count_equals_pledger_count One Pledge account per counted wallet
total_within_max_raise / every_pledge_within_per_wallet_cap The on-chain caps held
escrow_balance_matches_pledges_held The escrow token account holds every un-refunded pledge (zero once finalized)
settlement_outcome_matches_minimum_raise Succeeded if and only if the minimum raise was met
allocations_within_token_allocation The floor-divided allocations never exceed the allocation
rest_mirror_* (with --api) Crank's REST mirror agrees with chain on total, pledger count and status

Compare with the REST mirror

The same state is served, read-only and without authentication, at:

GET https://api.crank.ing/api/v1/ilo/status/                    # current launch on this cluster
GET https://api.crank.ing/api/v1/ilo/launches/                  # every indexed launch
GET https://api.crank.ing/api/v1/ilo/launches/<launch_id>/?wallet=<pubkey>

Each response carries source.last_indexed_slot (the chain slot the mirror reflects), explorer links for every account, a settlement block once the launch has settled, and -- with ?wallet= -- that wallet's allocation or refund. If the mirror and the chain ever disagree, the chain is right. The MCP equivalent is get_ilo_details.

Worked example: devnet rehearsal

Two rehearsal launches were run on Solana devnet on 1 October 2026 against the deployed program 7TjAk4i7EtDYagevPCtTddXBurXxTEADeNwujtzyTbj6, using throwaway test mints (not $CRANK) and throwaway pledger wallets. You can re-run the verifier against them yourself -- they are permanent devnet accounts.

Launch id Outcome Terms (test-mint units, 6 dp) Pledges
902 succeeded min 100, max 1,000, allocation 1,000,000 3 wallets: 150, 100, 50 (total 300)
903 failed, both pledges refunded min 1,000, max 5,000, allocation 1,000,000 2 wallets: 100, 50 (total 150)

Test mints: pledge 8YLT5Cx5FDzsYYF8v6ZXXrBfL8whqBgHTcYLvRcsuhF2, launch AHEcRi3xr8CamfdM41PKUU1gLukWWUuMY9R3wGDLNCzE.

$ node verify-ilo-settlement.mjs --launch-id 902 --cluster devnet
launch 902 on devnet -- status succeeded
  LaunchConfig BnPizJYZUzLxLeMebZF99dwhnp9uPymJVqEuTwR9DwEH
  total pledged 300000000 by 3 wallets; escrow balance 300000000
  uniform clearing rate 300000000/1000000000000 pledge base units per launch base unit
  [OK  ] sum_of_pledges_equals_total_pledged: sum=300000000 total_pledged=300000000
  [OK  ] pledge_account_count_equals_pledger_count: pledge_accounts=3 pledger_count=3
  [OK  ] total_within_max_raise: total=300000000 max_raise=1000000000
  [OK  ] escrow_balance_matches_pledges_held: escrow_balance=300000000 expected=300000000 (status=succeeded)
  [OK  ] settlement_outcome_matches_minimum_raise: total=300000000 min_raise=100000000 status=succeeded
  [OK  ] allocations_within_token_allocation: allocated=999999999999 token_allocation=1000000000000
$ echo $?
0

$ node verify-ilo-settlement.mjs --launch-id 903 --cluster devnet
launch 903 on devnet -- status failed
  LaunchConfig 8QhwFUkwpzTGVSE79rekch9jdaV8ftsaLLLEQtQmQaiW
  total pledged 150000000 by 2 wallets; escrow balance 0
  [OK  ] sum_of_pledges_equals_total_pledged: sum=150000000 total_pledged=150000000
  [OK  ] pledge_account_count_equals_pledger_count: pledge_accounts=2 pledger_count=2
  [OK  ] total_within_max_raise: total=150000000 max_raise=5000000000
  [OK  ] escrow_balance_matches_pledges_held: escrow_balance=0 expected=0 (status=failed)
  [OK  ] settlement_outcome_matches_minimum_raise: total=150000000 min_raise=1000000000 status=failed
$ echo $?
0

What the example shows:

  • Succeeded (902): 300 pledged against a 1,000,000 allocation gives one uniform clearing rate. The 150 / 100 / 50 pledgers are entitled to 500,000 / 333,333.333333 / 166,666.666666 launch tokens; the floor-division dust (one base unit) stays in the vault, which is why allocated is one below token_allocation. The escrow still holds the full 300 because the raise has not been collected (finalized would show 0).
  • Failed (903): 150 pledged against a 1,000 minimum, so finalize_launch set the status to failed and each pledger reclaimed their full pledge with refund_pledge. The escrow balance is therefore 0, and the verifier expects exactly that because both Pledge accounts carry refunded = true.

The REST comparison (--api) was not part of this rehearsal: the read-only ILO REST endpoints were not yet serving on any deployed environment at the time of the run.

Reproduce the rehearsal

The rehearsal is scripted in the Crank repository as scripts/ilo-devnet-rehearsal.mjs. It is devnet-only: it prints a plan and sends nothing unless given --execute, refuses any mainnet-looking RPC URL, and checks the RPC's genesis hash is devnet's before signing anything. Pledger wallets are generated in memory for the run and never written to disk.

cd scripts && npm run install:deps
node ilo-devnet-rehearsal.mjs                # plan only
node ilo-devnet-rehearsal.mjs --execute      # two fresh launches on devnet

Note on the devnet program binary: the program deployed on devnet at the time of this rehearsal predates two later source changes -- the per-wallet pledge cap and the launch-token-vault allocation check in finalize_launch. The cap field is carved out of reserved space, so the LaunchConfig layout above is unchanged and decodes it as 0 (uncapped); the rehearsal script detects the older finalize_launch account list automatically. The settlement rules and every check above are identical on both versions.

Before any launch exists

Until a launch escrow is created, /api/v1/ilo/status/ responds with "phase": "not_created" and "launch": null, and the verifier reports that no LaunchConfig exists for the requested id. Participation opens only after the off-chain gate clears (legal sign-off, the launch prerequisites, and the adoption milestone) -- the REST launch_gate block shows which leg is pending.

$CRANK is a utility token for use within the Crank rails. A pledge commits funds to a non-custodial escrow; it is not a purchase of any expectation of profit, and nothing here describes or implies a financial outcome.