@logswap/sdk 0.5.73 → 0.5.74

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/src/indexer.ts CHANGED
@@ -258,12 +258,7 @@ export async function poolPositionsFromChain(c: LogswapClient, key: PoolKey, x:
258
258
  functionName: "idOf",
259
259
  args: [key, floor],
260
260
  } as never)) as bigint;
261
- const [L, shares] = (await c.public.readContract({
262
- address: c.addresses.cPoolManager,
263
- abi: cPoolManagerAbi,
264
- functionName: "positions",
265
- args: [id],
266
- } as never)) as readonly [bigint, bigint, bigint, bigint];
261
+ const { L, shares } = await getPositionClass(c, id); // through extsload (C version 7)
267
262
  if (L === 0n) return null;
268
263
  return { id, poolId: pool, floor, cap: NO_CAP, capped: false, L, shares, active: x >= floor } as PoolPosition;
269
264
  }),
package/src/keys.ts CHANGED
@@ -15,7 +15,9 @@ import { encodeAbiParameters, keccak256, type Address, type Hex } from "viem";
15
15
 
16
16
  /**
17
17
  * The fee domain of the family, `FeeParams.FEE_MIN` / `FEE_MAX` (decisions 043): one bound for a
18
- * C market's φ and an F pool's alike, WAD. 1 bp and 1%. The F manager no longer answers
18
+ * C market's φ and an F pool's alike, WAD. 1 bp and 1% — plus 0, which since C version 7 / F
19
+ * version 26 is a fee-free market or pool, admitted only if the policy lists it (decisions 065).
20
+ * The mint fee lives in the same domain. The F manager no longer answers
19
21
  * `FEE_MIN()` / `FEE_MAX()` (F version 21 cut the getters for bytecode); every F policy still
20
22
  * checks φ against these at `onInitialize`, and the C side re-exposes them through the lens's
21
23
  * `cParams()`. Constants here, so a create form bounds φ with no read.
@@ -34,7 +36,8 @@ export interface PoolKey {
34
36
  tickSpacing: bigint;
35
37
  /**
36
38
  * The swap fee, WAD, fixed for the life of the market: {@link FEE_MIN} (1 bp) ≤ φ ≤ {@link FEE_MAX} (1%),
37
- * or `0n`, which selects `DEFAULT_FEE` (2 bp) at read time. Four fields since C version 4
39
+ * or `0n`, which since C version 7 is NO fee (decisions 065; `DEFAULT_FEE` is gone). The gate
40
+ * admits only its listed tiers (064: 2, 5, 10, 25, 50, 100 bp). Four fields since C version 4
38
41
  *: `kappa` and `alpha` left with the variance kernel, and `phiMin` — a floor with
39
42
  * nothing above it — became `phi`, named `swapFee` since C version 6 (decisions 059). The name
40
43
  * is not part of the encoding: no pool id changed with it.
@@ -57,10 +60,9 @@ const POOL_KEY_ABI = [
57
60
  /**
58
61
  * `poolId = keccak256(abi.encode(key))` — the primary key of the entire read model.
59
62
  *
60
- * Note the zero-vs-default caveat the Solidity type documents: a `swapFee` of `0` resolves to
61
- * `DEFAULT_FEE` at READ time, so a key carrying `0n` and one carrying the resolved default are
62
- * DIFFERENT markets with different ids, even though they behave identically. Always address a
63
- * market by the key it was initialized with.
63
+ * A key's `swapFee` is the market's fee as written — `0n` is a fee-free market since C version 7
64
+ * (decisions 065), not the 2 bp default it selected before. Always address a market by the key it
65
+ * was initialized with.
64
66
  */
65
67
  export function poolId(key: PoolKey): Hex {
66
68
  return keccak256(
package/src/liquidity.ts CHANGED
@@ -27,7 +27,7 @@ import { logswapLensAbi, logswapRouterAbi } from "./generated.js";
27
27
  import type { LogswapClient } from "./client.js";
28
28
  import type { PoolKey } from "./keys.js";
29
29
  import { NO_CAP } from "./ids.js";
30
- import { describeId, getPositionClass } from "./positions.js";
30
+ import { describeId, getPositionClass, presetMintFee } from "./positions.js";
31
31
  import {
32
32
  execute,
33
33
  OPEN_DELTA,
@@ -105,6 +105,11 @@ export interface CreateMarketArgs extends WriteOptions {
105
105
  targetFloor: bigint;
106
106
  /** The founding exposure; must exceed the manager's `minBackstopL` lock. */
107
107
  L: bigint;
108
+ /**
109
+ * The market's mint fee, WAD of newly-earning L — the creator's pick from the gate's menu
110
+ * (decisions 065). Defaults to {@link presetMintFee}: twice the swap fee, capped at `FEE_MAX`.
111
+ */
112
+ mintFee?: bigint;
108
113
  /** 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. */
109
114
  maxBaseIn: bigint;
110
115
  maxQuoteIn: bigint;
@@ -112,7 +117,9 @@ export interface CreateMarketArgs extends WriteOptions {
112
117
 
113
118
  /**
114
119
  * Create a market ATOMICALLY: `initialize` at `x0` plus the backstop-establishing first mint in
115
- * one transaction — the canonical creation path. The
120
+ * one transaction — the canonical creation path. Under the C gate (064) the router is a trusted
121
+ * forwarder: the gate checks the router's `user()` — the wallet sending this — against the
122
+ * creator list, so a listed creator opens and funds a market in this one call. The
116
123
  * two-step path let an attacker seat the PERMANENT backstop at a hostile rung between an honest
117
124
  * `initialize` and its first mint; here either both land or neither, and a front-run of the whole
118
125
  * call reverts it without moving a wei. No cap: the first mint IS the backstop and must be
@@ -125,6 +132,7 @@ export async function createMarket(c: LogswapClient, a: CreateMarketArgs): Promi
125
132
  args: [
126
133
  a.key,
127
134
  a.x0,
135
+ a.mintFee ?? presetMintFee(a.key.swapFee),
128
136
  a.targetFloor,
129
137
  a.L,
130
138
  a.maxBaseIn,
package/src/pools.ts CHANGED
@@ -18,8 +18,12 @@ import type { LogswapClient } from "./client.js";
18
18
  import { signerFor } from "./write.js";
19
19
  import { readCachedToken, registerCacheClear, writeCachedToken } from "./tokencache.js";
20
20
  import { poolId, type PoolKey } from "./keys.js";
21
+ import { presetMintFee } from "./positions.js";
21
22
 
22
- /** The manager's `getPool` view, decoded. Field-for-field with `PoolView` in the contract. */
23
+ /**
24
+ * A market's scalar state, field-for-field with `CPoolState.PoolView` — what the manager's `getPool`
25
+ * returned before C version 7 moved it behind `extsload` (decisions 065); read through the lens.
26
+ */
23
27
  export interface PoolState {
24
28
  initialized: boolean;
25
29
  backstopSeeded: boolean;
@@ -37,32 +41,31 @@ export interface PoolState {
37
41
  protocolOwed: bigint;
38
42
  minBackstopL: bigint;
39
43
  backstopFloor: bigint;
44
+ /** The market's mint fee, WAD of newly-earning L: proposed at birth, the gate's answer (065). */
45
+ mintFee: bigint;
40
46
  }
41
47
 
42
- /** Read a market's full state by key. */
48
+ /** Read a market's full state by key, through the lens (`getPools`; C version 7). All zero, `initialized` false, for a market nobody opened. */
43
49
  export async function getPool(c: LogswapClient, key: PoolKey): Promise<PoolState> {
44
- const v = await c.public.readContract({
45
- address: c.addresses.cPoolManager,
46
- abi: cPoolManagerAbi,
47
- functionName: "getPool",
48
- args: [poolId(key)],
49
- });
50
- return v as unknown as PoolState;
50
+ const v = (await c.public.readContract({
51
+ address: c.addresses.lens,
52
+ abi: logswapLensAbi,
53
+ functionName: "getPools",
54
+ args: [[poolId(key)]],
55
+ })) as readonly unknown[];
56
+ return v[0] as PoolState;
51
57
  }
52
58
 
53
59
  /**
54
- * The market's swap fee: the key's `swapFee` with `0n` resolved to `DEFAULT_FEE` — the manager's
55
- * `swapFeeOf` (`phiOf` before C version 6). Constant for the market's life (C version 4 — `phiEff`
56
- * and the variance kernel before). Reverts for a key nobody initialized, which `discoverMarkets`
57
- * relies on to drop a stale indexer's rows. (`swapFeeOf` in `fees.ts` is the fee one swap paid.)
60
+ * The market's swap fee — the key's own `swapFee` since C version 7: a fee of 0 is no fee, and
61
+ * `DEFAULT_FEE` / the manager's `swapFeeOf` are gone (decisions 065). Constant for the market's
62
+ * life. Still a read: it refuses a key nobody initialized, which `discoverMarkets` relies on to
63
+ * drop a stale indexer's rows. (`swapFeeOf` in `fees.ts` is the fee one swap paid.)
58
64
  */
59
65
  export async function marketSwapFee(c: LogswapClient, key: PoolKey): Promise<bigint> {
60
- return c.public.readContract({
61
- address: c.addresses.cPoolManager,
62
- abi: cPoolManagerAbi,
63
- functionName: "swapFeeOf",
64
- args: [key],
65
- }) as Promise<bigint>;
66
+ const s = await getPool(c, key);
67
+ if (!s.initialized) throw new Error(`logswap: market ${poolId(key)} is not initialized`);
68
+ return key.swapFee;
66
69
  }
67
70
 
68
71
  /**
@@ -417,16 +420,18 @@ export async function marketOpenedAt(c: LogswapClient, m: DiscoveredMarket): Pro
417
420
  }
418
421
 
419
422
  /**
420
- * Create a C market: `initialize(key, x0)` on the manager — a call, not a deployment. Manager-
421
- * direct by design: creation moves no funds, so the router adds nothing. The pool id is
422
- * `poolId(key)` (pure) and the market is tradeable immediately; the first mint funds it.
423
+ * Create a C market: `initialize(key, x0, mintFee)` on the manager — a call, not a deployment, and
424
+ * no funds move. The mint fee is the creator's pick from the gate's menu (065), preset at twice the
425
+ * swap fee ({@link presetMintFee}). Under a curated gate the CALLER must be a listed creator; the
426
+ * canonical path is the router's {@link createMarket}, which forwards its user to the gate and
427
+ * mints the backstop in the same transaction (064).
423
428
  */
424
- export async function initializeMarket(c: LogswapClient, a: { key: PoolKey; x0: bigint; account: Address }) {
429
+ export async function initializeMarket(c: LogswapClient, a: { key: PoolKey; x0: bigint; mintFee?: bigint; account: Address }) {
425
430
  const { request } = await c.public.simulateContract({
426
431
  address: c.addresses.cPoolManager,
427
432
  abi: cPoolManagerAbi,
428
433
  functionName: "initialize",
429
- args: [a.key, a.x0],
434
+ args: [a.key, a.x0, a.mintFee ?? presetMintFee(a.key.swapFee)],
430
435
  account: signerFor(c, a.account),
431
436
  } as never);
432
437
  return c.wallet!.writeContract(request as never);
package/src/positions.ts CHANGED
@@ -6,11 +6,15 @@
6
6
  * accounts holding the same class can have different claimable amounts.
7
7
  */
8
8
 
9
- import { parseAbiItem } from "viem";
10
- import type { Address } from "viem";
9
+ import { encodeAbiParameters, keccak256, parseAbiItem } from "viem";
10
+ import type { Address, Hex } from "viem";
11
11
  import { logswapLensAbi, cPoolManagerAbi } from "./generated.js";
12
12
  import type { LogswapClient } from "./client.js";
13
- import { poolId, type PoolKey } from "./keys.js";
13
+ import { FEE_MAX, poolId, type PoolKey } from "./keys.js";
14
+
15
+ /** `CPoolState.POSITIONS_SLOT`: the manager's `positions` mapping (`forge inspect CPoolManager storageLayout`). */
16
+ const C_POSITIONS_SLOT = 4n;
17
+ const U128 = (1n << 128n) - 1n;
14
18
  import { isPosition, NO_CAP, positionId, unpackFloorCap } from "./ids.js";
15
19
 
16
20
  /** A class's aggregate state — the whole class, not one holder's slice. */
@@ -25,14 +29,23 @@ export interface PositionClass {
25
29
  insideLast: bigint;
26
30
  }
27
31
 
32
+ /**
33
+ * A class's aggregate state, read through `extsload` (C version 7: the manager's public
34
+ * `positions` mapping is gone behind it, decisions 065). The slot is `CPoolState`'s:
35
+ * `keccak256(abi.encode(id, POSITIONS_SLOT = 4))`, two words — (L | shares << 128) and
36
+ * (aps | insideLast << 128). Pinned on chain by the integration suite.
37
+ */
28
38
  export async function getPositionClass(c: LogswapClient, id: bigint): Promise<PositionClass> {
29
- const r = (await c.public.readContract({
39
+ const slot = keccak256(encodeAbiParameters([{ type: "uint256" }, { type: "uint256" }], [id, C_POSITIONS_SLOT]));
40
+ const w = (await c.public.readContract({
30
41
  address: c.addresses.cPoolManager,
31
42
  abi: cPoolManagerAbi,
32
- functionName: "positions",
33
- args: [id],
34
- })) as readonly [bigint, bigint, bigint, bigint];
35
- return { L: r[0], shares: r[1], aps: r[2], insideLast: r[3] };
43
+ functionName: "extsload",
44
+ args: [slot, 2n],
45
+ } as never)) as readonly Hex[];
46
+ const w0 = BigInt(w[0]!), w1 = BigInt(w[1]!);
47
+ const hi = w1 >> 128n;
48
+ return { L: w0 & U128, shares: w0 >> 128n, aps: w1 & U128, insideLast: hi >= 1n << 127n ? hi - (1n << 128n) : hi };
36
49
  }
37
50
 
38
51
  /** A holder's share balance in a class. */
@@ -183,18 +196,27 @@ export function positionQuote(
183
196
  return (p.L * (upper - p.floor)) / 10n ** 18n;
184
197
  }
185
198
 
186
- /** The manager's `MINT_FEE`: 2 bp of newly-earning L, as a WAD fraction. */
187
- export const MINT_FEE = 2n * 10n ** 14n;
199
+ /**
200
+ * The mint fee a create form presets: twice the swap fee, capped at {@link FEE_MAX} (decisions 064
201
+ * (f): guidance, not a contract rule). The creator picks from the gate's or the policy's menu
202
+ * (065); a listed entry is what the chain accepts — read it with `policyMintFeeMenu`.
203
+ */
204
+ export function presetMintFee(swapFee: bigint): bigint {
205
+ const m = 2n * swapFee;
206
+ return m > FEE_MAX ? FEE_MAX : m;
207
+ }
188
208
 
189
209
  /**
190
210
  * The one-time mint fee at log-price `x`, in quote units — the amount `_mintCore` pulls ON TOP of
191
- * the position's own legs, so a mint's total quote pull is `positionQuote(p, x) + mintFee(p, x)`.
211
+ * the position's own legs, so a mint's total quote pull is `positionQuote(p, x) + mintFee(p, x, rate)`.
212
+ * `rate` is the MARKET's mint fee (`getPool(...).mintFee`, WAD of L): per market since C version 7,
213
+ * the gate's answer at birth (065); there is no global `MINT_FEE` any more.
192
214
  *
193
215
  * Mirrors the manager's exemption cases: a DORMANT mint pays nothing — floor above spot, or a
194
- * capped class spot has already passed (`cap <= x`). Anything earning pays `MINT_FEE · L`, all of
195
- * it the protocol's since C version 6 (decisions 058); with no collector it goes through `F` to the
196
- * LPs already earning, and with none earning it is not charged. This is NOT the swap fee:
197
- * that one is charged per unit of |Δ log p| on swaps; this is a flat charge on L, once, at mint.
216
+ * capped class spot has already passed (`cap <= x`). Anything earning pays `rate · L`, all of it
217
+ * the protocol's (decisions 058); with no collector it goes through `F` to the LPs already
218
+ * earning, and with none earning it is not charged. This is NOT the swap fee: that one is charged
219
+ * per unit of |Δ log p| on swaps; this is a flat charge on L, once, at mint.
198
220
  *
199
221
  * One rung of imprecision: for a floor or cap exactly AT spot the contract consults its `currentK`
200
222
  * pointer (an up-move counts the tick, a down-move does not), which this offline mirror cannot see.
@@ -203,9 +225,10 @@ export const MINT_FEE = 2n * 10n ** 14n;
203
225
  export function mintFee(
204
226
  p: { L: bigint; floor: bigint; cap: bigint; capped: boolean },
205
227
  x: bigint,
228
+ rate: bigint,
206
229
  ): bigint {
207
230
  if (p.floor > x || (p.capped && p.cap <= x)) return 0n;
208
- return (p.L * MINT_FEE) / 10n ** 18n;
231
+ return (p.L * rate) / 10n ** 18n;
209
232
  }
210
233
 
211
234
  /**