@steerprotocol/liquidity-meter 1.0.0 → 2.0.0
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 +18 -56
- package/bin/liquidity-depth.js +887 -0
- package/package.json +23 -32
- package/src/api.js +490 -0
- package/src/depth.js +492 -0
- package/src/index.js +6 -0
- package/src/prices.js +188 -0
- package/src/uniswapv3-subgraph.js +141 -0
- package/src/viem-onchain.js +233 -0
- package/src/withdraw.js +333 -0
- package/templates/README.md +37 -0
- package/templates/pools_minimal.csv +3 -0
- package/templates/pools_with_vault.csv +3 -0
- package/dist/adapters/onchain.d.ts +0 -51
- package/dist/adapters/onchain.js +0 -158
- package/dist/adapters/uniswapv3-subgraph.d.ts +0 -44
- package/dist/adapters/uniswapv3-subgraph.js +0 -105
- package/dist/adapters/withdraw.d.ts +0 -14
- package/dist/adapters/withdraw.js +0 -150
- package/dist/api.d.ts +0 -72
- package/dist/api.js +0 -180
- package/dist/cli/liquidity-depth.d.ts +0 -2
- package/dist/cli/liquidity-depth.js +0 -1160
- package/dist/core/depth.d.ts +0 -48
- package/dist/core/depth.js +0 -314
- package/dist/handlers/cron.d.ts +0 -27
- package/dist/handlers/cron.js +0 -68
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -7
- package/dist/prices.d.ts +0 -15
- package/dist/prices.js +0 -205
- package/dist/wagmi/config.d.ts +0 -2106
- package/dist/wagmi/config.js +0 -24
- package/dist/wagmi/generated.d.ts +0 -2019
- package/dist/wagmi/generated.js +0 -346
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Templates
|
|
2
|
+
|
|
3
|
+
This folder contains ready-to-edit templates to help you get started quickly.
|
|
4
|
+
|
|
5
|
+
Pick one of the CSVs below, copy it somewhere (or edit in place), fill in your addresses and options, then run the CLI with `--csv`.
|
|
6
|
+
|
|
7
|
+
Files
|
|
8
|
+
|
|
9
|
+
- pools_minimal.csv
|
|
10
|
+
- Minimal columns for running depth analysis per pool.
|
|
11
|
+
- Columns:
|
|
12
|
+
- pool: Uniswap v3 pool address (required)
|
|
13
|
+
- usd_sizes: Optional comma-separated USD sizes for price impact
|
|
14
|
+
|
|
15
|
+
- pools_with_vault.csv
|
|
16
|
+
- Includes Steer vault withdraw simulation columns.
|
|
17
|
+
- Columns:
|
|
18
|
+
- pool: Uniswap v3 pool address (required)
|
|
19
|
+
- vault: Steer vault address (required for withdraw simulation)
|
|
20
|
+
- owner: Address whose shares will be withdrawn (required for withdraw simulation)
|
|
21
|
+
- withdraw_pct: Percent of owner shares to withdraw (e.g., 25). Use `withdraw_shares` if you prefer exact shares.
|
|
22
|
+
- usd_sizes: Optional comma-separated USD sizes for price impact
|
|
23
|
+
|
|
24
|
+
Example commands
|
|
25
|
+
|
|
26
|
+
- Minimal (on-chain via Tevm fork):
|
|
27
|
+
- liquidity-depth --csv ./templates/pools_minimal.csv --outdir ./reports --rpc https://developer-access-mainnet.base.org
|
|
28
|
+
|
|
29
|
+
- With withdraw simulation (per-row):
|
|
30
|
+
- liquidity-depth --csv ./templates/pools_with_vault.csv --outdir ./reports --rpc https://developer-access-mainnet.base.org
|
|
31
|
+
|
|
32
|
+
Notes
|
|
33
|
+
|
|
34
|
+
- You can also set `--owner` and `--withdraw-pct` as CLI flags to apply globally and omit them from the CSV.
|
|
35
|
+
- Prices source defaults to `auto`. You can override per-row with a `prices` column or globally with `--prices`.
|
|
36
|
+
- If any row resolves invalid or missing addresses, that row will be skipped with a warning.
|
|
37
|
+
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { createMemoryClient } from 'tevm';
|
|
2
|
-
import { Tick } from '../core/depth.js';
|
|
3
|
-
export type PoolSnapshotOnchain = {
|
|
4
|
-
sqrtPriceX96: bigint;
|
|
5
|
-
tick: number;
|
|
6
|
-
liquidity: bigint;
|
|
7
|
-
feePips: number;
|
|
8
|
-
ticks: Tick[];
|
|
9
|
-
token0: {
|
|
10
|
-
decimals: number;
|
|
11
|
-
usdPrice: number;
|
|
12
|
-
id?: string;
|
|
13
|
-
symbol?: string;
|
|
14
|
-
};
|
|
15
|
-
token1: {
|
|
16
|
-
decimals: number;
|
|
17
|
-
usdPrice: number;
|
|
18
|
-
id?: string;
|
|
19
|
-
symbol?: string;
|
|
20
|
-
};
|
|
21
|
-
meta: {
|
|
22
|
-
token0: {
|
|
23
|
-
decimals: number;
|
|
24
|
-
id: string;
|
|
25
|
-
symbol?: string;
|
|
26
|
-
};
|
|
27
|
-
token1: {
|
|
28
|
-
decimals: number;
|
|
29
|
-
id: string;
|
|
30
|
-
symbol?: string;
|
|
31
|
-
};
|
|
32
|
-
poolId: string;
|
|
33
|
-
range: {
|
|
34
|
-
start: number;
|
|
35
|
-
end: number;
|
|
36
|
-
};
|
|
37
|
-
farPercent: number;
|
|
38
|
-
tickSpacing: number;
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
export declare function fetchPoolSnapshotViem(params: {
|
|
42
|
-
poolAddress: string;
|
|
43
|
-
rpcUrl?: string;
|
|
44
|
-
percentBuckets?: number[];
|
|
45
|
-
client?: ReturnType<typeof createMemoryClient>;
|
|
46
|
-
}): Promise<PoolSnapshotOnchain>;
|
|
47
|
-
export declare function fetchPoolSnapshotCore(params: {
|
|
48
|
-
poolAddress: string;
|
|
49
|
-
rpcUrl: string;
|
|
50
|
-
percentBuckets?: number[];
|
|
51
|
-
}): Promise<PoolSnapshotOnchain>;
|
package/dist/adapters/onchain.js
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
// On-chain fetcher for Uniswap v3 pools (TypeScript)
|
|
2
|
-
import { createMemoryClient, http } from 'tevm';
|
|
3
|
-
import { getAddress } from 'viem';
|
|
4
|
-
import { readContract } from '@wagmi/core';
|
|
5
|
-
import { configFromRpc } from '../wagmi/config.js';
|
|
6
|
-
import { erc20Abi, uniswapV3PoolAbi } from '../wagmi/generated.js';
|
|
7
|
-
// ABIs sourced from wagmi CLI generated file
|
|
8
|
-
function percentToTickDelta(pct) {
|
|
9
|
-
const r = 1 + pct / 100;
|
|
10
|
-
const delta = Math.log(r) / Math.log(1.0001);
|
|
11
|
-
return Math.max(1, Math.round(delta));
|
|
12
|
-
}
|
|
13
|
-
function floorDiv(a, b) {
|
|
14
|
-
const q = Math.trunc(a / b);
|
|
15
|
-
const r = a % b;
|
|
16
|
-
return (r !== 0 && ((r > 0) !== (b > 0))) ? q - 1 : q;
|
|
17
|
-
}
|
|
18
|
-
export async function fetchPoolSnapshotViem(params) {
|
|
19
|
-
const { poolAddress, rpcUrl, percentBuckets = [1, 2, 5, 10], client } = params;
|
|
20
|
-
let c = client;
|
|
21
|
-
if (!c) {
|
|
22
|
-
if (!rpcUrl)
|
|
23
|
-
throw new Error('rpcUrl required when client is not provided');
|
|
24
|
-
c = createMemoryClient({ fork: { transport: http(rpcUrl), blockTag: 'latest' }, loggingLevel: 'error' });
|
|
25
|
-
if (c.tevmReady)
|
|
26
|
-
await c.tevmReady();
|
|
27
|
-
}
|
|
28
|
-
const pool = getAddress(poolAddress);
|
|
29
|
-
const [slot0Raw, LRaw, feeRaw, spacingRaw, token0Addr, token1Addr] = await Promise.all([
|
|
30
|
-
c.readContract({ address: pool, abi: uniswapV3PoolAbi, functionName: 'slot0' }),
|
|
31
|
-
c.readContract({ address: pool, abi: uniswapV3PoolAbi, functionName: 'liquidity' }),
|
|
32
|
-
c.readContract({ address: pool, abi: uniswapV3PoolAbi, functionName: 'fee' }),
|
|
33
|
-
c.readContract({ address: pool, abi: uniswapV3PoolAbi, functionName: 'tickSpacing' }),
|
|
34
|
-
c.readContract({ address: pool, abi: uniswapV3PoolAbi, functionName: 'token0' }),
|
|
35
|
-
c.readContract({ address: pool, abi: uniswapV3PoolAbi, functionName: 'token1' }),
|
|
36
|
-
]);
|
|
37
|
-
const [dec0, sym0, dec1, sym1] = await Promise.all([
|
|
38
|
-
c.readContract({ address: token0Addr, abi: erc20Abi, functionName: 'decimals' }),
|
|
39
|
-
c.readContract({ address: token0Addr, abi: erc20Abi, functionName: 'symbol' }),
|
|
40
|
-
c.readContract({ address: token1Addr, abi: erc20Abi, functionName: 'decimals' }),
|
|
41
|
-
c.readContract({ address: token1Addr, abi: erc20Abi, functionName: 'symbol' }),
|
|
42
|
-
]);
|
|
43
|
-
const sqrtPriceX96 = slot0Raw[0];
|
|
44
|
-
const tick = Number(slot0Raw[1]);
|
|
45
|
-
const liquidity = LRaw;
|
|
46
|
-
const feePips = Number(feeRaw);
|
|
47
|
-
const tickSpacing = Number(spacingRaw);
|
|
48
|
-
const far = Math.max(...percentBuckets);
|
|
49
|
-
const delta = percentToTickDelta(far);
|
|
50
|
-
const start = tick - delta;
|
|
51
|
-
const end = tick + delta;
|
|
52
|
-
const compStart = floorDiv(start, tickSpacing);
|
|
53
|
-
const compEnd = floorDiv(end, tickSpacing);
|
|
54
|
-
const wordStart = floorDiv(compStart, 256);
|
|
55
|
-
const wordEnd = floorDiv(compEnd, 256);
|
|
56
|
-
const ticks = [];
|
|
57
|
-
for (let w = wordStart; w <= wordEnd; w++) {
|
|
58
|
-
const bitmap = await c.readContract({ address: pool, abi: uniswapV3PoolAbi, functionName: 'tickBitmap', args: [BigInt(w)] });
|
|
59
|
-
if (bitmap === 0n)
|
|
60
|
-
continue;
|
|
61
|
-
for (let bit = 0; bit < 256; bit++) {
|
|
62
|
-
if (((bitmap >> BigInt(bit)) & 1n) === 0n)
|
|
63
|
-
continue;
|
|
64
|
-
const compressed = w * 256 + bit;
|
|
65
|
-
const t = compressed * tickSpacing;
|
|
66
|
-
if (t < start || t > end)
|
|
67
|
-
continue;
|
|
68
|
-
const ret = await c.readContract({ address: pool, abi: uniswapV3PoolAbi, functionName: 'ticks', args: [t] });
|
|
69
|
-
const liquidityNet = ret[1];
|
|
70
|
-
ticks.push({ index: t, liquidityNet });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
sqrtPriceX96,
|
|
75
|
-
tick,
|
|
76
|
-
liquidity,
|
|
77
|
-
feePips,
|
|
78
|
-
ticks: ticks.sort((a, b) => a.index - b.index),
|
|
79
|
-
token0: { decimals: Number(dec0), usdPrice: 0, symbol: sym0, id: token0Addr },
|
|
80
|
-
token1: { decimals: Number(dec1), usdPrice: 0, symbol: sym1, id: token1Addr },
|
|
81
|
-
meta: {
|
|
82
|
-
token0: { decimals: Number(dec0), symbol: sym0, id: token0Addr },
|
|
83
|
-
token1: { decimals: Number(dec1), symbol: sym1, id: token1Addr },
|
|
84
|
-
poolId: pool,
|
|
85
|
-
range: { start, end },
|
|
86
|
-
farPercent: far,
|
|
87
|
-
tickSpacing,
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
// Fetch using wagmi/core actions (no TEVM), talking directly to RPC
|
|
92
|
-
export async function fetchPoolSnapshotCore(params) {
|
|
93
|
-
const { poolAddress, rpcUrl, percentBuckets = [1, 2, 5, 10] } = params;
|
|
94
|
-
const cfg = await configFromRpc(rpcUrl);
|
|
95
|
-
const chainId = cfg.chains[0].id;
|
|
96
|
-
const pool = getAddress(poolAddress);
|
|
97
|
-
const [slot0Raw, LRaw, feeRaw, spacingRaw, token0Addr, token1Addr] = await Promise.all([
|
|
98
|
-
readContract(cfg, { address: pool, abi: uniswapV3PoolAbi, functionName: 'slot0', chainId }),
|
|
99
|
-
readContract(cfg, { address: pool, abi: uniswapV3PoolAbi, functionName: 'liquidity', chainId }),
|
|
100
|
-
readContract(cfg, { address: pool, abi: uniswapV3PoolAbi, functionName: 'fee', chainId }),
|
|
101
|
-
readContract(cfg, { address: pool, abi: uniswapV3PoolAbi, functionName: 'tickSpacing', chainId }),
|
|
102
|
-
readContract(cfg, { address: pool, abi: uniswapV3PoolAbi, functionName: 'token0', chainId }),
|
|
103
|
-
readContract(cfg, { address: pool, abi: uniswapV3PoolAbi, functionName: 'token1', chainId }),
|
|
104
|
-
]);
|
|
105
|
-
const [dec0, sym0, dec1, sym1] = await Promise.all([
|
|
106
|
-
readContract(cfg, { address: token0Addr, abi: erc20Abi, functionName: 'decimals', chainId }),
|
|
107
|
-
readContract(cfg, { address: token0Addr, abi: erc20Abi, functionName: 'symbol', chainId }),
|
|
108
|
-
readContract(cfg, { address: token1Addr, abi: erc20Abi, functionName: 'decimals', chainId }),
|
|
109
|
-
readContract(cfg, { address: token1Addr, abi: erc20Abi, functionName: 'symbol', chainId }),
|
|
110
|
-
]);
|
|
111
|
-
const sqrtPriceX96 = slot0Raw[0];
|
|
112
|
-
const tick = Number(slot0Raw[1]);
|
|
113
|
-
const liquidity = LRaw;
|
|
114
|
-
const feePips = Number(feeRaw);
|
|
115
|
-
const tickSpacing = Number(spacingRaw);
|
|
116
|
-
const far = Math.max(...percentBuckets);
|
|
117
|
-
const delta = percentToTickDelta(far);
|
|
118
|
-
const start = tick - delta;
|
|
119
|
-
const end = tick + delta;
|
|
120
|
-
const compStart = floorDiv(start, tickSpacing);
|
|
121
|
-
const compEnd = floorDiv(end, tickSpacing);
|
|
122
|
-
const wordStart = floorDiv(compStart, 256);
|
|
123
|
-
const wordEnd = floorDiv(compEnd, 256);
|
|
124
|
-
const ticks = [];
|
|
125
|
-
for (let w = wordStart; w <= wordEnd; w++) {
|
|
126
|
-
const bitmap = (await readContract(cfg, { address: pool, abi: uniswapV3PoolAbi, functionName: 'tickBitmap', args: [BigInt(w)], chainId }));
|
|
127
|
-
if (bitmap === 0n)
|
|
128
|
-
continue;
|
|
129
|
-
for (let bit = 0; bit < 256; bit++) {
|
|
130
|
-
if (((bitmap >> BigInt(bit)) & 1n) === 0n)
|
|
131
|
-
continue;
|
|
132
|
-
const compressed = w * 256 + bit;
|
|
133
|
-
const t = compressed * tickSpacing;
|
|
134
|
-
if (t < start || t > end)
|
|
135
|
-
continue;
|
|
136
|
-
const ret = (await readContract(cfg, { address: pool, abi: uniswapV3PoolAbi, functionName: 'ticks', args: [t], chainId }));
|
|
137
|
-
const liquidityNet = ret[1];
|
|
138
|
-
ticks.push({ index: t, liquidityNet });
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
sqrtPriceX96,
|
|
143
|
-
tick,
|
|
144
|
-
liquidity,
|
|
145
|
-
feePips,
|
|
146
|
-
ticks: ticks.sort((a, b) => a.index - b.index),
|
|
147
|
-
token0: { decimals: Number(dec0), usdPrice: 0, symbol: sym0, id: token0Addr },
|
|
148
|
-
token1: { decimals: Number(dec1), usdPrice: 0, symbol: sym1, id: token1Addr },
|
|
149
|
-
meta: {
|
|
150
|
-
token0: { decimals: Number(dec0), symbol: sym0, id: token0Addr },
|
|
151
|
-
token1: { decimals: Number(dec1), symbol: sym1, id: token1Addr },
|
|
152
|
-
poolId: pool,
|
|
153
|
-
range: { start, end },
|
|
154
|
-
farPercent: far,
|
|
155
|
-
tickSpacing,
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Tick } from '../core/depth.js';
|
|
2
|
-
export type PoolSnapshot = {
|
|
3
|
-
sqrtPriceX96: bigint;
|
|
4
|
-
tick: number;
|
|
5
|
-
liquidity: bigint;
|
|
6
|
-
feePips: number;
|
|
7
|
-
ticks: Tick[];
|
|
8
|
-
token0: {
|
|
9
|
-
decimals: number;
|
|
10
|
-
usdPrice: number;
|
|
11
|
-
id?: string;
|
|
12
|
-
symbol?: string;
|
|
13
|
-
};
|
|
14
|
-
token1: {
|
|
15
|
-
decimals: number;
|
|
16
|
-
usdPrice: number;
|
|
17
|
-
id?: string;
|
|
18
|
-
symbol?: string;
|
|
19
|
-
};
|
|
20
|
-
meta: {
|
|
21
|
-
token0?: {
|
|
22
|
-
decimals: number;
|
|
23
|
-
id: string;
|
|
24
|
-
symbol?: string;
|
|
25
|
-
};
|
|
26
|
-
token1?: {
|
|
27
|
-
decimals: number;
|
|
28
|
-
id: string;
|
|
29
|
-
symbol?: string;
|
|
30
|
-
};
|
|
31
|
-
ethPriceUSD?: number;
|
|
32
|
-
poolId: string;
|
|
33
|
-
range: {
|
|
34
|
-
start: number;
|
|
35
|
-
end: number;
|
|
36
|
-
};
|
|
37
|
-
farPercent: number;
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
export declare function fetchPoolSnapshot(params: {
|
|
41
|
-
poolAddress: string;
|
|
42
|
-
percentBuckets?: number[];
|
|
43
|
-
subgraphUrl?: string;
|
|
44
|
-
}): Promise<PoolSnapshot>;
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
// Uniswap v3 subgraph client (TypeScript)
|
|
2
|
-
import axios from 'axios';
|
|
3
|
-
import { DepthInternals } from '../core/depth.js';
|
|
4
|
-
const DEFAULT_SUBGRAPH = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3';
|
|
5
|
-
function percentToTickDelta(pct) {
|
|
6
|
-
const r = 1 + pct / 100;
|
|
7
|
-
const delta = Math.log(r) / Math.log(1.0001);
|
|
8
|
-
return Math.max(1, Math.round(delta));
|
|
9
|
-
}
|
|
10
|
-
async function graphqlFetch(url, query, variables) {
|
|
11
|
-
const res = await axios.post(url, { query, variables }, {
|
|
12
|
-
headers: { 'content-type': 'application/json' },
|
|
13
|
-
maxRedirects: 5,
|
|
14
|
-
timeout: 15000,
|
|
15
|
-
validateStatus: (s) => s >= 200 && s < 300,
|
|
16
|
-
});
|
|
17
|
-
const body = res.data;
|
|
18
|
-
if (body.errors && body.errors.length) {
|
|
19
|
-
const msg = body.errors.map((e) => e.message).join('; ');
|
|
20
|
-
throw new Error(`Subgraph error: ${msg}`);
|
|
21
|
-
}
|
|
22
|
-
return body.data;
|
|
23
|
-
}
|
|
24
|
-
export async function fetchPoolSnapshot(params) {
|
|
25
|
-
const { poolAddress, percentBuckets = [1, 2, 5, 10], subgraphUrl = DEFAULT_SUBGRAPH } = params;
|
|
26
|
-
const id = poolAddress.toLowerCase();
|
|
27
|
-
const coreQuery = `
|
|
28
|
-
query PoolCore($id: ID!) {
|
|
29
|
-
bundle(id: "1") { ethPriceUSD }
|
|
30
|
-
pool(id: $id) {
|
|
31
|
-
id
|
|
32
|
-
sqrtPrice
|
|
33
|
-
tick
|
|
34
|
-
liquidity
|
|
35
|
-
feeTier
|
|
36
|
-
token0 { id symbol decimals derivedETH }
|
|
37
|
-
token1 { id symbol decimals derivedETH }
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
`;
|
|
41
|
-
const core = await graphqlFetch(subgraphUrl, coreQuery, { id });
|
|
42
|
-
if (!core.pool)
|
|
43
|
-
throw new Error(`Pool not found in subgraph: ${id}`);
|
|
44
|
-
const ethPriceUSD = Number(core.bundle?.ethPriceUSD ?? 0);
|
|
45
|
-
const pool = core.pool;
|
|
46
|
-
const sqrtPriceX96 = BigInt(pool.sqrtPrice);
|
|
47
|
-
const tick = Number(pool.tick);
|
|
48
|
-
const liquidity = BigInt(pool.liquidity);
|
|
49
|
-
const feePips = Number(pool.feeTier);
|
|
50
|
-
const token0 = {
|
|
51
|
-
decimals: Number(pool.token0.decimals),
|
|
52
|
-
usdPrice: Number(pool.token0.derivedETH) * ethPriceUSD,
|
|
53
|
-
symbol: pool.token0.symbol,
|
|
54
|
-
id: pool.token0.id,
|
|
55
|
-
};
|
|
56
|
-
const token1 = {
|
|
57
|
-
decimals: Number(pool.token1.decimals),
|
|
58
|
-
usdPrice: Number(pool.token1.derivedETH) * ethPriceUSD,
|
|
59
|
-
symbol: pool.token1.symbol,
|
|
60
|
-
id: pool.token1.id,
|
|
61
|
-
};
|
|
62
|
-
const far = Math.max(...percentBuckets);
|
|
63
|
-
const delta = percentToTickDelta(far);
|
|
64
|
-
const start = Math.max(DepthInternals.MIN_TICK, tick - delta);
|
|
65
|
-
const end = Math.min(DepthInternals.MAX_TICK, tick + delta);
|
|
66
|
-
const ticks = [];
|
|
67
|
-
const pageSize = 1000;
|
|
68
|
-
let skip = 0;
|
|
69
|
-
while (true) {
|
|
70
|
-
const ticksQuery = `
|
|
71
|
-
query PoolTicks($id: ID!, $start: Int!, $end: Int!, $first: Int!, $skip: Int!) {
|
|
72
|
-
pool(id: $id) {
|
|
73
|
-
ticks(where: { tickIdx_gte: $start, tickIdx_lte: $end }, orderBy: tickIdx, orderDirection: asc, first: $first, skip: $skip) {
|
|
74
|
-
tickIdx
|
|
75
|
-
liquidityNet
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
`;
|
|
80
|
-
const data = await graphqlFetch(subgraphUrl, ticksQuery, { id, start, end, first: pageSize, skip });
|
|
81
|
-
const page = data.pool?.ticks ?? [];
|
|
82
|
-
for (const t of page)
|
|
83
|
-
ticks.push({ index: Number(t.tickIdx), liquidityNet: BigInt(t.liquidityNet) });
|
|
84
|
-
if (page.length < pageSize)
|
|
85
|
-
break;
|
|
86
|
-
skip += pageSize;
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
sqrtPriceX96,
|
|
90
|
-
tick,
|
|
91
|
-
liquidity,
|
|
92
|
-
feePips,
|
|
93
|
-
ticks,
|
|
94
|
-
token0: { decimals: token0.decimals, usdPrice: token0.usdPrice, id: token0.id, symbol: token0.symbol },
|
|
95
|
-
token1: { decimals: token1.decimals, usdPrice: token1.usdPrice, id: token1.id, symbol: token1.symbol },
|
|
96
|
-
meta: {
|
|
97
|
-
token0: { decimals: token0.decimals, id: token0.id, symbol: token0.symbol },
|
|
98
|
-
token1: { decimals: token1.decimals, id: token1.id, symbol: token1.symbol },
|
|
99
|
-
ethPriceUSD,
|
|
100
|
-
poolId: id,
|
|
101
|
-
range: { start, end },
|
|
102
|
-
farPercent: far,
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { createMemoryClient } from 'tevm';
|
|
2
|
-
export type WithdrawParams = {
|
|
3
|
-
client: ReturnType<typeof createMemoryClient>;
|
|
4
|
-
vault: string;
|
|
5
|
-
owner: string;
|
|
6
|
-
withdrawPct?: number;
|
|
7
|
-
withdrawShares?: string | number | bigint;
|
|
8
|
-
debug?: boolean;
|
|
9
|
-
cliPool?: string;
|
|
10
|
-
viemChain?: any;
|
|
11
|
-
};
|
|
12
|
-
export declare function simulateVaultWithdraw(params: WithdrawParams): Promise<boolean>;
|
|
13
|
-
export declare function resolveViemChainFromClient(client: any): Promise<any>;
|
|
14
|
-
export declare function resolveTevmCommon(rpcUrl?: string): Promise<any>;
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { getAddress, encodeFunctionData, parseEther, createPublicClient, http as viemHttp } from 'viem';
|
|
2
|
-
import { sendTransaction as viemSendTransaction } from 'viem/actions';
|
|
3
|
-
import * as tevmCommon from 'tevm/common';
|
|
4
|
-
import * as viemChains from 'viem/chains';
|
|
5
|
-
import { erc20Abi, steerVaultAbi } from '../wagmi/generated.js';
|
|
6
|
-
const ERC20_ABI = erc20Abi;
|
|
7
|
-
const VAULT_ABI = steerVaultAbi;
|
|
8
|
-
export async function simulateVaultWithdraw(params) {
|
|
9
|
-
const { client, vault, owner, withdrawPct, withdrawShares, debug = false, cliPool, viemChain } = params;
|
|
10
|
-
if (!client)
|
|
11
|
-
throw new Error('simulateVaultWithdraw requires tevm client');
|
|
12
|
-
if (!vault)
|
|
13
|
-
throw new Error('Missing vault');
|
|
14
|
-
if (!owner)
|
|
15
|
-
throw new Error('Missing owner for withdraw simulation');
|
|
16
|
-
const vaultAddr = getAddress(vault);
|
|
17
|
-
const ownerAddr = getAddress(owner);
|
|
18
|
-
let poolAddr;
|
|
19
|
-
try {
|
|
20
|
-
poolAddr = await client.readContract({ address: vaultAddr, abi: VAULT_ABI, functionName: 'pool' });
|
|
21
|
-
}
|
|
22
|
-
catch (e) {
|
|
23
|
-
throw new Error(`Failed to read vault.pool(): ${e?.message || e}`);
|
|
24
|
-
}
|
|
25
|
-
let vDec = 18;
|
|
26
|
-
try {
|
|
27
|
-
vDec = Number(await client.readContract({ address: vaultAddr, abi: VAULT_ABI, functionName: 'decimals' }));
|
|
28
|
-
}
|
|
29
|
-
catch { }
|
|
30
|
-
const t0Addr = await client.readContract({ address: vaultAddr, abi: VAULT_ABI, functionName: 'token0' });
|
|
31
|
-
const t1Addr = await client.readContract({ address: vaultAddr, abi: VAULT_ABI, functionName: 'token1' });
|
|
32
|
-
const [t0Dec, t1Dec] = await Promise.all([
|
|
33
|
-
client.readContract({ address: t0Addr, abi: ERC20_ABI, functionName: 'decimals' }),
|
|
34
|
-
client.readContract({ address: t1Addr, abi: ERC20_ABI, functionName: 'decimals' }),
|
|
35
|
-
]);
|
|
36
|
-
let [t0Sym, t1Sym] = ['T0', 'T1'];
|
|
37
|
-
try {
|
|
38
|
-
t0Sym = await client.readContract({ address: t0Addr, abi: ERC20_ABI, functionName: 'symbol' });
|
|
39
|
-
}
|
|
40
|
-
catch { }
|
|
41
|
-
try {
|
|
42
|
-
t1Sym = await client.readContract({ address: t1Addr, abi: ERC20_ABI, functionName: 'symbol' });
|
|
43
|
-
}
|
|
44
|
-
catch { }
|
|
45
|
-
const fmtUnits = (bn, dec) => {
|
|
46
|
-
try {
|
|
47
|
-
let v = BigInt(bn);
|
|
48
|
-
const neg = v < 0n;
|
|
49
|
-
if (neg)
|
|
50
|
-
v = -v;
|
|
51
|
-
const s = v.toString().padStart(Number(dec) + 1, '0');
|
|
52
|
-
const i = s.slice(0, s.length - Number(dec));
|
|
53
|
-
const f = s.slice(s.length - Number(dec)).replace(/0+$/, '').slice(0, 8);
|
|
54
|
-
return (neg ? '-' : '') + i + (f ? '.' + f : '');
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
return String(bn);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
const fmt = (bn, dec, sym) => `${fmtUnits(bn, dec)} ${sym}`;
|
|
61
|
-
let shares = withdrawShares != null ? BigInt(withdrawShares) : undefined;
|
|
62
|
-
if (shares == null && withdrawPct != null) {
|
|
63
|
-
const bal = await client.readContract({ address: vaultAddr, abi: ERC20_ABI, functionName: 'balanceOf', args: [ownerAddr] });
|
|
64
|
-
const pct = BigInt(Math.max(0, Math.min(100, Math.floor(Number(withdrawPct)))));
|
|
65
|
-
shares = (BigInt(bal) * pct) / 100n;
|
|
66
|
-
}
|
|
67
|
-
const ownerShares = await client.readContract({ address: vaultAddr, abi: ERC20_ABI, functionName: 'balanceOf', args: [ownerAddr] });
|
|
68
|
-
const totalShares = await client.readContract({ address: vaultAddr, abi: ERC20_ABI, functionName: 'totalSupply' });
|
|
69
|
-
if (debug) {
|
|
70
|
-
const poolWarn = cliPool && cliPool.toLowerCase() !== poolAddr.toLowerCase() ? ` (CLI pool=${cliPool})` : '';
|
|
71
|
-
console.error(`[withdraw] vault=${vaultAddr} pool=${poolAddr}${poolWarn}`);
|
|
72
|
-
console.error(`[withdraw] owner=${ownerAddr} ownerShares=${fmt(ownerShares, vDec, 'shares')} totalSupply=${fmt(totalShares, vDec, 'shares')}`);
|
|
73
|
-
console.error(`[withdraw] planned shares=${fmt(shares ?? 0n, vDec, 'shares')} (${Number(withdrawPct ?? 0)}%)`);
|
|
74
|
-
}
|
|
75
|
-
if (!shares || shares <= 0n) {
|
|
76
|
-
if (debug)
|
|
77
|
-
console.error('[withdraw] no shares to withdraw; skipping');
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
await client.setBalance({ address: ownerAddr, value: parseEther('0.05') });
|
|
82
|
-
}
|
|
83
|
-
catch { }
|
|
84
|
-
try {
|
|
85
|
-
await client.impersonateAccount({ address: ownerAddr });
|
|
86
|
-
}
|
|
87
|
-
catch { }
|
|
88
|
-
const [o0b, o1b, v0b, v1b] = await Promise.all([
|
|
89
|
-
client.readContract({ address: t0Addr, abi: ERC20_ABI, functionName: 'balanceOf', args: [ownerAddr] }),
|
|
90
|
-
client.readContract({ address: t1Addr, abi: ERC20_ABI, functionName: 'balanceOf', args: [ownerAddr] }),
|
|
91
|
-
client.readContract({ address: t0Addr, abi: ERC20_ABI, functionName: 'balanceOf', args: [vaultAddr] }),
|
|
92
|
-
client.readContract({ address: t1Addr, abi: ERC20_ABI, functionName: 'balanceOf', args: [vaultAddr] }),
|
|
93
|
-
]);
|
|
94
|
-
if (debug) {
|
|
95
|
-
console.error(`[withdraw] owner balances before: ${fmt(o0b, t0Dec, t0Sym)}, ${fmt(o1b, t1Dec, t1Sym)}`);
|
|
96
|
-
console.error(`[withdraw] vault balances before: ${fmt(v0b, t0Dec, t0Sym)}, ${fmt(v1b, t1Dec, t1Sym)}`);
|
|
97
|
-
}
|
|
98
|
-
const data = encodeFunctionData({ abi: VAULT_ABI, functionName: 'withdraw', args: [shares, 0n, 0n, ownerAddr] });
|
|
99
|
-
try {
|
|
100
|
-
const chain = viemChain || await resolveViemChainFromClient(client);
|
|
101
|
-
const hash = await viemSendTransaction(client, { account: ownerAddr, to: vaultAddr, data, chain });
|
|
102
|
-
if (debug)
|
|
103
|
-
console.error(`[withdraw] tx sent: ${hash}`);
|
|
104
|
-
}
|
|
105
|
-
catch (e) {
|
|
106
|
-
if (debug)
|
|
107
|
-
console.error(`[withdraw] sendTransaction error: ${e?.message || e}`);
|
|
108
|
-
throw e;
|
|
109
|
-
}
|
|
110
|
-
await client.mine?.({ blocks: 1 });
|
|
111
|
-
const [o0a, o1a, v0a, v1a] = await Promise.all([
|
|
112
|
-
client.readContract({ address: t0Addr, abi: ERC20_ABI, functionName: 'balanceOf', args: [ownerAddr] }),
|
|
113
|
-
client.readContract({ address: t1Addr, abi: ERC20_ABI, functionName: 'balanceOf', args: [ownerAddr] }),
|
|
114
|
-
client.readContract({ address: t0Addr, abi: ERC20_ABI, functionName: 'balanceOf', args: [vaultAddr] }),
|
|
115
|
-
client.readContract({ address: t1Addr, abi: ERC20_ABI, functionName: 'balanceOf', args: [vaultAddr] }),
|
|
116
|
-
]);
|
|
117
|
-
const d0 = BigInt(o0a) - BigInt(o0b);
|
|
118
|
-
const d1 = BigInt(o1a) - BigInt(o1b);
|
|
119
|
-
if (debug) {
|
|
120
|
-
console.error(`[withdraw] owner balances after : ${fmt(o0a, t0Dec, t0Sym)}, ${fmt(o1a, t1Dec, t1Sym)}`);
|
|
121
|
-
console.error(`[withdraw] vault balances after : ${fmt(v0a, t0Dec, t0Sym)}, ${fmt(v1a, t1Dec, t1Sym)}`);
|
|
122
|
-
console.error(`[withdraw] owner deltas: ${fmt(d0, t0Dec, t0Sym)}, ${fmt(d1, t1Dec, t1Sym)}`);
|
|
123
|
-
}
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
export async function resolveViemChainFromClient(client) {
|
|
127
|
-
try {
|
|
128
|
-
const id = await client.getChainId();
|
|
129
|
-
for (const v of Object.values(viemChains)) {
|
|
130
|
-
if (v && typeof v === 'object' && 'id' in v && v.id === id)
|
|
131
|
-
return v;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
catch { }
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
export async function resolveTevmCommon(rpcUrl) {
|
|
138
|
-
try {
|
|
139
|
-
if (!rpcUrl)
|
|
140
|
-
return undefined;
|
|
141
|
-
const pc = createPublicClient({ transport: viemHttp(rpcUrl) });
|
|
142
|
-
const id = await pc.getChainId();
|
|
143
|
-
for (const v of Object.values(tevmCommon)) {
|
|
144
|
-
if (v && typeof v === 'object' && 'id' in v && v.id === id)
|
|
145
|
-
return v;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
catch { }
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
package/dist/api.d.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { computeMarketDepthBands } from './core/depth.js';
|
|
2
|
-
export type AnalyzeSource = 'tevm' | 'subgraph' | 'auto';
|
|
3
|
-
export type AnalyzeOptions = {
|
|
4
|
-
poolAddress: string;
|
|
5
|
-
source?: AnalyzeSource;
|
|
6
|
-
rpcUrl?: string;
|
|
7
|
-
subgraphUrl?: string;
|
|
8
|
-
percentBuckets?: number[];
|
|
9
|
-
usdSizes?: number[];
|
|
10
|
-
token0USD?: number;
|
|
11
|
-
token1USD?: number;
|
|
12
|
-
assumeStable?: 0 | 1;
|
|
13
|
-
prices?: 'reserve' | 'dexscreener' | 'llama' | 'coingecko' | 'auto';
|
|
14
|
-
reserveLimit?: number;
|
|
15
|
-
debug?: boolean;
|
|
16
|
-
withdraw?: {
|
|
17
|
-
vault: string;
|
|
18
|
-
owner: string;
|
|
19
|
-
withdrawPct?: number;
|
|
20
|
-
withdrawShares?: string | number | bigint;
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
export type AnalyzeResult = {
|
|
24
|
-
before: SingleAnalysis;
|
|
25
|
-
after?: SingleAnalysis;
|
|
26
|
-
};
|
|
27
|
-
export type SingleAnalysis = {
|
|
28
|
-
pool: string;
|
|
29
|
-
tokens: {
|
|
30
|
-
token0: {
|
|
31
|
-
id?: string;
|
|
32
|
-
symbol?: string;
|
|
33
|
-
};
|
|
34
|
-
token1: {
|
|
35
|
-
id?: string;
|
|
36
|
-
symbol?: string;
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
ethPriceUSD?: number;
|
|
40
|
-
tick: number;
|
|
41
|
-
sqrtPriceX96: string;
|
|
42
|
-
liquidity: string;
|
|
43
|
-
feePips: number;
|
|
44
|
-
range: {
|
|
45
|
-
start: number;
|
|
46
|
-
end: number;
|
|
47
|
-
};
|
|
48
|
-
percentBuckets: number[];
|
|
49
|
-
prices: {
|
|
50
|
-
token0?: {
|
|
51
|
-
address?: string;
|
|
52
|
-
symbol?: string;
|
|
53
|
-
usd?: number;
|
|
54
|
-
source?: string;
|
|
55
|
-
};
|
|
56
|
-
token1?: {
|
|
57
|
-
address?: string;
|
|
58
|
-
symbol?: string;
|
|
59
|
-
usd?: number;
|
|
60
|
-
source?: string;
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
buyCumToken1: string[];
|
|
64
|
-
sellCumToken0: string[];
|
|
65
|
-
metrics: ReturnType<typeof computeMarketDepthBands>;
|
|
66
|
-
priceImpacts?: {
|
|
67
|
-
usdSizes: number[];
|
|
68
|
-
buyPct: number[];
|
|
69
|
-
sellPct: number[];
|
|
70
|
-
};
|
|
71
|
-
};
|
|
72
|
-
export declare function analyzePool(opts: AnalyzeOptions): Promise<AnalyzeResult>;
|