@logswap/sdk 0.5.39 → 0.5.41
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 +11 -1
- package/dist/fpool.d.ts +208 -349
- package/dist/fpool.js +125 -58
- package/dist/generated.d.ts +114 -282
- package/dist/generated.js +93 -219
- package/package.json +1 -1
- package/src/client.ts +11 -1
- package/src/fpool.ts +159 -92
- package/src/generated.ts +93 -219
package/src/fpool.ts
CHANGED
|
@@ -63,28 +63,27 @@ export interface FPoolState {
|
|
|
63
63
|
compositeX: bigint;
|
|
64
64
|
/** Σ = ∫ Σ_j w_j (dx_j)², accrued even though the fee is fixed — the kernel stays calculable. */
|
|
65
65
|
bigSigma: bigint;
|
|
66
|
-
/**
|
|
66
|
+
/** The cold key (decisions 037): seeds the pool, hands it over, names it, gates it (private). A creator, a treasury, a governance contract. */
|
|
67
67
|
authority: Address;
|
|
68
68
|
/** The proposed next authority, until it accepts — zero when nothing is pending (decisions 020). */
|
|
69
69
|
pendingAuthority: Address;
|
|
70
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* The hot key that RUNS the pool (decisions 037): `harvest`, `dissolve`, `setLegs`. Zero means
|
|
72
|
+
* the authority itself — see {@link fPoolOperatorOf}. Appointed two-step on a private pool.
|
|
73
|
+
*/
|
|
71
74
|
operator: Address;
|
|
75
|
+
/** The proposed next operator, until it accepts — zero when nothing is pending. */
|
|
76
|
+
pendingOperator: Address;
|
|
72
77
|
/** The sponsor's own label for this pool, mutable and never identity. "" when unnamed. */
|
|
73
78
|
name: string;
|
|
74
|
-
/** `harvest` may not take Q/L under this (WAD log-distance); raise-only (decisions 021). */
|
|
75
|
-
minBuffer: bigint;
|
|
76
79
|
/** Only `allowed` accounts may receive minted shares. */
|
|
77
80
|
gateMint: boolean;
|
|
78
81
|
/** Only `allowed` takers may swap. */
|
|
79
82
|
gateSwap: boolean;
|
|
80
|
-
/** The block of the last
|
|
83
|
+
/** The block of the last harvest / composition action: no swap runs in it. */
|
|
81
84
|
restructureBlock: number;
|
|
82
|
-
/**
|
|
83
|
-
|
|
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;
|
|
85
|
+
/** The rung — see {@link FPoolKind}. */
|
|
86
|
+
kind: FPoolKind;
|
|
88
87
|
/** False before the first seed — and between a dissolve and the next one (decisions 032). */
|
|
89
88
|
seeded: boolean;
|
|
90
89
|
/**
|
|
@@ -116,7 +115,7 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
116
115
|
quote: Address;
|
|
117
116
|
phi: bigint;
|
|
118
117
|
authority: Address;
|
|
119
|
-
|
|
118
|
+
kind: number;
|
|
120
119
|
seeded: boolean;
|
|
121
120
|
gen: number;
|
|
122
121
|
n: number;
|
|
@@ -128,10 +127,10 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
128
127
|
shares: bigint;
|
|
129
128
|
theta0: bigint;
|
|
130
129
|
bigSigma: bigint;
|
|
131
|
-
|
|
130
|
+
harvestedTheta: bigint;
|
|
132
131
|
pendingAuthority: Address;
|
|
133
132
|
operator: Address;
|
|
134
|
-
|
|
133
|
+
pendingOperator: Address;
|
|
135
134
|
incomeTaken: bigint;
|
|
136
135
|
name: Hex;
|
|
137
136
|
}>("getPool", [poolId]);
|
|
@@ -157,7 +156,7 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
157
156
|
const weights = legs.map((l) => l[1]);
|
|
158
157
|
const x = legs.map((l) => l[2]); // derived by the manager: x_j = ln(w_j·L / R_j) (decisions 026)
|
|
159
158
|
const reserves = legs.map((l) => l[3]); // the state
|
|
160
|
-
const { quote, Q, L, phi, theta0, bigSigma, authority, pendingAuthority, operator,
|
|
159
|
+
const { quote, Q, L, phi, theta0, bigSigma, authority, pendingAuthority, operator, pendingOperator, gateMint, gateSwap, restructureBlock, seeded, gen } = p;
|
|
161
160
|
const totalSupply = p.shares;
|
|
162
161
|
|
|
163
162
|
return {
|
|
@@ -176,12 +175,12 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
176
175
|
authority,
|
|
177
176
|
pendingAuthority,
|
|
178
177
|
operator,
|
|
178
|
+
pendingOperator,
|
|
179
179
|
name: bytes32ToText(p.name),
|
|
180
|
-
minBuffer,
|
|
181
180
|
gateMint,
|
|
182
181
|
gateSwap,
|
|
183
182
|
restructureBlock: Number(restructureBlock),
|
|
184
|
-
|
|
183
|
+
kind: fPoolKindOf(p.kind),
|
|
185
184
|
seeded,
|
|
186
185
|
gen: Number(gen),
|
|
187
186
|
totalSupply,
|
|
@@ -189,7 +188,27 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
189
188
|
};
|
|
190
189
|
}
|
|
191
190
|
|
|
192
|
-
/**
|
|
191
|
+
/**
|
|
192
|
+
* The kind of pool (decisions 036, 038). `public`: the protocol's own pool — born with the fee
|
|
193
|
+
* collector as its authority (which may hand it to a governance contract); no gates, no operator.
|
|
194
|
+
* `private`, the desk: every entry — gates, a list, an operator that reshapes the legs between
|
|
195
|
+
* generations, an unbounded harvest, the seed kept. `pol`, a treasury's pool or a basket: no
|
|
196
|
+
* gates, no operator — composition and access fixed at birth; harvest unbounded, seed the
|
|
197
|
+
* seeder's. `pad`, a launch: a POL whose harvest never lifts the floor above the seed floor and
|
|
198
|
+
* whose seed the contract minted to 0xdead — one asset, open to all. private → pol → pad is the
|
|
199
|
+
* ladder of rigidity; public is a pol held by the protocol.
|
|
200
|
+
*/
|
|
201
|
+
export type FPoolKind = "public" | "private" | "pol" | "pad";
|
|
202
|
+
/** The key's enum values. */
|
|
203
|
+
export const F_KIND = { public: 0, private: 1, pol: 2, pad: 3 } as const;
|
|
204
|
+
const F_KIND_NAMES: readonly FPoolKind[] = ["public", "private", "pol", "pad"];
|
|
205
|
+
export function fPoolKindOf(n: number | bigint): FPoolKind {
|
|
206
|
+
const k = F_KIND_NAMES[Number(n)];
|
|
207
|
+
if (!k) throw new Error(`logswap: unknown pool kind ${n}`);
|
|
208
|
+
return k;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Which of the two products a pool is configured as — a pad is a launch, anything else a basket/pool. A UI has to choose what to render. */
|
|
193
212
|
export type FPoolShape = "launch" | "basket";
|
|
194
213
|
|
|
195
214
|
export interface FPoolDescription {
|
|
@@ -218,7 +237,7 @@ export async function describeFPool(c: LogswapClient, s: FPoolState): Promise<FP
|
|
|
218
237
|
? await Promise.all([rd<bigint>("feePerL"), rd<bigint>("edgePerL")])
|
|
219
238
|
: [0n, 0n];
|
|
220
239
|
return {
|
|
221
|
-
shape: s.
|
|
240
|
+
shape: s.kind === "pad" ? "launch" : "basket", // the kind, not n = 1 (decisions 036)
|
|
222
241
|
legs: s.bases.length,
|
|
223
242
|
atFloor: s.Q === 0n,
|
|
224
243
|
feePerL,
|
|
@@ -436,8 +455,9 @@ export async function fPoolPreviewRollIn(
|
|
|
436
455
|
* Remove quote, lifting θ — price-neutral, no base moves, and since decisions 028 a DIVIDEND to
|
|
437
456
|
* every share: the amount goes into the pool's pot and each holder's slice is settled lazily on
|
|
438
457
|
* 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.
|
|
440
|
-
* the lift stops at θ₀ (income only);
|
|
458
|
+
* its shares' and the locked seed's at 0xdead — is pushed to `to` at once. On a pad
|
|
459
|
+
* the lift stops at the seed floor θ₀ (income only); nothing else bounds it (decisions 035) — an
|
|
460
|
+
* unlocked pool's authority may take Q to zero.
|
|
441
461
|
*/
|
|
442
462
|
export async function fPoolHarvest(c: LogswapClient, a: { poolId: Hex; amount: bigint; to?: Address; account: Address }) {
|
|
443
463
|
return writeFPool(c, a.poolId, "harvest", [a.amount, a.to ?? a.account], a.account);
|
|
@@ -464,14 +484,6 @@ export async function fPoolDividendOf(c: LogswapClient, poolId: Hex, holder: Add
|
|
|
464
484
|
} as never) as Promise<bigint>;
|
|
465
485
|
}
|
|
466
486
|
|
|
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
|
-
|
|
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. */
|
|
471
|
-
export async function fPoolDeepen(c: LogswapClient, a: { poolId: Hex; amount: bigint; account: Address }) {
|
|
472
|
-
return writeFPool(c, a.poolId, "deepen", [a.amount], a.account);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
487
|
// ─── internals ────────────────────────────────────────────────────────────────
|
|
476
488
|
|
|
477
489
|
function defaultDeadline(): bigint {
|
|
@@ -784,8 +796,8 @@ export interface FPoolCreateArgs {
|
|
|
784
796
|
weights: bigint[];
|
|
785
797
|
/** Fixed fee, WAD. The base-for-base fee is pinned at 2φ on-chain. */
|
|
786
798
|
phi: bigint;
|
|
787
|
-
/**
|
|
788
|
-
|
|
799
|
+
/** The rung (decisions 036) — see {@link FPoolKind}. In the key: the pool's identity, never changed after. */
|
|
800
|
+
kind: FPoolKind;
|
|
789
801
|
authority: Address;
|
|
790
802
|
/**
|
|
791
803
|
* The differentiator, and nothing else. Identity is the key's hash, so one sponsor cannot run
|
|
@@ -825,7 +837,7 @@ export async function fPoolInitialize(c: LogswapClient, a: FPoolCreateArgs) {
|
|
|
825
837
|
const sum = a.weights.reduce((x, y) => x + y, 0n);
|
|
826
838
|
if (sum !== 10n ** 18n) throw new Error(`logswap: weights sum to ${sum}, expected 1e18`);
|
|
827
839
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
|
|
828
|
-
const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi,
|
|
840
|
+
const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, kind: F_KIND[a.kind], authority: a.authority, salt: a.salt ?? ZERO_SALT };
|
|
829
841
|
const wallet = requireWallet(c);
|
|
830
842
|
const { request } = await c.public.simulateContract({
|
|
831
843
|
address: c.addresses.fPoolManager!,
|
|
@@ -840,7 +852,7 @@ export async function fPoolInitialize(c: LogswapClient, a: FPoolCreateArgs) {
|
|
|
840
852
|
/** The pool id is the key's hash — pure, so it can be read before or after creation. */
|
|
841
853
|
export async function fPoolIdOf(c: LogswapClient, a: Omit<FPoolCreateArgs, "account">): Promise<Hex> {
|
|
842
854
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
|
|
843
|
-
const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi,
|
|
855
|
+
const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, kind: F_KIND[a.kind], authority: a.authority, salt: a.salt ?? ZERO_SALT };
|
|
844
856
|
return c.public.readContract({
|
|
845
857
|
address: c.addresses.fPoolManager!,
|
|
846
858
|
abi: fPoolManagerAbi,
|
|
@@ -877,64 +889,121 @@ export async function fPoolSeed(
|
|
|
877
889
|
}
|
|
878
890
|
|
|
879
891
|
/**
|
|
880
|
-
* Retire the live generation (decisions 025, 032)
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
* ({@link
|
|
884
|
-
*
|
|
892
|
+
* Retire the live generation (decisions 025, 032, 035) — the raw entry. Allowed only at an empty
|
|
893
|
+
* bid, Q ≤ L/1e9, on every pool: the market took it, or the authority harvested it out first
|
|
894
|
+
* ({@link fPoolRetire} does both in one transaction). The live (S, Q, R_j) become a frozen bundle
|
|
895
|
+
* every share of this generation redeems ({@link fPoolRedeem}) or rolls ({@link fPoolRollIn}),
|
|
896
|
+
* forever; the pool is unseeded until the next {@link fPoolSeed}, which opens generation
|
|
897
|
+
* `gen + 1` under the same id, gates and lists.
|
|
885
898
|
*/
|
|
886
899
|
export async function fPoolDissolve(c: LogswapClient, a: { poolId: Hex; account: Address }) {
|
|
887
900
|
return writeFPool(c, a.poolId, "dissolve", [], a.account);
|
|
888
901
|
}
|
|
889
902
|
|
|
890
903
|
/**
|
|
891
|
-
*
|
|
892
|
-
*
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
|
|
896
|
-
|
|
904
|
+
* Retire the live generation as ONE transaction: the harvests that empty the bid
|
|
905
|
+
* ({@link fPoolEmptyingHarvests} — a dividend to every share; none when it is already empty),
|
|
906
|
+
* then `dissolve` — the manager's `multicall`, the authority as sender. What a sponsor's
|
|
907
|
+
* "dissolve" button should call. Throws before sending on a pad that still bids.
|
|
908
|
+
*/
|
|
909
|
+
export async function fPoolRetire(c: LogswapClient, a: { poolId: Hex; account: Address }) {
|
|
910
|
+
const harvests = await fPoolEmptyingHarvests(c, a.poolId);
|
|
911
|
+
if (harvests.length === 0) return fPoolDissolve(c, a);
|
|
912
|
+
const enc = (functionName: string, args: unknown[]) =>
|
|
913
|
+
encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
|
|
914
|
+
const calls: Hex[] = [...harvests.map((amount) => enc("harvest", [a.poolId, amount, a.account])), enc("dissolve", [a.poolId])];
|
|
915
|
+
return writeFPool0(c, "multicall", [calls], a.account);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* The harvests that empty an unlocked pool's bid, in order — what a relaunch sends before its
|
|
920
|
+
* `dissolve` (decisions 035: a dissolve needs an empty bid on every pool). One `harvest(Q)` when a
|
|
921
|
+
* collector is set. With none, the cut on income STAYS in the pool as LP quote (decisions 002)
|
|
922
|
+
* and reads as taken income, so a second harvest — capital, no cut — takes the rest; the
|
|
923
|
+
* contract's own arithmetic, replayed. `[]` when the bid is already empty. Throws on a
|
|
924
|
+
* pad that still bids (its bid empties only as the market sells it down), and in
|
|
925
|
+
* the one shape two harvests cannot empty (no collector, income above Q — harvest by hand).
|
|
926
|
+
*/
|
|
927
|
+
export async function fPoolEmptyingHarvests(c: LogswapClient, poolId: Hex): Promise<bigint[]> {
|
|
928
|
+
const rd = <T>(functionName: string, args: readonly unknown[] = []) =>
|
|
929
|
+
c.public.readContract({ address: c.addresses.fPoolManager!, abi: fPoolManagerAbi, functionName, args } as never) as Promise<T>;
|
|
930
|
+
const [p, feePerL, fee, exempt, collector] = await Promise.all([
|
|
931
|
+
rd<{ Q: bigint; L: bigint; kind: number; incomeTaken: bigint }>("getPool", [poolId]),
|
|
932
|
+
rd<bigint>("feePerL", [poolId]),
|
|
933
|
+
rd<bigint>("HARVEST_FEE"),
|
|
934
|
+
rd<boolean>("harvestFeeExempt", [poolId]),
|
|
935
|
+
rd<Address>("feeCollector"),
|
|
936
|
+
]);
|
|
937
|
+
if (p.Q === 0n) return [];
|
|
938
|
+
if (p.kind === F_KIND.pad) throw new Error("logswap: a pad is retired only once its bid is empty (decisions 035)");
|
|
939
|
+
const earned = feePerL > 0n ? (feePerL * p.L) / WAD : 0n;
|
|
940
|
+
let taken = p.incomeTaken;
|
|
941
|
+
let q = p.Q;
|
|
942
|
+
const amounts: bigint[] = [];
|
|
943
|
+
for (let i = 0; i < 2 && q > 0n; i++) {
|
|
944
|
+
const amount = q;
|
|
945
|
+
const left = earned > taken ? earned - taken : 0n;
|
|
946
|
+
const taxable = amount < left ? amount : left;
|
|
947
|
+
taken += taxable;
|
|
948
|
+
const pFee = exempt ? 0n : (taxable * fee) / WAD;
|
|
949
|
+
const leaving = pFee !== 0n && collector !== ZERO_ADDRESS ? amount : amount - pFee;
|
|
950
|
+
q -= leaving;
|
|
951
|
+
amounts.push(amount);
|
|
952
|
+
}
|
|
953
|
+
if (q > 0n) throw new Error("logswap: the bid does not empty in two harvests (no collector, income above Q) — harvest by hand first");
|
|
954
|
+
return amounts;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* The relaunch as ONE transaction (decisions 025, 032, 035, 037): `harvest` the bid out — a
|
|
959
|
+
* dividend to every share, {@link fPoolEmptyingHarvests} — `dissolve` the live generation at the
|
|
960
|
+
* empty bid, `redeem` the caller's own shares of it (when `shares` > 0), optionally `setLegs` the
|
|
961
|
+
* next generation's composition (`legs`: a private pool's reshape — retire, admit, reweight), and
|
|
962
|
+
* `seed` it with what came back — the manager's `multicall`, one sender throughout, the same
|
|
963
|
+
* pool. The operator's when one is appointed and the seed's inventory is its own; by default the
|
|
964
|
+
* authority's. Gates, allowlist and names carry over; anything else goes in `extra`, pre-encoded
|
|
965
|
+
* against {@link fPoolManagerAbi}. The seed names reserves (decisions 029): `r0` outright — one
|
|
966
|
+
* per leg of the NEXT table, 0 for a retired leg — or `x0` marks against the next table's
|
|
967
|
+
* `weights`.
|
|
897
968
|
*/
|
|
898
969
|
export async function fPoolRelaunch(
|
|
899
970
|
c: LogswapClient,
|
|
900
971
|
a: {
|
|
901
972
|
poolId: Hex;
|
|
902
|
-
/** the
|
|
973
|
+
/** the caller's shares of the retiring generation to redeem inside the batch — its whole balance, usually; 0 to keep the claim */
|
|
903
974
|
shares: bigint;
|
|
904
975
|
L0: bigint;
|
|
905
976
|
Q0: bigint;
|
|
977
|
+
/** the next generation's composition (decisions 037): one weight per existing leg (0 retires it), then one per new base */
|
|
978
|
+
legs?: { weights: bigint[]; newBases?: Address[] };
|
|
906
979
|
extra?: Hex[];
|
|
907
980
|
account: Address;
|
|
908
981
|
} & ({ r0: bigint[] } | { x0: bigint[]; weights: bigint[] }),
|
|
909
982
|
) {
|
|
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));
|
|
983
|
+
const [gen, harvests] = await Promise.all([fPoolGen(c, a.poolId), fPoolEmptyingHarvests(c, a.poolId)]);
|
|
984
|
+
const r0 = "r0" in a ? a.r0 : a.x0.map((x, j) => (a.weights[j]! === 0n ? 0n : fPoolReserveAt((a.weights[j]! * a.L0) / WAD, x)));
|
|
912
985
|
const enc = (functionName: string, args: unknown[]) =>
|
|
913
986
|
encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
|
|
914
987
|
const calls: Hex[] = [
|
|
988
|
+
...harvests.map((amount) => enc("harvest", [a.poolId, amount, a.account])),
|
|
915
989
|
enc("dissolve", [a.poolId]),
|
|
916
990
|
...(a.shares > 0n ? [enc("redeem", [a.poolId, BigInt(gen), a.account, a.shares])] : []),
|
|
991
|
+
...(a.legs ? [enc("setLegs", [a.poolId, a.legs.weights, a.legs.newBases ?? []])] : []),
|
|
917
992
|
enc("seed", [a.poolId, a.L0, r0, a.Q0]),
|
|
918
993
|
...(a.extra ?? []),
|
|
919
994
|
];
|
|
920
995
|
return writeFPool0(c, "multicall", [calls], a.account);
|
|
921
996
|
}
|
|
922
997
|
|
|
923
|
-
/**
|
|
998
|
+
/** Where a pad's seed lives (decisions 036): `seed` mints every share of a pad here itself. */
|
|
924
999
|
export const DEAD_ADDRESS: Address = "0x000000000000000000000000000000000000dEaD";
|
|
925
|
-
/** The manager's `MIN_SHARES`: the seed mints this much to 0xdead
|
|
1000
|
+
/** The manager's `MIN_SHARES`: the seed mints this much to 0xdead on every pool, the rest to the seeder — or, on a pad, to 0xdead too. */
|
|
926
1001
|
export const F_MIN_SHARES = 1000n;
|
|
927
1002
|
|
|
928
1003
|
export interface FPoolLaunchArgs extends Omit<FPoolCreateArgs, "account"> {
|
|
929
1004
|
L0: bigint;
|
|
930
1005
|
/** Quote the seed pulls. A pad seeds at 0 — pure float, a resting ask, nothing raised. */
|
|
931
1006
|
Q0: bigint;
|
|
932
|
-
/**
|
|
933
|
-
* Send every share the seed mints to the creator (`L0 − MIN_SHARES`) to 0xdead in the same
|
|
934
|
-
* batch: the float and the raise can never be burned out from under the buyers. The lever
|
|
935
|
-
* (harvest) is the authority's and survives it; the exit does not. A pad's default posture.
|
|
936
|
-
*/
|
|
937
|
-
lockSeed?: boolean;
|
|
938
1007
|
/**
|
|
939
1008
|
* An opening buy of leg `j` (an index into `bases` AS GIVEN) for `amountIn` of quote, ON THE
|
|
940
1009
|
* MANAGER — the same sender, a plain allowance to the manager, the router nowhere near it. A
|
|
@@ -945,8 +1014,6 @@ export interface FPoolLaunchArgs extends Omit<FPoolCreateArgs, "account"> {
|
|
|
945
1014
|
openBuy?: { j: number; amountIn: bigint; minOut?: bigint };
|
|
946
1015
|
/** The pool's label, set in the batch (≤ 31 bytes of UTF-8). */
|
|
947
1016
|
name?: string;
|
|
948
|
-
/** Gate minting, list the creator, appoint them operator (decisions 021) — three calls, in the batch. */
|
|
949
|
-
privatePool?: boolean;
|
|
950
1017
|
/**
|
|
951
1018
|
* The pool already exists (a previous launch got as far as `initialize` and no further — a seed
|
|
952
1019
|
* that reverted, a closed tab, before the launch was one transaction), so `initialize` is left
|
|
@@ -973,24 +1040,25 @@ export function fPoolLaunchCalls(
|
|
|
973
1040
|
const given = "r0" in a ? a.r0 : a.x0.map((x, j) => fPoolReserveAt((a.weights[j]! * a.L0) / WAD, x));
|
|
974
1041
|
if (given.length !== a.bases.length) throw new Error(`logswap: ${given.length} reserves for ${a.bases.length} legs`);
|
|
975
1042
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights, given);
|
|
976
|
-
const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi,
|
|
1043
|
+
const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, kind: F_KIND[a.kind], authority: a.authority, salt: a.salt ?? ZERO_SALT };
|
|
977
1044
|
const enc = (functionName: string, args: unknown[]) =>
|
|
978
1045
|
encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
|
|
979
1046
|
const calls: Hex[] = [];
|
|
980
1047
|
if (!a.initialized) calls.push(enc("initialize", [key]));
|
|
981
1048
|
calls.push(enc("seed", [poolId, a.L0, companions[1]!, a.Q0]));
|
|
982
|
-
// gen 0 by construction: a launch is the pool's FIRST seed — a relaunch is `fPoolRelaunch
|
|
983
|
-
|
|
1049
|
+
// gen 0 by construction: a launch is the pool's FIRST seed — a relaunch is `fPoolRelaunch`.
|
|
1050
|
+
// No transfer to 0xdead: on a pad `seed` mints every share there itself (decisions 036).
|
|
984
1051
|
if (a.openBuy) {
|
|
985
1052
|
const j = bases.indexOf(a.bases[a.openBuy.j]!);
|
|
986
1053
|
if (j < 0) throw new Error(`logswap: openBuy.j = ${a.openBuy.j} is not a leg`);
|
|
987
1054
|
calls.push(enc("swapQuoteIn", [poolId, BigInt(j), a.openBuy.amountIn, a.openBuy.minOut ?? 0n, a.account]));
|
|
988
1055
|
}
|
|
989
1056
|
if (a.name) calls.push(enc("setName", [poolId, textToBytes32(a.name)]));
|
|
990
|
-
if (a.
|
|
1057
|
+
if (a.kind === "private") {
|
|
1058
|
+
// the desk's opening posture: gated minting, the creator listed. No appointment: the
|
|
1059
|
+
// authority is the operator until one is appointed (decisions 037)
|
|
991
1060
|
calls.push(enc("setGates", [poolId, true, false]));
|
|
992
1061
|
calls.push(enc("setAllowed", [poolId, [a.account], true]));
|
|
993
|
-
calls.push(enc("appointOperator", [poolId, a.account]));
|
|
994
1062
|
}
|
|
995
1063
|
calls.push(...(a.extra ?? []));
|
|
996
1064
|
return calls;
|
|
@@ -1096,45 +1164,44 @@ export async function fPoolSponsorName(c: LogswapClient, who: Address): Promise<
|
|
|
1096
1164
|
return bytes32ToText(word);
|
|
1097
1165
|
}
|
|
1098
1166
|
|
|
1099
|
-
/**
|
|
1100
|
-
export
|
|
1101
|
-
return
|
|
1167
|
+
/** Who runs the pool (decisions 037): the appointed operator, or the authority until one is. */
|
|
1168
|
+
export function fPoolOperatorOf(s: Pick<FPoolState, "authority" | "operator">): Address {
|
|
1169
|
+
return s.operator === ZERO_ADDRESS ? s.authority : s.operator;
|
|
1102
1170
|
}
|
|
1103
1171
|
|
|
1104
1172
|
/**
|
|
1105
|
-
*
|
|
1106
|
-
*
|
|
1173
|
+
* Propose the operator (authority; private pools only) — the hot key that runs the pool: `harvest`,
|
|
1174
|
+
* `dissolve`, `setLegs`. Two-step: the proposed address takes the role with
|
|
1175
|
+
* {@link fPoolAcceptOperator}. The zero address clears at once — the role returns to the authority.
|
|
1107
1176
|
*/
|
|
1108
|
-
export async function
|
|
1109
|
-
return writeFPool(c, a.poolId, "
|
|
1177
|
+
export async function fPoolAppointOperator(c: LogswapClient, a: { poolId: Hex; operator: Address; account: Address }) {
|
|
1178
|
+
return writeFPool(c, a.poolId, "appointOperator", [a.operator], a.account);
|
|
1110
1179
|
}
|
|
1111
1180
|
|
|
1112
|
-
/**
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
* {@link fPoolReserveAt} from a mark). For a live leg leave `r` undefined — its price is the
|
|
1116
|
-
* market's, the reserve scales with the liquidity. Base moves single-sidedly through the lock;
|
|
1117
|
-
* shares adjust to hold the share value.
|
|
1118
|
-
*/
|
|
1119
|
-
export async function fPoolSetLegL(
|
|
1120
|
-
c: LogswapClient,
|
|
1121
|
-
a: { poolId: Hex; j: number; newLj: bigint; r?: bigint; account: Address },
|
|
1122
|
-
) {
|
|
1123
|
-
return writeFPool(c, a.poolId, "setLegL", [BigInt(a.j), a.newLj, a.r ?? 0n], a.account);
|
|
1181
|
+
/** The proposed operator takes the role. */
|
|
1182
|
+
export async function fPoolAcceptOperator(c: LogswapClient, a: { poolId: Hex; account: Address }) {
|
|
1183
|
+
return writeFPool(c, a.poolId, "acceptOperator", [], a.account);
|
|
1124
1184
|
}
|
|
1125
1185
|
|
|
1126
|
-
/**
|
|
1127
|
-
|
|
1186
|
+
/**
|
|
1187
|
+
* The next generation's composition (operator; private pools only; ONLY while the pool is
|
|
1188
|
+
* unseeded — between a dissolve and the seed that prices it, decisions 037). `weights` has one
|
|
1189
|
+
* entry per leg the pool has ever had, in index order (0 retires a leg; indices are never reused),
|
|
1190
|
+
* then one per base in `newBases`, appended; they sum to 1e18 exactly. No value moves: the
|
|
1191
|
+
* following {@link fPoolSeed} names every reserve. A live reshape is {@link fPoolRelaunch} with
|
|
1192
|
+
* `legs` set.
|
|
1193
|
+
*/
|
|
1194
|
+
export async function fPoolSetLegs(
|
|
1128
1195
|
c: LogswapClient,
|
|
1129
|
-
a: { poolId: Hex;
|
|
1196
|
+
a: { poolId: Hex; weights: bigint[]; newBases?: Address[]; account: Address },
|
|
1130
1197
|
) {
|
|
1131
|
-
return writeFPool(c, a.poolId, "
|
|
1198
|
+
return writeFPool(c, a.poolId, "setLegs", [a.weights, a.newBases ?? []], a.account);
|
|
1132
1199
|
}
|
|
1133
1200
|
|
|
1134
1201
|
/**
|
|
1135
1202
|
* Move shares (ERC-6909 `transfer`) — to another holder, or to `0xdead` to lock them for good.
|
|
1136
1203
|
* Burning to the dead address is a sponsor's way to make its liquidity permanent: the shares can
|
|
1137
|
-
* never be redeemed
|
|
1204
|
+
* never be redeemed.
|
|
1138
1205
|
*/
|
|
1139
1206
|
export async function fPoolTransferShares(
|
|
1140
1207
|
c: LogswapClient,
|
|
@@ -1160,7 +1227,7 @@ export interface DiscoveredFPool {
|
|
|
1160
1227
|
bases: Address[];
|
|
1161
1228
|
weights: bigint[];
|
|
1162
1229
|
phi: bigint;
|
|
1163
|
-
|
|
1230
|
+
kind: FPoolKind;
|
|
1164
1231
|
authority: Address;
|
|
1165
1232
|
/** The block the pool was initialized in — its age, for annualising realized income. */
|
|
1166
1233
|
block: bigint;
|
|
@@ -1179,7 +1246,7 @@ export interface DiscoveredFPool {
|
|
|
1179
1246
|
|
|
1180
1247
|
/**
|
|
1181
1248
|
* Every F pool ever created, from `Initialize` logs alone. Possible only because the event
|
|
1182
|
-
* carries the full key preimage (bases, weights, phi,
|
|
1249
|
+
* carries the full key preimage (bases, weights, phi, pad) — the poolId is a hash and could
|
|
1183
1250
|
* never be inverted; contracts PR #17 exists for exactly this call.
|
|
1184
1251
|
*/
|
|
1185
1252
|
export async function discoverFPools(
|
|
@@ -1210,7 +1277,7 @@ export async function discoverFPools(
|
|
|
1210
1277
|
return logs.map((l) => {
|
|
1211
1278
|
const a = l.args as {
|
|
1212
1279
|
poolId: Hex; quote: Address; authority: Address; bases: readonly Address[];
|
|
1213
|
-
weights: readonly bigint[]; phi: bigint;
|
|
1280
|
+
weights: readonly bigint[]; phi: bigint; kind: number;
|
|
1214
1281
|
};
|
|
1215
1282
|
const q0 = q0Of.get(a.poolId.toLowerCase());
|
|
1216
1283
|
return {
|
|
@@ -1219,12 +1286,12 @@ export async function discoverFPools(
|
|
|
1219
1286
|
bases: [...a.bases],
|
|
1220
1287
|
weights: [...a.weights],
|
|
1221
1288
|
phi: a.phi,
|
|
1222
|
-
|
|
1289
|
+
kind: fPoolKindOf(a.kind),
|
|
1223
1290
|
authority: a.authority,
|
|
1224
1291
|
block: l.blockNumber ?? 0n,
|
|
1225
1292
|
seeded: q0 !== undefined,
|
|
1226
1293
|
q0: q0 ?? 0n,
|
|
1227
|
-
shape: a.
|
|
1294
|
+
shape: a.kind === F_KIND.pad ? "launch" : "basket", // the kind, not n = 1: a mono POL is a pool (decisions 036)
|
|
1228
1295
|
};
|
|
1229
1296
|
});
|
|
1230
1297
|
}
|
|
@@ -1263,7 +1330,7 @@ export async function fPoolShareHolders(
|
|
|
1263
1330
|
.sort((a, b) => (b.shares > a.shares ? 1 : -1));
|
|
1264
1331
|
}
|
|
1265
1332
|
|
|
1266
|
-
/** Every F pool's state in one lens call (the raw manager struct: quote, phi, authority,
|
|
1333
|
+
/** Every F pool's state in one lens call (the raw manager struct: quote, phi, authority, kind, seeded, gen, n, Q, L, shares, theta0, bigSigma, harvestedTheta). */
|
|
1267
1334
|
export async function getFPoolsRaw(c: LogswapClient, poolIds: Hex[]): Promise<readonly unknown[]> {
|
|
1268
1335
|
const { logswapLensAbi } = await import("./generated.js");
|
|
1269
1336
|
return (await c.public.readContract({ address: c.addresses.lens, abi: logswapLensAbi, functionName: "getFPools", args: [poolIds] } as never)) as readonly unknown[];
|