@logswap/sdk 0.5.36 → 0.5.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logswap/sdk",
3
- "version": "0.5.36",
3
+ "version": "0.5.38",
4
4
  "description": "TypeScript SDK for Logswap, the liquidity primitive introducing the logarithmic invariant — typed wrappers over the CPoolManager / FPoolManager / Router / Lens surface.",
5
5
  "license": "GPL-2.0-or-later",
6
6
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  "clean": "rm -rf dist src/generated.ts",
27
27
  "test": "vitest run",
28
28
  "test:watch": "vitest",
29
- "prepublishOnly": "npm run build"
29
+ "prepublishOnly": "npm run codegen && npm run build"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "viem": "^2.0.0"
package/src/client.ts CHANGED
@@ -71,7 +71,14 @@ export const EXPECTED_VERSION = 3n;
71
71
  * passes it; when one lands, raise this constant in the same change, which is the discipline
72
72
  * `EXPECTED_VERSION` enforces mechanically and this one asks for.
73
73
  */
74
- export const EXPECTED_F_VERSION = 5n;
74
+ // 7 (2026-09-15): the reserves are the state and the marks derived, `lockStrike`, the dividend
75
+ // harvest, the relaunch (`dissolve(poolId, successor)`, `rollIn`), the reserve-named seed, the
76
+ // sponsor facet — decisions 025–031.
77
+ // 8 (2026-09-15): the relaunch is in place (decisions 032) — every share id carries the
78
+ // generation (`fPoolShareId(poolId, gen)`), `dissolve(poolId)`, `redeem`, `claim` / `dividendOf`
79
+ // / `rollIn` / `previewRollIn` take the generation, `Pool.gen` replaces `dissolved` and
80
+ // `successor`. A 7 deployment's share ids do not even resolve with this SDK.
81
+ export const EXPECTED_F_VERSION = 8n;
75
82
 
76
83
  export async function assertVersion(c: LogswapClient, expected = EXPECTED_VERSION): Promise<void> {
77
84
  const { cPoolManagerAbi } = await import("./generated.js");
package/src/fpool.ts CHANGED
@@ -23,8 +23,17 @@
23
23
  * `addresses.router`.
24
24
  */
25
25
 
26
- import { toFunctionSelector, type Address, type Hash, type Hex } from "viem";
27
- import { fPoolManagerAbi, logswapRouterAbi } from "./generated.js";
26
+ import { encodeFunctionData, toFunctionSelector, type Address, type Hash, type Hex } from "viem";
27
+ import { fPoolManagerAbi as fPoolPrimitiveAbi, fPoolSponsorAbi, logswapRouterAbi } from "./generated.js";
28
+
29
+ /**
30
+ * The F manager's ABI is the UNION of its two compiled halves (decisions 031): `FPoolManager`
31
+ * holds the primitive and forwards every other selector from its fallback to `FPoolSponsor` —
32
+ * the lever, `claim`, `dissolve`, the handover, the gates, the desk, the names — by delegatecall.
33
+ * One address from outside, so one ABI here; both halves carry the base's events and errors,
34
+ * and viem tolerates the duplicates.
35
+ */
36
+ export const fPoolManagerAbi = [...fPoolPrimitiveAbi, ...fPoolSponsorAbi] as const;
28
37
  import type { LogswapClient } from "./client.js";
29
38
 
30
39
  const WAD = 10n ** 18n;
@@ -70,12 +79,22 @@ export interface FPoolState {
70
79
  gateSwap: boolean;
71
80
  /** The block of the last lever / composition action: no swap runs in it. */
72
81
  restructureBlock: number;
73
- /** Whether harvest is bounded by θ ≤ θ₀ — income only, never the floor's backing. */
74
- feesOnly: boolean;
82
+ /**
83
+ * The strike is locked (decisions 027, formerly `feesOnly`): `harvest` resets the floor to θ₀
84
+ * and never past it, so only income can leave; and the pool cannot be dissolved while it bids.
85
+ * The pad's promise. Pairs with a locked seed (shares at 0xdead) — the class is the two bits.
86
+ */
87
+ lockStrike: boolean;
88
+ /** False before the first seed — and between a dissolve and the next one (decisions 032). */
75
89
  seeded: boolean;
76
- dissolved: boolean;
90
+ /**
91
+ * The live GENERATION (decisions 032). Every dissolve retires one into a frozen bundle its
92
+ * shares redeem ({@link fPoolRedeem}) and opens the next; the share id carries it
93
+ * ({@link fPoolShareId}), so generations `0 … gen-1` are retired claims and `gen` is the pool.
94
+ */
95
+ gen: number;
77
96
  totalSupply: bigint;
78
- /** Base holdings the state implies, `R_j = w_j·L·e^{−x_j}`. */
97
+ /** What the pool holds of each base — the STATE since decisions 026; the mark `x` is derived from it. */
79
98
  reserves: bigint[];
80
99
  }
81
100
 
@@ -97,9 +116,9 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
97
116
  quote: Address;
98
117
  phi: bigint;
99
118
  authority: Address;
100
- feesOnly: boolean;
119
+ lockStrike: boolean;
101
120
  seeded: boolean;
102
- dissolved: boolean;
121
+ gen: number;
103
122
  n: number;
104
123
  restructureBlock: number;
105
124
  gateMint: boolean;
@@ -136,11 +155,9 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
136
155
 
137
156
  const bases = legs.map((l) => l[0]);
138
157
  const weights = legs.map((l) => l[1]);
139
- const x = legs.map((l) => l[2]);
140
- // the ISOLATION SHADOW, not the derived reserve: what this pool actually holds of the token.
141
- // In a singleton the contract's balance backs many pools, so this is the only per-pool figure.
142
- const reserves = legs.map((l) => l[3]);
143
- const { quote, Q, L, phi, theta0, bigSigma, authority, pendingAuthority, operator, minBuffer, gateMint, gateSwap, restructureBlock, feesOnly, seeded, dissolved } = p;
158
+ const x = legs.map((l) => l[2]); // derived by the manager: x_j = ln(w_j·L / R_j) (decisions 026)
159
+ const reserves = legs.map((l) => l[3]); // the state
160
+ const { quote, Q, L, phi, theta0, bigSigma, authority, pendingAuthority, operator, minBuffer, gateMint, gateSwap, restructureBlock, lockStrike, seeded, gen } = p;
144
161
  const totalSupply = p.shares;
145
162
 
146
163
  return {
@@ -164,9 +181,9 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
164
181
  gateMint,
165
182
  gateSwap,
166
183
  restructureBlock: Number(restructureBlock),
167
- feesOnly,
184
+ lockStrike,
168
185
  seeded,
169
- dissolved,
186
+ gen: Number(gen),
170
187
  totalSupply,
171
188
  reserves,
172
189
  };
@@ -355,16 +372,101 @@ export async function fPoolZapOut(
355
372
  );
356
373
  }
357
374
 
375
+ /**
376
+ * Roll a retired generation's bundle into the pool's live one (decisions 025, 032), on the
377
+ * router under one lock: redeem `shares` of generation `gen` in place — the legs, the quote and
378
+ * any unpulled dividend come back — buy through the pool's own curve only what the live
379
+ * generation's ratio lacks, mint `dL` of it to `to`, and refund the rest in kind. Quote the
380
+ * bundle does not cover is pulled from the payer, at most `maxQuoteIn`; surplus base is
381
+ * refunded, never sold. Size `dL` with {@link fPoolPreviewRollIn}. The router must be the
382
+ * caller's ERC-6909 operator ({@link approveRouterForFPool}).
383
+ */
384
+ export async function fPoolRollIn(
385
+ c: LogswapClient,
386
+ a: {
387
+ poolId: Hex;
388
+ /** the retired generation whose shares roll */
389
+ gen: bigint | number;
390
+ shares: bigint;
391
+ dL: bigint;
392
+ maxQuoteIn?: bigint;
393
+ minShares?: bigint;
394
+ to?: Address;
395
+ account: Address;
396
+ deadline?: bigint;
397
+ },
398
+ ) {
399
+ const to = a.to ?? a.account;
400
+ return writeRouter(
401
+ c,
402
+ "rollIn",
403
+ [a.poolId, BigInt(a.gen), a.shares, a.dL, a.maxQuoteIn ?? 0n, a.minShares ?? 0n, to, a.deadline ?? defaultDeadline()],
404
+ a.account,
405
+ );
406
+ }
407
+
408
+ /**
409
+ * The roll `holder`'s `shares` of retired generation `gen` fund into the live one WITHOUT a
410
+ * trade: the largest `dL` the bundle's base covers on every leg, and the quote the bundle is
411
+ * short of (pass it as `maxQuoteIn`) or over (refunded — the up direction's distribution) at
412
+ * that `dL`. A leg the live generation admitted since makes `dL` zero: such a roll must buy, so
413
+ * size it below with `previewZapIn` in mind. Zero for the live generation, or before the next
414
+ * seed.
415
+ */
416
+ export async function fPoolPreviewRollIn(
417
+ c: LogswapClient,
418
+ poolId: Hex,
419
+ gen: bigint | number,
420
+ holder: Address,
421
+ shares: bigint,
422
+ ): Promise<{ dL: bigint; quoteShort: bigint; quoteExcess: bigint }> {
423
+ const { logswapLensAbi } = await import("./generated.js");
424
+ const [dL, quoteShort, quoteExcess] = (await c.public.readContract({
425
+ address: c.addresses.lens,
426
+ abi: logswapLensAbi,
427
+ functionName: "previewRollIn",
428
+ args: [poolId, BigInt(gen), holder, shares],
429
+ } as never)) as readonly [bigint, bigint, bigint];
430
+ return { dL, quoteShort, quoteExcess };
431
+ }
432
+
358
433
  // ─── the floor lever (authority only) ─────────────────────────────────────────
359
434
 
360
435
  /**
361
- * Remove quote, lifting θ — price-neutral, pro-rata, no base moves. Under `feesOnly` the lift
362
- * stops at θ₀, so the authority extracts income and never the proceeds backing the floor.
436
+ * Remove quote, lifting θ — price-neutral, no base moves, and since decisions 028 a DIVIDEND to
437
+ * every share: the amount goes into the pool's pot and each holder's slice is settled lazily on
438
+ * their next transfer, mint or burn, or pulled with {@link fPoolClaim}. The sponsor's own slice —
439
+ * its shares' and the locked seed's at 0xdead — is pushed to `to` at once. Under `lockStrike`
440
+ * the lift stops at θ₀ (income only); on every live pool it stops at `LEVER_FLOOR` (Q/L ≥ ln 1.25).
363
441
  */
364
442
  export async function fPoolHarvest(c: LogswapClient, a: { poolId: Hex; amount: bigint; to?: Address; account: Address }) {
365
443
  return writeFPool(c, a.poolId, "harvest", [a.amount, a.to ?? a.account], a.account);
366
444
  }
367
445
 
446
+ /**
447
+ * Pull one's settled dividend (decisions 028) on a generation's shares without moving them;
448
+ * anyone with shares. `gen` defaults to the live generation; a retired generation's harvests
449
+ * stay claimable under its own id (decisions 032).
450
+ */
451
+ export async function fPoolClaim(c: LogswapClient, a: { poolId: Hex; gen?: bigint | number; to?: Address; account: Address }) {
452
+ const gen = a.gen ?? (await fPoolGen(c, a.poolId));
453
+ return writeFPool(c, a.poolId, "claim", [BigInt(gen), a.to ?? a.account], a.account);
454
+ }
455
+
456
+ /** A holder's dividend as of now on generation `gen` (the live one by default) — banked plus pending on the current balance (decisions 028). */
457
+ export async function fPoolDividendOf(c: LogswapClient, poolId: Hex, holder: Address, gen?: bigint | number): Promise<bigint> {
458
+ const g = gen ?? (await fPoolGen(c, poolId));
459
+ return c.public.readContract({
460
+ address: c.addresses.fPoolManager!,
461
+ abi: fPoolManagerAbi,
462
+ functionName: "dividendOf",
463
+ args: [poolId, BigInt(g), holder],
464
+ } as never) as Promise<bigint>;
465
+ }
466
+
467
+ /** The lever's floor: `harvest` may not take Q/L under ln 1.25 — the floor stays ≥ 20% below the mark (decisions 025). */
468
+ export const LEVER_FLOOR = 223143551314209755n;
469
+
368
470
  /** Commit quote, deepening θ — the other sign of `harvest`, the same word as the C floor edit (decisions 011). The mark does not move: governance may write θ, never x. */
369
471
  export async function fPoolDeepen(c: LogswapClient, a: { poolId: Hex; amount: bigint; account: Address }) {
370
472
  return writeFPool(c, a.poolId, "deepen", [a.amount], a.account);
@@ -396,6 +498,19 @@ async function writeFPool(c: LogswapClient, poolId: Hex, functionName: string, a
396
498
  return wallet.writeContract(request as never);
397
499
  }
398
500
 
501
+ /** A manager write that is NOT keyed by a poolId — `multicall`. */
502
+ async function writeFPool0(c: LogswapClient, functionName: string, args: unknown[], account: Address) {
503
+ const wallet = requireWallet(c);
504
+ const { request } = await c.public.simulateContract({
505
+ address: c.addresses.fPoolManager!,
506
+ abi: fPoolManagerAbi,
507
+ functionName,
508
+ args,
509
+ account,
510
+ } as never);
511
+ return wallet.writeContract(request as never);
512
+ }
513
+
399
514
  async function writeRouter(
400
515
  c: LogswapClient,
401
516
  functionName: string,
@@ -494,9 +609,29 @@ export async function approveRouterForFPool(c: LogswapClient, account: Address)
494
609
  // than read from the chain: it is pure, and a client that has to call to learn an id cannot build
495
610
  // a multicall that uses it.
496
611
 
497
- /** The ERC-6909 id carrying a pool's shares. Bit 255 set. */
498
- export function fPoolShareId(poolId: Hex): bigint {
499
- return (1n << 255n) | (BigInt(poolId) >> 1n);
612
+ /**
613
+ * The ERC-6909 id carrying generation `gen` of a pool's shares (decisions 032): bit 255 set (the
614
+ * share flag), the pool id's top 239 bits, the generation in the low 16. Each generation's shares
615
+ * are their own asset — a retired one is a claim on its bundle, the live one on the pool.
616
+ */
617
+ export function fPoolShareId(poolId: Hex, gen: bigint | number = 0n): bigint {
618
+ return (1n << 255n) | ((BigInt(poolId) >> 17n) << 16n) | BigInt(gen);
619
+ }
620
+
621
+ /** The generation a share id carries — its low 16 bits. */
622
+ export function fPoolGenOfShareId(id: bigint): number {
623
+ return Number(id & 0xffffn);
624
+ }
625
+
626
+ /** The pool's live generation, one read. */
627
+ export async function fPoolGen(c: LogswapClient, poolId: Hex): Promise<number> {
628
+ const p = (await c.public.readContract({
629
+ address: c.addresses.fPoolManager!,
630
+ abi: fPoolManagerAbi,
631
+ functionName: "getPool",
632
+ args: [poolId],
633
+ } as never)) as { gen: number };
634
+ return Number(p.gen);
500
635
  }
501
636
 
502
637
  /** The ERC-6909 id carrying claims on a token. Bit 255 clear, so it can never meet a share id. */
@@ -504,15 +639,65 @@ export function fPoolClaimId(token: Address): bigint {
504
639
  return BigInt(token);
505
640
  }
506
641
 
507
- export async function fPoolShareBalance(c: LogswapClient, poolId: Hex, owner: Address): Promise<bigint> {
642
+ /** A holder's shares of generation `gen` — the live one by default. */
643
+ export async function fPoolShareBalance(c: LogswapClient, poolId: Hex, owner: Address, gen?: bigint | number): Promise<bigint> {
644
+ const g = gen ?? (await fPoolGen(c, poolId));
508
645
  return c.public.readContract({
509
646
  address: c.addresses.fPoolManager!,
510
647
  abi: fPoolManagerAbi,
511
648
  functionName: "balanceOf",
512
- args: [owner, fPoolShareId(poolId)],
649
+ args: [owner, fPoolShareId(poolId, g)],
513
650
  } as never) as Promise<bigint>;
514
651
  }
515
652
 
653
+ /** A retired generation's bundle (decisions 032): what is left to redeem — supply, quote, base per leg index. */
654
+ export async function fPoolBundleOf(
655
+ c: LogswapClient,
656
+ poolId: Hex,
657
+ gen: bigint | number,
658
+ ): Promise<{ supply: bigint; quote: bigint; base: bigint[] }> {
659
+ const [supply, quote, base] = (await c.public.readContract({
660
+ address: c.addresses.fPoolManager!,
661
+ abi: fPoolManagerAbi,
662
+ functionName: "bundleOf",
663
+ args: [poolId, BigInt(gen)],
664
+ } as never)) as readonly [bigint, bigint, readonly bigint[]];
665
+ return { supply, quote, base: [...base] };
666
+ }
667
+
668
+ /** A holder's claim on one retired generation, as the lens reports it: what `redeem` pays now. */
669
+ export interface FPoolClaimRow {
670
+ gen: number;
671
+ shares: bigint;
672
+ base: bigint[];
673
+ quote: bigint;
674
+ dividend: bigint;
675
+ }
676
+
677
+ /** A holder's claims on a pool's retired generations — one row per generation still held (decisions 032). */
678
+ export async function fPoolClaimsOf(c: LogswapClient, poolId: Hex, owner: Address): Promise<FPoolClaimRow[]> {
679
+ const { logswapLensAbi } = await import("./generated.js");
680
+ const rows = (await c.public.readContract({
681
+ address: c.addresses.lens,
682
+ abi: logswapLensAbi,
683
+ functionName: "fClaimsOf",
684
+ args: [poolId, owner],
685
+ } as never)) as readonly { gen: bigint; shares: bigint; base: readonly bigint[]; quote: bigint; dividend: bigint }[];
686
+ return rows.map((r) => ({ gen: Number(r.gen), shares: r.shares, base: [...r.base], quote: r.quote, dividend: r.dividend }));
687
+ }
688
+
689
+ /**
690
+ * Redeem `shares` of a RETIRED generation (decisions 032): its bundle, pro rata — the base per
691
+ * leg and the quote — plus the owner's settled dividend. Not `burn`: nothing can move a bundle's
692
+ * ratio, so there is no slippage to bound. The owner, or an operator of theirs.
693
+ */
694
+ export async function fPoolRedeem(
695
+ c: LogswapClient,
696
+ a: { poolId: Hex; gen: bigint | number; shares: bigint; owner?: Address; account: Address },
697
+ ) {
698
+ return writeFPool(c, a.poolId, "redeem", [BigInt(a.gen), a.owner ?? a.account, a.shares], a.account);
699
+ }
700
+
516
701
  export async function fPoolClaimBalance(c: LogswapClient, token: Address, owner: Address): Promise<bigint> {
517
702
  return c.public.readContract({
518
703
  address: c.addresses.fPoolManager!,
@@ -599,8 +784,8 @@ export interface FPoolCreateArgs {
599
784
  weights: bigint[];
600
785
  /** Fixed fee, WAD. The base-for-base fee is pinned at 2φ on-chain. */
601
786
  phi: bigint;
602
- /** true: the authority's harvest stops at the seed strike θ₀ (the launch posture). */
603
- feesOnly: boolean;
787
+ /** true: the strike is locked — harvest stops at θ₀ and the pool cannot be dissolved live (the pad's posture; decisions 027). */
788
+ lockStrike: boolean;
604
789
  authority: Address;
605
790
  /**
606
791
  * The differentiator, and nothing else. Identity is the key's hash, so one sponsor cannot run
@@ -640,7 +825,7 @@ export async function fPoolInitialize(c: LogswapClient, a: FPoolCreateArgs) {
640
825
  const sum = a.weights.reduce((x, y) => x + y, 0n);
641
826
  if (sum !== 10n ** 18n) throw new Error(`logswap: weights sum to ${sum}, expected 1e18`);
642
827
  const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
643
- const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, feesOnly: a.feesOnly, authority: a.authority, salt: a.salt ?? ZERO_SALT };
828
+ const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, lockStrike: a.lockStrike, authority: a.authority, salt: a.salt ?? ZERO_SALT };
644
829
  const wallet = requireWallet(c);
645
830
  const { request } = await c.public.simulateContract({
646
831
  address: c.addresses.fPoolManager!,
@@ -655,7 +840,7 @@ export async function fPoolInitialize(c: LogswapClient, a: FPoolCreateArgs) {
655
840
  /** The pool id is the key's hash — pure, so it can be read before or after creation. */
656
841
  export async function fPoolIdOf(c: LogswapClient, a: Omit<FPoolCreateArgs, "account">): Promise<Hex> {
657
842
  const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
658
- const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, feesOnly: a.feesOnly, authority: a.authority, salt: a.salt ?? ZERO_SALT };
843
+ const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, lockStrike: a.lockStrike, authority: a.authority, salt: a.salt ?? ZERO_SALT };
659
844
  return c.public.readContract({
660
845
  address: c.addresses.fPoolManager!,
661
846
  abi: fPoolManagerAbi,
@@ -665,22 +850,76 @@ export async function fPoolIdOf(c: LogswapClient, a: Omit<FPoolCreateArgs, "acco
665
850
  }
666
851
 
667
852
  /**
668
- * Arm the pool: deposit the full basket at marks `x0` plus `Q0` of quote. Authority-only on-chain
669
- * (an open seed lets anyone front-run the creator for the strike). `x0` must align with the
670
- * SORTED bases — use {@link sortFPoolLegs} on (bases, weights, x0) together when building forms.
853
+ * The reserve a leg holds at a mark: `R = Lj·e^{−x}`, WAD math. The seed names RESERVES since
854
+ * decisions 029 — the sponsor states holdings and the price follows, `p = Lj / R` — so this is
855
+ * the one conversion from a price to what the pool will hold, done here, on the way in. Float
856
+ * precision (~1e-16 relative) is what a seed needs: any reserve is a legal seed, and the mark
857
+ * the pool derives from it is the one the sponsor meant to within that.
858
+ */
859
+ export function fPoolReserveAt(Lj: bigint, x: bigint): bigint {
860
+ const e = Math.exp(-Number(x) / 1e18);
861
+ return (Lj * BigInt(Math.round(e * 1e18))) / 10n ** 18n;
862
+ }
863
+
864
+ /**
865
+ * Arm the pool: deposit `r0[j]` of each base plus `Q0` of quote against exposure `L0`. Pass either
866
+ * the reserves outright (`r0`) or the marks the pool should open at (`x0`, converted here with
867
+ * {@link fPoolReserveAt} against the key's weights). Authority-only on-chain (an open seed lets
868
+ * anyone front-run the creator for the strike). Arrays align with the SORTED bases — use
869
+ * {@link sortFPoolLegs} on (bases, weights, x0) together when building forms.
671
870
  */
672
871
  export async function fPoolSeed(
673
872
  c: LogswapClient,
674
- a: { poolId: Hex; L0: bigint; x0: bigint[]; Q0: bigint; account: Address },
873
+ a: { poolId: Hex; L0: bigint; Q0: bigint; account: Address } & ({ r0: bigint[] } | { x0: bigint[]; weights: bigint[] }),
675
874
  ) {
676
- return writeFPool(c, a.poolId, "seed", [a.L0, a.x0, a.Q0], a.account);
875
+ const r0 = "r0" in a ? a.r0 : a.x0.map((x, j) => fPoolReserveAt((a.weights[j]! * a.L0) / 10n ** 18n, x));
876
+ return writeFPool(c, a.poolId, "seed", [a.L0, r0, a.Q0], a.account);
677
877
  }
678
878
 
679
- /** End of life. Gated on-chain to the authority and to Q at dust (the raise is not abortable). */
879
+ /**
880
+ * Retire the live generation (decisions 025, 032). Allowed at Q ≤ L/1e9 on every pool — the
881
+ * market filled the ask — and on a pool that is not strike-locked at any Q. The live (S, Q, R_j)
882
+ * become a frozen bundle every share of this generation redeems ({@link fPoolRedeem}) or rolls
883
+ * ({@link fPoolRollIn}), forever; the pool is unseeded until the next {@link fPoolSeed}, which
884
+ * opens generation `gen + 1` under the same id, gates, lists and promises.
885
+ */
680
886
  export async function fPoolDissolve(c: LogswapClient, a: { poolId: Hex; account: Address }) {
681
887
  return writeFPool(c, a.poolId, "dissolve", [], a.account);
682
888
  }
683
889
 
890
+ /**
891
+ * The sponsor's relaunch as ONE transaction (decisions 025, 032): `dissolve` the live generation,
892
+ * `redeem` the sponsor's own shares of it (when `shares` > 0), `seed` the next generation with
893
+ * what came back — the manager's `multicall`, the sponsor as sender throughout, the same pool.
894
+ * Gates, allowlist, operator, `minBuffer` and names carry over; anything else goes in `extra`,
895
+ * pre-encoded against {@link fPoolManagerAbi}. The seed names reserves (decisions 029): `r0`
896
+ * outright, or `x0` marks against the key's `weights`.
897
+ */
898
+ export async function fPoolRelaunch(
899
+ c: LogswapClient,
900
+ a: {
901
+ poolId: Hex;
902
+ /** the sponsor's shares of the retiring generation to redeem inside the batch — its whole balance, usually; 0 to keep the claim */
903
+ shares: bigint;
904
+ L0: bigint;
905
+ Q0: bigint;
906
+ extra?: Hex[];
907
+ account: Address;
908
+ } & ({ r0: bigint[] } | { x0: bigint[]; weights: bigint[] }),
909
+ ) {
910
+ const gen = await fPoolGen(c, a.poolId);
911
+ const r0 = "r0" in a ? a.r0 : a.x0.map((x, j) => fPoolReserveAt((a.weights[j]! * a.L0) / WAD, x));
912
+ const enc = (functionName: string, args: unknown[]) =>
913
+ encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
914
+ const calls: Hex[] = [
915
+ enc("dissolve", [a.poolId]),
916
+ ...(a.shares > 0n ? [enc("redeem", [a.poolId, BigInt(gen), a.account, a.shares])] : []),
917
+ enc("seed", [a.poolId, a.L0, r0, a.Q0]),
918
+ ...(a.extra ?? []),
919
+ ];
920
+ return writeFPool0(c, "multicall", [calls], a.account);
921
+ }
922
+
684
923
  /**
685
924
  * Propose the next authority — step one of two (contracts `2aa6b7b`, decisions 020). Nothing moves
686
925
  * until `next` accepts; proposing again (the zero address included) cancels a pending proposal.
@@ -697,9 +936,6 @@ export async function fPoolAcceptAuthority(c: LogswapClient, a: { poolId: Hex; a
697
936
 
698
937
  // ─── the sponsor's controls and the desk (contracts bd3dd87, decisions 021) ───────────────
699
938
 
700
- /** `setLegL`'s "keep the stored mark" sentinel — the only legal `x` for a live leg. */
701
- export const NO_X = -(2n ** 255n);
702
-
703
939
  /** Turn the mint / swap gates on or off (authority). `burn` is never gated. */
704
940
  export async function fPoolSetGates(
705
941
  c: LogswapClient,
@@ -773,22 +1009,24 @@ export async function fPoolRaiseMinBuffer(c: LogswapClient, a: { poolId: Hex; mi
773
1009
 
774
1010
  /**
775
1011
  * Set leg `j`'s liquidity (operator, sole LP only): grow, shrink, retire (`newLj = 0`), or re-admit
776
- * a retired leg at a named mark `x`. For a live leg leave `x` undefined — its mark is the market's.
777
- * The base moves single-sidedly through the lock; shares adjust to hold the share value.
1012
+ * a retired leg holding `r` of the base (decisions 029 — the price follows, `p = newLj / r`; use
1013
+ * {@link fPoolReserveAt} from a mark). For a live leg leave `r` undefined — its price is the
1014
+ * market's, the reserve scales with the liquidity. Base moves single-sidedly through the lock;
1015
+ * shares adjust to hold the share value.
778
1016
  */
779
1017
  export async function fPoolSetLegL(
780
1018
  c: LogswapClient,
781
- a: { poolId: Hex; j: number; newLj: bigint; x?: bigint; account: Address },
1019
+ a: { poolId: Hex; j: number; newLj: bigint; r?: bigint; account: Address },
782
1020
  ) {
783
- return writeFPool(c, a.poolId, "setLegL", [BigInt(a.j), a.newLj, a.x ?? NO_X], a.account);
1021
+ return writeFPool(c, a.poolId, "setLegL", [BigInt(a.j), a.newLj, a.r ?? 0n], a.account);
784
1022
  }
785
1023
 
786
- /** Admit a base the pool has never held, at a named mark, with liquidity `Lj` (operator, sole LP only). */
1024
+ /** Admit a base the pool has never held: `R` of it against liquidity `Lj`, so at `p = Lj / R` (operator, sole LP only). */
787
1025
  export async function fPoolAdmitLeg(
788
1026
  c: LogswapClient,
789
- a: { poolId: Hex; base: Address; Lj: bigint; x: bigint; account: Address },
1027
+ a: { poolId: Hex; base: Address; Lj: bigint; R: bigint; account: Address },
790
1028
  ) {
791
- return writeFPool(c, a.poolId, "admitLeg", [a.base, a.Lj, a.x], a.account);
1029
+ return writeFPool(c, a.poolId, "admitLeg", [a.base, a.Lj, a.R], a.account);
792
1030
  }
793
1031
 
794
1032
  /**
@@ -798,14 +1036,15 @@ export async function fPoolAdmitLeg(
798
1036
  */
799
1037
  export async function fPoolTransferShares(
800
1038
  c: LogswapClient,
801
- a: { poolId: Hex; to: Address; shares: bigint; account: Address },
1039
+ a: { poolId: Hex; to: Address; shares: bigint; account: Address; gen?: bigint | number },
802
1040
  ) {
803
1041
  const wallet = requireWallet(c);
1042
+ const gen = a.gen ?? (await fPoolGen(c, a.poolId));
804
1043
  const { request } = await c.public.simulateContract({
805
1044
  address: c.addresses.fPoolManager!,
806
1045
  abi: fPoolManagerAbi,
807
1046
  functionName: "transfer",
808
- args: [a.to, fPoolShareId(a.poolId), a.shares],
1047
+ args: [a.to, fPoolShareId(a.poolId, gen), a.shares],
809
1048
  account: a.account,
810
1049
  } as never);
811
1050
  return wallet.writeContract(request as never);
@@ -819,7 +1058,7 @@ export interface DiscoveredFPool {
819
1058
  bases: Address[];
820
1059
  weights: bigint[];
821
1060
  phi: bigint;
822
- feesOnly: boolean;
1061
+ lockStrike: boolean;
823
1062
  authority: Address;
824
1063
  /** The block the pool was initialized in — its age, for annualising realized income. */
825
1064
  block: bigint;
@@ -838,7 +1077,7 @@ export interface DiscoveredFPool {
838
1077
 
839
1078
  /**
840
1079
  * Every F pool ever created, from `Initialize` logs alone. Possible only because the event
841
- * carries the full key preimage (bases, weights, phi, feesOnly) — the poolId is a hash and could
1080
+ * carries the full key preimage (bases, weights, phi, lockStrike) — the poolId is a hash and could
842
1081
  * never be inverted; contracts PR #17 exists for exactly this call.
843
1082
  */
844
1083
  export async function discoverFPools(
@@ -869,7 +1108,7 @@ export async function discoverFPools(
869
1108
  return logs.map((l) => {
870
1109
  const a = l.args as {
871
1110
  poolId: Hex; quote: Address; authority: Address; bases: readonly Address[];
872
- weights: readonly bigint[]; phi: bigint; feesOnly: boolean;
1111
+ weights: readonly bigint[]; phi: bigint; lockStrike: boolean;
873
1112
  };
874
1113
  const q0 = q0Of.get(a.poolId.toLowerCase());
875
1114
  return {
@@ -878,7 +1117,7 @@ export async function discoverFPools(
878
1117
  bases: [...a.bases],
879
1118
  weights: [...a.weights],
880
1119
  phi: a.phi,
881
- feesOnly: a.feesOnly,
1120
+ lockStrike: a.lockStrike,
882
1121
  authority: a.authority,
883
1122
  block: l.blockNumber ?? 0n,
884
1123
  seeded: q0 !== undefined,
@@ -897,12 +1136,12 @@ export async function discoverFPools(
897
1136
  export async function fPoolShareHolders(
898
1137
  c: LogswapClient,
899
1138
  poolIdHex: Hex,
900
- opts: { fromBlock?: bigint } = {},
1139
+ opts: { fromBlock?: bigint; gen?: bigint | number } = {},
901
1140
  ): Promise<Array<{ holder: Address; shares: bigint }>> {
902
1141
  type Ev = Extract<(typeof fPoolManagerAbi)[number], { type: "event"; name: "Transfer" }>;
903
1142
  const ev = fPoolManagerAbi.find((x): x is Ev => x.type === "event" && x.name === "Transfer");
904
1143
  if (!ev) throw new Error("logswap: 6909 Transfer event missing from the generated ABI");
905
- const id = fPoolShareId(poolIdHex);
1144
+ const id = fPoolShareId(poolIdHex, opts.gen ?? (await fPoolGen(c, poolIdHex)));
906
1145
  const logs = await c.public.getLogs({
907
1146
  address: c.addresses.fPoolManager!,
908
1147
  event: ev,
@@ -922,7 +1161,7 @@ export async function fPoolShareHolders(
922
1161
  .sort((a, b) => (b.shares > a.shares ? 1 : -1));
923
1162
  }
924
1163
 
925
- /** Every F pool's state in one lens call (the raw manager struct: quote, phi, authority, feesOnly, seeded, dissolved, n, Q, L, shares, theta0, bigSigma, leverTheta). */
1164
+ /** Every F pool's state in one lens call (the raw manager struct: quote, phi, authority, lockStrike, seeded, gen, n, Q, L, shares, theta0, bigSigma, leverTheta). */
926
1165
  export async function getFPoolsRaw(c: LogswapClient, poolIds: Hex[]): Promise<readonly unknown[]> {
927
1166
  const { logswapLensAbi } = await import("./generated.js");
928
1167
  return (await c.public.readContract({ address: c.addresses.lens, abi: logswapLensAbi, functionName: "getFPools", args: [poolIds] } as never)) as readonly unknown[];