@logswap/sdk 0.5.73 → 0.5.75
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 +40 -0
- package/dist/admin.d.ts +195 -0
- package/dist/admin.js +91 -0
- package/dist/client.d.ts +11 -2
- package/dist/client.js +12 -2
- package/dist/fees.d.ts +2 -1
- package/dist/fees.js +2 -1
- package/dist/fpool.d.ts +181 -2
- package/dist/fpool.js +5 -9
- package/dist/generated.d.ts +1113 -214
- package/dist/generated.js +736 -101
- package/dist/history.d.ts +82 -2
- package/dist/history.js +108 -16
- 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 +19 -10
- package/dist/pools.js +33 -25
- package/dist/positions.d.ts +23 -9
- package/dist/positions.js +39 -17
- package/package.json +1 -1
- package/src/admin.ts +123 -1
- package/src/client.ts +14 -2
- package/src/fees.ts +4 -3
- package/src/fpool.ts +16 -12
- package/src/generated.ts +736 -101
- package/src/history.ts +150 -17
- package/src/indexer.ts +1 -6
- package/src/keys.ts +8 -6
- package/src/liquidity.ts +10 -2
- package/src/pools.ts +45 -35
- package/src/positions.ts +43 -19
package/dist/positions.js
CHANGED
|
@@ -5,18 +5,31 @@
|
|
|
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
|
+
import { getEventLogs } from "./history.js";
|
|
16
|
+
/**
|
|
17
|
+
* A class's aggregate state, read through `extsload` (C version 7: the manager's public
|
|
18
|
+
* `positions` mapping is gone behind it, decisions 065). The slot is `CPoolState`'s:
|
|
19
|
+
* `keccak256(abi.encode(id, POSITIONS_SLOT = 4))`, two words — (L | shares << 128) and
|
|
20
|
+
* (aps | insideLast << 128). Pinned on chain by the integration suite.
|
|
21
|
+
*/
|
|
12
22
|
export async function getPositionClass(c, id) {
|
|
13
|
-
const
|
|
23
|
+
const slot = keccak256(encodeAbiParameters([{ type: "uint256" }, { type: "uint256" }], [id, C_POSITIONS_SLOT]));
|
|
24
|
+
const w = (await c.public.readContract({
|
|
14
25
|
address: c.addresses.cPoolManager,
|
|
15
26
|
abi: cPoolManagerAbi,
|
|
16
|
-
functionName: "
|
|
17
|
-
args: [
|
|
27
|
+
functionName: "extsload",
|
|
28
|
+
args: [slot, 2n],
|
|
18
29
|
}));
|
|
19
|
-
|
|
30
|
+
const w0 = BigInt(w[0]), w1 = BigInt(w[1]);
|
|
31
|
+
const hi = w1 >> 128n;
|
|
32
|
+
return { L: w0 & U128, shares: w0 >> 128n, aps: w1 & U128, insideLast: hi >= 1n << 127n ? hi - (1n << 128n) : hi };
|
|
20
33
|
}
|
|
21
34
|
/** A holder's share balance in a class. */
|
|
22
35
|
export async function balanceOf(c, owner, id) {
|
|
@@ -129,26 +142,35 @@ export function positionQuote(p, x) {
|
|
|
129
142
|
const upper = p.capped && p.cap <= x ? p.cap : x;
|
|
130
143
|
return (p.L * (upper - p.floor)) / 10n ** 18n;
|
|
131
144
|
}
|
|
132
|
-
/**
|
|
133
|
-
|
|
145
|
+
/**
|
|
146
|
+
* The mint fee a create form presets: twice the swap fee, capped at {@link FEE_MAX} (decisions 064
|
|
147
|
+
* (f): guidance, not a contract rule). The creator picks from the gate's or the policy's menu
|
|
148
|
+
* (065); a listed entry is what the chain accepts — read it with `policyMintFeeMenu`.
|
|
149
|
+
*/
|
|
150
|
+
export function presetMintFee(swapFee) {
|
|
151
|
+
const m = 2n * swapFee;
|
|
152
|
+
return m > FEE_MAX ? FEE_MAX : m;
|
|
153
|
+
}
|
|
134
154
|
/**
|
|
135
155
|
* 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)`.
|
|
156
|
+
* the position's own legs, so a mint's total quote pull is `positionQuote(p, x) + mintFee(p, x, rate)`.
|
|
157
|
+
* `rate` is the MARKET's mint fee (`getPool(...).mintFee`, WAD of L): per market since C version 7,
|
|
158
|
+
* the gate's answer at birth (065); there is no global `MINT_FEE` any more.
|
|
137
159
|
*
|
|
138
160
|
* 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
|
-
*
|
|
161
|
+
* capped class spot has already passed (`cap <= x`). Anything earning pays `rate · L`, all of it
|
|
162
|
+
* the protocol's (decisions 058); with no collector it goes through `F` to the LPs already
|
|
163
|
+
* earning, and with none earning it is not charged. This is NOT the swap fee: that one is charged
|
|
164
|
+
* per unit of |Δ log p| on swaps; this is a flat charge on L, once, at mint.
|
|
143
165
|
*
|
|
144
166
|
* One rung of imprecision: for a floor or cap exactly AT spot the contract consults its `currentK`
|
|
145
167
|
* pointer (an up-move counts the tick, a down-move does not), which this offline mirror cannot see.
|
|
146
168
|
* Preview with it; let the chain be authoritative on that boundary.
|
|
147
169
|
*/
|
|
148
|
-
export function mintFee(p, x) {
|
|
170
|
+
export function mintFee(p, x, rate) {
|
|
149
171
|
if (p.floor > x || (p.capped && p.cap <= x))
|
|
150
172
|
return 0n;
|
|
151
|
-
return (p.L *
|
|
173
|
+
return (p.L * rate) / 10n ** 18n;
|
|
152
174
|
}
|
|
153
175
|
export async function positionsOf(c, key, owner) {
|
|
154
176
|
const rows = (await c.public.readContract({
|
|
@@ -170,9 +192,9 @@ const ID_REGISTERED = parseAbiItem("event IdRegistered(uint256 indexed id, bytes
|
|
|
170
192
|
* holder's slice is L · shares_owner / shares_class, and fees from the lens. One log query plus
|
|
171
193
|
* a read per registered class; fine for a pool's lifetime of classes.
|
|
172
194
|
*/
|
|
173
|
-
export async function holdingsOf(c, key, owner, fromBlock = 0n) {
|
|
195
|
+
export async function holdingsOf(c, key, owner, fromBlock = 0n, opts = {}) {
|
|
174
196
|
const pool = poolId(key);
|
|
175
|
-
const logs = await c
|
|
197
|
+
const { logs } = await getEventLogs(c, { indexer: opts.indexer, address: c.addresses.cPoolManager, event: ID_REGISTERED, args: { poolId: pool }, fromBlock });
|
|
176
198
|
const ids = logs.map((l) => l.args.id ?? 0n).filter((id) => id !== 0n);
|
|
177
199
|
if (ids.length === 0)
|
|
178
200
|
return [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logswap/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.75",
|
|
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
|
@@ -37,6 +37,8 @@ export interface LogswapAddresses {
|
|
|
37
37
|
* absent; a deployment with none asks nobody.
|
|
38
38
|
*/
|
|
39
39
|
policyRegistry?: Address;
|
|
40
|
+
/** The devnet's token factory (`TokenFactory`, a mock): where launchable tokens come from. Optional. */
|
|
41
|
+
tokenFactory?: Address;
|
|
40
42
|
/** Optional — the router falls back to plain approvals when absent. */
|
|
41
43
|
permit2?: Address;
|
|
42
44
|
}
|
|
@@ -113,8 +115,15 @@ function guardChain(wallet: WalletClient, pub: PublicClient): WalletClient {
|
|
|
113
115
|
* (`MINT_FEE_PROTOCOL_CUT` and `setFeeParams` are gone, decisions 058); the swap-fee tax at payout,
|
|
114
116
|
* `swapFeeTax` / `setSwapFeeTax` (060); `phi` is `swapFee` and `phiOf` `swapFeeOf` (059) — field
|
|
115
117
|
* names only, no pool id moved.
|
|
118
|
+
*
|
|
119
|
+
* **7** since 2026-10-12 (logswap-contract#60, decisions 064–065): the typed views (`getPool`,
|
|
120
|
+
* `ticks`, `tickBitmapWord`, `positions`, `feeAcct`, `idInfo`) are behind `extsload` — the pool
|
|
121
|
+
* reads through the lens's `getPools`, a class through `CPoolState`'s layout; a fee of 0 is no
|
|
122
|
+
* fee (`swapFeeOf` and `DEFAULT_FEE` are gone); the mint fee is the market's, proposed at
|
|
123
|
+
* `initialize` / `createMarket` and answered by the gate; the gate (`gate`, `setGate`) is the
|
|
124
|
+
* public policy, set at construction. No pool id moved.
|
|
116
125
|
*/
|
|
117
|
-
export const EXPECTED_VERSION =
|
|
126
|
+
export const EXPECTED_VERSION = 7n;
|
|
118
127
|
/**
|
|
119
128
|
* The F manager's own tag (5 — the key's salt, pool names, the sponsor registry;
|
|
120
129
|
* 4 was the income-only harvest cut).
|
|
@@ -191,8 +200,11 @@ export const EXPECTED_VERSION = 6n;
|
|
|
191
200
|
// 25 (logswap-contract#59): the views moved behind `extsload` (062) — this SDK reads them through the
|
|
192
201
|
// lens (`fPool`, `getFPools`, `fBundleOf`, `fDividendOf`, `fLienOf`); the capped F pool, `FPoolKey.cap`
|
|
193
202
|
// appended so every pool id moved (061); the lien, `lock` / `unlock` (063); θ is `logFloor`, θ₀ `refLogFloor`.
|
|
203
|
+
// 26 (logswap-contract#60): the mint fee is per pool, proposed at `initialize(key, mintFee)` and answered
|
|
204
|
+
// by the policy's `onInitialize` from its menu (065); `MINT_FEE` is gone; a fee of 0 is no fee; the
|
|
205
|
+
// policies curate creators, a public pool is uncapped, one pad per base (064). No pool id moved.
|
|
194
206
|
// A breaking bump: raised in the same change.
|
|
195
|
-
export const EXPECTED_F_VERSION =
|
|
207
|
+
export const EXPECTED_F_VERSION = 26n;
|
|
196
208
|
|
|
197
209
|
export async function assertVersion(c: LogswapClient, expected = EXPECTED_VERSION): Promise<void> {
|
|
198
210
|
const { cPoolManagerAbi, fPoolManagerAbi } = await import("./generated.js");
|
package/src/fees.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import type { LogswapClient } from "./client.js";
|
|
15
15
|
import { cPoolManagerAbi } from "./generated.js";
|
|
16
16
|
import type { DiscoveredMarket } from "./pools.js";
|
|
17
|
+
import { getEventLogs } from "./history.js";
|
|
17
18
|
|
|
18
19
|
const WAD = 10n ** 18n;
|
|
19
20
|
const YEAR_SECONDS = 31_536_000;
|
|
@@ -85,7 +86,7 @@ export async function blockAtOrAfter(c: LogswapClient, t: bigint, lo: bigint, hi
|
|
|
85
86
|
export async function swapFees(
|
|
86
87
|
c: LogswapClient,
|
|
87
88
|
m: DiscoveredMarket,
|
|
88
|
-
opts: { since?: bigint; fromBlock?: bigint; toBlock?: bigint } = {},
|
|
89
|
+
opts: { since?: bigint; fromBlock?: bigint; toBlock?: bigint; /** The rindexer GraphQL endpoint: the Swap rows from there, the node otherwise. */ indexer?: string } = {},
|
|
89
90
|
): Promise<SwapFeeWindow> {
|
|
90
91
|
const toBlock = opts.toBlock ?? (await c.public.getBlockNumber());
|
|
91
92
|
const [opened, last] = await Promise.all([
|
|
@@ -112,11 +113,11 @@ export async function swapFees(
|
|
|
112
113
|
|
|
113
114
|
const logs = fromBlock > toBlock
|
|
114
115
|
? []
|
|
115
|
-
: await c
|
|
116
|
+
: (await getEventLogs<{ fee: bigint }>(c, { indexer: opts.indexer, address: c.addresses.cPoolManager, event: ev, args: { poolId: m.poolId }, fromBlock, toBlock })).logs;
|
|
116
117
|
|
|
117
118
|
// the event carries the fee the swap charged (decisions 050), exactly — no re-derivation from φ
|
|
118
119
|
let fees = 0n;
|
|
119
|
-
for (const l of logs) fees += l.args.fee
|
|
120
|
+
for (const l of logs) fees += l.args.fee;
|
|
120
121
|
|
|
121
122
|
const to = last.timestamp;
|
|
122
123
|
return { fromBlock, toBlock, from, to, seconds: to > from ? to - from : 0n, swapFees: fees, swaps: logs.length };
|
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);
|
|
@@ -1140,19 +1150,13 @@ export async function fPoolIdOf(c: LogswapClient, a: Omit<FPoolCreateArgs, "acco
|
|
|
1140
1150
|
* without naming one; a pad's carries its split, so it is rebuilt from the policy's record with nonce 0 — pass `opts.salt` for another nonce). The result is checked against the id, so a wrong salt throws rather than
|
|
1141
1151
|
* returning a key that names another pool. What {@link fPoolMigrate} needs when handed an id.
|
|
1142
1152
|
*/
|
|
1143
|
-
export async function fPoolKeyOfPool(c: LogswapClient, poolId: Hex, opts: { salt?: Hex; fromBlock?: bigint } = {}): Promise<FPoolKey> {
|
|
1153
|
+
export async function fPoolKeyOfPool(c: LogswapClient, poolId: Hex, opts: { salt?: Hex; fromBlock?: bigint; /** The rindexer GraphQL endpoint: `FInitialize` from there, the node otherwise. The salt is in neither (the event does not carry it). */ indexer?: string } = {}): Promise<FPoolKey> {
|
|
1144
1154
|
type Ev = Extract<(typeof fPoolManagerAbi)[number], { type: "event"; name: "FInitialize" }>;
|
|
1145
1155
|
const ev = fPoolManagerAbi.find((x): x is Ev => x.type === "event" && x.name === "FInitialize")!;
|
|
1146
|
-
const logs = await c.
|
|
1147
|
-
address: c.addresses.fPoolManager!,
|
|
1148
|
-
event: ev,
|
|
1149
|
-
args: { poolId },
|
|
1150
|
-
fromBlock: opts.fromBlock ?? 0n,
|
|
1151
|
-
toBlock: "latest",
|
|
1152
|
-
});
|
|
1156
|
+
const { logs } = await getEventLogs(c, { indexer: opts.indexer, address: c.addresses.fPoolManager!, event: ev as never, args: { poolId }, fromBlock: opts.fromBlock ?? 0n });
|
|
1153
1157
|
const l = logs[0];
|
|
1154
1158
|
if (!l) throw new Error(`logswap: no FInitialize for F pool ${poolId}`);
|
|
1155
|
-
const a = l.args as { quote: Address; creator: Address; bases: readonly Address[]; weights: readonly bigint[]; swapFee: bigint; policy: Address; cap: number };
|
|
1159
|
+
const a = l.args as unknown as { quote: Address; creator: Address; bases: readonly Address[]; weights: readonly bigint[]; swapFee: bigint; policy: Address; cap: number | bigint };
|
|
1156
1160
|
const key: FPoolKey = {
|
|
1157
1161
|
quote: a.quote,
|
|
1158
1162
|
bases: [...a.bases],
|
|
@@ -1437,7 +1441,7 @@ export function fPoolLaunchCalls(
|
|
|
1437
1441
|
const enc = (functionName: string, args: unknown[]) =>
|
|
1438
1442
|
encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
|
|
1439
1443
|
const calls: Hex[] = [];
|
|
1440
|
-
if (!a.initialized) calls.push(enc("initialize", [key]));
|
|
1444
|
+
if (!a.initialized) calls.push(enc("initialize", [key, a.mintFee ?? presetMintFee(a.swapFee)]));
|
|
1441
1445
|
calls.push(enc("seed", [poolId, a.L0, companions[1]!, a.Q0]));
|
|
1442
1446
|
// gen 0 by construction: a launch is the pool's FIRST seed — a relaunch is `fPoolRelaunch`.
|
|
1443
1447
|
// No transfer to 0xdead: on a pad `seed` mints every share there itself.
|