@logswap/sdk 0.5.72 → 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/dist/indexer.js CHANGED
@@ -138,13 +138,13 @@ export async function indexerHealthy(url, signal) {
138
138
  * everywhere else, so a wrong id fails loudly instead of addressing the wrong pool.
139
139
  */
140
140
  export async function discoverMarketsIndexed(url, signal) {
141
- const nodes = await gqlAll(url, "allInitializes", "poolId base quote tickSpacing phi x0 blockNumber txHash contractAddress", { signal });
141
+ const nodes = await gqlAll(url, "allInitializes", "poolId base quote tickSpacing swapFee x0 blockNumber txHash contractAddress", { signal });
142
142
  return nodes.map((n) => {
143
143
  const key = {
144
144
  base: n.base,
145
145
  quote: n.quote,
146
146
  tickSpacing: BigInt(n.tickSpacing),
147
- swapFee: BigInt(n.phi), // the indexer's column keeps the event's old name, `phi`
147
+ swapFee: BigInt(n.swapFee), // rindexer names the column after the event's field: `swapFee` since C version 6 (logswap-be #40)
148
148
  };
149
149
  return {
150
150
  key,
@@ -204,12 +204,7 @@ export async function poolPositionsFromChain(c, key, x) {
204
204
  functionName: "idOf",
205
205
  args: [key, floor],
206
206
  }));
207
- const [L, shares] = (await c.public.readContract({
208
- address: c.addresses.cPoolManager,
209
- abi: cPoolManagerAbi,
210
- functionName: "positions",
211
- args: [id],
212
- }));
207
+ const { L, shares } = await getPositionClass(c, id); // through extsload (C version 7)
213
208
  if (L === 0n)
214
209
  return null;
215
210
  return { id, poolId: pool, floor, cap: NO_CAP, capped: false, L, shares, active: x >= floor };
package/dist/keys.d.ts CHANGED
@@ -13,7 +13,9 @@
13
13
  import { type Address, type Hex } from "viem";
14
14
  /**
15
15
  * The fee domain of the family, `FeeParams.FEE_MIN` / `FEE_MAX` (decisions 043): one bound for a
16
- * C market's φ and an F pool's alike, WAD. 1 bp and 1%. The F manager no longer answers
16
+ * C market's φ and an F pool's alike, WAD. 1 bp and 1% — plus 0, which since C version 7 / F
17
+ * version 26 is a fee-free market or pool, admitted only if the policy lists it (decisions 065).
18
+ * The mint fee lives in the same domain. The F manager no longer answers
17
19
  * `FEE_MIN()` / `FEE_MAX()` (F version 21 cut the getters for bytecode); every F policy still
18
20
  * checks φ against these at `onInitialize`, and the C side re-exposes them through the lens's
19
21
  * `cParams()`. Constants here, so a create form bounds φ with no read.
@@ -31,7 +33,8 @@ export interface PoolKey {
31
33
  tickSpacing: bigint;
32
34
  /**
33
35
  * The swap fee, WAD, fixed for the life of the market: {@link FEE_MIN} (1 bp) ≤ φ ≤ {@link FEE_MAX} (1%),
34
- * or `0n`, which selects `DEFAULT_FEE` (2 bp) at read time. Four fields since C version 4
36
+ * or `0n`, which since C version 7 is NO fee (decisions 065; `DEFAULT_FEE` is gone). The gate
37
+ * admits only its listed tiers (064: 2, 5, 10, 25, 50, 100 bp). Four fields since C version 4
35
38
  *: `kappa` and `alpha` left with the variance kernel, and `phiMin` — a floor with
36
39
  * nothing above it — became `phi`, named `swapFee` since C version 6 (decisions 059). The name
37
40
  * is not part of the encoding: no pool id changed with it.
@@ -41,10 +44,9 @@ export interface PoolKey {
41
44
  /**
42
45
  * `poolId = keccak256(abi.encode(key))` — the primary key of the entire read model.
43
46
  *
44
- * Note the zero-vs-default caveat the Solidity type documents: a `swapFee` of `0` resolves to
45
- * `DEFAULT_FEE` at READ time, so a key carrying `0n` and one carrying the resolved default are
46
- * DIFFERENT markets with different ids, even though they behave identically. Always address a
47
- * market by the key it was initialized with.
47
+ * A key's `swapFee` is the market's fee as written — `0n` is a fee-free market since C version 7
48
+ * (decisions 065), not the 2 bp default it selected before. Always address a market by the key it
49
+ * was initialized with.
48
50
  */
49
51
  export declare function poolId(key: PoolKey): Hex;
50
52
  /** Structural equality of two keys — i.e. "the same market". */
package/dist/keys.js CHANGED
@@ -13,7 +13,9 @@
13
13
  import { encodeAbiParameters, keccak256 } from "viem";
14
14
  /**
15
15
  * The fee domain of the family, `FeeParams.FEE_MIN` / `FEE_MAX` (decisions 043): one bound for a
16
- * C market's φ and an F pool's alike, WAD. 1 bp and 1%. The F manager no longer answers
16
+ * C market's φ and an F pool's alike, WAD. 1 bp and 1% — plus 0, which since C version 7 / F
17
+ * version 26 is a fee-free market or pool, admitted only if the policy lists it (decisions 065).
18
+ * The mint fee lives in the same domain. The F manager no longer answers
17
19
  * `FEE_MIN()` / `FEE_MAX()` (F version 21 cut the getters for bytecode); every F policy still
18
20
  * checks φ against these at `onInitialize`, and the C side re-exposes them through the lens's
19
21
  * `cParams()`. Constants here, so a create form bounds φ with no read.
@@ -34,10 +36,9 @@ const POOL_KEY_ABI = [
34
36
  /**
35
37
  * `poolId = keccak256(abi.encode(key))` — the primary key of the entire read model.
36
38
  *
37
- * Note the zero-vs-default caveat the Solidity type documents: a `swapFee` of `0` resolves to
38
- * `DEFAULT_FEE` at READ time, so a key carrying `0n` and one carrying the resolved default are
39
- * DIFFERENT markets with different ids, even though they behave identically. Always address a
40
- * market by the key it was initialized with.
39
+ * A key's `swapFee` is the market's fee as written — `0n` is a fee-free market since C version 7
40
+ * (decisions 065), not the 2 bp default it selected before. Always address a market by the key it
41
+ * was initialized with.
41
42
  */
42
43
  export function poolId(key) {
43
44
  return keccak256(encodeAbiParameters(POOL_KEY_ABI, [
@@ -61,13 +61,20 @@ export interface CreateMarketArgs extends WriteOptions {
61
61
  targetFloor: bigint;
62
62
  /** The founding exposure; must exceed the manager's `minBackstopL` lock. */
63
63
  L: bigint;
64
+ /**
65
+ * The market's mint fee, WAD of newly-earning L — the creator's pick from the gate's menu
66
+ * (decisions 065). Defaults to {@link presetMintFee}: twice the swap fee, capped at `FEE_MAX`.
67
+ */
68
+ mintFee?: bigint;
64
69
  /** 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
70
  maxBaseIn: bigint;
66
71
  maxQuoteIn: bigint;
67
72
  }
68
73
  /**
69
74
  * Create a market ATOMICALLY: `initialize` at `x0` plus the backstop-establishing first mint in
70
- * one transaction — the canonical creation path. The
75
+ * one transaction — the canonical creation path. Under the C gate (064) the router is a trusted
76
+ * forwarder: the gate checks the router's `user()` — the wallet sending this — against the
77
+ * creator list, so a listed creator opens and funds a market in this one call. The
71
78
  * two-step path let an attacker seat the PERMANENT backstop at a hostile rung between an honest
72
79
  * `initialize` and its first mint; here either both land or neither, and a front-run of the whole
73
80
  * call reverts it without moving a wei. No cap: the first mint IS the backstop and must be
package/dist/liquidity.js CHANGED
@@ -23,7 +23,7 @@
23
23
  */
24
24
  import { logswapLensAbi, logswapRouterAbi } from "./generated.js";
25
25
  import { NO_CAP } from "./ids.js";
26
- import { describeId, getPositionClass } from "./positions.js";
26
+ import { describeId, getPositionClass, presetMintFee } from "./positions.js";
27
27
  import { execute, OPEN_DELTA, settleInKind, settleOnly, swapExactInAction, swapExactOutAction, updateAction, } from "./actions.js";
28
28
  import { requireWallet, resolveDeadline, resolveRecipient, sendRouterWrite, sendRouterWriteWithPermits, withSlippage, } from "./write.js";
29
29
  import { quoteExactIn, quoteExactOut } from "./swap.js";
@@ -55,7 +55,9 @@ export async function mint(c, a) {
55
55
  }
56
56
  /**
57
57
  * Create a market ATOMICALLY: `initialize` at `x0` plus the backstop-establishing first mint in
58
- * one transaction — the canonical creation path. The
58
+ * one transaction — the canonical creation path. Under the C gate (064) the router is a trusted
59
+ * forwarder: the gate checks the router's `user()` — the wallet sending this — against the
60
+ * creator list, so a listed creator opens and funds a market in this one call. The
59
61
  * two-step path let an attacker seat the PERMANENT backstop at a hostile rung between an honest
60
62
  * `initialize` and its first mint; here either both land or neither, and a front-run of the whole
61
63
  * call reverts it without moving a wei. No cap: the first mint IS the backstop and must be
@@ -68,6 +70,7 @@ export async function createMarket(c, a) {
68
70
  args: [
69
71
  a.key,
70
72
  a.x0,
73
+ a.mintFee ?? presetMintFee(a.key.swapFee),
71
74
  a.targetFloor,
72
75
  a.L,
73
76
  a.maxBaseIn,
package/dist/pools.d.ts CHANGED
@@ -14,7 +14,10 @@
14
14
  import { type Address, type Hex } from "viem";
15
15
  import type { LogswapClient } from "./client.js";
16
16
  import { type PoolKey } from "./keys.js";
17
- /** The manager's `getPool` view, decoded. Field-for-field with `PoolView` in the contract. */
17
+ /**
18
+ * A market's scalar state, field-for-field with `CPoolState.PoolView` — what the manager's `getPool`
19
+ * returned before C version 7 moved it behind `extsload` (decisions 065); read through the lens.
20
+ */
18
21
  export interface PoolState {
19
22
  initialized: boolean;
20
23
  backstopSeeded: boolean;
@@ -32,14 +35,16 @@ export interface PoolState {
32
35
  protocolOwed: bigint;
33
36
  minBackstopL: bigint;
34
37
  backstopFloor: bigint;
38
+ /** The market's mint fee, WAD of newly-earning L: proposed at birth, the gate's answer (065). */
39
+ mintFee: bigint;
35
40
  }
36
- /** Read a market's full state by key. */
41
+ /** Read a market's full state by key, through the lens (`getPools`; C version 7). All zero, `initialized` false, for a market nobody opened. */
37
42
  export declare function getPool(c: LogswapClient, key: PoolKey): Promise<PoolState>;
38
43
  /**
39
- * The market's swap fee: the key's `swapFee` with `0n` resolved to `DEFAULT_FEE` — the manager's
40
- * `swapFeeOf` (`phiOf` before C version 6). Constant for the market's life (C version 4 — `phiEff`
41
- * and the variance kernel before). Reverts for a key nobody initialized, which `discoverMarkets`
42
- * relies on to drop a stale indexer's rows. (`swapFeeOf` in `fees.ts` is the fee one swap paid.)
44
+ * The market's swap fee — the key's own `swapFee` since C version 7: a fee of 0 is no fee, and
45
+ * `DEFAULT_FEE` / the manager's `swapFeeOf` are gone (decisions 065). Constant for the market's
46
+ * life. Still a read: it refuses a key nobody initialized, which `discoverMarkets` relies on to
47
+ * drop a stale indexer's rows. (`swapFeeOf` in `fees.ts` is the fee one swap paid.)
43
48
  */
44
49
  export declare function marketSwapFee(c: LogswapClient, key: PoolKey): Promise<bigint>;
45
50
  /**
@@ -203,12 +208,15 @@ export declare function annualise(perUnitL: bigint, elapsedSeconds: bigint): num
203
208
  /** When a market was created, as a unix timestamp — the denominator for {@link annualise}. */
204
209
  export declare function marketOpenedAt(c: LogswapClient, m: DiscoveredMarket): Promise<bigint>;
205
210
  /**
206
- * Create a C market: `initialize(key, x0)` on the manager — a call, not a deployment. Manager-
207
- * direct by design: creation moves no funds, so the router adds nothing. The pool id is
208
- * `poolId(key)` (pure) and the market is tradeable immediately; the first mint funds it.
211
+ * Create a C market: `initialize(key, x0, mintFee)` on the manager — a call, not a deployment, and
212
+ * no funds move. The mint fee is the creator's pick from the gate's menu (065), preset at twice the
213
+ * swap fee ({@link presetMintFee}). Under a curated gate the CALLER must be a listed creator; the
214
+ * canonical path is the router's {@link createMarket}, which forwards its user to the gate and
215
+ * mints the backstop in the same transaction (064).
209
216
  */
210
217
  export declare function initializeMarket(c: LogswapClient, a: {
211
218
  key: PoolKey;
212
219
  x0: bigint;
220
+ mintFee?: bigint;
213
221
  account: Address;
214
222
  }): Promise<`0x${string}`>;
package/dist/pools.js CHANGED
@@ -16,29 +16,28 @@ import { logswapLensAbi, cPoolManagerAbi } from "./generated.js";
16
16
  import { signerFor } from "./write.js";
17
17
  import { readCachedToken, registerCacheClear, writeCachedToken } from "./tokencache.js";
18
18
  import { poolId } from "./keys.js";
19
- /** Read a market's full state by key. */
19
+ import { presetMintFee } from "./positions.js";
20
+ /** Read a market's full state by key, through the lens (`getPools`; C version 7). All zero, `initialized` false, for a market nobody opened. */
20
21
  export async function getPool(c, key) {
21
- const v = await c.public.readContract({
22
- address: c.addresses.cPoolManager,
23
- abi: cPoolManagerAbi,
24
- functionName: "getPool",
25
- args: [poolId(key)],
26
- });
27
- return v;
22
+ const v = (await c.public.readContract({
23
+ address: c.addresses.lens,
24
+ abi: logswapLensAbi,
25
+ functionName: "getPools",
26
+ args: [[poolId(key)]],
27
+ }));
28
+ return v[0];
28
29
  }
29
30
  /**
30
- * The market's swap fee: the key's `swapFee` with `0n` resolved to `DEFAULT_FEE` — the manager's
31
- * `swapFeeOf` (`phiOf` before C version 6). Constant for the market's life (C version 4 — `phiEff`
32
- * and the variance kernel before). Reverts for a key nobody initialized, which `discoverMarkets`
33
- * relies on to drop a stale indexer's rows. (`swapFeeOf` in `fees.ts` is the fee one swap paid.)
31
+ * The market's swap fee — the key's own `swapFee` since C version 7: a fee of 0 is no fee, and
32
+ * `DEFAULT_FEE` / the manager's `swapFeeOf` are gone (decisions 065). Constant for the market's
33
+ * life. Still a read: it refuses a key nobody initialized, which `discoverMarkets` relies on to
34
+ * drop a stale indexer's rows. (`swapFeeOf` in `fees.ts` is the fee one swap paid.)
34
35
  */
35
36
  export async function marketSwapFee(c, key) {
36
- return c.public.readContract({
37
- address: c.addresses.cPoolManager,
38
- abi: cPoolManagerAbi,
39
- functionName: "swapFeeOf",
40
- args: [key],
41
- });
37
+ const s = await getPool(c, key);
38
+ if (!s.initialized)
39
+ throw new Error(`logswap: market ${poolId(key)} is not initialized`);
40
+ return key.swapFee;
42
41
  }
43
42
  /**
44
43
  * Price from the stored log-price. **Lossy** — `x` is exact and this is a float, so use it for
@@ -332,16 +331,18 @@ export async function marketOpenedAt(c, m) {
332
331
  return b.timestamp;
333
332
  }
334
333
  /**
335
- * Create a C market: `initialize(key, x0)` on the manager — a call, not a deployment. Manager-
336
- * direct by design: creation moves no funds, so the router adds nothing. The pool id is
337
- * `poolId(key)` (pure) and the market is tradeable immediately; the first mint funds it.
334
+ * Create a C market: `initialize(key, x0, mintFee)` on the manager — a call, not a deployment, and
335
+ * no funds move. The mint fee is the creator's pick from the gate's menu (065), preset at twice the
336
+ * swap fee ({@link presetMintFee}). Under a curated gate the CALLER must be a listed creator; the
337
+ * canonical path is the router's {@link createMarket}, which forwards its user to the gate and
338
+ * mints the backstop in the same transaction (064).
338
339
  */
339
340
  export async function initializeMarket(c, a) {
340
341
  const { request } = await c.public.simulateContract({
341
342
  address: c.addresses.cPoolManager,
342
343
  abi: cPoolManagerAbi,
343
344
  functionName: "initialize",
344
- args: [a.key, a.x0],
345
+ args: [a.key, a.x0, a.mintFee ?? presetMintFee(a.key.swapFee)],
345
346
  account: signerFor(c, a.account),
346
347
  });
347
348
  return c.wallet.writeContract(request);
@@ -19,6 +19,12 @@ export interface PositionClass {
19
19
  /** Snapshot of `F_above[floor] − F_above[cap]` at the class's last touch. */
20
20
  insideLast: bigint;
21
21
  }
22
+ /**
23
+ * A class's aggregate state, read through `extsload` (C version 7: the manager's public
24
+ * `positions` mapping is gone behind it, decisions 065). The slot is `CPoolState`'s:
25
+ * `keccak256(abi.encode(id, POSITIONS_SLOT = 4))`, two words — (L | shares << 128) and
26
+ * (aps | insideLast << 128). Pinned on chain by the integration suite.
27
+ */
22
28
  export declare function getPositionClass(c: LogswapClient, id: bigint): Promise<PositionClass>;
23
29
  /** A holder's share balance in a class. */
24
30
  export declare function balanceOf(c: LogswapClient, owner: Address, id: bigint): Promise<bigint>;
@@ -103,17 +109,23 @@ export declare function positionQuote(p: {
103
109
  cap: bigint;
104
110
  capped: boolean;
105
111
  }, x: bigint): bigint;
106
- /** The manager's `MINT_FEE`: 2 bp of newly-earning L, as a WAD fraction. */
107
- export declare const MINT_FEE: bigint;
112
+ /**
113
+ * The mint fee a create form presets: twice the swap fee, capped at {@link FEE_MAX} (decisions 064
114
+ * (f): guidance, not a contract rule). The creator picks from the gate's or the policy's menu
115
+ * (065); a listed entry is what the chain accepts — read it with `policyMintFeeMenu`.
116
+ */
117
+ export declare function presetMintFee(swapFee: bigint): bigint;
108
118
  /**
109
119
  * The one-time mint fee at log-price `x`, in quote units — the amount `_mintCore` pulls ON TOP of
110
- * the position's own legs, so a mint's total quote pull is `positionQuote(p, x) + mintFee(p, x)`.
120
+ * the position's own legs, so a mint's total quote pull is `positionQuote(p, x) + mintFee(p, x, rate)`.
121
+ * `rate` is the MARKET's mint fee (`getPool(...).mintFee`, WAD of L): per market since C version 7,
122
+ * the gate's answer at birth (065); there is no global `MINT_FEE` any more.
111
123
  *
112
124
  * Mirrors the manager's exemption cases: a DORMANT mint pays nothing — floor above spot, or a
113
- * capped class spot has already passed (`cap <= x`). Anything earning pays `MINT_FEE · L`, all of
114
- * it the protocol's since C version 6 (decisions 058); with no collector it goes through `F` to the
115
- * LPs already earning, and with none earning it is not charged. This is NOT the swap fee:
116
- * that one is charged per unit of |Δ log p| on swaps; this is a flat charge on L, once, at mint.
125
+ * capped class spot has already passed (`cap <= x`). Anything earning pays `rate · L`, all of it
126
+ * the protocol's (decisions 058); with no collector it goes through `F` to the LPs already
127
+ * earning, and with none earning it is not charged. This is NOT the swap fee: that one is charged
128
+ * per unit of |Δ log p| on swaps; this is a flat charge on L, once, at mint.
117
129
  *
118
130
  * One rung of imprecision: for a floor or cap exactly AT spot the contract consults its `currentK`
119
131
  * pointer (an up-move counts the tick, a down-move does not), which this offline mirror cannot see.
@@ -124,7 +136,7 @@ export declare function mintFee(p: {
124
136
  floor: bigint;
125
137
  cap: bigint;
126
138
  capped: boolean;
127
- }, x: bigint): bigint;
139
+ }, x: bigint, rate: bigint): bigint;
128
140
  /**
129
141
  * Every UNCAPPED position `owner` holds in a market, enumerated fully on-chain by the lens — a
130
142
  * bitmap walk plus a balance check per live floor. No indexer, no log scan. Capped classes are
package/dist/positions.js CHANGED
@@ -5,18 +5,30 @@
5
5
  * (pool, floor, cap). Holders own ERC-6909 shares of a class; fees are tracked per holder, so two
6
6
  * accounts holding the same class can have different claimable amounts.
7
7
  */
8
- import { parseAbiItem } from "viem";
8
+ import { encodeAbiParameters, keccak256, parseAbiItem } from "viem";
9
9
  import { logswapLensAbi, cPoolManagerAbi } from "./generated.js";
10
- import { poolId } from "./keys.js";
10
+ import { FEE_MAX, poolId } from "./keys.js";
11
+ /** `CPoolState.POSITIONS_SLOT`: the manager's `positions` mapping (`forge inspect CPoolManager storageLayout`). */
12
+ const C_POSITIONS_SLOT = 4n;
13
+ const U128 = (1n << 128n) - 1n;
11
14
  import { isPosition, NO_CAP, positionId, unpackFloorCap } from "./ids.js";
15
+ /**
16
+ * A class's aggregate state, read through `extsload` (C version 7: the manager's public
17
+ * `positions` mapping is gone behind it, decisions 065). The slot is `CPoolState`'s:
18
+ * `keccak256(abi.encode(id, POSITIONS_SLOT = 4))`, two words — (L | shares << 128) and
19
+ * (aps | insideLast << 128). Pinned on chain by the integration suite.
20
+ */
12
21
  export async function getPositionClass(c, id) {
13
- const r = (await c.public.readContract({
22
+ const slot = keccak256(encodeAbiParameters([{ type: "uint256" }, { type: "uint256" }], [id, C_POSITIONS_SLOT]));
23
+ const w = (await c.public.readContract({
14
24
  address: c.addresses.cPoolManager,
15
25
  abi: cPoolManagerAbi,
16
- functionName: "positions",
17
- args: [id],
26
+ functionName: "extsload",
27
+ args: [slot, 2n],
18
28
  }));
19
- return { L: r[0], shares: r[1], aps: r[2], insideLast: r[3] };
29
+ const w0 = BigInt(w[0]), w1 = BigInt(w[1]);
30
+ const hi = w1 >> 128n;
31
+ return { L: w0 & U128, shares: w0 >> 128n, aps: w1 & U128, insideLast: hi >= 1n << 127n ? hi - (1n << 128n) : hi };
20
32
  }
21
33
  /** A holder's share balance in a class. */
22
34
  export async function balanceOf(c, owner, id) {
@@ -129,26 +141,35 @@ export function positionQuote(p, x) {
129
141
  const upper = p.capped && p.cap <= x ? p.cap : x;
130
142
  return (p.L * (upper - p.floor)) / 10n ** 18n;
131
143
  }
132
- /** The manager's `MINT_FEE`: 2 bp of newly-earning L, as a WAD fraction. */
133
- export const MINT_FEE = 2n * 10n ** 14n;
144
+ /**
145
+ * The mint fee a create form presets: twice the swap fee, capped at {@link FEE_MAX} (decisions 064
146
+ * (f): guidance, not a contract rule). The creator picks from the gate's or the policy's menu
147
+ * (065); a listed entry is what the chain accepts — read it with `policyMintFeeMenu`.
148
+ */
149
+ export function presetMintFee(swapFee) {
150
+ const m = 2n * swapFee;
151
+ return m > FEE_MAX ? FEE_MAX : m;
152
+ }
134
153
  /**
135
154
  * The one-time mint fee at log-price `x`, in quote units — the amount `_mintCore` pulls ON TOP of
136
- * the position's own legs, so a mint's total quote pull is `positionQuote(p, x) + mintFee(p, x)`.
155
+ * the position's own legs, so a mint's total quote pull is `positionQuote(p, x) + mintFee(p, x, rate)`.
156
+ * `rate` is the MARKET's mint fee (`getPool(...).mintFee`, WAD of L): per market since C version 7,
157
+ * the gate's answer at birth (065); there is no global `MINT_FEE` any more.
137
158
  *
138
159
  * Mirrors the manager's exemption cases: a DORMANT mint pays nothing — floor above spot, or a
139
- * capped class spot has already passed (`cap <= x`). Anything earning pays `MINT_FEE · L`, all of
140
- * it the protocol's since C version 6 (decisions 058); with no collector it goes through `F` to the
141
- * LPs already earning, and with none earning it is not charged. This is NOT the swap fee:
142
- * that one is charged per unit of |Δ log p| on swaps; this is a flat charge on L, once, at mint.
160
+ * capped class spot has already passed (`cap <= x`). Anything earning pays `rate · L`, all of it
161
+ * the protocol's (decisions 058); with no collector it goes through `F` to the LPs already
162
+ * earning, and with none earning it is not charged. This is NOT the swap fee: that one is charged
163
+ * per unit of |Δ log p| on swaps; this is a flat charge on L, once, at mint.
143
164
  *
144
165
  * One rung of imprecision: for a floor or cap exactly AT spot the contract consults its `currentK`
145
166
  * pointer (an up-move counts the tick, a down-move does not), which this offline mirror cannot see.
146
167
  * Preview with it; let the chain be authoritative on that boundary.
147
168
  */
148
- export function mintFee(p, x) {
169
+ export function mintFee(p, x, rate) {
149
170
  if (p.floor > x || (p.capped && p.cap <= x))
150
171
  return 0n;
151
- return (p.L * MINT_FEE) / 10n ** 18n;
172
+ return (p.L * rate) / 10n ** 18n;
152
173
  }
153
174
  export async function positionsOf(c, key, owner) {
154
175
  const rows = (await c.public.readContract({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logswap/sdk",
3
- "version": "0.5.72",
3
+ "version": "0.5.74",
4
4
  "description": "TypeScript SDK for Logswap, the liquidity primitive introducing the logarithmic invariant — typed wrappers over the CPoolManager / FPoolManager / Router / Lens surface.",
5
5
  "license": "GPL-2.0-or-later",
6
6
  "type": "module",
package/src/admin.ts CHANGED
@@ -9,7 +9,8 @@
9
9
  * is the policy's (`PadPolicy.setSchedule`), capped by the manager at `POT_CUT_MAX`.
10
10
  */
11
11
 
12
- import type { Address, Hash } from "viem";
12
+ import type { Address, Hash, Hex } from "viem";
13
+ import type { PoolKey } from "./keys.js";
13
14
  import type { LogswapClient } from "./client.js";
14
15
  import { cPoolManagerAbi, fPoolManagerAbi, padPolicyAbi, polPolicyAbi, policyRegistryAbi, publicPolicyAbi } from "./generated.js";
15
16
  import { bytes32ToText, fPolicyBaseAbi, fPoolKeyOfPool, fPoolKindOfPolicy, textToBytes32, type FPoolKey, type FPoolKind } from "./fpool.js";
@@ -258,6 +259,127 @@ export async function policyFeeAllowed(c: LogswapClient, policy: Address, fee: b
258
259
  return { curated, listed, allowed: !curated || listed };
259
260
  }
260
261
 
262
+ /** `setMintFeeListed` on any protocol policy (065): an entry of its mint-fee menu. The collector's. */
263
+ export async function policySetMintFeeListed(c: LogswapClient, policy: Address, mintFee: bigint, listed: boolean): Promise<Hash> {
264
+ return write(c, policy, fPolicyBaseAbi, "setMintFeeListed", [mintFee, listed]);
265
+ }
266
+
267
+ /** `setMintFeeCurated` on any protocol policy: on, a birth proposing an unlisted mint fee is refused (`MintFeeNotListed`). */
268
+ export async function policySetMintFeeCurated(c: LogswapClient, policy: Address, curated: boolean): Promise<Hash> {
269
+ return write(c, policy, fPolicyBaseAbi, "setMintFeeCurated", [curated]);
270
+ }
271
+
272
+ /**
273
+ * A policy's mint-fee menu, for a create form (065): which of `candidates` it lists, and whether it
274
+ * curates at all (uncurated, any fee in the domain is accepted — the desk's policy never curates).
275
+ * The candidates are the app's — typically {@link presetMintFee} of each listed swap tier.
276
+ */
277
+ export async function policyMintFeeMenu(c: LogswapClient, policy: Address, candidates: readonly bigint[]): Promise<{ curated: boolean; listed: bigint[] }> {
278
+ const rd = (functionName: string, args: readonly unknown[] = []) =>
279
+ c.public.readContract({ address: policy, abi: fPolicyBaseAbi, functionName, args } as never) as Promise<boolean>;
280
+ const [curated, ...flags] = await Promise.all([rd("mintFeeCurated"), ...candidates.map((m) => rd("mintFeeListed", [m]))]);
281
+ return { curated, listed: candidates.filter((_, i) => flags[i]) };
282
+ }
283
+
284
+ /** `setCreatorListed` on any protocol policy (064): a creator onboarded under a service agreement. */
285
+ export async function policySetCreatorListed(c: LogswapClient, policy: Address, who: Address, listed: boolean): Promise<Hash> {
286
+ return write(c, policy, fPolicyBaseAbi, "setCreatorListed", [who, listed]);
287
+ }
288
+
289
+ /** `setCreatorCurated` on any protocol policy: on, only listed creators may create on that kind (C: the caller; F: `key.creator`). */
290
+ export async function policySetCreatorCurated(c: LogswapClient, policy: Address, curated: boolean): Promise<Hash> {
291
+ return write(c, policy, fPolicyBaseAbi, "setCreatorCurated", [curated]);
292
+ }
293
+
294
+ /** `PublicPolicy.setPairListed` (064): a C pair a market may be born on while pairs are curated. Directional (base, quote). */
295
+ export async function gateSetPairListed(c: LogswapClient, policy: Address, base: Address, quote: Address, listed: boolean): Promise<Hash> {
296
+ return write(c, policy, publicPolicyAbi, "setPairListed", [base, quote, listed]);
297
+ }
298
+
299
+ /** `PublicPolicy.setPairCurated` (064). */
300
+ export async function gateSetPairCurated(c: LogswapClient, policy: Address, curated: boolean): Promise<Hash> {
301
+ return write(c, policy, publicPolicyAbi, "setPairCurated", [curated]);
302
+ }
303
+
304
+ /** `PublicPolicy.setSpacingListed` (064): a C tick spacing, WAD (10 bp = 1e15, 100 bp = 1e16). Always consulted. */
305
+ export async function gateSetSpacingListed(c: LogswapClient, policy: Address, tickSpacing: bigint, listed: boolean): Promise<Hash> {
306
+ return write(c, policy, publicPolicyAbi, "setSpacingListed", [tickSpacing, listed]);
307
+ }
308
+
309
+ /** `PublicPolicy.setForwarderTrusted` (064): a router whose `user()` the gate takes as the creator. */
310
+ export async function gateSetForwarderTrusted(c: LogswapClient, policy: Address, forwarder: Address, trusted: boolean): Promise<Hash> {
311
+ return write(c, policy, publicPolicyAbi, "setForwarderTrusted", [forwarder, trusted]);
312
+ }
313
+
314
+ /** `CPoolManager.setGate` (064): name, change or lift (`address(0)`) the C gate. The C collector's. */
315
+ export async function setCGate(c: LogswapClient, gate: Address): Promise<Hash> {
316
+ return write(c, c.addresses.cPoolManager, cPoolManagerAbi, "setGate", [gate]);
317
+ }
318
+
319
+ /** The C manager's gate (`address(0)`: permissionless, a fee-free market then refused). */
320
+ export async function cGate(c: LogswapClient): Promise<Address> {
321
+ return c.public.readContract({ address: c.addresses.cPoolManager, abi: cPoolManagerAbi, functionName: "gate" } as never) as Promise<Address>;
322
+ }
323
+
324
+ export interface CGateRules {
325
+ creatorOk: boolean;
326
+ quoteOk: boolean;
327
+ pairOk: boolean;
328
+ feeOk: boolean;
329
+ spacingOk: boolean;
330
+ /** A market on (base, quote, fee) already exists, whatever its spacing: the gate refuses another (064 (b)). */
331
+ exists: boolean;
332
+ /** Whether `router` is a trusted forwarder — `createMarket` through it is then checked against `creator`. */
333
+ routerTrusted: boolean;
334
+ /** Every check passes: the gate would admit this market from this creator (the mint fee aside — see {@link policyMintFeeMenu}). */
335
+ admitted: boolean;
336
+ }
337
+
338
+ /**
339
+ * The public policy's C checks for one candidate market and creator, in one round of reads — what
340
+ * the create form shows before a wallet is asked (064). `router` is the router the form will go
341
+ * through (`c.addresses.router` by default).
342
+ */
343
+ export async function cGateRules(c: LogswapClient, a: { key: PoolKey; creator: Address; router?: Address; gate?: Address }): Promise<CGateRules> {
344
+ const g = a.gate ?? (c.addresses.publicPolicy as Address);
345
+ const rd = <T>(functionName: string, args: readonly unknown[] = []) =>
346
+ c.public.readContract({ address: g, abi: publicPolicyAbi, functionName, args } as never) as Promise<T>;
347
+ const { key } = a;
348
+ const [creatorCurated, creatorListed, quoteCurated, quoteListed, pairCurated, pairListed, feeCurated, feeListed, spacingListed, market, routerTrusted] =
349
+ await Promise.all([
350
+ rd<boolean>("creatorCurated"),
351
+ rd<boolean>("creatorListed", [a.creator]),
352
+ rd<boolean>("quoteCurated"),
353
+ rd<boolean>("quoteListed", [key.quote]),
354
+ rd<boolean>("pairCurated"),
355
+ rd<boolean>("pairListed", [key.base, key.quote]),
356
+ rd<boolean>("feeCurated"),
357
+ rd<boolean>("feeListed", [key.swapFee]),
358
+ rd<boolean>("spacingListed", [key.tickSpacing]),
359
+ rd<Hex>("cMarketOf", [key.base, key.quote, key.swapFee]),
360
+ rd<boolean>("forwarderTrusted", [a.router ?? c.addresses.router]),
361
+ ]);
362
+ const r = {
363
+ creatorOk: !creatorCurated || creatorListed,
364
+ quoteOk: !quoteCurated || quoteListed,
365
+ pairOk: !pairCurated || pairListed,
366
+ feeOk: !feeCurated || feeListed,
367
+ spacingOk: spacingListed,
368
+ exists: BigInt(market) !== 0n,
369
+ routerTrusted,
370
+ };
371
+ return { ...r, admitted: r.creatorOk && r.quoteOk && r.pairOk && r.feeOk && r.spacingOk && !r.exists };
372
+ }
373
+
374
+ /**
375
+ * Every refusal a birth can meet at the gate or a policy (064, 065), for `decodeErrorResult` on a
376
+ * failed simulation: the public policy's errors plus the pad's `PadExists` (one pad per base).
377
+ */
378
+ export const gateErrorAbi = [
379
+ ...publicPolicyAbi.filter((x) => x.type === "error"),
380
+ ...padPolicyAbi.filter((x) => x.type === "error" && x.name === "PadExists"),
381
+ ] as const;
382
+
261
383
  /** `PublicPolicy.setListed`: an asset a curated seed may hold. */
262
384
  export async function publicSetListed(c: LogswapClient, policy: Address, token: Address, listed: boolean): Promise<Hash> {
263
385
  return write(c, policy, publicPolicyAbi, "setListed", [token, listed]);
package/src/client.ts CHANGED
@@ -113,8 +113,15 @@ function guardChain(wallet: WalletClient, pub: PublicClient): WalletClient {
113
113
  * (`MINT_FEE_PROTOCOL_CUT` and `setFeeParams` are gone, decisions 058); the swap-fee tax at payout,
114
114
  * `swapFeeTax` / `setSwapFeeTax` (060); `phi` is `swapFee` and `phiOf` `swapFeeOf` (059) — field
115
115
  * names only, no pool id moved.
116
+ *
117
+ * **7** since 2026-10-12 (logswap-contract#60, decisions 064–065): the typed views (`getPool`,
118
+ * `ticks`, `tickBitmapWord`, `positions`, `feeAcct`, `idInfo`) are behind `extsload` — the pool
119
+ * reads through the lens's `getPools`, a class through `CPoolState`'s layout; a fee of 0 is no
120
+ * fee (`swapFeeOf` and `DEFAULT_FEE` are gone); the mint fee is the market's, proposed at
121
+ * `initialize` / `createMarket` and answered by the gate; the gate (`gate`, `setGate`) is the
122
+ * public policy, set at construction. No pool id moved.
116
123
  */
117
- export const EXPECTED_VERSION = 6n;
124
+ export const EXPECTED_VERSION = 7n;
118
125
  /**
119
126
  * The F manager's own tag (5 — the key's salt, pool names, the sponsor registry;
120
127
  * 4 was the income-only harvest cut).
@@ -191,8 +198,11 @@ export const EXPECTED_VERSION = 6n;
191
198
  // 25 (logswap-contract#59): the views moved behind `extsload` (062) — this SDK reads them through the
192
199
  // lens (`fPool`, `getFPools`, `fBundleOf`, `fDividendOf`, `fLienOf`); the capped F pool, `FPoolKey.cap`
193
200
  // appended so every pool id moved (061); the lien, `lock` / `unlock` (063); θ is `logFloor`, θ₀ `refLogFloor`.
201
+ // 26 (logswap-contract#60): the mint fee is per pool, proposed at `initialize(key, mintFee)` and answered
202
+ // by the policy's `onInitialize` from its menu (065); `MINT_FEE` is gone; a fee of 0 is no fee; the
203
+ // policies curate creators, a public pool is uncapped, one pad per base (064). No pool id moved.
194
204
  // A breaking bump: raised in the same change.
195
- export const EXPECTED_F_VERSION = 25n;
205
+ export const EXPECTED_F_VERSION = 26n;
196
206
 
197
207
  export async function assertVersion(c: LogswapClient, expected = EXPECTED_VERSION): Promise<void> {
198
208
  const { cPoolManagerAbi, fPoolManagerAbi } = await import("./generated.js");