@bordrless/shared 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,8 +27,8 @@ TOKEN_HOOK_FLAGS.BEFORE_TRANSFER | TOKEN_HOOK_FLAGS.TRANSFER_RETURNS_DELTA;
27
27
 
28
28
  ## Where to read more
29
29
 
30
- - The SDK page: https://bordrless.xyz/sdk
31
- - The standard in full: https://bordrless.xyz/docs
30
+ - The SDK page: https://bordrless.app/sdk
31
+ - The standard in full: https://bordrless.app/docs
32
32
 
33
33
  Status: the programs run on localnet and devnet until they are deployed to mainnet; they keep an
34
34
  upgrade authority until audited.
package/dist/api.d.ts CHANGED
@@ -143,6 +143,8 @@ export interface ConfigInspection {
143
143
  /** As `CustomHookInfo.upgradeable`; null without a hook or when its program could not be read. */
144
144
  hookUpgradeable: boolean | null;
145
145
  registryReady: boolean | null;
146
+ /** The hook is Half-Life: the launch prepares it for the mint and lights its furnace itself. */
147
+ halfLife?: boolean;
146
148
  }
147
149
  /** Live figures of a launch's token rules; each null when the launch lacks the rule or it is not tracked. */
148
150
  export interface RuleStats {
@@ -351,12 +353,27 @@ export interface PortfolioEntry {
351
353
  /** For a launch token with a creator's own hook (§5.8): the hook, so the row can say so; null otherwise (absent from backends that predate it). */
352
354
  customHook?: CustomHookInfo | null;
353
355
  }
356
+ /** A launch the wallet created, with the creator fee waiting for it (`Portfolio.created`). */
357
+ export interface CreatedLaunch {
358
+ mint: Address;
359
+ symbol: string;
360
+ name: string;
361
+ image: string | null;
362
+ status: LaunchStatus;
363
+ creatorFeeBps: number;
364
+ /** Lamports of bridged SOL accrued and not yet claimed (the launch's quote holding, what a claim pays); null when the chain could not be read. */
365
+ claimable: Amount | null;
366
+ /** Lamports claimed here, all time; null when not known. */
367
+ claimedTotal?: Amount | null;
368
+ }
354
369
  export interface Portfolio {
355
370
  owner: Address;
356
371
  lamports: Amount;
357
372
  /** What the wallet holds, plus launches it no longer holds when it has holder rewards to claim there. */
358
373
  entries: PortfolioEntry[];
359
374
  totalUsd: number | null;
375
+ /** The launches this wallet created, those with fees to claim first (absent from backends that predate it). */
376
+ created: CreatedLaunch[];
360
377
  }
361
378
  /** Native SOL (bridged on the way in, unbridged on the way out) or bridged SOL already held. */
362
379
  export type SolMode = 'SOL' | 'BSOL';
@@ -518,6 +535,11 @@ export interface RewardsPrepareRequest {
518
535
  /** Unwrap the bridged SOL to SOL and close the temporary holding. */
519
536
  unwrap: boolean;
520
537
  }
538
+ /** Claim the creator fees of launches the owner created: up to about 4 mints per transaction, paid as SOL. */
539
+ export interface CreatorPrepareRequest {
540
+ owner: Address;
541
+ mints: Address[];
542
+ }
521
543
  /** Share with holders: send SOL that every holder of the token receives pro rata. */
522
544
  export interface SharePrepareRequest {
523
545
  owner: Address;
package/dist/policy.d.ts CHANGED
@@ -230,9 +230,14 @@ export declare const rewardsOn: (r: Pick<LaunchRulesInput, "holderFeeBuyBps" | "
230
230
  export declare const defaultCreatorFeeBps: (r: LaunchRulesInput) => number;
231
231
  /** The four presets of §7.1 (2026-10-07: Holders first, Fair start, Scorched and Community are gone). */
232
232
  export type RulePresetName = 'Plain' | 'Diamond hands' | 'Burn' | 'Paid to hold';
233
- /** The two ways beside the presets (§7.1): Custom mixes the rules by hand with the form's controls; Build your own launches from a `LaunchConfig` a creator made with the SDK (§5.7). */
234
- export type RulePathName = 'Custom' | 'Build your own';
235
- /** What the launch form's rules row can be set to: a preset, or one of the two paths. */
233
+ /**
234
+ * The ways beside the presets (§7.1): Half-Life launches from Bordrless's own `LaunchConfig` with
235
+ * the Half-Life hook (programs/half_life), prepared and lit for the mint by the launch itself;
236
+ * Custom mixes the rules by hand with the form's controls; Build your own launches from a
237
+ * `LaunchConfig` a creator made with the SDK (§5.7).
238
+ */
239
+ export type RulePathName = 'Half-Life' | 'Custom' | 'Build your own';
240
+ /** What the launch form's rules row can be set to: a preset, or one of the paths. */
236
241
  export type RuleModeName = RulePresetName | RulePathName;
237
242
  export interface RulePreset {
238
243
  name: RulePresetName;
@@ -259,7 +264,7 @@ export interface RulePath {
259
264
  * every one). Holder rewards set "both sides" take the same rate on buys and sells.
260
265
  */
261
266
  export declare const RULE_PRESETS: readonly RulePreset[];
262
- /** The two paths beside the presets, in order, with the words the home console and the launch form print. */
267
+ /** The paths beside the presets, in order, with the words the home console and the launch form print. */
263
268
  export declare const RULE_PATHS: readonly RulePath[];
264
269
  /** The preset the launch form starts on. */
265
270
  export declare const DEFAULT_RULE_PRESET: RulePresetName;
@@ -332,7 +337,7 @@ export declare function launchBeforeSwap(side: Side, amountIn: bigint, p: Launch
332
337
  export declare function launchAfterSwap(side: Side, amountOut: bigint, p: LaunchFeeParams): HookCut;
333
338
  /** A swap on a launch pool, step by step. */
334
339
  export interface LaunchSwap {
335
- /** Bridged SOL to the creator: from a buy's input, or from a sell's output after the protocol fee. */
340
+ /** Bridged SOL to the creator: from a buy's input, or from a sell's output after the LP fee. */
336
341
  creatorFee: bigint;
337
342
  /** Bridged SOL to holders, likewise. */
338
343
  holderFee: bigint;
@@ -342,13 +347,13 @@ export interface LaunchSwap {
342
347
  holderFeeOn: boolean;
343
348
  /** What reached the input vault. */
344
349
  received: bigint;
345
- /** The LP fee, in the input token, on what reached the vault. */
350
+ /** The LP fee, Bordrless's in bridged SOL (it never stays in a launch pool): on a buy from what reached the vault, on a sell from the curve's output. The DEX's event reports it inside `protocol_fee` with `lp_fee` 0. */
346
351
  lpFee: bigint;
347
352
  /** Bordrless's share of the creator and holder fees (`LAUNCH_PROTOCOL_SHARE_BPS` of them, rounded up; 0 when they are 0), always in bridged SOL: on a buy from what reached the vault, before the curve; on a sell held back from the delivery. */
348
353
  protocolFee: bigint;
349
- /** What went into the curve: `received` less the LP fee (and, on a buy, the protocol fee); negative when the fees take more than arrived. */
354
+ /** What went into the curve: on a buy `received` less the LP fee and the protocol fee (negative when they take more than arrived); on a sell `received`. */
350
355
  netIn: bigint;
351
- /** What the curve gave (on a sell, before the protocol fee and the creator and holder fees); null when the swap fails. */
356
+ /** What the curve gave (on a sell, before the LP fee, the creator and holder fees and the protocol fee); null when the swap fails. */
352
357
  amountOut: bigint | null;
353
358
  /** What reaches the recipient; null when the swap fails. */
354
359
  delivered: bigint | null;
@@ -362,21 +367,26 @@ export declare function curveOutput(r: Reserves, side: Side, netIn: bigint): big
362
367
  * §5.4; the Rust reference is `bordrless_program_tests::launch::quote_launch_swap`): Bordrless
363
368
  * takes `protocolShareBps` of the creator and holder fees (the hooks' cuts; the kit takes none of
364
369
  * its own, so the cuts are exactly those fees), in bridged SOL, and nothing when they are nothing.
365
- * A buy: the creator and holder fees from the input, Bordrless's share of them and the LP fee on
366
- * what reached the vault, the curve, the burn from the output. A sell: the burn from the input, the
367
- * LP fee on what reached the vault, the curve, then the creator and holder fees from the output and
368
- * Bordrless's share of them held back from the delivery. A sell takes its input out of the kit's
369
- * eligible count before `after_swap` reads it (every seller is a holder: the pool and the launch
370
- * never sell). An ordinary pool's flat rate is `quoteSwap`.
370
+ * A buy: the creator and holder fees from the input, Bordrless's share of them and the LP fee (both
371
+ * Bordrless's, in SOL) on what reached the vault, the curve, the burn from the output. A sell: the
372
+ * burn from the input, the curve on all the rest, the LP fee (Bordrless's) from the curve's output,
373
+ * then the creator and holder fees from what is left and Bordrless's share of them held back from the
374
+ * delivery. A launch pool's LP fee never stays in the pool (its LP is locked for ever). A sell takes
375
+ * its input out of the kit's eligible count before `after_swap` reads it (every seller is a holder:
376
+ * the pool and the launch never sell). An ordinary pool's flat rate is `quoteSwap`.
371
377
  */
372
378
  export declare function quoteLaunchSwap(r: Reserves, side: Side, amountIn: bigint, lpFeeBps: number, protocolShareBps: number, p: LaunchFeeParams): LaunchSwap;
373
- /** What a launch-pool swap adds to its input reserve (the Rust reference's `to_reserve_in`): what reached the vault, less a buy's protocol fee. */
374
- export declare const launchSwapToReserve: (side: Side, q: Pick<LaunchSwap, "received" | "protocolFee">) => bigint;
375
379
  /**
376
- * Whether a buy that lands leaves the pool's quote reserve at or above the graduation threshold, so
377
- * `graduate` can follow it in the same transaction (§8.2: the input after the creator and holder
378
- * fees, less the protocol fee, joins the reserve; the launch graduates once `quote_reserve >=
379
- * graduation_quote`).
380
+ * What a launch-pool swap adds to its input reserve (the Rust reference's `to_reserve_in`): on a buy
381
+ * what goes into the curve (what reached the vault less the LP fee and the share, both Bordrless's;
382
+ * 0 when they take it all), on a sell what reached the vault.
383
+ */
384
+ export declare const launchSwapToReserve: (side: Side, q: Pick<LaunchSwap, "received" | "netIn">) => bigint;
385
+ /**
386
+ * Whether a buy that lands leaves the launch ready to graduate, so `graduate` can follow it in the
387
+ * same transaction: the pool's quote reserve at or above the graduation threshold, or the curve sold
388
+ * out (the buy takes every token it has left). The LP fee does not compound, so the two come
389
+ * together, and a hook's cut on buys can keep the reserve just short of the threshold.
380
390
  */
381
391
  export declare function buyGraduates(r: Reserves, q: LaunchSwap, graduationQuote: bigint): boolean;
382
392
  /**
package/dist/policy.js CHANGED
@@ -334,8 +334,9 @@ export const RULE_PRESETS = [
334
334
  { name: 'Burn', slug: 'burn', description: 'Burn 0.5% on buys and sells, holder rewards 0.5%, creator fee 0.5%.', creatorFeeBps: 50, rules: { ...NO_RULES, holderFeeBuyBps: 50, holderFeeSellBps: 50, burnBuyBps: 50, burnSellBps: 50 } },
335
335
  { name: 'Paid to hold', slug: 'paid-to-hold', description: 'Holder rewards 2% on sells only, creator wallet lock 30 d, creator fee 0.5%.', creatorFeeBps: 50, rules: { ...NO_RULES, holderFeeSellBps: 200, creatorLockDays: 30 } },
336
336
  ];
337
- /** The two paths beside the presets, in order, with the words the home console and the launch form print. */
337
+ /** The paths beside the presets, in order, with the words the home console and the launch form print. */
338
338
  export const RULE_PATHS = [
339
+ { name: 'Half-Life', slug: 'half-life', description: 'An exit fee that halves every 6 hours held: 20% to sell at once, 0% after 2 days, burned. Buys are free.' },
339
340
  { name: 'Custom', slug: 'custom', description: 'Mix the rules by hand; holder rewards and burn can differ on buys and sells.' },
340
341
  { name: 'Build your own', slug: 'build-your-own', description: 'Write a hook with the SDK, make a launch config, paste its key.' },
341
342
  ];
@@ -482,23 +483,25 @@ export function curveOutput(r, side, netIn) {
482
483
  * §5.4; the Rust reference is `bordrless_program_tests::launch::quote_launch_swap`): Bordrless
483
484
  * takes `protocolShareBps` of the creator and holder fees (the hooks' cuts; the kit takes none of
484
485
  * its own, so the cuts are exactly those fees), in bridged SOL, and nothing when they are nothing.
485
- * A buy: the creator and holder fees from the input, Bordrless's share of them and the LP fee on
486
- * what reached the vault, the curve, the burn from the output. A sell: the burn from the input, the
487
- * LP fee on what reached the vault, the curve, then the creator and holder fees from the output and
488
- * Bordrless's share of them held back from the delivery. A sell takes its input out of the kit's
489
- * eligible count before `after_swap` reads it (every seller is a holder: the pool and the launch
490
- * never sell). An ordinary pool's flat rate is `quoteSwap`.
486
+ * A buy: the creator and holder fees from the input, Bordrless's share of them and the LP fee (both
487
+ * Bordrless's, in SOL) on what reached the vault, the curve, the burn from the output. A sell: the
488
+ * burn from the input, the curve on all the rest, the LP fee (Bordrless's) from the curve's output,
489
+ * then the creator and holder fees from what is left and Bordrless's share of them held back from the
490
+ * delivery. A launch pool's LP fee never stays in the pool (its LP is locked for ever). A sell takes
491
+ * its input out of the kit's eligible count before `after_swap` reads it (every seller is a holder:
492
+ * the pool and the launch never sell). An ordinary pool's flat rate is `quoteSwap`.
491
493
  */
492
494
  export function quoteLaunchSwap(r, side, amountIn, lpFeeBps, protocolShareBps, p) {
493
495
  const before = launchBeforeSwap(side, amountIn, p);
494
496
  const received = amountIn - before.creatorFee - before.holderFee - before.burn;
495
497
  const eligibleAtFees = side === 'sell' ? (p.eligible > amountIn ? p.eligible - amountIn : 0n) : p.eligible;
496
498
  const holderFeeOn = (side === 'buy' ? p.holderFeeBuyBps : p.holderFeeSellBps) > 0 && eligibleAtFees >= p.minEligible;
497
- const lpFee = feeAmount(received, lpFeeBps);
499
+ // A buy's LP fee is on the SOL that reached the vault; a sell's on the curve's output.
500
+ const buyLpFee = side === 'buy' ? feeAmount(received, lpFeeBps) : 0n;
498
501
  // A buy's cuts are the hook's quote fees; a sell's input side cuts nothing (the burn is not a cut), so its share is taken from the output.
499
502
  const inputProtocolFee = side === 'buy' ? protocolShare(before.creatorFee + before.holderFee, protocolShareBps) : 0n;
500
- const netIn = received - lpFee - inputProtocolFee;
501
- const failed = (failure, protocolFee = inputProtocolFee) => ({ ...before, holderFeeOn, received, lpFee, protocolFee, netIn, amountOut: null, delivered: null, failure });
503
+ const netIn = received - buyLpFee - inputProtocolFee;
504
+ const failed = (failure, protocolFee = inputProtocolFee, carried = {}) => ({ ...before, holderFeeOn, received, lpFee: buyLpFee, protocolFee, netIn, amountOut: null, delivered: null, failure, ...carried });
502
505
  if (received <= 0n || netIn <= 0n)
503
506
  return failed('fees_exceed_input');
504
507
  const out = curveOutput(r, side, netIn);
@@ -508,24 +511,33 @@ export function quoteLaunchSwap(r, side, amountIn, lpFeeBps, protocolShareBps, p
508
511
  return failed('insufficient_liquidity');
509
512
  if (side === 'buy') {
510
513
  const after = launchAfterSwap('buy', out, p);
511
- return { ...before, burn: after.burn, holderFeeOn, received, lpFee, protocolFee: inputProtocolFee, netIn, amountOut: out, delivered: out - after.burn, failure: null };
514
+ return { ...before, burn: after.burn, holderFeeOn, received, lpFee: buyLpFee, protocolFee: inputProtocolFee, netIn, amountOut: out, delivered: out - after.burn, failure: null };
512
515
  }
513
- const after = launchAfterSwap('sell', out, { ...p, eligible: eligibleAtFees });
516
+ // A sell: the LP fee leaves the curve's output before the hook is told the rest.
517
+ const lpFee = feeAmount(out, lpFeeBps);
518
+ if (lpFee >= out)
519
+ return failed('no_output', 0n, { lpFee });
520
+ const told = out - lpFee;
521
+ const after = launchAfterSwap('sell', told, { ...p, eligible: eligibleAtFees });
514
522
  const protocolFee = protocolShare(after.creatorFee + after.holderFee, protocolShareBps);
515
- if (after.creatorFee + after.holderFee + protocolFee >= out)
516
- return failed('no_output', protocolFee);
517
- return { creatorFee: after.creatorFee, holderFee: after.holderFee, burn: before.burn, holderFeeOn, received, lpFee, protocolFee, netIn, amountOut: out, delivered: out - after.creatorFee - after.holderFee - protocolFee, failure: null };
523
+ if (after.creatorFee + after.holderFee + protocolFee >= told)
524
+ return failed('no_output', protocolFee, { creatorFee: after.creatorFee, holderFee: after.holderFee, lpFee });
525
+ return { creatorFee: after.creatorFee, holderFee: after.holderFee, burn: before.burn, holderFeeOn, received, lpFee, protocolFee, netIn, amountOut: out, delivered: told - after.creatorFee - after.holderFee - protocolFee, failure: null };
518
526
  }
519
- /** What a launch-pool swap adds to its input reserve (the Rust reference's `to_reserve_in`): what reached the vault, less a buy's protocol fee. */
520
- export const launchSwapToReserve = (side, q) => (side === 'buy' ? q.received - q.protocolFee : q.received);
521
527
  /**
522
- * Whether a buy that lands leaves the pool's quote reserve at or above the graduation threshold, so
523
- * `graduate` can follow it in the same transaction (§8.2: the input after the creator and holder
524
- * fees, less the protocol fee, joins the reserve; the launch graduates once `quote_reserve >=
525
- * graduation_quote`).
528
+ * What a launch-pool swap adds to its input reserve (the Rust reference's `to_reserve_in`): on a buy
529
+ * what goes into the curve (what reached the vault less the LP fee and the share, both Bordrless's;
530
+ * 0 when they take it all), on a sell what reached the vault.
531
+ */
532
+ export const launchSwapToReserve = (side, q) => (side === 'buy' ? (q.netIn > 0n ? q.netIn : 0n) : q.received);
533
+ /**
534
+ * Whether a buy that lands leaves the launch ready to graduate, so `graduate` can follow it in the
535
+ * same transaction: the pool's quote reserve at or above the graduation threshold, or the curve sold
536
+ * out (the buy takes every token it has left). The LP fee does not compound, so the two come
537
+ * together, and a hook's cut on buys can keep the reserve just short of the threshold.
526
538
  */
527
539
  export function buyGraduates(r, q, graduationQuote) {
528
- return q.failure === null && r.quoteReserve + launchSwapToReserve('buy', q) >= graduationQuote;
540
+ return q.failure === null && (r.quoteReserve + launchSwapToReserve('buy', q) >= graduationQuote || q.amountOut === r.baseReserve);
529
541
  }
530
542
  /**
531
543
  * §8.2 `maxAmountIn` on a curve pool: the largest buy the curve can still fill with these fees (one
@@ -12,7 +12,33 @@ export declare const PROGRAM_IDS: {
12
12
  readonly taxHook: "8tjnVSreJGBRQFyDBf1SyyhBgLsdBxa2rHYh9sbxFyX7";
13
13
  /** The v2 token-rules hook (docs/hooks-v2.md §4): holder rewards, max wallet and the two locks. */
14
14
  readonly kit: "14RJQXPdJfkehit6ezktjd3xujamf8nVSKw2shKamaEH";
15
+ /** Half-Life (programs/half_life): an exit fee that halves every six hours a token is held, burned. */
16
+ readonly halfLife: "53SpmtkdPWQ63mWoDeXk8P9tuwiT4ed2Wx4fwfy5NSF8";
15
17
  };
18
+ /**
19
+ * Half-Life, Bordrless's own token hook (programs/half_life/README.md): the `LaunchConfig` the
20
+ * launch form's Half-Life path launches from (no kit rules, creator fee 1%, the hook with
21
+ * `flags`), and the fee curve the program has fixed: `maxFeePpm` for tokens that just arrived,
22
+ * halved every `halfLifeSecs`, linear within each, zero from `zeroAfterSecs`.
23
+ */
24
+ export declare const HALF_LIFE: {
25
+ readonly program: "53SpmtkdPWQ63mWoDeXk8P9tuwiT4ed2Wx4fwfy5NSF8";
26
+ readonly launchConfig: "ABz5Je9FznnotUQxxaj28vn18t1Wv9SsDzEfDxGLRJY";
27
+ /** `BEFORE_TRANSFER | TRANSFER_RETURNS_DELTA | WRITES_HOOK_DATA`. */
28
+ readonly flags: 193;
29
+ readonly creatorFeeBps: 100;
30
+ readonly maxFeePpm: 200000;
31
+ readonly halfLifeSecs: 21600;
32
+ readonly zeroAfterSecs: 172800;
33
+ /** The token program's signer of the hook's callbacks (`["hook-authority", half_life]`). */
34
+ readonly tokenHookSigner: "FBZPj9PmV9dXL8U4qmRffXdXm11e23KEBnNgEVXxhfhF";
35
+ readonly readme: "https://github.com/BordrlessDex/bordrless-programs/tree/main/programs/half_life";
36
+ };
37
+ /**
38
+ * Half-Life's exit fee, in parts per million, for tokens `ageSecs` old: the program's `fee_ppm`
39
+ * (20% at 0, halved every six hours, linear within each, 0 from 48 h).
40
+ */
41
+ export declare function halfLifeFeePpm(ageSecs: number): number;
16
42
  /** The wSOL mint, which stands for native SOL on the bridge. */
17
43
  export declare const NATIVE_MINT = "So11111111111111111111111111111111111111112";
18
44
  export declare const TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
package/dist/programs.js CHANGED
@@ -12,7 +12,43 @@ export const PROGRAM_IDS = {
12
12
  taxHook: '8tjnVSreJGBRQFyDBf1SyyhBgLsdBxa2rHYh9sbxFyX7',
13
13
  /** The v2 token-rules hook (docs/hooks-v2.md §4): holder rewards, max wallet and the two locks. */
14
14
  kit: '14RJQXPdJfkehit6ezktjd3xujamf8nVSKw2shKamaEH',
15
+ /** Half-Life (programs/half_life): an exit fee that halves every six hours a token is held, burned. */
16
+ halfLife: '53SpmtkdPWQ63mWoDeXk8P9tuwiT4ed2Wx4fwfy5NSF8',
15
17
  };
18
+ /**
19
+ * Half-Life, Bordrless's own token hook (programs/half_life/README.md): the `LaunchConfig` the
20
+ * launch form's Half-Life path launches from (no kit rules, creator fee 1%, the hook with
21
+ * `flags`), and the fee curve the program has fixed: `maxFeePpm` for tokens that just arrived,
22
+ * halved every `halfLifeSecs`, linear within each, zero from `zeroAfterSecs`.
23
+ */
24
+ export const HALF_LIFE = {
25
+ program: PROGRAM_IDS.halfLife,
26
+ launchConfig: 'ABz5Je9FznnotUQxxaj28vn18t1Wv9SsDzEfDxGLRJY',
27
+ /** `BEFORE_TRANSFER | TRANSFER_RETURNS_DELTA | WRITES_HOOK_DATA`. */
28
+ flags: 193,
29
+ creatorFeeBps: 100,
30
+ maxFeePpm: 200_000,
31
+ halfLifeSecs: 21_600,
32
+ zeroAfterSecs: 172_800,
33
+ /** The token program's signer of the hook's callbacks (`["hook-authority", half_life]`). */
34
+ tokenHookSigner: 'FBZPj9PmV9dXL8U4qmRffXdXm11e23KEBnNgEVXxhfhF',
35
+ readme: 'https://github.com/BordrlessDex/bordrless-programs/tree/main/programs/half_life',
36
+ };
37
+ /**
38
+ * Half-Life's exit fee, in parts per million, for tokens `ageSecs` old: the program's `fee_ppm`
39
+ * (20% at 0, halved every six hours, linear within each, 0 from 48 h).
40
+ */
41
+ export function halfLifeFeePpm(ageSecs) {
42
+ if (!(ageSecs > 0))
43
+ return HALF_LIFE.maxFeePpm;
44
+ const halvings = Math.floor(ageSecs / HALF_LIFE.halfLifeSecs);
45
+ if (halvings >= HALF_LIFE.zeroAfterSecs / HALF_LIFE.halfLifeSecs)
46
+ return 0;
47
+ const into = Math.floor(ageSecs) % HALF_LIFE.halfLifeSecs;
48
+ const hi = Math.floor(HALF_LIFE.maxFeePpm / 2 ** halvings);
49
+ const lo = Math.floor(HALF_LIFE.maxFeePpm / 2 ** (halvings + 1));
50
+ return hi - Math.floor(((hi - lo) * into) / HALF_LIFE.halfLifeSecs);
51
+ }
16
52
  /** The wSOL mint, which stands for native SOL on the bridge. */
17
53
  export const NATIVE_MINT = 'So11111111111111111111111111111111111111112';
18
54
  export const TOKEN_PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bordrless/shared",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Bordrless policy (the same math as crates/bordrless-core), program addresses and the API types shared by the server and the site. No Solana dependencies.",
6
6
  "license": "Apache-2.0",