@logswap/sdk 0.5.51 → 0.5.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fees.d.ts +52 -0
- package/dist/fees.js +79 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +2 -1
- package/src/fees.ts +103 -0
- package/src/index.ts +1 -0
package/dist/fees.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fees.ts — swap fees over a window, from `Swap` logs.
|
|
3
|
+
*
|
|
4
|
+
* `F` cannot answer "what did takers pay this week": it is per unit of L, it carries the mint-fee
|
|
5
|
+
* credits as well as swap fees, and it has no window. Each `Swap` event carries the quote leg and
|
|
6
|
+
* φ, and the fee is a fixed fraction of that leg, so the sum over a window is exact up to rounding:
|
|
7
|
+
*
|
|
8
|
+
* quote in (price up): the curve takes q/(1+φ), the fee is q·φ/(1+φ)
|
|
9
|
+
* quote out (price down): the curve releases q/(1−φ), the fee is q·φ/(1−φ)
|
|
10
|
+
*
|
|
11
|
+
* Both equal φ·L·|Δx| summed over the swap's segments — the same φ·|dx| per unit L that `F` books.
|
|
12
|
+
*/
|
|
13
|
+
import type { LogswapClient } from "./client.js";
|
|
14
|
+
import type { DiscoveredMarket } from "./pools.js";
|
|
15
|
+
/** The fee one swap paid, in quote, from its `Swap` event's fields. */
|
|
16
|
+
export declare function swapFeeOf(e: {
|
|
17
|
+
baseIn: boolean;
|
|
18
|
+
amountIn: bigint;
|
|
19
|
+
amountOut: bigint;
|
|
20
|
+
}, phi: bigint): bigint;
|
|
21
|
+
/** Swap fees paid over a window, in the market's quote units. */
|
|
22
|
+
export interface SwapFeeWindow {
|
|
23
|
+
fromBlock: bigint;
|
|
24
|
+
toBlock: bigint;
|
|
25
|
+
/** Window start, unix seconds — `since`, or the first block's timestamp, never before the market opened. */
|
|
26
|
+
from: bigint;
|
|
27
|
+
/** Window end, unix seconds — the last block's timestamp. */
|
|
28
|
+
to: bigint;
|
|
29
|
+
seconds: bigint;
|
|
30
|
+
swapFees: bigint;
|
|
31
|
+
swaps: number;
|
|
32
|
+
}
|
|
33
|
+
/** The first block with timestamp ≥ `t`, by bisection over `[lo, hi]`; `hi` when none is earlier. */
|
|
34
|
+
export declare function blockAtOrAfter(c: LogswapClient, t: bigint, lo: bigint, hi: bigint): Promise<bigint>;
|
|
35
|
+
/**
|
|
36
|
+
* Sum the swap fees of market `m` over a window: from `since` (unix seconds) or `fromBlock`, to
|
|
37
|
+
* `toBlock` (default latest). Either start is clamped to the market's opening, so a period longer
|
|
38
|
+
* than the market's life measures its whole life and `seconds` says how long that was.
|
|
39
|
+
*/
|
|
40
|
+
export declare function swapFees(c: LogswapClient, m: DiscoveredMarket, opts?: {
|
|
41
|
+
since?: bigint;
|
|
42
|
+
fromBlock?: bigint;
|
|
43
|
+
toBlock?: bigint;
|
|
44
|
+
}): Promise<SwapFeeWindow>;
|
|
45
|
+
/**
|
|
46
|
+
* The window's swap fees over the market's CURRENT active L, annualised linearly over the window.
|
|
47
|
+
* A fraction: 0.12 is 12%. Linear: fees are paid out, so none compound.
|
|
48
|
+
*
|
|
49
|
+
* Per unit of L. An uncapped position is worth L·(x − ξ + 1), so its return on capital is this
|
|
50
|
+
* divided by (x − ξ + 1).
|
|
51
|
+
*/
|
|
52
|
+
export declare function swapFeeApy(w: Pick<SwapFeeWindow, "swapFees" | "seconds">, lActive: bigint): number;
|
package/dist/fees.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fees.ts — swap fees over a window, from `Swap` logs.
|
|
3
|
+
*
|
|
4
|
+
* `F` cannot answer "what did takers pay this week": it is per unit of L, it carries the mint-fee
|
|
5
|
+
* credits as well as swap fees, and it has no window. Each `Swap` event carries the quote leg and
|
|
6
|
+
* φ, and the fee is a fixed fraction of that leg, so the sum over a window is exact up to rounding:
|
|
7
|
+
*
|
|
8
|
+
* quote in (price up): the curve takes q/(1+φ), the fee is q·φ/(1+φ)
|
|
9
|
+
* quote out (price down): the curve releases q/(1−φ), the fee is q·φ/(1−φ)
|
|
10
|
+
*
|
|
11
|
+
* Both equal φ·L·|Δx| summed over the swap's segments — the same φ·|dx| per unit L that `F` books.
|
|
12
|
+
*/
|
|
13
|
+
import { cPoolManagerAbi } from "./generated.js";
|
|
14
|
+
const WAD = 10n ** 18n;
|
|
15
|
+
const YEAR_SECONDS = 31_536_000;
|
|
16
|
+
/** The fee one swap paid, in quote, from its `Swap` event's fields. */
|
|
17
|
+
export function swapFeeOf(e, phi) {
|
|
18
|
+
return e.baseIn ? (e.amountOut * phi) / (WAD - phi) : (e.amountIn * phi) / (WAD + phi);
|
|
19
|
+
}
|
|
20
|
+
/** The first block with timestamp ≥ `t`, by bisection over `[lo, hi]`; `hi` when none is earlier. */
|
|
21
|
+
export async function blockAtOrAfter(c, t, lo, hi) {
|
|
22
|
+
while (lo < hi) {
|
|
23
|
+
const mid = (lo + hi) / 2n;
|
|
24
|
+
if ((await c.public.getBlock({ blockNumber: mid })).timestamp < t)
|
|
25
|
+
lo = mid + 1n;
|
|
26
|
+
else
|
|
27
|
+
hi = mid;
|
|
28
|
+
}
|
|
29
|
+
return lo;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Sum the swap fees of market `m` over a window: from `since` (unix seconds) or `fromBlock`, to
|
|
33
|
+
* `toBlock` (default latest). Either start is clamped to the market's opening, so a period longer
|
|
34
|
+
* than the market's life measures its whole life and `seconds` says how long that was.
|
|
35
|
+
*/
|
|
36
|
+
export async function swapFees(c, m, opts = {}) {
|
|
37
|
+
const toBlock = opts.toBlock ?? (await c.public.getBlockNumber());
|
|
38
|
+
const [opened, last] = await Promise.all([
|
|
39
|
+
c.public.getBlock({ blockNumber: m.blockNumber }),
|
|
40
|
+
c.public.getBlock({ blockNumber: toBlock }),
|
|
41
|
+
]);
|
|
42
|
+
let fromBlock;
|
|
43
|
+
let from;
|
|
44
|
+
if (opts.since !== undefined && opts.since > opened.timestamp) {
|
|
45
|
+
fromBlock = await blockAtOrAfter(c, opts.since, m.blockNumber, toBlock);
|
|
46
|
+
from = opts.since;
|
|
47
|
+
}
|
|
48
|
+
else if (opts.fromBlock !== undefined && opts.fromBlock > m.blockNumber) {
|
|
49
|
+
fromBlock = opts.fromBlock;
|
|
50
|
+
from = (await c.public.getBlock({ blockNumber: fromBlock })).timestamp;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
fromBlock = m.blockNumber;
|
|
54
|
+
from = opened.timestamp;
|
|
55
|
+
}
|
|
56
|
+
const ev = cPoolManagerAbi.find((x) => x.type === "event" && x.name === "Swap");
|
|
57
|
+
if (!ev)
|
|
58
|
+
throw new Error("logswap: Swap event missing from the generated ABI");
|
|
59
|
+
const logs = fromBlock > toBlock
|
|
60
|
+
? []
|
|
61
|
+
: await c.public.getLogs({ address: c.addresses.cPoolManager, event: ev, args: { poolId: m.poolId }, fromBlock, toBlock });
|
|
62
|
+
let fees = 0n;
|
|
63
|
+
for (const l of logs)
|
|
64
|
+
fees += swapFeeOf({ baseIn: l.args.baseIn, amountIn: l.args.amountIn, amountOut: l.args.amountOut }, l.args.phi);
|
|
65
|
+
const to = last.timestamp;
|
|
66
|
+
return { fromBlock, toBlock, from, to, seconds: to > from ? to - from : 0n, swapFees: fees, swaps: logs.length };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The window's swap fees over the market's CURRENT active L, annualised linearly over the window.
|
|
70
|
+
* A fraction: 0.12 is 12%. Linear: fees are paid out, so none compound.
|
|
71
|
+
*
|
|
72
|
+
* Per unit of L. An uncapped position is worth L·(x − ξ + 1), so its return on capital is this
|
|
73
|
+
* divided by (x − ξ + 1).
|
|
74
|
+
*/
|
|
75
|
+
export function swapFeeApy(w, lActive) {
|
|
76
|
+
if (w.seconds <= 0n || lActive <= 0n)
|
|
77
|
+
return 0;
|
|
78
|
+
return (Number(w.swapFees) / Number(lActive)) * (YEAR_SECONDS / Number(w.seconds));
|
|
79
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -19,5 +19,6 @@ export * from "./onboard.js";
|
|
|
19
19
|
export * from "./indexer.js";
|
|
20
20
|
export * from "./tokencache.js";
|
|
21
21
|
export * from "./fpool.js";
|
|
22
|
+
export * from "./fees.js";
|
|
22
23
|
export { cPoolManagerAbi, fPoolManagerAbi, logswapLensAbi, logswapRouterAbi, padPolicyAbi, polPolicyAbi, policyRegistryAbi, privatePolicyAbi, publicPolicyAbi } from "./generated.js";
|
|
23
24
|
export * from "./admin.js";
|
package/dist/index.js
CHANGED
|
@@ -19,5 +19,6 @@ export * from "./onboard.js";
|
|
|
19
19
|
export * from "./indexer.js";
|
|
20
20
|
export * from "./tokencache.js";
|
|
21
21
|
export * from "./fpool.js";
|
|
22
|
+
export * from "./fees.js";
|
|
22
23
|
export { cPoolManagerAbi, fPoolManagerAbi, logswapLensAbi, logswapRouterAbi, padPolicyAbi, polPolicyAbi, policyRegistryAbi, privatePolicyAbi, publicPolicyAbi } from "./generated.js";
|
|
23
24
|
export * from "./admin.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logswap/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.52",
|
|
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",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"typecheck": "tsc --noEmit",
|
|
26
26
|
"clean": "rm -rf dist src/generated.ts",
|
|
27
27
|
"test": "vitest run",
|
|
28
|
+
"test:isolated": "bash scripts/test-isolated.sh",
|
|
28
29
|
"test:watch": "vitest",
|
|
29
30
|
"prepublishOnly": "npm run codegen && npm run build"
|
|
30
31
|
},
|
package/src/fees.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fees.ts — swap fees over a window, from `Swap` logs.
|
|
3
|
+
*
|
|
4
|
+
* `F` cannot answer "what did takers pay this week": it is per unit of L, it carries the mint-fee
|
|
5
|
+
* credits as well as swap fees, and it has no window. Each `Swap` event carries the quote leg and
|
|
6
|
+
* φ, and the fee is a fixed fraction of that leg, so the sum over a window is exact up to rounding:
|
|
7
|
+
*
|
|
8
|
+
* quote in (price up): the curve takes q/(1+φ), the fee is q·φ/(1+φ)
|
|
9
|
+
* quote out (price down): the curve releases q/(1−φ), the fee is q·φ/(1−φ)
|
|
10
|
+
*
|
|
11
|
+
* Both equal φ·L·|Δx| summed over the swap's segments — the same φ·|dx| per unit L that `F` books.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { LogswapClient } from "./client.js";
|
|
15
|
+
import { cPoolManagerAbi } from "./generated.js";
|
|
16
|
+
import type { DiscoveredMarket } from "./pools.js";
|
|
17
|
+
|
|
18
|
+
const WAD = 10n ** 18n;
|
|
19
|
+
const YEAR_SECONDS = 31_536_000;
|
|
20
|
+
|
|
21
|
+
/** The fee one swap paid, in quote, from its `Swap` event's fields. */
|
|
22
|
+
export function swapFeeOf(e: { baseIn: boolean; amountIn: bigint; amountOut: bigint }, phi: bigint): bigint {
|
|
23
|
+
return e.baseIn ? (e.amountOut * phi) / (WAD - phi) : (e.amountIn * phi) / (WAD + phi);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Swap fees paid over a window, in the market's quote units. */
|
|
27
|
+
export interface SwapFeeWindow {
|
|
28
|
+
fromBlock: bigint;
|
|
29
|
+
toBlock: bigint;
|
|
30
|
+
/** Window start, unix seconds — `since`, or the first block's timestamp, never before the market opened. */
|
|
31
|
+
from: bigint;
|
|
32
|
+
/** Window end, unix seconds — the last block's timestamp. */
|
|
33
|
+
to: bigint;
|
|
34
|
+
seconds: bigint;
|
|
35
|
+
swapFees: bigint;
|
|
36
|
+
swaps: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The first block with timestamp ≥ `t`, by bisection over `[lo, hi]`; `hi` when none is earlier. */
|
|
40
|
+
export async function blockAtOrAfter(c: LogswapClient, t: bigint, lo: bigint, hi: bigint): Promise<bigint> {
|
|
41
|
+
while (lo < hi) {
|
|
42
|
+
const mid = (lo + hi) / 2n;
|
|
43
|
+
if ((await c.public.getBlock({ blockNumber: mid })).timestamp < t) lo = mid + 1n;
|
|
44
|
+
else hi = mid;
|
|
45
|
+
}
|
|
46
|
+
return lo;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Sum the swap fees of market `m` over a window: from `since` (unix seconds) or `fromBlock`, to
|
|
51
|
+
* `toBlock` (default latest). Either start is clamped to the market's opening, so a period longer
|
|
52
|
+
* than the market's life measures its whole life and `seconds` says how long that was.
|
|
53
|
+
*/
|
|
54
|
+
export async function swapFees(
|
|
55
|
+
c: LogswapClient,
|
|
56
|
+
m: DiscoveredMarket,
|
|
57
|
+
opts: { since?: bigint; fromBlock?: bigint; toBlock?: bigint } = {},
|
|
58
|
+
): Promise<SwapFeeWindow> {
|
|
59
|
+
const toBlock = opts.toBlock ?? (await c.public.getBlockNumber());
|
|
60
|
+
const [opened, last] = await Promise.all([
|
|
61
|
+
c.public.getBlock({ blockNumber: m.blockNumber }),
|
|
62
|
+
c.public.getBlock({ blockNumber: toBlock }),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
let fromBlock: bigint;
|
|
66
|
+
let from: bigint;
|
|
67
|
+
if (opts.since !== undefined && opts.since > opened.timestamp) {
|
|
68
|
+
fromBlock = await blockAtOrAfter(c, opts.since, m.blockNumber, toBlock);
|
|
69
|
+
from = opts.since;
|
|
70
|
+
} else if (opts.fromBlock !== undefined && opts.fromBlock > m.blockNumber) {
|
|
71
|
+
fromBlock = opts.fromBlock;
|
|
72
|
+
from = (await c.public.getBlock({ blockNumber: fromBlock })).timestamp;
|
|
73
|
+
} else {
|
|
74
|
+
fromBlock = m.blockNumber;
|
|
75
|
+
from = opened.timestamp;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type Ev = Extract<(typeof cPoolManagerAbi)[number], { type: "event"; name: "Swap" }>;
|
|
79
|
+
const ev = cPoolManagerAbi.find((x): x is Ev => x.type === "event" && x.name === "Swap");
|
|
80
|
+
if (!ev) throw new Error("logswap: Swap event missing from the generated ABI");
|
|
81
|
+
|
|
82
|
+
const logs = fromBlock > toBlock
|
|
83
|
+
? []
|
|
84
|
+
: await c.public.getLogs({ address: c.addresses.cPoolManager, event: ev, args: { poolId: m.poolId }, fromBlock, toBlock });
|
|
85
|
+
|
|
86
|
+
let fees = 0n;
|
|
87
|
+
for (const l of logs) fees += swapFeeOf({ baseIn: l.args.baseIn!, amountIn: l.args.amountIn!, amountOut: l.args.amountOut! }, l.args.phi!);
|
|
88
|
+
|
|
89
|
+
const to = last.timestamp;
|
|
90
|
+
return { fromBlock, toBlock, from, to, seconds: to > from ? to - from : 0n, swapFees: fees, swaps: logs.length };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The window's swap fees over the market's CURRENT active L, annualised linearly over the window.
|
|
95
|
+
* A fraction: 0.12 is 12%. Linear: fees are paid out, so none compound.
|
|
96
|
+
*
|
|
97
|
+
* Per unit of L. An uncapped position is worth L·(x − ξ + 1), so its return on capital is this
|
|
98
|
+
* divided by (x − ξ + 1).
|
|
99
|
+
*/
|
|
100
|
+
export function swapFeeApy(w: Pick<SwapFeeWindow, "swapFees" | "seconds">, lActive: bigint): number {
|
|
101
|
+
if (w.seconds <= 0n || lActive <= 0n) return 0;
|
|
102
|
+
return (Number(w.swapFees) / Number(lActive)) * (YEAR_SECONDS / Number(w.seconds));
|
|
103
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -19,5 +19,6 @@ export * from "./onboard.js";
|
|
|
19
19
|
export * from "./indexer.js";
|
|
20
20
|
export * from "./tokencache.js";
|
|
21
21
|
export * from "./fpool.js";
|
|
22
|
+
export * from "./fees.js";
|
|
22
23
|
export { cPoolManagerAbi, fPoolManagerAbi, logswapLensAbi, logswapRouterAbi, padPolicyAbi, polPolicyAbi, policyRegistryAbi, privatePolicyAbi, publicPolicyAbi } from "./generated.js";
|
|
23
24
|
export * from "./admin.js";
|