@logswap/sdk 0.5.36 → 0.5.37
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 +5 -1
- package/dist/fpool.d.ts +3655 -23
- package/dist/fpool.js +139 -28
- package/dist/generated.d.ts +1823 -248
- package/dist/generated.js +1378 -238
- package/package.json +2 -2
- package/src/client.ts +5 -1
- package/src/fpool.ts +205 -41
- package/src/generated.ts +1380 -238
package/dist/fpool.js
CHANGED
|
@@ -22,8 +22,16 @@
|
|
|
22
22
|
* twice, forever. One router is one spender — see `fPoolZapIn`/`fPoolZapOut` below, which target
|
|
23
23
|
* `addresses.router`.
|
|
24
24
|
*/
|
|
25
|
-
import { toFunctionSelector } from "viem";
|
|
26
|
-
import { fPoolManagerAbi, logswapRouterAbi } from "./generated.js";
|
|
25
|
+
import { encodeFunctionData, toFunctionSelector } from "viem";
|
|
26
|
+
import { fPoolManagerAbi as fPoolPrimitiveAbi, fPoolSponsorAbi, logswapRouterAbi } from "./generated.js";
|
|
27
|
+
/**
|
|
28
|
+
* The F manager's ABI is the UNION of its two compiled halves (decisions 031): `FPoolManager`
|
|
29
|
+
* holds the primitive and forwards every other selector from its fallback to `FPoolSponsor` —
|
|
30
|
+
* the lever, `claim`, `dissolve`, the handover, the gates, the desk, the names — by delegatecall.
|
|
31
|
+
* One address from outside, so one ABI here; both halves carry the base's events and errors,
|
|
32
|
+
* and viem tolerates the duplicates.
|
|
33
|
+
*/
|
|
34
|
+
export const fPoolManagerAbi = [...fPoolPrimitiveAbi, ...fPoolSponsorAbi];
|
|
27
35
|
const WAD = 10n ** 18n;
|
|
28
36
|
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
29
37
|
/** Read a basket pool whole. Batched — viem multicalls these when the transport allows. */
|
|
@@ -57,11 +65,9 @@ export async function getFPool(c, poolId) {
|
|
|
57
65
|
]);
|
|
58
66
|
const bases = legs.map((l) => l[0]);
|
|
59
67
|
const weights = legs.map((l) => l[1]);
|
|
60
|
-
const x = legs.map((l) => l[2]);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const reserves = legs.map((l) => l[3]);
|
|
64
|
-
const { quote, Q, L, phi, theta0, bigSigma, authority, pendingAuthority, operator, minBuffer, gateMint, gateSwap, restructureBlock, feesOnly, seeded, dissolved } = p;
|
|
68
|
+
const x = legs.map((l) => l[2]); // derived by the manager: x_j = ln(w_j·L / R_j) (decisions 026)
|
|
69
|
+
const reserves = legs.map((l) => l[3]); // the state
|
|
70
|
+
const { quote, Q, L, phi, theta0, bigSigma, authority, pendingAuthority, operator, minBuffer, gateMint, gateSwap, restructureBlock, lockStrike, seeded, dissolved, successor } = p;
|
|
65
71
|
const totalSupply = p.shares;
|
|
66
72
|
return {
|
|
67
73
|
poolId,
|
|
@@ -84,9 +90,10 @@ export async function getFPool(c, poolId) {
|
|
|
84
90
|
gateMint,
|
|
85
91
|
gateSwap,
|
|
86
92
|
restructureBlock: Number(restructureBlock),
|
|
87
|
-
|
|
93
|
+
lockStrike,
|
|
88
94
|
seeded,
|
|
89
95
|
dissolved,
|
|
96
|
+
successor,
|
|
90
97
|
totalSupply,
|
|
91
98
|
reserves,
|
|
92
99
|
};
|
|
@@ -200,14 +207,61 @@ export async function fPoolZapOut(c, a) {
|
|
|
200
207
|
const to = a.to ?? a.account;
|
|
201
208
|
return writeRouter(c, "zapOut", [a.poolId, a.shares, a.tokenOut, a.minOut ?? 0n, to, a.deadline ?? defaultDeadline()], a.account);
|
|
202
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Roll a dissolved pool's bundle into its successor (decisions 025), on the router under one
|
|
212
|
+
* lock: burn `shares` of `fromPool` in place — the legs, the quote and any unpulled dividend come
|
|
213
|
+
* back — buy through `toPool`'s own curve only what its ratio lacks, mint `dL` of it to `to`, and
|
|
214
|
+
* refund the rest in kind. Quote the bundle does not cover is pulled from the payer, at most
|
|
215
|
+
* `maxQuoteIn`; surplus base is refunded, never sold. Size `dL` with {@link fPoolPreviewRollIn}.
|
|
216
|
+
* The router must be the caller's ERC-6909 operator ({@link approveRouterForFPool}).
|
|
217
|
+
*/
|
|
218
|
+
export async function fPoolRollIn(c, a) {
|
|
219
|
+
const to = a.to ?? a.account;
|
|
220
|
+
return writeRouter(c, "rollIn", [a.fromPool, a.toPool, a.shares, a.dL, a.maxQuoteIn ?? 0n, a.minShares ?? 0n, to, a.deadline ?? defaultDeadline()], a.account);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The roll `holder`'s `shares` of `fromPool` fund into `toPool` WITHOUT a trade: the largest `dL`
|
|
224
|
+
* the bundle's base covers on every successor leg, and the quote the bundle is short of (pass it
|
|
225
|
+
* as `maxQuoteIn`) or over (refunded — the up direction's distribution) at that `dL`. A
|
|
226
|
+
* successor leg the bundle does not hold makes `dL` zero: such a roll must buy, so size it below
|
|
227
|
+
* with `previewZapIn` in mind.
|
|
228
|
+
*/
|
|
229
|
+
export async function fPoolPreviewRollIn(c, fromPool, toPool, holder, shares) {
|
|
230
|
+
const { logswapLensAbi } = await import("./generated.js");
|
|
231
|
+
const [dL, quoteShort, quoteExcess] = (await c.public.readContract({
|
|
232
|
+
address: c.addresses.lens,
|
|
233
|
+
abi: logswapLensAbi,
|
|
234
|
+
functionName: "previewRollIn",
|
|
235
|
+
args: [fromPool, toPool, holder, shares],
|
|
236
|
+
}));
|
|
237
|
+
return { dL, quoteShort, quoteExcess };
|
|
238
|
+
}
|
|
203
239
|
// ─── the floor lever (authority only) ─────────────────────────────────────────
|
|
204
240
|
/**
|
|
205
|
-
* Remove quote, lifting θ — price-neutral,
|
|
206
|
-
*
|
|
241
|
+
* Remove quote, lifting θ — price-neutral, no base moves, and since decisions 028 a DIVIDEND to
|
|
242
|
+
* every share: the amount goes into the pool's pot and each holder's slice is settled lazily on
|
|
243
|
+
* their next transfer, mint or burn, or pulled with {@link fPoolClaim}. The sponsor's own slice —
|
|
244
|
+
* its shares' and the locked seed's at 0xdead — is pushed to `to` at once. Under `lockStrike`
|
|
245
|
+
* the lift stops at θ₀ (income only); on every live pool it stops at `LEVER_FLOOR` (Q/L ≥ ln 1.25).
|
|
207
246
|
*/
|
|
208
247
|
export async function fPoolHarvest(c, a) {
|
|
209
248
|
return writeFPool(c, a.poolId, "harvest", [a.amount, a.to ?? a.account], a.account);
|
|
210
249
|
}
|
|
250
|
+
/** Pull one's settled dividend (decisions 028) without moving shares; anyone with shares. */
|
|
251
|
+
export async function fPoolClaim(c, a) {
|
|
252
|
+
return writeFPool(c, a.poolId, "claim", [a.to ?? a.account], a.account);
|
|
253
|
+
}
|
|
254
|
+
/** A holder's dividend as of now — banked plus pending on the current balance (decisions 028). */
|
|
255
|
+
export async function fPoolDividendOf(c, poolId, holder) {
|
|
256
|
+
return c.public.readContract({
|
|
257
|
+
address: c.addresses.fPoolManager,
|
|
258
|
+
abi: fPoolManagerAbi,
|
|
259
|
+
functionName: "dividendOf",
|
|
260
|
+
args: [poolId, holder],
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/** The lever's floor: `harvest` may not take Q/L under ln 1.25 — the floor stays ≥ 20% below the mark (decisions 025). */
|
|
264
|
+
export const LEVER_FLOOR = 223143551314209755n;
|
|
211
265
|
/** 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. */
|
|
212
266
|
export async function fPoolDeepen(c, a) {
|
|
213
267
|
return writeFPool(c, a.poolId, "deepen", [a.amount], a.account);
|
|
@@ -235,6 +289,18 @@ async function writeFPool(c, poolId, functionName, args, account) {
|
|
|
235
289
|
});
|
|
236
290
|
return wallet.writeContract(request);
|
|
237
291
|
}
|
|
292
|
+
/** A manager write that is NOT keyed by a poolId — `multicall`. */
|
|
293
|
+
async function writeFPool0(c, functionName, args, account) {
|
|
294
|
+
const wallet = requireWallet(c);
|
|
295
|
+
const { request } = await c.public.simulateContract({
|
|
296
|
+
address: c.addresses.fPoolManager,
|
|
297
|
+
abi: fPoolManagerAbi,
|
|
298
|
+
functionName,
|
|
299
|
+
args,
|
|
300
|
+
account,
|
|
301
|
+
});
|
|
302
|
+
return wallet.writeContract(request);
|
|
303
|
+
}
|
|
238
304
|
async function writeRouter(c, functionName, args, account,
|
|
239
305
|
/** Tokens the call pulls: missing Permit2 allowances are SIGNED and ride in the same tx. */
|
|
240
306
|
pullTokens = []) {
|
|
@@ -433,7 +499,7 @@ export async function fPoolInitialize(c, a) {
|
|
|
433
499
|
if (sum !== 10n ** 18n)
|
|
434
500
|
throw new Error(`logswap: weights sum to ${sum}, expected 1e18`);
|
|
435
501
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
|
|
436
|
-
const key = { quote: a.quote, bases, weights: companions[0], phi: a.phi,
|
|
502
|
+
const key = { quote: a.quote, bases, weights: companions[0], phi: a.phi, lockStrike: a.lockStrike, authority: a.authority, salt: a.salt ?? ZERO_SALT };
|
|
437
503
|
const wallet = requireWallet(c);
|
|
438
504
|
const { request } = await c.public.simulateContract({
|
|
439
505
|
address: c.addresses.fPoolManager,
|
|
@@ -447,7 +513,7 @@ export async function fPoolInitialize(c, a) {
|
|
|
447
513
|
/** The pool id is the key's hash — pure, so it can be read before or after creation. */
|
|
448
514
|
export async function fPoolIdOf(c, a) {
|
|
449
515
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
|
|
450
|
-
const key = { quote: a.quote, bases, weights: companions[0], phi: a.phi,
|
|
516
|
+
const key = { quote: a.quote, bases, weights: companions[0], phi: a.phi, lockStrike: a.lockStrike, authority: a.authority, salt: a.salt ?? ZERO_SALT };
|
|
451
517
|
return c.public.readContract({
|
|
452
518
|
address: c.addresses.fPoolManager,
|
|
453
519
|
abi: fPoolManagerAbi,
|
|
@@ -456,16 +522,61 @@ export async function fPoolIdOf(c, a) {
|
|
|
456
522
|
});
|
|
457
523
|
}
|
|
458
524
|
/**
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
525
|
+
* The reserve a leg holds at a mark: `R = Lj·e^{−x}`, WAD math. The seed names RESERVES since
|
|
526
|
+
* decisions 029 — the sponsor states holdings and the price follows, `p = Lj / R` — so this is
|
|
527
|
+
* the one conversion from a price to what the pool will hold, done here, on the way in. Float
|
|
528
|
+
* precision (~1e-16 relative) is what a seed needs: any reserve is a legal seed, and the mark
|
|
529
|
+
* the pool derives from it is the one the sponsor meant to within that.
|
|
530
|
+
*/
|
|
531
|
+
export function fPoolReserveAt(Lj, x) {
|
|
532
|
+
const e = Math.exp(-Number(x) / 1e18);
|
|
533
|
+
return (Lj * BigInt(Math.round(e * 1e18))) / 10n ** 18n;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Arm the pool: deposit `r0[j]` of each base plus `Q0` of quote against exposure `L0`. Pass either
|
|
537
|
+
* the reserves outright (`r0`) or the marks the pool should open at (`x0`, converted here with
|
|
538
|
+
* {@link fPoolReserveAt} against the key's weights). Authority-only on-chain (an open seed lets
|
|
539
|
+
* anyone front-run the creator for the strike). Arrays align with the SORTED bases — use
|
|
540
|
+
* {@link sortFPoolLegs} on (bases, weights, x0) together when building forms.
|
|
462
541
|
*/
|
|
463
542
|
export async function fPoolSeed(c, a) {
|
|
464
|
-
|
|
543
|
+
const r0 = "r0" in a ? a.r0 : a.x0.map((x, j) => fPoolReserveAt((a.weights[j] * a.L0) / 10n ** 18n, x));
|
|
544
|
+
return writeFPool(c, a.poolId, "seed", [a.L0, r0, a.Q0], a.account);
|
|
465
545
|
}
|
|
466
|
-
/**
|
|
546
|
+
/**
|
|
547
|
+
* End of life (decisions 025). Allowed at Q ≤ L/1e9 on every pool — the market filled the ask —
|
|
548
|
+
* and on a pool that is not strike-locked at any Q, provided `successor` names an initialized
|
|
549
|
+
* pool of the same authority and quote: the relaunch. After it every share is a frozen in-kind
|
|
550
|
+
* claim redeemed by `burn` (or rolled with {@link fPoolRollIn}), forever; the pool's `successor`
|
|
551
|
+
* carries the lineage. Never a second dissolve on one pool: the successor is a NEW pool.
|
|
552
|
+
*/
|
|
467
553
|
export async function fPoolDissolve(c, a) {
|
|
468
|
-
return writeFPool(c, a.poolId, "dissolve", [], a.account);
|
|
554
|
+
return writeFPool(c, a.poolId, "dissolve", [a.successor ?? ZERO_SALT], a.account);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* The sponsor's relaunch as ONE transaction (decisions 025): `initialize` the successor key,
|
|
558
|
+
* `dissolve` the old pool into it, `burn` the sponsor's own shares, `seed` the successor with what
|
|
559
|
+
* came back — the manager's `multicall`, the sponsor as sender throughout. Gates, allowlist,
|
|
560
|
+
* operator, floor guard and name are re-applied afterwards, or appended to `extra` pre-encoded
|
|
561
|
+
* against {@link fPoolManagerAbi}. Returns the tx hash; the successor's id is {@link fPoolIdOf}
|
|
562
|
+
* of `nextKey`.
|
|
563
|
+
*/
|
|
564
|
+
export async function fPoolRelaunch(c, a) {
|
|
565
|
+
const { bases, companions } = sortFPoolLegs(a.nextKey.bases, a.nextKey.weights);
|
|
566
|
+
const key = {
|
|
567
|
+
quote: a.nextKey.quote, bases, weights: companions[0], phi: a.nextKey.phi, lockStrike: a.nextKey.lockStrike,
|
|
568
|
+
authority: a.nextKey.authority, salt: a.nextKey.salt ?? ZERO_SALT,
|
|
569
|
+
};
|
|
570
|
+
const next = await fPoolIdOf(c, a.nextKey);
|
|
571
|
+
const enc = (functionName, args) => encodeFunctionData({ abi: fPoolManagerAbi, functionName, args });
|
|
572
|
+
const calls = [
|
|
573
|
+
enc("initialize", [key]),
|
|
574
|
+
enc("dissolve", [a.poolId, next]),
|
|
575
|
+
enc("burn", [a.poolId, a.account, a.shares, 0n]),
|
|
576
|
+
enc("seed", [next, a.L0, a.r0, a.Q0]),
|
|
577
|
+
...(a.extra ?? []),
|
|
578
|
+
];
|
|
579
|
+
return writeFPool0(c, "multicall", [calls], a.account);
|
|
469
580
|
}
|
|
470
581
|
/**
|
|
471
582
|
* Propose the next authority — step one of two (contracts `2aa6b7b`, decisions 020). Nothing moves
|
|
@@ -480,8 +591,6 @@ export async function fPoolAcceptAuthority(c, a) {
|
|
|
480
591
|
return writeFPool(c, a.poolId, "acceptAuthority", [], a.account);
|
|
481
592
|
}
|
|
482
593
|
// ─── the sponsor's controls and the desk (contracts bd3dd87, decisions 021) ───────────────
|
|
483
|
-
/** `setLegL`'s "keep the stored mark" sentinel — the only legal `x` for a live leg. */
|
|
484
|
-
export const NO_X = -(2n ** 255n);
|
|
485
594
|
/** Turn the mint / swap gates on or off (authority). `burn` is never gated. */
|
|
486
595
|
export async function fPoolSetGates(c, a) {
|
|
487
596
|
return writeFPool(c, a.poolId, "setGates", [a.gateMint, a.gateSwap], a.account);
|
|
@@ -541,15 +650,17 @@ export async function fPoolRaiseMinBuffer(c, a) {
|
|
|
541
650
|
}
|
|
542
651
|
/**
|
|
543
652
|
* Set leg `j`'s liquidity (operator, sole LP only): grow, shrink, retire (`newLj = 0`), or re-admit
|
|
544
|
-
* a retired leg
|
|
545
|
-
*
|
|
653
|
+
* a retired leg holding `r` of the base (decisions 029 — the price follows, `p = newLj / r`; use
|
|
654
|
+
* {@link fPoolReserveAt} from a mark). For a live leg leave `r` undefined — its price is the
|
|
655
|
+
* market's, the reserve scales with the liquidity. Base moves single-sidedly through the lock;
|
|
656
|
+
* shares adjust to hold the share value.
|
|
546
657
|
*/
|
|
547
658
|
export async function fPoolSetLegL(c, a) {
|
|
548
|
-
return writeFPool(c, a.poolId, "setLegL", [BigInt(a.j), a.newLj, a.
|
|
659
|
+
return writeFPool(c, a.poolId, "setLegL", [BigInt(a.j), a.newLj, a.r ?? 0n], a.account);
|
|
549
660
|
}
|
|
550
|
-
/** Admit a base the pool has never held
|
|
661
|
+
/** Admit a base the pool has never held: `R` of it against liquidity `Lj`, so at `p = Lj / R` (operator, sole LP only). */
|
|
551
662
|
export async function fPoolAdmitLeg(c, a) {
|
|
552
|
-
return writeFPool(c, a.poolId, "admitLeg", [a.base, a.Lj, a.
|
|
663
|
+
return writeFPool(c, a.poolId, "admitLeg", [a.base, a.Lj, a.R], a.account);
|
|
553
664
|
}
|
|
554
665
|
/**
|
|
555
666
|
* Move shares (ERC-6909 `transfer`) — to another holder, or to `0xdead` to lock them for good.
|
|
@@ -569,7 +680,7 @@ export async function fPoolTransferShares(c, a) {
|
|
|
569
680
|
}
|
|
570
681
|
/**
|
|
571
682
|
* Every F pool ever created, from `Initialize` logs alone. Possible only because the event
|
|
572
|
-
* carries the full key preimage (bases, weights, phi,
|
|
683
|
+
* carries the full key preimage (bases, weights, phi, lockStrike) — the poolId is a hash and could
|
|
573
684
|
* never be inverted; contracts PR #17 exists for exactly this call.
|
|
574
685
|
*/
|
|
575
686
|
export async function discoverFPools(c, opts = {}) {
|
|
@@ -602,7 +713,7 @@ export async function discoverFPools(c, opts = {}) {
|
|
|
602
713
|
bases: [...a.bases],
|
|
603
714
|
weights: [...a.weights],
|
|
604
715
|
phi: a.phi,
|
|
605
|
-
|
|
716
|
+
lockStrike: a.lockStrike,
|
|
606
717
|
authority: a.authority,
|
|
607
718
|
block: l.blockNumber ?? 0n,
|
|
608
719
|
seeded: q0 !== undefined,
|
|
@@ -641,7 +752,7 @@ export async function fPoolShareHolders(c, poolIdHex, opts = {}) {
|
|
|
641
752
|
.map(([holder, shares]) => ({ holder: holder, shares }))
|
|
642
753
|
.sort((a, b) => (b.shares > a.shares ? 1 : -1));
|
|
643
754
|
}
|
|
644
|
-
/** Every F pool's state in one lens call (the raw manager struct: quote, phi, authority,
|
|
755
|
+
/** Every F pool's state in one lens call (the raw manager struct: quote, phi, authority, lockStrike, seeded, dissolved, successor, n, Q, L, shares, theta0, bigSigma, leverTheta). */
|
|
645
756
|
export async function getFPoolsRaw(c, poolIds) {
|
|
646
757
|
const { logswapLensAbi } = await import("./generated.js");
|
|
647
758
|
return (await c.public.readContract({ address: c.addresses.lens, abi: logswapLensAbi, functionName: "getFPools", args: [poolIds] }));
|