@logswap/sdk 0.5.52 → 0.5.54
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/fpool.d.ts +42 -7
- package/dist/fpool.js +52 -14
- package/dist/indexer.d.ts +15 -0
- package/dist/indexer.js +57 -34
- package/dist/liquidity.d.ts +43 -15
- package/dist/liquidity.js +46 -25
- package/dist/pools.d.ts +2 -0
- package/dist/pools.js +6 -14
- package/dist/write.d.ts +8 -0
- package/dist/write.js +16 -1
- package/package.json +1 -1
- package/src/fpool.ts +60 -15
- package/src/indexer.ts +63 -41
- package/src/liquidity.ts +82 -37
- package/src/pools.ts +8 -13
- package/src/write.ts +17 -1
package/dist/fpool.d.ts
CHANGED
|
@@ -807,7 +807,12 @@ export interface FPoolSwapArgs {
|
|
|
807
807
|
/** Leg index into `bases`. */
|
|
808
808
|
j: number;
|
|
809
809
|
amountIn: bigint;
|
|
810
|
-
|
|
810
|
+
/**
|
|
811
|
+
* The least the swap must return. Required: an F swap is exact-in, and without a floor on the
|
|
812
|
+
* output a front-run takes the difference. {@link fPoolQuoteSwap} is exact; bound it with
|
|
813
|
+
* {@link withSlippage} (`"output"`).
|
|
814
|
+
*/
|
|
815
|
+
minOut: bigint;
|
|
811
816
|
/** Who receives the out leg. Defaults to the account. */
|
|
812
817
|
to?: Address;
|
|
813
818
|
/** Absolute unix seconds. Defaulted to a bounded window, as every router write is. */
|
|
@@ -825,10 +830,12 @@ export declare function fPoolSwapBaseForBase(c: LogswapClient, a: FPoolSwapArgs
|
|
|
825
830
|
k: number;
|
|
826
831
|
}): Promise<`0x${string}`>;
|
|
827
832
|
/** Deposit dL/L of every reserve, receive shares ∝ dL. θ is untouched by construction. */
|
|
828
|
-
export declare function fPoolMint(c: LogswapClient,
|
|
833
|
+
export declare function fPoolMint(c: LogswapClient,
|
|
834
|
+
/** `maxQuoteIn` is required: the quote leg is Q·dL/L at landing, and Q moves with every swap. */
|
|
835
|
+
a: {
|
|
829
836
|
poolId: Hex;
|
|
830
837
|
dL: bigint;
|
|
831
|
-
maxQuoteIn
|
|
838
|
+
maxQuoteIn: bigint;
|
|
832
839
|
account: Address;
|
|
833
840
|
to?: Address;
|
|
834
841
|
}): Promise<`0x${string}`>;
|
|
@@ -837,13 +844,28 @@ export declare function fPoolMint(c: LogswapClient, a: {
|
|
|
837
844
|
* By owner since contracts `c782811` (decisions 019): the account burns its own shares; an
|
|
838
845
|
* operator may pass another owner's address, as on the C manager's `burnById`.
|
|
839
846
|
*/
|
|
840
|
-
export declare function fPoolBurn(c: LogswapClient,
|
|
847
|
+
export declare function fPoolBurn(c: LogswapClient,
|
|
848
|
+
/** `minQuoteOut` is required: the quote slice is Q·shares/S at landing. See {@link fPoolPreviewBurn}. */
|
|
849
|
+
a: {
|
|
841
850
|
poolId: Hex;
|
|
842
851
|
shares: bigint;
|
|
843
|
-
minQuoteOut
|
|
852
|
+
minQuoteOut: bigint;
|
|
844
853
|
account: Address;
|
|
845
854
|
owner?: Address;
|
|
846
855
|
}): Promise<`0x${string}`>;
|
|
856
|
+
/**
|
|
857
|
+
* What burning `shares` returns at today's state — the manager's own arithmetic: every reserve and
|
|
858
|
+
* the quote pro rata, rounded down (FPoolManager.burn). Exact until the next swap or mint.
|
|
859
|
+
*/
|
|
860
|
+
export declare function fPoolPreviewBurn(st: Pick<FPoolState, "Q" | "reserves" | "totalSupply">, shares: bigint): {
|
|
861
|
+
quoteOut: bigint;
|
|
862
|
+
basesOut: bigint[];
|
|
863
|
+
};
|
|
864
|
+
/**
|
|
865
|
+
* The quote {@link fPoolMint} pulls for `dL` at today's state: Q·dL/L, rounded up (the pool never
|
|
866
|
+
* under-collects), before any mint fee — so bound it with {@link withSlippage} (`"input"`).
|
|
867
|
+
*/
|
|
868
|
+
export declare function fPoolPreviewMintQuote(st: Pick<FPoolState, "Q" | "L">, dL: bigint): bigint;
|
|
847
869
|
/**
|
|
848
870
|
* Quote needed to zap into exposure `dL`. An UPPER bound: buying each leg raises its mark, so the
|
|
849
871
|
* mint consumes less base than was bought for it and every zap ends with a refund. Size from this
|
|
@@ -874,15 +896,28 @@ export declare function fPoolZapIn(c: LogswapClient, a: {
|
|
|
874
896
|
* cross the floor; only the burn's own quote slice touches the quote leg. Into the quote every
|
|
875
897
|
* base leg is sold, which is the one path a drained quote leg can block.
|
|
876
898
|
*/
|
|
877
|
-
export declare function fPoolZapOut(c: LogswapClient,
|
|
899
|
+
export declare function fPoolZapOut(c: LogswapClient,
|
|
900
|
+
/** `minOut` is required: the sale routes every leg through the curve. See {@link fPoolPreviewZapOut}. */
|
|
901
|
+
a: {
|
|
878
902
|
poolId: Hex;
|
|
879
903
|
shares: bigint;
|
|
880
904
|
tokenOut: Address;
|
|
881
|
-
minOut
|
|
905
|
+
minOut: bigint;
|
|
882
906
|
to?: Address;
|
|
883
907
|
account: Address;
|
|
884
908
|
deadline?: bigint;
|
|
885
909
|
}): Promise<`0x${string}`>;
|
|
910
|
+
/**
|
|
911
|
+
* What {@link fPoolZapOut} returns at today's state: the router's own `zapOut`, simulated as the
|
|
912
|
+
* holder with no bound. Exact, but it needs what the real call needs — the shares, and the router
|
|
913
|
+
* as the holder's ERC-6909 operator — so it throws where the zap-out itself would revert.
|
|
914
|
+
*/
|
|
915
|
+
export declare function fPoolPreviewZapOut(c: LogswapClient, a: {
|
|
916
|
+
poolId: Hex;
|
|
917
|
+
shares: bigint;
|
|
918
|
+
tokenOut: Address;
|
|
919
|
+
account: Address;
|
|
920
|
+
}): Promise<bigint>;
|
|
886
921
|
/**
|
|
887
922
|
* Roll a retired generation's bundle into the pool's live one (decisions 025, 032), on the
|
|
888
923
|
* router under one lock: redeem `shares` of generation `gen` in place — the legs, the quote and
|
package/dist/fpool.js
CHANGED
|
@@ -45,6 +45,7 @@ export { fPoolManagerAbi, padPolicyAbi, polPolicyAbi, privatePolicyAbi, publicPo
|
|
|
45
45
|
* in for the base — a call through it to any of the four policies decodes the base's functions.
|
|
46
46
|
*/
|
|
47
47
|
export const fPolicyBaseAbi = polPolicyAbi;
|
|
48
|
+
import { signerFor } from "./write.js";
|
|
48
49
|
const WAD = 10n ** 18n;
|
|
49
50
|
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
50
51
|
/** The optional questions a policy declared at birth (decisions 044), as bits of `Pool.asks`. */
|
|
@@ -222,7 +223,7 @@ export function fPoolShareValue(s) {
|
|
|
222
223
|
async function fSwap(c, a, kind, k = 0) {
|
|
223
224
|
const st = await getFPool(c, a.poolId);
|
|
224
225
|
const pull = kind === FPoolQuoteKind.QuoteIn ? st.quote : st.bases[a.j];
|
|
225
|
-
return writeRouter(c, "swapExactIn", [a.poolId, kind, BigInt(a.j), BigInt(k), a.amountIn, a.minOut
|
|
226
|
+
return writeRouter(c, "swapExactIn", [a.poolId, kind, BigInt(a.j), BigInt(k), a.amountIn, a.minOut, a.to ?? a.account, a.deadline ?? defaultDeadline()], a.account, [pull]);
|
|
226
227
|
}
|
|
227
228
|
export async function fPoolSwapQuoteIn(c, a) {
|
|
228
229
|
return fSwap(c, a, FPoolQuoteKind.QuoteIn);
|
|
@@ -240,17 +241,37 @@ export async function fPoolSwapBaseForBase(c, a) {
|
|
|
240
241
|
}
|
|
241
242
|
// ─── liquidity ────────────────────────────────────────────────────────────────
|
|
242
243
|
/** Deposit dL/L of every reserve, receive shares ∝ dL. θ is untouched by construction. */
|
|
243
|
-
export async function fPoolMint(c,
|
|
244
|
+
export async function fPoolMint(c,
|
|
245
|
+
/** `maxQuoteIn` is required: the quote leg is Q·dL/L at landing, and Q moves with every swap. */
|
|
246
|
+
a) {
|
|
244
247
|
// shares go to `to` (the account by default); on a gated pool `to` must be allowed
|
|
245
|
-
return writeFPool(c, a.poolId, "mint", [a.to ?? a.account, a.dL, a.maxQuoteIn
|
|
248
|
+
return writeFPool(c, a.poolId, "mint", [a.to ?? a.account, a.dL, a.maxQuoteIn], a.account);
|
|
246
249
|
}
|
|
247
250
|
/**
|
|
248
251
|
* Burn shares for the pro-rata slice of every reserve. Available at any Q, and after dissolution.
|
|
249
252
|
* By owner since contracts `c782811` (decisions 019): the account burns its own shares; an
|
|
250
253
|
* operator may pass another owner's address, as on the C manager's `burnById`.
|
|
251
254
|
*/
|
|
252
|
-
export async function fPoolBurn(c,
|
|
253
|
-
|
|
255
|
+
export async function fPoolBurn(c,
|
|
256
|
+
/** `minQuoteOut` is required: the quote slice is Q·shares/S at landing. See {@link fPoolPreviewBurn}. */
|
|
257
|
+
a) {
|
|
258
|
+
return writeFPool(c, a.poolId, "burn", [a.owner ?? a.account, a.shares, a.minQuoteOut], a.account);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* What burning `shares` returns at today's state — the manager's own arithmetic: every reserve and
|
|
262
|
+
* the quote pro rata, rounded down (FPoolManager.burn). Exact until the next swap or mint.
|
|
263
|
+
*/
|
|
264
|
+
export function fPoolPreviewBurn(st, shares) {
|
|
265
|
+
if (st.totalSupply === 0n)
|
|
266
|
+
return { quoteOut: 0n, basesOut: st.reserves.map(() => 0n) };
|
|
267
|
+
return { quoteOut: (st.Q * shares) / st.totalSupply, basesOut: st.reserves.map((r) => (r * shares) / st.totalSupply) };
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* The quote {@link fPoolMint} pulls for `dL` at today's state: Q·dL/L, rounded up (the pool never
|
|
271
|
+
* under-collects), before any mint fee — so bound it with {@link withSlippage} (`"input"`).
|
|
272
|
+
*/
|
|
273
|
+
export function fPoolPreviewMintQuote(st, dL) {
|
|
274
|
+
return st.L === 0n ? 0n : (st.Q * dL + st.L - 1n) / st.L;
|
|
254
275
|
}
|
|
255
276
|
// ─── zaps (on the router — one router, one Permit2 spender) ───────────────────
|
|
256
277
|
/**
|
|
@@ -288,9 +309,26 @@ export async function fPoolZapIn(c, a) {
|
|
|
288
309
|
* cross the floor; only the burn's own quote slice touches the quote leg. Into the quote every
|
|
289
310
|
* base leg is sold, which is the one path a drained quote leg can block.
|
|
290
311
|
*/
|
|
291
|
-
export async function fPoolZapOut(c,
|
|
312
|
+
export async function fPoolZapOut(c,
|
|
313
|
+
/** `minOut` is required: the sale routes every leg through the curve. See {@link fPoolPreviewZapOut}. */
|
|
314
|
+
a) {
|
|
292
315
|
const to = a.to ?? a.account;
|
|
293
|
-
return writeRouter(c, "zapOut", [a.poolId, a.shares, a.tokenOut, a.minOut
|
|
316
|
+
return writeRouter(c, "zapOut", [a.poolId, a.shares, a.tokenOut, a.minOut, to, a.deadline ?? defaultDeadline()], a.account);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* What {@link fPoolZapOut} returns at today's state: the router's own `zapOut`, simulated as the
|
|
320
|
+
* holder with no bound. Exact, but it needs what the real call needs — the shares, and the router
|
|
321
|
+
* as the holder's ERC-6909 operator — so it throws where the zap-out itself would revert.
|
|
322
|
+
*/
|
|
323
|
+
export async function fPoolPreviewZapOut(c, a) {
|
|
324
|
+
const { result } = await c.public.simulateContract({
|
|
325
|
+
address: c.addresses.router,
|
|
326
|
+
abi: logswapRouterAbi,
|
|
327
|
+
functionName: "zapOut",
|
|
328
|
+
args: [a.poolId, a.shares, a.tokenOut, 0n, a.account, defaultDeadline()],
|
|
329
|
+
account: a.account,
|
|
330
|
+
});
|
|
331
|
+
return result;
|
|
294
332
|
}
|
|
295
333
|
/**
|
|
296
334
|
* Roll a retired generation's bundle into the pool's live one (decisions 025, 032), on the
|
|
@@ -373,7 +411,7 @@ async function writeFPool(c, poolId, functionName, args, account) {
|
|
|
373
411
|
// one place all seven helpers share, is what keeps a caller from ever omitting it. The `as
|
|
374
412
|
// never` casts blind tsc to arg counts, so this file's encode test is the real guard.
|
|
375
413
|
args: [poolId, ...args],
|
|
376
|
-
account,
|
|
414
|
+
account: signerFor(c, account),
|
|
377
415
|
});
|
|
378
416
|
return wallet.writeContract(request);
|
|
379
417
|
}
|
|
@@ -385,7 +423,7 @@ async function writeFPool0(c, functionName, args, account) {
|
|
|
385
423
|
abi: fPoolManagerAbi,
|
|
386
424
|
functionName,
|
|
387
425
|
args,
|
|
388
|
-
account,
|
|
426
|
+
account: signerFor(c, account),
|
|
389
427
|
});
|
|
390
428
|
return wallet.writeContract(request);
|
|
391
429
|
}
|
|
@@ -410,7 +448,7 @@ pullTokens = []) {
|
|
|
410
448
|
abi: logswapRouterAbi,
|
|
411
449
|
functionName: fn,
|
|
412
450
|
args: sendArgs,
|
|
413
|
-
account,
|
|
451
|
+
account: signerFor(c, account),
|
|
414
452
|
});
|
|
415
453
|
return wallet.writeContract(request);
|
|
416
454
|
}
|
|
@@ -465,7 +503,7 @@ export async function approveRouterForFPool(c, account) {
|
|
|
465
503
|
abi: fPoolManagerAbi,
|
|
466
504
|
functionName: "setOperator",
|
|
467
505
|
args: [c.addresses.router, true],
|
|
468
|
-
account,
|
|
506
|
+
account: signerFor(c, account),
|
|
469
507
|
});
|
|
470
508
|
return wallet.writeContract(request);
|
|
471
509
|
}
|
|
@@ -863,7 +901,7 @@ async function writePolicy(c, abi, a, functionName, args) {
|
|
|
863
901
|
abi,
|
|
864
902
|
functionName,
|
|
865
903
|
args: [a.poolId, ...args],
|
|
866
|
-
account: a.account,
|
|
904
|
+
account: signerFor(c, a.account),
|
|
867
905
|
});
|
|
868
906
|
return wallet.writeContract(request);
|
|
869
907
|
}
|
|
@@ -934,7 +972,7 @@ export async function fPoolSetSponsorName(c, a) {
|
|
|
934
972
|
abi: policyRegistryAbi,
|
|
935
973
|
functionName: "setSponsorName",
|
|
936
974
|
args: [a.who, textToBytes32(a.name)],
|
|
937
|
-
account: a.account,
|
|
975
|
+
account: signerFor(c, a.account),
|
|
938
976
|
});
|
|
939
977
|
return wallet.writeContract(request);
|
|
940
978
|
}
|
|
@@ -998,7 +1036,7 @@ export async function fPoolTransferShares(c, a) {
|
|
|
998
1036
|
abi: fPoolManagerAbi,
|
|
999
1037
|
functionName: "transfer",
|
|
1000
1038
|
args: [a.to, fPoolShareId(a.poolId, gen), a.shares],
|
|
1001
|
-
account: a.account,
|
|
1039
|
+
account: signerFor(c, a.account),
|
|
1002
1040
|
});
|
|
1003
1041
|
return wallet.writeContract(request);
|
|
1004
1042
|
}
|
package/dist/indexer.d.ts
CHANGED
|
@@ -25,7 +25,22 @@ export interface IndexerError {
|
|
|
25
25
|
* `Syntax Error: Invalid character escape sequence`. Variables carry the value untouched.
|
|
26
26
|
*/
|
|
27
27
|
export declare function bytea(hex: Hex): string;
|
|
28
|
+
/** How long an indexer call may take before the caller falls back (to the chain, or an empty view). */
|
|
29
|
+
export declare const INDEXER_TIMEOUT_MS = 8000;
|
|
28
30
|
export declare function gql<T>(url: string, query: string, variables?: Record<string, unknown>, signal?: AbortSignal): Promise<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Every row of a connection, page by page. A single `first: 1000` silently drops row 1001 onward —
|
|
33
|
+
* a pool's 1001st position class, a deployment's 1001st market — with nothing to say so. Pages by
|
|
34
|
+
* cursor in primary-key order, so rows inserted while paging cannot shift the window.
|
|
35
|
+
*/
|
|
36
|
+
export declare function gqlAll<N>(url: string, field: string, selection: string, opts?: {
|
|
37
|
+
vars?: Record<string, {
|
|
38
|
+
type: string;
|
|
39
|
+
value: unknown;
|
|
40
|
+
}>;
|
|
41
|
+
condition?: string;
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
}): Promise<N[]>;
|
|
29
44
|
/** A position class as the chain registered it: the floor/cap geometry, fixed for the id's life. */
|
|
30
45
|
export interface RegisteredId {
|
|
31
46
|
id: bigint;
|
package/dist/indexer.js
CHANGED
|
@@ -25,15 +25,24 @@ import { NO_CAP } from "./ids.js";
|
|
|
25
25
|
export function bytea(hex) {
|
|
26
26
|
return `\\x${hex.replace(/^0x/, "")}`;
|
|
27
27
|
}
|
|
28
|
+
/** How long an indexer call may take before the caller falls back (to the chain, or an empty view). */
|
|
29
|
+
export const INDEXER_TIMEOUT_MS = 8_000;
|
|
28
30
|
export async function gql(url, query, variables, signal) {
|
|
31
|
+
// A hung indexer must not hang its caller: every consumer has a fallback, and it only runs when
|
|
32
|
+
// this call ends. The caller's signal, if any, still wins.
|
|
29
33
|
const res = await fetch(url, {
|
|
30
34
|
method: "POST",
|
|
31
35
|
headers: { "content-type": "application/json" },
|
|
32
36
|
body: JSON.stringify({ query, variables }),
|
|
33
|
-
signal,
|
|
37
|
+
signal: signal ?? AbortSignal.timeout(INDEXER_TIMEOUT_MS),
|
|
34
38
|
});
|
|
35
39
|
if (!res.ok)
|
|
36
40
|
throw new Error(`indexer: HTTP ${res.status} from ${url}`);
|
|
41
|
+
// an Access login page or a proxy's error page answers 200 with HTML; say so, rather than
|
|
42
|
+
// "Unexpected token <"
|
|
43
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
44
|
+
if (!ct.includes("json"))
|
|
45
|
+
throw new Error(`indexer: ${url} answered ${ct || "no content-type"}, not JSON (a login page?)`);
|
|
37
46
|
const body = (await res.json());
|
|
38
47
|
if (body.errors?.length)
|
|
39
48
|
throw new Error(`indexer: ${body.errors.map((e) => e.message).join("; ")}`);
|
|
@@ -41,13 +50,43 @@ export async function gql(url, query, variables, signal) {
|
|
|
41
50
|
throw new Error("indexer: empty response");
|
|
42
51
|
return body.data;
|
|
43
52
|
}
|
|
53
|
+
const PAGE = 1000;
|
|
54
|
+
const MAX_PAGES = 100;
|
|
55
|
+
/**
|
|
56
|
+
* Every row of a connection, page by page. A single `first: 1000` silently drops row 1001 onward —
|
|
57
|
+
* a pool's 1001st position class, a deployment's 1001st market — with nothing to say so. Pages by
|
|
58
|
+
* cursor in primary-key order, so rows inserted while paging cannot shift the window.
|
|
59
|
+
*/
|
|
60
|
+
export async function gqlAll(url, field, selection, opts = {}) {
|
|
61
|
+
const vars = opts.vars ?? {};
|
|
62
|
+
const decl = Object.entries(vars).map(([k, v]) => `, $${k}: ${v.type}`).join("");
|
|
63
|
+
const cond = opts.condition ? `, condition: ${opts.condition}` : "";
|
|
64
|
+
const q = `query Page($after: Cursor${decl}) {
|
|
65
|
+
${field}(first: ${PAGE}, after: $after, orderBy: PRIMARY_KEY_ASC${cond}) {
|
|
66
|
+
pageInfo { hasNextPage endCursor } nodes { ${selection} }
|
|
67
|
+
}
|
|
68
|
+
}`;
|
|
69
|
+
const values = Object.fromEntries(Object.entries(vars).map(([k, v]) => [k, v.value]));
|
|
70
|
+
const out = [];
|
|
71
|
+
let after = null;
|
|
72
|
+
for (let i = 0; i < MAX_PAGES; i++) {
|
|
73
|
+
const d = await gql(url, q, { ...values, after }, opts.signal);
|
|
74
|
+
const page = d[field];
|
|
75
|
+
out.push(...page.nodes);
|
|
76
|
+
if (!page.pageInfo.hasNextPage || !page.pageInfo.endCursor)
|
|
77
|
+
return out;
|
|
78
|
+
after = page.pageInfo.endCursor;
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`indexer: ${field} has more than ${PAGE * MAX_PAGES} rows`);
|
|
81
|
+
}
|
|
44
82
|
/** Every position class ever registered in a pool. Historical — some may since have burnt to zero. */
|
|
45
83
|
export async function registeredIds(url, poolId, signal) {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
84
|
+
const nodes = await gqlAll(url, "allIdRegistereds", "id floor cap capped", {
|
|
85
|
+
vars: { pool: { type: "String!", value: bytea(poolId) } },
|
|
86
|
+
condition: "{poolId: $pool}",
|
|
87
|
+
signal,
|
|
88
|
+
});
|
|
89
|
+
return nodes.map((n) => ({
|
|
51
90
|
id: BigInt(n.id),
|
|
52
91
|
floor: BigInt(n.floor),
|
|
53
92
|
cap: BigInt(n.cap),
|
|
@@ -99,11 +138,8 @@ export async function indexerHealthy(url, signal) {
|
|
|
99
138
|
* everywhere else, so a wrong id fails loudly instead of addressing the wrong pool.
|
|
100
139
|
*/
|
|
101
140
|
export async function discoverMarketsIndexed(url, signal) {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
} } }`;
|
|
105
|
-
const d = await gql(url, q, undefined, signal);
|
|
106
|
-
return d.allInitializes.nodes.map((n) => {
|
|
141
|
+
const nodes = await gqlAll(url, "allInitializes", "poolId base quote tickSpacing phi x0 blockNumber txHash contractAddress", { signal });
|
|
142
|
+
return nodes.map((n) => {
|
|
107
143
|
const key = {
|
|
108
144
|
base: n.base,
|
|
109
145
|
quote: n.quote,
|
|
@@ -116,6 +152,7 @@ export async function discoverMarketsIndexed(url, signal) {
|
|
|
116
152
|
x0: BigInt(n.x0),
|
|
117
153
|
blockNumber: BigInt(n.blockNumber),
|
|
118
154
|
transactionHash: n.txHash,
|
|
155
|
+
manager: n.contractAddress,
|
|
119
156
|
};
|
|
120
157
|
});
|
|
121
158
|
}
|
|
@@ -129,29 +166,15 @@ export async function discoverMarketsBest(c, url, signal) {
|
|
|
129
166
|
if (url) {
|
|
130
167
|
try {
|
|
131
168
|
const markets = await discoverMarketsIndexed(url, signal);
|
|
132
|
-
// The indexer is a CACHE and the chain is the truth. After a redeploy
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
address: c.addresses.cPoolManager,
|
|
142
|
-
abi: cPoolManagerAbi,
|
|
143
|
-
functionName: "phiOf",
|
|
144
|
-
args: [m.key],
|
|
145
|
-
});
|
|
146
|
-
return m;
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
}))).filter((m) => m !== null);
|
|
152
|
-
if (alive.length)
|
|
153
|
-
return { markets: alive, source: "indexer" };
|
|
154
|
-
}
|
|
169
|
+
// The indexer is a CACHE and the chain is the truth. After a redeploy it keeps serving the
|
|
170
|
+
// previous deployment's markets, and calling the new manager with those keys makes every
|
|
171
|
+
// read revert, refresh-proof. Each row names the manager that emitted it, so keep only the
|
|
172
|
+
// rows of the manager this client is pointed at — no read per row. None left means the
|
|
173
|
+
// indexer has not seen this deployment: the chain scan is the answer.
|
|
174
|
+
const mine = c.addresses.cPoolManager.toLowerCase();
|
|
175
|
+
const alive = markets.filter((m) => m.manager?.toLowerCase() === mine);
|
|
176
|
+
if (alive.length)
|
|
177
|
+
return { markets: alive, source: "indexer" };
|
|
155
178
|
}
|
|
156
179
|
catch {
|
|
157
180
|
/* fall through — a missing indexer must not cost you the market list */
|
package/dist/liquidity.d.ts
CHANGED
|
@@ -33,8 +33,14 @@ export interface MintArgs extends WriteOptions {
|
|
|
33
33
|
cap?: bigint;
|
|
34
34
|
/** Exposure to add. */
|
|
35
35
|
L: bigint;
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/**
|
|
37
|
+
* The most the mint may pull, per token. REQUIRED: the pulled amounts depend on spot when the
|
|
38
|
+
* transaction lands, so an unbounded mint can be sandwiched into depositing far more than the
|
|
39
|
+
* preview showed. Derive them from the preview with {@link withSlippage} (`"input"`); pass
|
|
40
|
+
* {@link UNBOUNDED} only where the price cannot move under you.
|
|
41
|
+
*/
|
|
42
|
+
maxBaseIn: bigint;
|
|
43
|
+
maxQuoteIn: bigint;
|
|
38
44
|
}
|
|
39
45
|
/**
|
|
40
46
|
* Mint at `targetFloor`, blending across the two adjacent rungs when it is off-grid.
|
|
@@ -55,8 +61,9 @@ export interface CreateMarketArgs extends WriteOptions {
|
|
|
55
61
|
targetFloor: bigint;
|
|
56
62
|
/** The founding exposure; must exceed the manager's `minBackstopL` lock. */
|
|
57
63
|
L: bigint;
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
/** Required, as on {@link mint}. Creation is atomic at `x0`, so the flows are fixed by the arguments; {@link UNBOUNDED} is safe here only for that reason. */
|
|
65
|
+
maxBaseIn: bigint;
|
|
66
|
+
maxQuoteIn: bigint;
|
|
60
67
|
}
|
|
61
68
|
/**
|
|
62
69
|
* Create a market ATOMICALLY: `initialize` at `x0` plus the backstop-establishing first mint in
|
|
@@ -72,12 +79,13 @@ export interface ZapArgs extends WriteOptions {
|
|
|
72
79
|
/** Fund entirely from base (true) or entirely from quote (false). */
|
|
73
80
|
fundWithBase: boolean;
|
|
74
81
|
amountIn: bigint;
|
|
75
|
-
/** The least the swap leg may return (contracts 7c82eed, decisions 043): the zap's own slippage bound, as `swapExactIn`'s.
|
|
76
|
-
minSwapOut
|
|
82
|
+
/** The least the swap leg may return (contracts 7c82eed, decisions 043): the zap's own slippage bound, as `swapExactIn`'s. Required. */
|
|
83
|
+
minSwapOut: bigint;
|
|
77
84
|
targetFloor: bigint;
|
|
78
85
|
cap?: bigint;
|
|
79
86
|
L: bigint;
|
|
80
|
-
|
|
87
|
+
/** The most of the funding token the zap may pull. Required. */
|
|
88
|
+
maxIn: bigint;
|
|
81
89
|
}
|
|
82
90
|
/**
|
|
83
91
|
* Fund a mint from a single asset: the router swaps the imbalanced part through the curve, then
|
|
@@ -96,8 +104,9 @@ export interface UpdateArgs extends WriteOptions {
|
|
|
96
104
|
toCap?: bigint;
|
|
97
105
|
/** New exposure. `0n` exits the position entirely. */
|
|
98
106
|
newL: bigint;
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
/** Required: see {@link MintArgs}. {@link previewEdit} gives the exact flows to bound. */
|
|
108
|
+
maxBaseIn: bigint;
|
|
109
|
+
maxQuoteIn: bigint;
|
|
101
110
|
}
|
|
102
111
|
/** The primitive, through the router's named entry: resize and reposition in one call, in kind. */
|
|
103
112
|
export declare function update(c: LogswapClient, a: UpdateArgs): Promise<Hash>;
|
|
@@ -107,16 +116,16 @@ export interface MoveArgs extends WriteOptions {
|
|
|
107
116
|
shares: bigint;
|
|
108
117
|
toFloor: bigint;
|
|
109
118
|
toCap?: bigint;
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
/** Required: see {@link MintArgs}. */
|
|
120
|
+
maxBaseIn: bigint;
|
|
121
|
+
maxQuoteIn: bigint;
|
|
112
122
|
}
|
|
113
123
|
/**
|
|
114
124
|
* Reposition at constant exposure — the L-pinned parent of `floor`, `cap` and `shift`; in kind.
|
|
115
125
|
*
|
|
116
126
|
* **Bound it.** The router used to settle this entry with no limit, which meant a reposition that
|
|
117
127
|
* priced differently by inclusion time could pull the whole allowance (security-review2 M-01).
|
|
118
|
-
* The
|
|
119
|
-
* `maxQuoteIn` from a preview for anything user-facing.
|
|
128
|
+
* The bounds are required; {@link UNBOUNDED} is an explicit, visible opt-out.
|
|
120
129
|
*/
|
|
121
130
|
export declare function move(c: LogswapClient, a: MoveArgs): Promise<Hash>;
|
|
122
131
|
/** Which token the edit settles in. `kind` moves both legs; `base` / `quote` nets the other side through the curve. */
|
|
@@ -128,6 +137,13 @@ export interface EditArgs extends WriteOptions, PairBounds {
|
|
|
128
137
|
shares: bigint;
|
|
129
138
|
/** Default `kind`. */
|
|
130
139
|
settleIn?: SettleIn;
|
|
140
|
+
/**
|
|
141
|
+
* Tolerance in basis points, applied to the flows the edit is expected to move (the lens's
|
|
142
|
+
* exact preview, plus a quote of the netting swap when `settleIn` is one token). Required: an
|
|
143
|
+
* edit's flows depend on spot when it lands. An explicit bound in `PairBounds` overrides the one
|
|
144
|
+
* derived for that token.
|
|
145
|
+
*/
|
|
146
|
+
slippageBps: number;
|
|
131
147
|
}
|
|
132
148
|
export interface EditPreview {
|
|
133
149
|
/** Signed, + = the caller deposits; the mint fee is inside `dQuote`. */
|
|
@@ -146,11 +162,22 @@ export declare function previewEdit(c: LogswapClient, a: {
|
|
|
146
162
|
toCap?: bigint;
|
|
147
163
|
newL: bigint;
|
|
148
164
|
}): Promise<EditPreview>;
|
|
165
|
+
/**
|
|
166
|
+
* Bounds for a pair of signed flows (+ = the caller pays, − = the caller receives), at `bps` of
|
|
167
|
+
* tolerance: a payment is capped from above, a receipt floored from below, and a token that does
|
|
168
|
+
* not move in that direction is bound to zero.
|
|
169
|
+
*/
|
|
170
|
+
export declare function boundsFromFlows(dBase: bigint, dQuote: bigint, bps: number): Required<PairBounds>;
|
|
149
171
|
/**
|
|
150
172
|
* The general edit as a batch: `[update]`, plus — when `settleIn` is one token — the one swap that
|
|
151
173
|
* nets the other. The other side's flow is read off the preview: released → sold with an exact-in
|
|
152
174
|
* of `OPEN_DELTA`; owed → bought with an exact-out of `OPEN_DELTA`. The settlement then binds the
|
|
153
175
|
* other token to zero both ways, so a stale preview fails loudly rather than moving it.
|
|
176
|
+
*
|
|
177
|
+
* **Bounded by default.** The preview gives the edit's exact flows at today's state; `slippageBps`
|
|
178
|
+
* turns them into the settlement's bounds. With `settleIn` one token, the netting swap is quoted
|
|
179
|
+
* on today's curve, before the update changes the active liquidity — close for a small edit, and
|
|
180
|
+
* for a large one a bound that reverts rather than one that lets a sandwich through.
|
|
154
181
|
*/
|
|
155
182
|
export declare function edit(c: LogswapClient, a: EditArgs & {
|
|
156
183
|
toFloor: bigint;
|
|
@@ -213,8 +240,9 @@ export interface BurnArgs extends WriteOptions {
|
|
|
213
240
|
key: PoolKey;
|
|
214
241
|
ids: readonly bigint[];
|
|
215
242
|
shares: readonly bigint[];
|
|
216
|
-
|
|
217
|
-
|
|
243
|
+
/** The least of each token the burn must return. Required: a burn's legs depend on spot. */
|
|
244
|
+
minBaseOut: bigint;
|
|
245
|
+
minQuoteOut: bigint;
|
|
218
246
|
}
|
|
219
247
|
/**
|
|
220
248
|
* Exit, in kind, across one or more classes. Accrued fees are swept into the same settlement.
|