@logswap/sdk 0.5.40 → 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 +207 -288
- package/dist/fpool.js +125 -54
- package/dist/generated.d.ts +112 -226
- package/dist/generated.js +91 -167
- package/package.json +1 -1
- package/src/client.ts +11 -1
- package/src/fpool.ts +158 -86
- package/src/generated.ts +91 -167
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;
|
|
@@ -131,7 +130,7 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
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,9 +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
487
|
// ─── internals ────────────────────────────────────────────────────────────────
|
|
471
488
|
|
|
472
489
|
function defaultDeadline(): bigint {
|
|
@@ -779,8 +796,8 @@ export interface FPoolCreateArgs {
|
|
|
779
796
|
weights: bigint[];
|
|
780
797
|
/** Fixed fee, WAD. The base-for-base fee is pinned at 2φ on-chain. */
|
|
781
798
|
phi: bigint;
|
|
782
|
-
/**
|
|
783
|
-
|
|
799
|
+
/** The rung (decisions 036) — see {@link FPoolKind}. In the key: the pool's identity, never changed after. */
|
|
800
|
+
kind: FPoolKind;
|
|
784
801
|
authority: Address;
|
|
785
802
|
/**
|
|
786
803
|
* The differentiator, and nothing else. Identity is the key's hash, so one sponsor cannot run
|
|
@@ -820,7 +837,7 @@ export async function fPoolInitialize(c: LogswapClient, a: FPoolCreateArgs) {
|
|
|
820
837
|
const sum = a.weights.reduce((x, y) => x + y, 0n);
|
|
821
838
|
if (sum !== 10n ** 18n) throw new Error(`logswap: weights sum to ${sum}, expected 1e18`);
|
|
822
839
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
|
|
823
|
-
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 };
|
|
824
841
|
const wallet = requireWallet(c);
|
|
825
842
|
const { request } = await c.public.simulateContract({
|
|
826
843
|
address: c.addresses.fPoolManager!,
|
|
@@ -835,7 +852,7 @@ export async function fPoolInitialize(c: LogswapClient, a: FPoolCreateArgs) {
|
|
|
835
852
|
/** The pool id is the key's hash — pure, so it can be read before or after creation. */
|
|
836
853
|
export async function fPoolIdOf(c: LogswapClient, a: Omit<FPoolCreateArgs, "account">): Promise<Hex> {
|
|
837
854
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
|
|
838
|
-
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 };
|
|
839
856
|
return c.public.readContract({
|
|
840
857
|
address: c.addresses.fPoolManager!,
|
|
841
858
|
abi: fPoolManagerAbi,
|
|
@@ -872,64 +889,121 @@ export async function fPoolSeed(
|
|
|
872
889
|
}
|
|
873
890
|
|
|
874
891
|
/**
|
|
875
|
-
* Retire the live generation (decisions 025, 032)
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
* ({@link
|
|
879
|
-
*
|
|
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.
|
|
880
898
|
*/
|
|
881
899
|
export async function fPoolDissolve(c: LogswapClient, a: { poolId: Hex; account: Address }) {
|
|
882
900
|
return writeFPool(c, a.poolId, "dissolve", [], a.account);
|
|
883
901
|
}
|
|
884
902
|
|
|
885
903
|
/**
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
*
|
|
890
|
-
|
|
891
|
-
|
|
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`.
|
|
892
968
|
*/
|
|
893
969
|
export async function fPoolRelaunch(
|
|
894
970
|
c: LogswapClient,
|
|
895
971
|
a: {
|
|
896
972
|
poolId: Hex;
|
|
897
|
-
/** the
|
|
973
|
+
/** the caller's shares of the retiring generation to redeem inside the batch — its whole balance, usually; 0 to keep the claim */
|
|
898
974
|
shares: bigint;
|
|
899
975
|
L0: bigint;
|
|
900
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[] };
|
|
901
979
|
extra?: Hex[];
|
|
902
980
|
account: Address;
|
|
903
981
|
} & ({ r0: bigint[] } | { x0: bigint[]; weights: bigint[] }),
|
|
904
982
|
) {
|
|
905
|
-
const gen = await fPoolGen(c, a.poolId);
|
|
906
|
-
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)));
|
|
907
985
|
const enc = (functionName: string, args: unknown[]) =>
|
|
908
986
|
encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
|
|
909
987
|
const calls: Hex[] = [
|
|
988
|
+
...harvests.map((amount) => enc("harvest", [a.poolId, amount, a.account])),
|
|
910
989
|
enc("dissolve", [a.poolId]),
|
|
911
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 ?? []])] : []),
|
|
912
992
|
enc("seed", [a.poolId, a.L0, r0, a.Q0]),
|
|
913
993
|
...(a.extra ?? []),
|
|
914
994
|
];
|
|
915
995
|
return writeFPool0(c, "multicall", [calls], a.account);
|
|
916
996
|
}
|
|
917
997
|
|
|
918
|
-
/**
|
|
998
|
+
/** Where a pad's seed lives (decisions 036): `seed` mints every share of a pad here itself. */
|
|
919
999
|
export const DEAD_ADDRESS: Address = "0x000000000000000000000000000000000000dEaD";
|
|
920
|
-
/** 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. */
|
|
921
1001
|
export const F_MIN_SHARES = 1000n;
|
|
922
1002
|
|
|
923
1003
|
export interface FPoolLaunchArgs extends Omit<FPoolCreateArgs, "account"> {
|
|
924
1004
|
L0: bigint;
|
|
925
1005
|
/** Quote the seed pulls. A pad seeds at 0 — pure float, a resting ask, nothing raised. */
|
|
926
1006
|
Q0: bigint;
|
|
927
|
-
/**
|
|
928
|
-
* Send every share the seed mints to the creator (`L0 − MIN_SHARES`) to 0xdead in the same
|
|
929
|
-
* batch: the float and the raise can never be burned out from under the buyers. The lever
|
|
930
|
-
* (harvest) is the authority's and survives it; the exit does not. A pad's default posture.
|
|
931
|
-
*/
|
|
932
|
-
lockSeed?: boolean;
|
|
933
1007
|
/**
|
|
934
1008
|
* An opening buy of leg `j` (an index into `bases` AS GIVEN) for `amountIn` of quote, ON THE
|
|
935
1009
|
* MANAGER — the same sender, a plain allowance to the manager, the router nowhere near it. A
|
|
@@ -940,8 +1014,6 @@ export interface FPoolLaunchArgs extends Omit<FPoolCreateArgs, "account"> {
|
|
|
940
1014
|
openBuy?: { j: number; amountIn: bigint; minOut?: bigint };
|
|
941
1015
|
/** The pool's label, set in the batch (≤ 31 bytes of UTF-8). */
|
|
942
1016
|
name?: string;
|
|
943
|
-
/** Gate minting, list the creator, appoint them operator (decisions 021) — three calls, in the batch. */
|
|
944
|
-
privatePool?: boolean;
|
|
945
1017
|
/**
|
|
946
1018
|
* The pool already exists (a previous launch got as far as `initialize` and no further — a seed
|
|
947
1019
|
* that reverted, a closed tab, before the launch was one transaction), so `initialize` is left
|
|
@@ -968,24 +1040,25 @@ export function fPoolLaunchCalls(
|
|
|
968
1040
|
const given = "r0" in a ? a.r0 : a.x0.map((x, j) => fPoolReserveAt((a.weights[j]! * a.L0) / WAD, x));
|
|
969
1041
|
if (given.length !== a.bases.length) throw new Error(`logswap: ${given.length} reserves for ${a.bases.length} legs`);
|
|
970
1042
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights, given);
|
|
971
|
-
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 };
|
|
972
1044
|
const enc = (functionName: string, args: unknown[]) =>
|
|
973
1045
|
encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
|
|
974
1046
|
const calls: Hex[] = [];
|
|
975
1047
|
if (!a.initialized) calls.push(enc("initialize", [key]));
|
|
976
1048
|
calls.push(enc("seed", [poolId, a.L0, companions[1]!, a.Q0]));
|
|
977
|
-
// gen 0 by construction: a launch is the pool's FIRST seed — a relaunch is `fPoolRelaunch
|
|
978
|
-
|
|
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).
|
|
979
1051
|
if (a.openBuy) {
|
|
980
1052
|
const j = bases.indexOf(a.bases[a.openBuy.j]!);
|
|
981
1053
|
if (j < 0) throw new Error(`logswap: openBuy.j = ${a.openBuy.j} is not a leg`);
|
|
982
1054
|
calls.push(enc("swapQuoteIn", [poolId, BigInt(j), a.openBuy.amountIn, a.openBuy.minOut ?? 0n, a.account]));
|
|
983
1055
|
}
|
|
984
1056
|
if (a.name) calls.push(enc("setName", [poolId, textToBytes32(a.name)]));
|
|
985
|
-
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)
|
|
986
1060
|
calls.push(enc("setGates", [poolId, true, false]));
|
|
987
1061
|
calls.push(enc("setAllowed", [poolId, [a.account], true]));
|
|
988
|
-
calls.push(enc("appointOperator", [poolId, a.account]));
|
|
989
1062
|
}
|
|
990
1063
|
calls.push(...(a.extra ?? []));
|
|
991
1064
|
return calls;
|
|
@@ -1091,45 +1164,44 @@ export async function fPoolSponsorName(c: LogswapClient, who: Address): Promise<
|
|
|
1091
1164
|
return bytes32ToText(word);
|
|
1092
1165
|
}
|
|
1093
1166
|
|
|
1094
|
-
/**
|
|
1095
|
-
export
|
|
1096
|
-
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;
|
|
1097
1170
|
}
|
|
1098
1171
|
|
|
1099
1172
|
/**
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
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.
|
|
1102
1176
|
*/
|
|
1103
|
-
export async function
|
|
1104
|
-
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);
|
|
1105
1179
|
}
|
|
1106
1180
|
|
|
1107
|
-
/**
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
* {@link fPoolReserveAt} from a mark). For a live leg leave `r` undefined — its price is the
|
|
1111
|
-
* market's, the reserve scales with the liquidity. Base moves single-sidedly through the lock;
|
|
1112
|
-
* shares adjust to hold the share value.
|
|
1113
|
-
*/
|
|
1114
|
-
export async function fPoolSetLegL(
|
|
1115
|
-
c: LogswapClient,
|
|
1116
|
-
a: { poolId: Hex; j: number; newLj: bigint; r?: bigint; account: Address },
|
|
1117
|
-
) {
|
|
1118
|
-
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);
|
|
1119
1184
|
}
|
|
1120
1185
|
|
|
1121
|
-
/**
|
|
1122
|
-
|
|
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(
|
|
1123
1195
|
c: LogswapClient,
|
|
1124
|
-
a: { poolId: Hex;
|
|
1196
|
+
a: { poolId: Hex; weights: bigint[]; newBases?: Address[]; account: Address },
|
|
1125
1197
|
) {
|
|
1126
|
-
return writeFPool(c, a.poolId, "
|
|
1198
|
+
return writeFPool(c, a.poolId, "setLegs", [a.weights, a.newBases ?? []], a.account);
|
|
1127
1199
|
}
|
|
1128
1200
|
|
|
1129
1201
|
/**
|
|
1130
1202
|
* Move shares (ERC-6909 `transfer`) — to another holder, or to `0xdead` to lock them for good.
|
|
1131
1203
|
* Burning to the dead address is a sponsor's way to make its liquidity permanent: the shares can
|
|
1132
|
-
* never be redeemed
|
|
1204
|
+
* never be redeemed.
|
|
1133
1205
|
*/
|
|
1134
1206
|
export async function fPoolTransferShares(
|
|
1135
1207
|
c: LogswapClient,
|
|
@@ -1155,7 +1227,7 @@ export interface DiscoveredFPool {
|
|
|
1155
1227
|
bases: Address[];
|
|
1156
1228
|
weights: bigint[];
|
|
1157
1229
|
phi: bigint;
|
|
1158
|
-
|
|
1230
|
+
kind: FPoolKind;
|
|
1159
1231
|
authority: Address;
|
|
1160
1232
|
/** The block the pool was initialized in — its age, for annualising realized income. */
|
|
1161
1233
|
block: bigint;
|
|
@@ -1174,7 +1246,7 @@ export interface DiscoveredFPool {
|
|
|
1174
1246
|
|
|
1175
1247
|
/**
|
|
1176
1248
|
* Every F pool ever created, from `Initialize` logs alone. Possible only because the event
|
|
1177
|
-
* 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
|
|
1178
1250
|
* never be inverted; contracts PR #17 exists for exactly this call.
|
|
1179
1251
|
*/
|
|
1180
1252
|
export async function discoverFPools(
|
|
@@ -1205,7 +1277,7 @@ export async function discoverFPools(
|
|
|
1205
1277
|
return logs.map((l) => {
|
|
1206
1278
|
const a = l.args as {
|
|
1207
1279
|
poolId: Hex; quote: Address; authority: Address; bases: readonly Address[];
|
|
1208
|
-
weights: readonly bigint[]; phi: bigint;
|
|
1280
|
+
weights: readonly bigint[]; phi: bigint; kind: number;
|
|
1209
1281
|
};
|
|
1210
1282
|
const q0 = q0Of.get(a.poolId.toLowerCase());
|
|
1211
1283
|
return {
|
|
@@ -1214,12 +1286,12 @@ export async function discoverFPools(
|
|
|
1214
1286
|
bases: [...a.bases],
|
|
1215
1287
|
weights: [...a.weights],
|
|
1216
1288
|
phi: a.phi,
|
|
1217
|
-
|
|
1289
|
+
kind: fPoolKindOf(a.kind),
|
|
1218
1290
|
authority: a.authority,
|
|
1219
1291
|
block: l.blockNumber ?? 0n,
|
|
1220
1292
|
seeded: q0 !== undefined,
|
|
1221
1293
|
q0: q0 ?? 0n,
|
|
1222
|
-
shape: a.
|
|
1294
|
+
shape: a.kind === F_KIND.pad ? "launch" : "basket", // the kind, not n = 1: a mono POL is a pool (decisions 036)
|
|
1223
1295
|
};
|
|
1224
1296
|
});
|
|
1225
1297
|
}
|
|
@@ -1258,7 +1330,7 @@ export async function fPoolShareHolders(
|
|
|
1258
1330
|
.sort((a, b) => (b.shares > a.shares ? 1 : -1));
|
|
1259
1331
|
}
|
|
1260
1332
|
|
|
1261
|
-
/** 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). */
|
|
1262
1334
|
export async function getFPoolsRaw(c: LogswapClient, poolIds: Hex[]): Promise<readonly unknown[]> {
|
|
1263
1335
|
const { logswapLensAbi } = await import("./generated.js");
|
|
1264
1336
|
return (await c.public.readContract({ address: c.addresses.lens, abi: logswapLensAbi, functionName: "getFPools", args: [poolIds] } as never)) as readonly unknown[];
|