@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/dist/client.d.ts +1 -1
- package/dist/client.js +8 -1
- package/dist/fpool.d.ts +3888 -27
- package/dist/fpool.js +198 -35
- package/dist/generated.d.ts +2078 -292
- package/dist/generated.js +1479 -193
- package/package.json +2 -2
- package/src/client.ts +8 -1
- package/src/fpool.ts +290 -51
- package/src/generated.ts +1481 -193
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logswap/sdk",
|
|
3
|
-
"version": "0.5.
|
|
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
|
-
|
|
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
|
-
/**
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
119
|
+
lockStrike: boolean;
|
|
101
120
|
seeded: boolean;
|
|
102
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
184
|
+
lockStrike,
|
|
168
185
|
seeded,
|
|
169
|
-
|
|
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,
|
|
362
|
-
*
|
|
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
|
-
/**
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
|
|
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
|
|
603
|
-
|
|
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,
|
|
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,
|
|
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
|
-
*
|
|
669
|
-
*
|
|
670
|
-
*
|
|
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;
|
|
873
|
+
a: { poolId: Hex; L0: bigint; Q0: bigint; account: Address } & ({ r0: bigint[] } | { x0: bigint[]; weights: bigint[] }),
|
|
675
874
|
) {
|
|
676
|
-
|
|
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
|
-
/**
|
|
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
|
|
777
|
-
*
|
|
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;
|
|
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.
|
|
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
|
|
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;
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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;
|
|
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
|
-
|
|
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,
|
|
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[];
|