Settle, migrate, and claim
Everything after the window closes, or after a hard-capped sale closes early, is permissionless. Blast runs a keeper for these calls; any app or bot can run them too.
Settle
Section titled “Settle”settle(presale, clock), by anyone, while the sale is Open, once either:
now ≥ end_ms; or- the sale can close early: its hard cap has
closes_when_filled, the hard cap is in force (now ≥ start_ms + starts_after_ms), andtotal_commitments ≥ raise_target.
Otherwise it aborts with EPresaleNotEnded. The outcome:
- Total commitments at or above the raise: the sale becomes
Fundedand emitsPresaleFunded. - Below the raise: the sale becomes
Cancelledand emitsPresaleCancelled.
Early close
Section titled “Early close”Closing early ends the window at the settle. The stored duration_ms becomes the time the sale ran, so end_ms and the liquidity deadline (end_ms + liquidity_window_ms) count from the close. PresaleFunded carries both as end_ms and liquidity_deadline_ms, earlier than the ones PresaleOpened announced.
- A sale without a hard cap never closes early, even if it reached its raise on day one. Later wallets can still join at the common fill rate.
- A sale that overflowed past its raise before its hard cap started can close from the moment the hard cap starts.
- A hard-capped sale with
closes_when_filledset tofalseruns to its end, so the pool can open at an announced time.
Settling and migrating can run in one transaction. Because migration is permissionless, the wallet whose commitment fills the raise can commit, settle, migrate, and buy first from the new pool in one transaction. The pool’s seed price is the same as at a timed end; only who reliably buys first changes. Teams that want a public pool time set closes_when_filled to false.
Migrate
Section titled “Migrate”Anyone may migrate a Funded sale until someone cancels it. Migration pays Blast (or holds Blast’s fees on a partnered sale), pays or vests every raise and token recipient, creates the pool at the tier new recorded in VenueConfig, seeds a full-range position, and shares a Vault that holds it. If the venue call fails, the whole transaction reverts.
Bluefin
Section titled “Bluefin”// Settle and migrate a funded Bluefin sale in one transaction.const tx = new Transaction();tx.moveCall({ target: `${PKG}::blast_presale::settle`, typeArguments: [MEME, SUI], arguments: [tx.object(PRESALE), tx.object.clock()],});const [fee] = tx.splitCoins(tx.gas, [creationFee]); // Bluefin's current pool-creation feetx.moveCall({ target: `${PKG}::bluefin::migrate`, typeArguments: [MEME, SUI], arguments: [ tx.object(PRESALE), tx.object(FEE_POLICY), tx.object(VENUE_CONFIG), tx.object(MEME_CURRENCY), tx.object(SUI_CURRENCY), tx.object(BLUEFIN_CONFIG), fee, tx.object.clock(), ],});creationFee must equal what Bluefin’s config::get_pool_creation_fee_amount<SUI> returns at submission time; Bluefin rejects any other amount. VENUE_CONFIG is the shared VenueConfig, read for the tier bluefin::new recorded. MEME_CURRENCY and SUI_CURRENCY are the two coins’ shared Coin Registry Currency objects. The pool is created as Pool<Meme, Quote>, with the sale coin always as coin A.
cetus::migrate<Meme, Quote, CoinA, CoinB>(sale, fee_policy, venue_config, cetus_config, pools, burn_manager, clock)
CoinAandCoinBare the sale’s two types in Cetus’s canonical order, the onefactory::is_right_orderaccepts. Dev-inspect either order to find it. The wrong order aborts withEInvalidCoinOrder.- Shared objects: the presale,
FeePolicy,VenueConfig(read for the reservedPoolCreationCapand the tick spacingcetus::newrecorded), CetusGlobalConfig, CetusPools, and the Cetus LP-burnBurnManager. - The position is burned through Cetus LP-burn. The vault keeps only the proof that collects its fees.
Partnered sales
Section titled “Partnered sales”On a sale that names a partner, follow migration with pay_protocol_fees_with_partner(presale, fee_policy, partner, clock). Anyone can call it, once. It pays the partner its cut of Blast’s raise fee and pays Blast the rest plus Blast’s token fee.
claim(presale, participant, clock), by anyone, while the sale is Migrated or Cancelled. Payment always goes to participant.
Migrated: the participant’s tokens and overflow refund.Cancelled: the full commitment back.- Each wallet claims once; the claim removes its commitment record.
- With participant vesting, the tokens go into a new shared
Vesting<Meme>that starts at the claim. The refund is paid at once.
Batch many claims in one transaction:
const claims = new Transaction();for (const participant of participants) { claims.moveCall({ target: `${PKG}::blast_presale::claim`, typeArguments: [MEME, SUI], arguments: [claims.object(PRESALE), claims.pure.address(participant), claims.object.clock()], });}Claimed reports tokens, quote_refund, remaining_participants, and vesting_id.
Vested payouts
Section titled “Vested payouts”Recipients’ vested shares start at migration; participants’ vested tokens start at their claim. Each schedule is a shared Vesting<T> from the immutable blast_fun_vesting package, with a fixed beneficiary and no cancellation authority.
blast_fun_linear_vesting::claim<T>(vesting, clock): anyone; sends everything newly vested to the beneficiary. Aborts withENothingClaimableif nothing new has vested.- Vested amount at time
t: nothing beforestart + cliff_ms; everything fromstart + period_ms × periods; otherwisefloor(total × floor((t − start) ÷ period_ms) ÷ periods). blast_fun_linear_vesting::close_irrevocable<T>(vesting): anyone; deletes a drained schedule.
Cancel a stalled sale
Section titled “Cancel a stalled sale”cancel_unlaunched(presale, clock), by anyone, once now ≥ end_ms + liquidity_window_ms while the sale is Funded. After an early close, end_ms is the close. The sale becomes Cancelled and every participant can claim a full refund. A sale nobody settled can be settled and cancelled in one transaction after the deadline.
The minted tokens stay locked in a cancelled sale forever. The creator can destroy the PresaleCap with destroy_cap(presale, presale_cap).