@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/history.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { type AbiEvent, type Address, type Hex } from "viem";
|
|
17
17
|
import type { LogswapClient } from "./client.js";
|
|
18
|
+
import type { FPoolKind } from "./fpool.js";
|
|
18
19
|
/** One event, as `getLogs` returns it plus its block's timestamp (unix seconds). */
|
|
19
20
|
export interface HistoryLog<A = Record<string, unknown>> {
|
|
20
21
|
address: Address;
|
|
@@ -28,7 +29,14 @@ export interface HistoryLog<A = Record<string, unknown>> {
|
|
|
28
29
|
export interface EventLogsQuery {
|
|
29
30
|
/** The rindexer GraphQL endpoint; absent means the chain. */
|
|
30
31
|
indexer?: string;
|
|
31
|
-
|
|
32
|
+
/** The emitter, or several: the four policies share one table per event (indexer phase 1). */
|
|
33
|
+
address: Address | readonly Address[];
|
|
34
|
+
/**
|
|
35
|
+
* The emitter's name in the indexer's config, for an event two indexed contracts share — only
|
|
36
|
+
* needed when the address is not in the client's artifact (a policy the registry approved later):
|
|
37
|
+
* see {@link indexedContractOf}.
|
|
38
|
+
*/
|
|
39
|
+
contract?: string;
|
|
32
40
|
event: AbiEvent;
|
|
33
41
|
/** Indexed arguments to match, as `getLogs` takes them; an array matches any of its values. */
|
|
34
42
|
args?: Record<string, unknown>;
|
|
@@ -53,9 +61,81 @@ export declare function getEventLogs<A = Record<string, unknown>>(c: LogswapClie
|
|
|
53
61
|
logs: HistoryLog<A>[];
|
|
54
62
|
source: "indexer" | "chain";
|
|
55
63
|
}>;
|
|
56
|
-
|
|
64
|
+
/**
|
|
65
|
+
* A policy's name in the indexer for one of its events: `Policy`, or the kind's own contract for the
|
|
66
|
+
* two-signature `Listed`. Pass it as {@link EventLogsQuery.contract} for a policy outside the artifact.
|
|
67
|
+
*/
|
|
68
|
+
export declare function indexedContractOf(kind: FPoolKind, event: string): string;
|
|
69
|
+
export declare function tableFor(c: LogswapClient, address: Address, event: string, contract?: string): {
|
|
57
70
|
field: string;
|
|
58
71
|
type: string;
|
|
59
72
|
};
|
|
60
73
|
/** How far back the chain path looks for a `latest` tape: a tape is recent by definition. */
|
|
61
74
|
export declare const LATEST_SCAN_BLOCKS = 5000n;
|
|
75
|
+
/** A row of {@link getEventsLogs}: one of the asked events, named. */
|
|
76
|
+
export interface NamedHistoryLog extends HistoryLog {
|
|
77
|
+
eventName: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Several events of one contract (or of several emitters sharing them), merged oldest first and
|
|
81
|
+
* tagged with `eventName` — the shape of viem's multi-event `getLogs`, which is what the chain path
|
|
82
|
+
* runs. One indexer table per event; if any of them cannot answer, the whole read goes to the node,
|
|
83
|
+
* so a history is never half the indexer's and half the node's.
|
|
84
|
+
*/
|
|
85
|
+
export declare function getEventsLogs(c: LogswapClient, q: Omit<EventLogsQuery, "event" | "args" | "latest" | "contract" | "transactionHashes"> & {
|
|
86
|
+
events: readonly AbiEvent[];
|
|
87
|
+
/** The emitters' policy kind, to name a shared event's table for a policy outside the artifact. */
|
|
88
|
+
kind?: FPoolKind;
|
|
89
|
+
/** The emitter's name in the indexer (e.g. `PolicyRegistry`), for a contract outside the artifact; `kind` wins for a policy. */
|
|
90
|
+
contract?: string;
|
|
91
|
+
}): Promise<{
|
|
92
|
+
logs: NamedHistoryLog[];
|
|
93
|
+
source: "indexer" | "chain";
|
|
94
|
+
}>;
|
|
95
|
+
/** The devnet's token factory event (`src/mocks/TokenFactory.sol`): provenance for every launchable token. */
|
|
96
|
+
export declare const TOKEN_CREATED: {
|
|
97
|
+
readonly name: "TokenCreated";
|
|
98
|
+
readonly type: "event";
|
|
99
|
+
readonly inputs: readonly [{
|
|
100
|
+
readonly type: "address";
|
|
101
|
+
readonly name: "creator";
|
|
102
|
+
readonly indexed: true;
|
|
103
|
+
}, {
|
|
104
|
+
readonly type: "address";
|
|
105
|
+
readonly name: "token";
|
|
106
|
+
readonly indexed: true;
|
|
107
|
+
}, {
|
|
108
|
+
readonly type: "string";
|
|
109
|
+
readonly name: "name";
|
|
110
|
+
}, {
|
|
111
|
+
readonly type: "string";
|
|
112
|
+
readonly name: "symbol";
|
|
113
|
+
}, {
|
|
114
|
+
readonly type: "uint256";
|
|
115
|
+
readonly name: "supply";
|
|
116
|
+
}];
|
|
117
|
+
};
|
|
118
|
+
export interface CreatedToken {
|
|
119
|
+
creator: Address;
|
|
120
|
+
token: Address;
|
|
121
|
+
name: string;
|
|
122
|
+
symbol: string;
|
|
123
|
+
supply: bigint;
|
|
124
|
+
blockNumber: bigint;
|
|
125
|
+
/** Unix seconds; set when the indexer answered. */
|
|
126
|
+
blockTimestamp?: number;
|
|
127
|
+
transactionHash: Hex;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Every token the deployment's factory created, oldest first — the token list's provenance (a token
|
|
131
|
+
* named like a factory token but deployed elsewhere is not in it). Optionally one creator's.
|
|
132
|
+
*/
|
|
133
|
+
export declare function tokensCreated(c: LogswapClient, q: {
|
|
134
|
+
indexer?: string;
|
|
135
|
+
creator?: Address;
|
|
136
|
+
fromBlock?: bigint;
|
|
137
|
+
signal?: AbortSignal;
|
|
138
|
+
}): Promise<{
|
|
139
|
+
tokens: CreatedToken[];
|
|
140
|
+
source: "indexer" | "chain";
|
|
141
|
+
}>;
|
package/dist/history.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* the decoding (the inputs' types — numbers as bigint, addresses checksummed, arrays from JSON).
|
|
14
14
|
* With no indexer URL, or when the query fails, it asks the node and says so in `source`.
|
|
15
15
|
*/
|
|
16
|
-
import { getAddress } from "viem";
|
|
16
|
+
import { getAddress, parseAbiItem } from "viem";
|
|
17
17
|
import { bytea, gql } from "./indexer.js";
|
|
18
18
|
import { blockAtOrAfter } from "./fees.js";
|
|
19
19
|
/**
|
|
@@ -32,29 +32,57 @@ export async function getEventLogs(c, q) {
|
|
|
32
32
|
return { logs: await fromChain(c, q), source: "chain" };
|
|
33
33
|
}
|
|
34
34
|
// ─── the indexer path ────────────────────────────────────────────────────────
|
|
35
|
-
/**
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* What the rindexer deployment calls each contract — the prefix of a table two contracts share
|
|
37
|
+
* (logswap-be `devnet/indexer/rindexer.yaml`). The four policies are one factory-tracked contract,
|
|
38
|
+
* `Policy`, except `Listed`, whose two signatures are two contracts (`PublicPolicy`, `PrivatePolicy`).
|
|
39
|
+
*/
|
|
40
|
+
function contractName(c, address, event) {
|
|
37
41
|
const a = address.toLowerCase();
|
|
38
|
-
|
|
42
|
+
const is = (x) => x !== undefined && a === x.toLowerCase();
|
|
43
|
+
const { addresses: d } = c;
|
|
44
|
+
if (is(d.cPoolManager))
|
|
39
45
|
return "LogswapManager";
|
|
40
|
-
if (
|
|
46
|
+
if (is(d.fPoolManager))
|
|
41
47
|
return "FPoolManager";
|
|
42
|
-
if (
|
|
48
|
+
if (is(d.router))
|
|
43
49
|
return "LogswapRouter";
|
|
50
|
+
if (is(d.policyRegistry))
|
|
51
|
+
return "PolicyRegistry";
|
|
52
|
+
if (is(d.tokenFactory))
|
|
53
|
+
return "TokenFactory";
|
|
54
|
+
if (is(d.publicPolicy))
|
|
55
|
+
return indexedContractOf("public", event);
|
|
56
|
+
if (is(d.privatePolicy))
|
|
57
|
+
return indexedContractOf("private", event);
|
|
58
|
+
if (is(d.polPolicy))
|
|
59
|
+
return indexedContractOf("pol", event);
|
|
60
|
+
if (is(d.padPolicy))
|
|
61
|
+
return indexedContractOf("pad", event);
|
|
44
62
|
return null;
|
|
45
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* A policy's name in the indexer for one of its events: `Policy`, or the kind's own contract for the
|
|
66
|
+
* two-signature `Listed`. Pass it as {@link EventLogsQuery.contract} for a policy outside the artifact.
|
|
67
|
+
*/
|
|
68
|
+
export function indexedContractOf(kind, event) {
|
|
69
|
+
if (event === "Listed")
|
|
70
|
+
return kind === "private" ? "PrivatePolicy" : "PublicPolicy";
|
|
71
|
+
return "Policy";
|
|
72
|
+
}
|
|
46
73
|
/** Events whose name two indexed contracts share: postgraphile prefixes their tables. */
|
|
47
|
-
const SHARED = new Set(["Transfer"]);
|
|
74
|
+
const SHARED = new Set(["Transfer", "Approval", "OperatorSet", "CollectorProposed", "CollectorAccepted", "Approved", "Listed"]);
|
|
48
75
|
const plural = (n) => (/(s|x|ch|sh)$/.test(n) ? `${n}es` : `${n}s`);
|
|
49
76
|
/** A column's GraphQL name: postgraphile camelCases the snake_case rindexer writes (`F` → `f`). */
|
|
50
77
|
const column = (name) => name[0].toLowerCase() + name.slice(1);
|
|
51
|
-
export function tableFor(c, address, event) {
|
|
52
|
-
const prefix = SHARED.has(event) ? contractName(c, address) : "";
|
|
78
|
+
export function tableFor(c, address, event, contract) {
|
|
79
|
+
const prefix = SHARED.has(event) ? (contract ?? contractName(c, address, event)) : "";
|
|
53
80
|
if (prefix === null)
|
|
54
81
|
throw new Error(`history: no indexed contract at ${address}`);
|
|
55
82
|
const type = `${prefix}${event}`;
|
|
56
83
|
return { field: `all${plural(type)}`, type };
|
|
57
84
|
}
|
|
85
|
+
const emitters = (q) => (Array.isArray(q.address) ? [...q.address] : [q.address]);
|
|
58
86
|
function decode(type, v) {
|
|
59
87
|
if (v === null || v === undefined)
|
|
60
88
|
return v;
|
|
@@ -71,7 +99,10 @@ const MAX_PAGES = 200;
|
|
|
71
99
|
/** Pages in flight at once, after the first. */
|
|
72
100
|
const PARALLEL_PAGES = 8;
|
|
73
101
|
async function fromIndexer(c, q) {
|
|
74
|
-
const
|
|
102
|
+
const all = emitters(q);
|
|
103
|
+
if (all.length === 0)
|
|
104
|
+
return [];
|
|
105
|
+
const { field, type } = tableFor(c, all[0], q.event.name, q.contract);
|
|
75
106
|
const inputs = q.event.inputs;
|
|
76
107
|
const condition = {};
|
|
77
108
|
const filter = {};
|
|
@@ -134,11 +165,11 @@ async function fromIndexer(c, q) {
|
|
|
134
165
|
}
|
|
135
166
|
}
|
|
136
167
|
// a table can hold a previous deployment's rows (an indexer not yet reset): keep this contract's
|
|
137
|
-
const want =
|
|
168
|
+
const want = new Map(all.map((a) => [a.toLowerCase(), a]));
|
|
138
169
|
return rows
|
|
139
|
-
.filter((r) => String(r.contractAddress).toLowerCase()
|
|
170
|
+
.filter((r) => want.has(String(r.contractAddress).toLowerCase()))
|
|
140
171
|
.map((r) => ({
|
|
141
|
-
address:
|
|
172
|
+
address: want.get(String(r.contractAddress).toLowerCase()),
|
|
142
173
|
args: Object.fromEntries(inputs.map((i) => [i.name, decode(i.type, r[column(i.name)])])),
|
|
143
174
|
blockNumber: BigInt(r.blockNumber),
|
|
144
175
|
blockTimestamp: Math.floor(Date.parse(r.blockTimestamp) / 1000),
|
|
@@ -152,12 +183,15 @@ async function fromIndexer(c, q) {
|
|
|
152
183
|
export const LATEST_SCAN_BLOCKS = 5000n;
|
|
153
184
|
async function fromChain(c, q) {
|
|
154
185
|
let fromBlock = q.fromBlock ?? 0n;
|
|
155
|
-
const toBlock = q.toBlock ?? (await c.public.getBlockNumber());
|
|
186
|
+
const toBlock = q.toBlock ?? (await c.public.getBlockNumber({ cacheTime: 0 })); // uncached: viem caches the head, and a scan bounded by a stale one misses the block just mined
|
|
156
187
|
if (q.since !== undefined)
|
|
157
188
|
fromBlock = await blockAtOrAfter(c, BigInt(q.since), fromBlock, toBlock);
|
|
158
189
|
if (q.latest !== undefined && toBlock - fromBlock > LATEST_SCAN_BLOCKS)
|
|
159
190
|
fromBlock = toBlock - LATEST_SCAN_BLOCKS;
|
|
160
|
-
|
|
191
|
+
const all = emitters(q);
|
|
192
|
+
if (all.length === 0)
|
|
193
|
+
return [];
|
|
194
|
+
let logs = (await c.public.getLogs({ address: Array.isArray(q.address) ? all : q.address, event: q.event, args: q.args, fromBlock, toBlock }));
|
|
161
195
|
if (q.transactionHashes) {
|
|
162
196
|
const want = new Set(q.transactionHashes.map((h) => h.toLowerCase()));
|
|
163
197
|
logs = logs.filter((l) => want.has(l.transactionHash.toLowerCase()));
|
|
@@ -165,10 +199,68 @@ async function fromChain(c, q) {
|
|
|
165
199
|
if (q.latest !== undefined)
|
|
166
200
|
logs = logs.slice(-q.latest);
|
|
167
201
|
return logs.map((l) => ({
|
|
168
|
-
address:
|
|
202
|
+
address: l.address ? getAddress(l.address) : all[0],
|
|
169
203
|
args: l.args,
|
|
170
204
|
blockNumber: l.blockNumber,
|
|
171
205
|
transactionHash: l.transactionHash,
|
|
172
206
|
logIndex: l.logIndex,
|
|
173
207
|
}));
|
|
174
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Several events of one contract (or of several emitters sharing them), merged oldest first and
|
|
211
|
+
* tagged with `eventName` — the shape of viem's multi-event `getLogs`, which is what the chain path
|
|
212
|
+
* runs. One indexer table per event; if any of them cannot answer, the whole read goes to the node,
|
|
213
|
+
* so a history is never half the indexer's and half the node's.
|
|
214
|
+
*/
|
|
215
|
+
export async function getEventsLogs(c, q) {
|
|
216
|
+
const order = (a, b) => (a.blockNumber === b.blockNumber ? a.logIndex - b.logIndex : a.blockNumber < b.blockNumber ? -1 : 1);
|
|
217
|
+
if (q.indexer && q.events.length > 0) {
|
|
218
|
+
try {
|
|
219
|
+
const got = await Promise.all(q.events.map(async (event) => {
|
|
220
|
+
const contract = q.kind ? indexedContractOf(q.kind, event.name) : q.contract;
|
|
221
|
+
return (await fromIndexer(c, { ...q, event, contract })).map((l) => ({ ...l, eventName: event.name }));
|
|
222
|
+
}));
|
|
223
|
+
return { logs: got.flat().sort(order), source: "indexer" };
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
/* the indexer is a cache: the node holds the same events */
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const all = emitters(q);
|
|
230
|
+
if (all.length === 0 || q.events.length === 0)
|
|
231
|
+
return { logs: [], source: "chain" };
|
|
232
|
+
let fromBlock = q.fromBlock ?? 0n;
|
|
233
|
+
const toBlock = q.toBlock ?? (await c.public.getBlockNumber({ cacheTime: 0 })); // uncached: viem caches the head, and a scan bounded by a stale one misses the block just mined
|
|
234
|
+
if (q.since !== undefined)
|
|
235
|
+
fromBlock = await blockAtOrAfter(c, BigInt(q.since), fromBlock, toBlock);
|
|
236
|
+
const logs = (await c.public.getLogs({ address: Array.isArray(q.address) ? all : q.address, events: q.events, fromBlock, toBlock }));
|
|
237
|
+
return {
|
|
238
|
+
logs: logs
|
|
239
|
+
.map((l) => ({ eventName: l.eventName, address: l.address ? getAddress(l.address) : all[0], args: l.args, blockNumber: l.blockNumber, transactionHash: l.transactionHash, logIndex: l.logIndex }))
|
|
240
|
+
.sort(order),
|
|
241
|
+
source: "chain",
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/** The devnet's token factory event (`src/mocks/TokenFactory.sol`): provenance for every launchable token. */
|
|
245
|
+
export const TOKEN_CREATED = parseAbiItem("event TokenCreated(address indexed creator, address indexed token, string name, string symbol, uint256 supply)");
|
|
246
|
+
/**
|
|
247
|
+
* Every token the deployment's factory created, oldest first — the token list's provenance (a token
|
|
248
|
+
* named like a factory token but deployed elsewhere is not in it). Optionally one creator's.
|
|
249
|
+
*/
|
|
250
|
+
export async function tokensCreated(c, q) {
|
|
251
|
+
const factory = c.addresses.tokenFactory;
|
|
252
|
+
if (!factory)
|
|
253
|
+
throw new Error("logswap: tokensCreated needs addresses.tokenFactory");
|
|
254
|
+
const { logs, source } = await getEventLogs(c, {
|
|
255
|
+
indexer: q.indexer,
|
|
256
|
+
address: factory,
|
|
257
|
+
event: TOKEN_CREATED,
|
|
258
|
+
args: q.creator ? { creator: q.creator } : undefined,
|
|
259
|
+
fromBlock: q.fromBlock,
|
|
260
|
+
signal: q.signal,
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
tokens: logs.map((l) => ({ ...l.args, blockNumber: l.blockNumber, ...(l.blockTimestamp !== undefined ? { blockTimestamp: l.blockTimestamp } : {}), transactionHash: l.transactionHash })),
|
|
264
|
+
source,
|
|
265
|
+
};
|
|
266
|
+
}
|
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
|
/**
|
|
@@ -114,7 +119,8 @@ export declare function discoverMarkets(c: LogswapClient, opts?: {
|
|
|
114
119
|
fromBlock?: bigint;
|
|
115
120
|
toBlock?: bigint;
|
|
116
121
|
base?: Address;
|
|
117
|
-
quote?: Address;
|
|
122
|
+
quote?: Address; /** The rindexer GraphQL endpoint: `Initialize` from there, the node otherwise. */
|
|
123
|
+
indexer?: string;
|
|
118
124
|
}): Promise<DiscoveredMarket[]>;
|
|
119
125
|
/**
|
|
120
126
|
* Ladder shape: the live FLOORS, in log-price WAD — **not** tick indices.
|
|
@@ -203,12 +209,15 @@ export declare function annualise(perUnitL: bigint, elapsedSeconds: bigint): num
|
|
|
203
209
|
/** When a market was created, as a unix timestamp — the denominator for {@link annualise}. */
|
|
204
210
|
export declare function marketOpenedAt(c: LogswapClient, m: DiscoveredMarket): Promise<bigint>;
|
|
205
211
|
/**
|
|
206
|
-
* Create a C market: `initialize(key, x0)` on the manager — a call, not a deployment
|
|
207
|
-
*
|
|
208
|
-
*
|
|
212
|
+
* Create a C market: `initialize(key, x0, mintFee)` on the manager — a call, not a deployment, and
|
|
213
|
+
* no funds move. The mint fee is the creator's pick from the gate's menu (065), preset at twice the
|
|
214
|
+
* swap fee ({@link presetMintFee}). Under a curated gate the CALLER must be a listed creator; the
|
|
215
|
+
* canonical path is the router's {@link createMarket}, which forwards its user to the gate and
|
|
216
|
+
* mints the backstop in the same transaction (064).
|
|
209
217
|
*/
|
|
210
218
|
export declare function initializeMarket(c: LogswapClient, a: {
|
|
211
219
|
key: PoolKey;
|
|
212
220
|
x0: bigint;
|
|
221
|
+
mintFee?: bigint;
|
|
213
222
|
account: Address;
|
|
214
223
|
}): Promise<`0x${string}`>;
|
package/dist/pools.js
CHANGED
|
@@ -16,29 +16,29 @@ 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
|
+
import { getEventLogs } from "./history.js";
|
|
21
|
+
/** 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
22
|
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;
|
|
23
|
+
const v = (await c.public.readContract({
|
|
24
|
+
address: c.addresses.lens,
|
|
25
|
+
abi: logswapLensAbi,
|
|
26
|
+
functionName: "getPools",
|
|
27
|
+
args: [[poolId(key)]],
|
|
28
|
+
}));
|
|
29
|
+
return v[0];
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
|
-
* The market's swap fee
|
|
31
|
-
* `
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* The market's swap fee — the key's own `swapFee` since C version 7: a fee of 0 is no fee, and
|
|
33
|
+
* `DEFAULT_FEE` / the manager's `swapFeeOf` are gone (decisions 065). Constant for the market's
|
|
34
|
+
* life. Still a read: it refuses a key nobody initialized, which `discoverMarkets` relies on to
|
|
35
|
+
* drop a stale indexer's rows. (`swapFeeOf` in `fees.ts` is the fee one swap paid.)
|
|
34
36
|
*/
|
|
35
37
|
export async function marketSwapFee(c, key) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
args: [key],
|
|
41
|
-
});
|
|
38
|
+
const s = await getPool(c, key);
|
|
39
|
+
if (!s.initialized)
|
|
40
|
+
throw new Error(`logswap: market ${poolId(key)} is not initialized`);
|
|
41
|
+
return key.swapFee;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
44
|
* Price from the stored log-price. **Lossy** — `x` is exact and this is a float, so use it for
|
|
@@ -112,12 +112,18 @@ export async function discoverMarkets(c, opts = {}) {
|
|
|
112
112
|
const ev = cPoolManagerAbi.find((x) => x.type === "event" && x.name === "Initialize");
|
|
113
113
|
if (!ev)
|
|
114
114
|
throw new Error("logswap: Initialize event missing from the generated ABI");
|
|
115
|
-
const
|
|
115
|
+
const args = {};
|
|
116
|
+
if (opts.base)
|
|
117
|
+
args.base = opts.base;
|
|
118
|
+
if (opts.quote)
|
|
119
|
+
args.quote = opts.quote;
|
|
120
|
+
const { logs } = await getEventLogs(c, {
|
|
121
|
+
indexer: opts.indexer,
|
|
116
122
|
address: c.addresses.cPoolManager,
|
|
117
123
|
event: ev,
|
|
118
|
-
args
|
|
124
|
+
args,
|
|
119
125
|
fromBlock: opts.fromBlock ?? 0n,
|
|
120
|
-
toBlock: opts.toBlock
|
|
126
|
+
toBlock: opts.toBlock,
|
|
121
127
|
});
|
|
122
128
|
return logs.map((l) => {
|
|
123
129
|
const a = l.args;
|
|
@@ -332,16 +338,18 @@ export async function marketOpenedAt(c, m) {
|
|
|
332
338
|
return b.timestamp;
|
|
333
339
|
}
|
|
334
340
|
/**
|
|
335
|
-
* Create a C market: `initialize(key, x0)` on the manager — a call, not a deployment
|
|
336
|
-
*
|
|
337
|
-
*
|
|
341
|
+
* Create a C market: `initialize(key, x0, mintFee)` on the manager — a call, not a deployment, and
|
|
342
|
+
* no funds move. The mint fee is the creator's pick from the gate's menu (065), preset at twice the
|
|
343
|
+
* swap fee ({@link presetMintFee}). Under a curated gate the CALLER must be a listed creator; the
|
|
344
|
+
* canonical path is the router's {@link createMarket}, which forwards its user to the gate and
|
|
345
|
+
* mints the backstop in the same transaction (064).
|
|
338
346
|
*/
|
|
339
347
|
export async function initializeMarket(c, a) {
|
|
340
348
|
const { request } = await c.public.simulateContract({
|
|
341
349
|
address: c.addresses.cPoolManager,
|
|
342
350
|
abi: cPoolManagerAbi,
|
|
343
351
|
functionName: "initialize",
|
|
344
|
-
args: [a.key, a.x0],
|
|
352
|
+
args: [a.key, a.x0, a.mintFee ?? presetMintFee(a.key.swapFee)],
|
|
345
353
|
account: signerFor(c, a.account),
|
|
346
354
|
});
|
|
347
355
|
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
|
|
@@ -154,7 +166,9 @@ export declare function positionsOf(c: LogswapClient, key: PoolKey, owner: Addre
|
|
|
154
166
|
* holder's slice is L · shares_owner / shares_class, and fees from the lens. One log query plus
|
|
155
167
|
* a read per registered class; fine for a pool's lifetime of classes.
|
|
156
168
|
*/
|
|
157
|
-
export declare function holdingsOf(c: LogswapClient, key: PoolKey, owner: Address, fromBlock?: bigint
|
|
169
|
+
export declare function holdingsOf(c: LogswapClient, key: PoolKey, owner: Address, fromBlock?: bigint, opts?: {
|
|
170
|
+
indexer?: string;
|
|
171
|
+
}): Promise<HeldPositionRow[]>;
|
|
158
172
|
/** A holding's legs, exact from the lens: base (base units), the quote leg, claimable fees, and
|
|
159
173
|
* value = base·p + quote + fees in quote units — L + quote + fees for an uncapped active class. */
|
|
160
174
|
export declare function legsOf(c: LogswapClient, key: PoolKey, id: bigint, holder: Address): Promise<{
|