@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/README.md +25 -0
- package/dist/admin.d.ts +195 -0
- package/dist/admin.js +91 -0
- package/dist/client.d.ts +9 -2
- package/dist/client.js +12 -2
- package/dist/fpool.d.ts +179 -1
- package/dist/fpool.js +4 -2
- package/dist/generated.d.ts +1113 -214
- package/dist/generated.js +736 -101
- package/dist/indexer.js +1 -6
- package/dist/keys.d.ts +8 -6
- package/dist/keys.js +6 -5
- package/dist/liquidity.d.ts +8 -1
- package/dist/liquidity.js +5 -2
- package/dist/pools.d.ts +17 -9
- package/dist/pools.js +23 -22
- package/dist/positions.d.ts +20 -8
- package/dist/positions.js +36 -15
- package/package.json +1 -1
- package/src/admin.ts +123 -1
- package/src/client.ts +12 -2
- package/src/fpool.ts +13 -3
- package/src/generated.ts +736 -101
- package/src/indexer.ts +1 -6
- package/src/keys.ts +8 -6
- package/src/liquidity.ts +10 -2
- package/src/pools.ts +29 -24
- package/src/positions.ts +39 -16
package/dist/indexer.js
CHANGED
|
@@ -204,12 +204,7 @@ export async function poolPositionsFromChain(c, key, x) {
|
|
|
204
204
|
functionName: "idOf",
|
|
205
205
|
args: [key, floor],
|
|
206
206
|
}));
|
|
207
|
-
const
|
|
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
|
|
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
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
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
|
|
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
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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, [
|
package/dist/liquidity.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
-
/**
|
|
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
|
|
40
|
-
* `
|
|
41
|
-
*
|
|
42
|
-
*
|
|
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
|
|
207
|
-
*
|
|
208
|
-
*
|
|
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
|
-
|
|
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.
|
|
23
|
-
abi:
|
|
24
|
-
functionName: "
|
|
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
|
|
31
|
-
* `
|
|
32
|
-
*
|
|
33
|
-
*
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
336
|
-
*
|
|
337
|
-
*
|
|
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);
|
package/dist/positions.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
107
|
-
|
|
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 `
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
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
|
|
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: "
|
|
17
|
-
args: [
|
|
26
|
+
functionName: "extsload",
|
|
27
|
+
args: [slot, 2n],
|
|
18
28
|
}));
|
|
19
|
-
|
|
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
|
-
/**
|
|
133
|
-
|
|
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 `
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
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 *
|
|
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.
|
|
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 =
|
|
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 =
|
|
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");
|
package/src/fpool.ts
CHANGED
|
@@ -50,6 +50,7 @@ export const fPolicyBaseAbi = polPolicyAbi;
|
|
|
50
50
|
import type { LogswapClient } from "./client.js";
|
|
51
51
|
import { fallBackUnlessRejected, signerFor } from "./write.js";
|
|
52
52
|
import { getEventLogs } from "./history.js";
|
|
53
|
+
import { presetMintFee } from "./positions.js";
|
|
53
54
|
|
|
54
55
|
const WAD = 10n ** 18n;
|
|
55
56
|
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" as const;
|
|
@@ -72,8 +73,10 @@ export interface FPoolState {
|
|
|
72
73
|
Q: bigint;
|
|
73
74
|
/** Total exposure. Grows on mint, shrinks on burn; never moves on a swap. */
|
|
74
75
|
L: bigint;
|
|
75
|
-
/** The swap fee, WAD (`phi` before F version 25, decisions 059).
|
|
76
|
+
/** The swap fee, WAD (`phi` before F version 25, decisions 059). 0 is no fee (F version 26, decisions 065), admitted only if the policy lists it. */
|
|
76
77
|
swapFee: bigint;
|
|
78
|
+
/** The pool's mint fee, WAD of newly-minted L: proposed at birth, the policy's answer from its menu (F version 26, decisions 065). */
|
|
79
|
+
mintFee: bigint;
|
|
77
80
|
/** θ₀, the reference log floor: where the floor stood at the seed (re-based by a capital harvest and at each step); income is counted from it. `theta0` before F version 25. */
|
|
78
81
|
refLogFloor: bigint;
|
|
79
82
|
/** θ = X − Q/L, the log of the pool's current floor price: where the bid runs out. Derived, never stored. `theta` before F version 25. */
|
|
@@ -163,6 +166,7 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
163
166
|
Q: p.Q,
|
|
164
167
|
L: p.L,
|
|
165
168
|
swapFee: p.swapFee,
|
|
169
|
+
mintFee: p.mintFee,
|
|
166
170
|
refLogFloor: p.refLogFloor,
|
|
167
171
|
logFloor: v.logFloor,
|
|
168
172
|
compositeX: v.compositeX,
|
|
@@ -996,6 +1000,12 @@ export interface FPoolCreateArgs {
|
|
|
996
1000
|
weights: bigint[];
|
|
997
1001
|
/** The swap fee, WAD (`phi` before F version 25). The base-for-base fee is pinned at 2φ on-chain. A curated policy admits only its listed tiers (decisions 059). */
|
|
998
1002
|
swapFee: bigint;
|
|
1003
|
+
/**
|
|
1004
|
+
* The pool's mint fee, WAD of newly-minted L — the creator's pick from the policy's menu (F version
|
|
1005
|
+
* 26, decisions 065; a curated menu refuses anything else with `MintFeeNotListed`). Defaults to
|
|
1006
|
+
* {@link presetMintFee}: twice the swap fee, capped at `FEE_MAX`.
|
|
1007
|
+
*/
|
|
1008
|
+
mintFee?: bigint;
|
|
999
1009
|
/**
|
|
1000
1010
|
* The common cap (decisions 061): d = ln(cap price / floor price) in 1e-6 log units, a key field;
|
|
1001
1011
|
* 0 or absent is uncapped. From a leverage λ: {@link fPoolCapOfLeverage}.
|
|
@@ -1119,7 +1129,7 @@ export async function fPoolInitialize(c: LogswapClient, a: FPoolCreateArgs) {
|
|
|
1119
1129
|
address: c.addresses.fPoolManager!,
|
|
1120
1130
|
abi: fPoolManagerAbi,
|
|
1121
1131
|
functionName: "initialize",
|
|
1122
|
-
args: [key],
|
|
1132
|
+
args: [key, a.mintFee ?? presetMintFee(a.swapFee)],
|
|
1123
1133
|
account: wallet.account,
|
|
1124
1134
|
} as never);
|
|
1125
1135
|
return c.wallet!.writeContract(request as never);
|
|
@@ -1437,7 +1447,7 @@ export function fPoolLaunchCalls(
|
|
|
1437
1447
|
const enc = (functionName: string, args: unknown[]) =>
|
|
1438
1448
|
encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
|
|
1439
1449
|
const calls: Hex[] = [];
|
|
1440
|
-
if (!a.initialized) calls.push(enc("initialize", [key]));
|
|
1450
|
+
if (!a.initialized) calls.push(enc("initialize", [key, a.mintFee ?? presetMintFee(a.swapFee)]));
|
|
1441
1451
|
calls.push(enc("seed", [poolId, a.L0, companions[1]!, a.Q0]));
|
|
1442
1452
|
// gen 0 by construction: a launch is the pool's FIRST seed — a relaunch is `fPoolRelaunch`.
|
|
1443
1453
|
// No transfer to 0xdead: on a pad `seed` mints every share there itself.
|