Launches
bordrless-programsHolder rewards
A share of each trade, paid to holders in SOL, claimable any time. The kit does it with the 64 bytes and one number per token.
The accounting#
The launch pool’s hook pays the holder fee, in bridged SOL, into the reward vault. KitConfig keeps acc_per_share, the lamports earned so far per eligible base unit (scaled by 1012); each holder’s 64 bytes keep a snapshot of it and what it is owed. Every callback syncs first, then settles each holder it touches at its balance before the operation.
pub fn sync(&mut self, vault_amount: u64, now: i64) -> Result<()> {
let released = self.release(now)?; // the share stream; paused while nobody is eligible
let total = vault_amount + self.total_claimed;
let fresh = total - self.seen + released; // what arrived since the last sync
self.seen = total;
if self.divides() { // eligible > 0 and eligible >= min_eligible
let pot = fresh + self.held;
self.held = 0;
if pot > 0 {
let scaled = u128::from(pot) * SCALE + self.rem; // SCALE = 10^12
let inc = scaled / u128::from(self.eligible);
self.rem = scaled - inc * u128::from(self.eligible); // the exact remainder, kept
self.acc_per_share += inc;
self.total_distributed += pot;
}
} else {
self.held += fresh; // waits until holders hold enough
}
Ok(())
}
Below min_eligible (a thousandth of the supply held) nothing is divided: what arrives waits in held, and the pool hook takes no holder fee. The division keeps its remainder, so nothing is paid twice and nothing is lost.
Claims#
claim syncs, settles the holder and pays min(owed, reward vault) to its bridged-SOL holding. It writes bytes 0 to 23 through write_hook_data and keeps 24 to 63 as read, so a claim never unlocks an early buyer. A wallet that sold everything keeps what it earned until it claims. The site claims up to about four tokens in one transaction and unwraps the SOL.
Share with holders#
share(amount) lets anyone send bridged SOL to every holder, pro rata. It streams over 3,600 seconds, so buying just before a share and selling after it gains nothing. While nobody is eligible the stream pauses, and resumes with the same time left.
Why the vault always covers it#
After every instruction the reward vault holds at least what every holder could claim, plus what is held, plus what is still streaming, because:
- eligible is the sum of the balances that will be settled: every balance change passes through a kit callback, minting is impossible, and excluded owners never claim;
- every balance change of a holder is settled first, at the balance before it;
- every rounding favours the vault: the division, owed and stream releases all floor;
- shared lamports count as seen when they arrive and reach holders only as they are released.
The off-chain mirror#
What the site shows as claimable is a pure function of three accounts (KitConfig, the reward vault, the holding), read in one call:
released = what the stream releases by now // 0 while nobody is eligible
pending = vault.amount + total_claimed - seen + released
acc = acc_per_share
if eligible > 0 and eligible >= min_eligible and pending + held > 0:
acc = acc_per_share + ((pending + held) * SCALE + rem) / eligible
claimable = 0 for the pool and the launch,
else owed + balance * (acc - snapshot) / SCALE // floors
a claim pays min(claimable, vault.amount)