packages/core/src/fees.ts
The fee split and the sharing-config shareholders. Shown whole, as it was in the repository when this site was built. Line numbers link: add #L12 to the address.
packages/core/src/fees.ts104 lines
1// Where a Solary coin's creator fees go.2//3// pump.fun charges every trade and pays the coin's creator fee into a per-coin vault. A Solary coin's creator is its4// locked pump.fun fee-sharing config, which splits that vault between two shareholders:5// 1. the Solary vault (NFT holders + $SOLARY buyback + platform), and6// 2. the coin's creator (optional: only when they keep a cut).7// The keeper then splits what reaches the Solary vault between the three buckets, in proportion to their bps.8// All shares are basis points of the coin's total creator fees (10 000 = 100%).9 10export const BPS = 10_000;11 12export const FEE_RULES = {13 /** fixed: the platform's cut of every coin's creator fees */14 platformBps: 1_000,15 /** fixed: buys $SOLARY on the market and burns it */16 buybackBps: 1_000,17 /** the creator picks the NFT holders' share between these (inclusive), in steps */18 holdersMinBps: 5_000,19 holdersMaxBps: 8_000,20 holdersStepBps: 500,21 holdersDefaultBps: 7_000,22} as const;23 24export interface FeeSplit {25 holdersBps: number;26 creatorBps: number;27 buybackBps: number;28 platformBps: number;29}30 31/** The whole split for a chosen holders' share. The creator keeps whatever the holders and fixed cuts leave. */32export function feeSplit(holdersBps: number = FEE_RULES.holdersDefaultBps): FeeSplit {33 const creatorBps = BPS - holdersBps - FEE_RULES.buybackBps - FEE_RULES.platformBps;34 return { holdersBps, creatorBps, buybackBps: FEE_RULES.buybackBps, platformBps: FEE_RULES.platformBps };35}36 37/** Empty = valid. */38export function holdersBpsError(holdersBps: number): string | null {39 if (!Number.isInteger(holdersBps)) return 'holder share must be a whole number of bps';40 if (holdersBps < FEE_RULES.holdersMinBps || holdersBps > FEE_RULES.holdersMaxBps)41 return `holder share must be between ${FEE_RULES.holdersMinBps / 100}% and ${FEE_RULES.holdersMaxBps / 100}%`;42 if ((holdersBps - FEE_RULES.holdersMinBps) % FEE_RULES.holdersStepBps !== 0) return `holder share moves in steps of ${FEE_RULES.holdersStepBps / 100}%`;43 return null;44}45 46/** Every allowed holders' share, low to high (for the launch form's slider). */47export function holdersOptions(): number[] {48 const out: number[] = [];49 for (let b = FEE_RULES.holdersMinBps; b <= FEE_RULES.holdersMaxBps; b += FEE_RULES.holdersStepBps) out.push(b);50 return out;51}52 53/** The pump.fun fee-sharing shareholders for a split: the vault first (the keeper's scan matches on it), then the creator. */54export function sharingShareholders(split: FeeSplit, vault: string, creator: string): Array<{ address: string; bps: number }> {55 const vaultBps = BPS - split.creatorBps;56 const out = [{ address: vault, bps: vaultBps }];57 if (split.creatorBps > 0) out.push({ address: creator, bps: split.creatorBps });58 return out;59}60 61/** The vault's part of the split (everything but the creator's cut). */62export const vaultBps = (split: Pick<FeeSplit, 'creatorBps'>) => BPS - split.creatorBps;63 64export interface VaultSplit {65 holders: bigint;66 buyback: bigint;67 platform: bigint;68}69 70/**71 * Split lamports that reached the Solary vault for one coin. Integer math, rounded down per bucket; the rounding72 * remainder goes to the holders so nothing is lost.73 */74export function splitVaultAmount(lamports: bigint, split: FeeSplit): VaultSplit {75 const vb = BigInt(vaultBps(split));76 if (vb === 0n || lamports <= 0n) return { holders: 0n, buyback: 0n, platform: 0n };77 const buyback = (lamports * BigInt(split.buybackBps)) / vb;78 const platform = (lamports * BigInt(split.platformBps)) / vb;79 return { holders: lamports - buyback - platform, buyback, platform };80}81 82/** "70%" from 7000 bps. */83export function bpsToPct(bps: number): string {84 const p = bps / 100;85 return `${Number.isInteger(p) ? p : p.toFixed(1)}%`;86}87 88/**89 * pump.fun's creator fee on the bonding curve, used ONLY for "per 100 SOL traded" previews. pump.fun sets and90 * changes it (after graduation PumpSwap's creator fee moves with market cap), so the site labels it as an estimate.91 * [VERIFY] against pump.fun's current fee schedule before launch.92 */93export const PUMP_CURVE_CREATOR_FEE_BPS_ESTIMATE = 30;94 95/** Estimated SOL per bucket for `solTraded` of volume on the curve (preview only). */96export function perVolumePreview(split: FeeSplit, solTraded = 100): Record<keyof FeeSplit, number> {97 const fees = (solTraded * PUMP_CURVE_CREATOR_FEE_BPS_ESTIMATE) / BPS;98 return {99 holdersBps: (fees * split.holdersBps) / BPS,100 creatorBps: (fees * split.creatorBps) / BPS,101 buybackBps: (fees * split.buybackBps) / BPS,102 platformBps: (fees * split.platformBps) / BPS,103 };104}