@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 CHANGED
@@ -807,7 +807,12 @@ export interface FPoolSwapArgs {
807
807
  /** Leg index into `bases`. */
808
808
  j: number;
809
809
  amountIn: bigint;
810
- minOut?: bigint;
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, a: {
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?: bigint;
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, a: {
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?: bigint;
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, a: {
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?: bigint;
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 ?? 0n, a.to ?? a.account, a.deadline ?? defaultDeadline()], a.account, [pull]);
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, a) {
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 ?? 2n ** 256n - 1n], a.account);
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, a) {
253
- return writeFPool(c, a.poolId, "burn", [a.owner ?? a.account, a.shares, a.minQuoteOut ?? 0n], a.account);
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, a) {
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 ?? 0n, to, a.deadline ?? defaultDeadline()], a.account);
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 q = `query Ids($pool: String!) {
47
- allIdRegistereds(condition: {poolId: $pool}, first: 1000) { nodes { id floor cap capped } }
48
- }`;
49
- const d = await gql(url, q, { pool: bytea(poolId) }, signal);
50
- return d.allIdRegistereds.nodes.map((n) => ({
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 q = `{ allInitializes(first: 1000) { nodes {
103
- poolId base quote tickSpacing phi x0 blockNumber txHash
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 the indexer keeps
133
- // serving the previous deployment's markets; calling the new manager with those keys makes
134
- // every read revert, refresh-proof (it presented as "phiEff reverted", forever). So verify
135
- // each candidate against the manager the client is actually pointed at, and keep only the
136
- // markets that exist there; if none survive, the chain scan is the answer.
137
- if (markets.length) {
138
- const alive = (await Promise.all(markets.map(async (m) => {
139
- try {
140
- await c.public.readContract({
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 */
@@ -33,8 +33,14 @@ export interface MintArgs extends WriteOptions {
33
33
  cap?: bigint;
34
34
  /** Exposure to add. */
35
35
  L: bigint;
36
- maxBaseIn?: bigint;
37
- maxQuoteIn?: bigint;
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
- maxBaseIn?: bigint;
59
- maxQuoteIn?: bigint;
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. 0 = unbounded. */
76
- minSwapOut?: bigint;
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
- maxIn?: bigint;
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
- maxBaseIn?: bigint;
100
- maxQuoteIn?: bigint;
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
- maxBaseIn?: bigint;
111
- maxQuoteIn?: bigint;
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 defaults below are unbounded for callers who genuinely want that; pass `maxBaseIn` /
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
- minBaseOut?: bigint;
217
- minQuoteOut?: bigint;
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.