@provablehq/shield-swap-cli 0.7.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/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/balances-6SU4DCKM.js +66 -0
- package/dist/balances-6SU4DCKM.js.map +1 -0
- package/dist/chunk-2OT6LZPW.js +178 -0
- package/dist/chunk-2OT6LZPW.js.map +1 -0
- package/dist/chunk-IBVZHLUT.js +152 -0
- package/dist/chunk-IBVZHLUT.js.map +1 -0
- package/dist/chunk-IHYFMX5A.js +54 -0
- package/dist/chunk-IHYFMX5A.js.map +1 -0
- package/dist/collect-G3GNFL57.js +240 -0
- package/dist/collect-G3GNFL57.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/liquidity-RB5MKGPA.js +352 -0
- package/dist/liquidity-RB5MKGPA.js.map +1 -0
- package/dist/liquidity-e2e-WCTSSZYS.js +309 -0
- package/dist/liquidity-e2e-WCTSSZYS.js.map +1 -0
- package/dist/mint-6OKWODQG.js +253 -0
- package/dist/mint-6OKWODQG.js.map +1 -0
- package/dist/pools-NWQNFJ7W.js +184 -0
- package/dist/pools-NWQNFJ7W.js.map +1 -0
- package/dist/positions-MILT3BRU.js +134 -0
- package/dist/positions-MILT3BRU.js.map +1 -0
- package/dist/session.d.ts +161 -0
- package/dist/session.js +31 -0
- package/dist/session.js.map +1 -0
- package/dist/setup-CZI3SHUT.js +229 -0
- package/dist/setup-CZI3SHUT.js.map +1 -0
- package/dist/swap-W72XGG7Y.js +143 -0
- package/dist/swap-W72XGG7Y.js.map +1 -0
- package/dist/swap-concurrent-IHGWJMST.js +169 -0
- package/dist/swap-concurrent-IHGWJMST.js.map +1 -0
- package/dist/swap-history-FBXGMVRJ.js +371 -0
- package/dist/swap-history-FBXGMVRJ.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/mint.ts"],"sourcesContent":["/**\n * Mint — open a liquidity position and become the market for a pair.\n *\n * A position deposits both tokens over a price range. While the pool trades\n * inside that range the position earns a cut of every trade; outside it, it earns\n * nothing and sits in one token. The range is chosen here as a percentage either\n * side of the pool's current price, then aligned to the pool's tick spacing —\n * the contract only accepts bounds on that grid.\n *\n * `previewMint` does the arithmetic before anything is signed: the aligned\n * bounds, the liquidity the deposit backs, and — the number that matters — how\n * much of each token is actually consumed. A pair of amounts that balances at\n * one price falls short at another, so the mint takes only what the range needs\n * and the rest stays in the account.\n *\n * Tick insert hints are deliberately not passed. `mint` derives both, and for\n * the upper bound it applies a correction a caller cannot: finalize inserts\n * tick_lower before validating the upper hint, so when no initialized tick sits\n * between the bounds the upper predecessor is the just-inserted lower tick\n * rather than the one visible on chain. Passing an explicit `tickUpperHint`\n * disables that correction and reverts on exactly that case.\n *\n * SPENDS REAL FUNDS with --execute. Without it, prints the plan and stops.\n *\n * Usage:\n * shield-swap mint --pair USDCx:ETH --percent 1\n * shield-swap mint --pair USDCx:ETH --percent 1 --execute\n * shield-swap mint --pair USDCx:ETH --amount0 0.5 --amount1 0.0002 --execute\n * shield-swap mint --pool <poolKey> --percent 1 --range-pct 10 --execute\n * shield-swap mint --pair USDCx:ETH --percent 1 --json\n */\nimport { loadSession, formatAmount, namedAmounts, pollUntil } from '../session.js'\nimport { flags, step, done, warn, output, confirmed, run, fail } from '../shared.js'\n\nconst USAGE = `shield-swap mint — open a liquidity position\n\n --pair <symbol:symbol> pool to enter, e.g. USDCx:ETH (or --pool)\n --pool <poolKey> exact pool, skipping pair lookup\n --amount <symbol>:<decimal> how much of one named token, e.g. USDCx:0.5.\n Repeatable, once per side. Prefer this — it does\n not depend on knowing the pool's token order\n --amount0 <decimal> token0 to commit, in human units\n --amount1 <decimal> token1 to commit, in human units\n --percent <n> commit n% of the private balance of both sides\n --range-pct <n> range half-width around the price, default 5\n --network <testnet|mainnet> default testnet\n --execute actually submit\n --json machine-readable output\n\nEither --percent or at least one amount. A side left unnamed commits its whole\nprivate balance as a ceiling — the plan shows what the range actually consumes,\nwhich is never more than that.\n\n--amount0/--amount1 follow the POOL's token order, which is fixed on chain and\nneed NOT match the order in --pair: naming --pair USDCx:ETH does not make USDCx\nside 0. --amount names the token instead and cannot be transposed. The plan names\nboth symbols either way.`\n\n/**\n * Runs the `mint` subcommand.\n *\n * @param argv Arguments after the subcommand name, as the dispatcher supplies them.\n */\nexport async function main(argv: string[]): Promise<void> {\n const args = flags(\n {\n pair: { type: 'string' },\n pool: { type: 'string' },\n amount: { type: 'string', multiple: true },\n amount0: { type: 'string' },\n amount1: { type: 'string' },\n percent: { type: 'string' },\n 'range-pct': { type: 'string' },\n },\n USAGE,\n argv,\n )\n\n if (!args.pair && !args.pool) fail(`--pair or --pool is required.\\n\\n${USAGE}`)\n const bySymbol = (args.amount as string[] | undefined) ?? []\n const anyAmount = bySymbol.length > 0 || !!args.amount0 || !!args.amount1\n if (!args.percent && !anyAmount) {\n fail(`--percent, --amount, --amount0, or --amount1 is required.\\n\\n${USAGE}`)\n }\n if (args.percent && anyAmount) {\n fail(`--percent and the amount flags are alternatives, not both.\\n\\n${USAGE}`)\n }\n\n const percent = args.percent ? Number(args.percent) : undefined\n if (percent !== undefined && (!(percent > 0) || percent > 100)) {\n fail(`--percent must be greater than 0 and at most 100, got ${args.percent as string}`)\n }\n\n /** Basis points of a whole, so `--percent 12.5` is exact rather than rounded to 12. */\n const share = (total: bigint, pct: number) => (total * BigInt(Math.round(pct * 100))) / 10_000n\n\n await run(async () => {\n const { client, account, network } = await loadSession({ network: args.network as string | undefined })\n done(`session on ${network}`)\n\n // Resolve the pool first: every amount below is denominated in the pool's own\n // token0/token1 order, which is fixed on chain and need not match --pair.\n let poolKey = args.pool as string | undefined\n if (!poolKey) {\n const [left, right] = (args.pair as string).split(':')\n if (!left || !right) throw new Error(`\"${args.pair as string}\" is not symbol:symbol, e.g. USDCx:ETH`)\n const [a, b] = await Promise.all([client.tokenData(left), client.tokenData(right)])\n step(`looking for a ${a.symbol}/${b.symbol} pool`)\n const listed = (await client.api.getPools({ limit: 100 })).data as Array<{\n key: string\n token0: string\n token1: string\n }>\n const matches = listed.filter(\n (pool) =>\n (pool.token0 === a.id && pool.token1 === b.id) || (pool.token0 === b.id && pool.token1 === a.id),\n )\n if (!matches.length) {\n throw new Error(\n `no pool pairs ${a.symbol} with ${b.symbol} on ${network}. Run \\`shield-swap pools\\` to see what exists.`,\n )\n }\n // A pair can have several pools, one per fee tier. The deepest is the one a\n // trader would route through, so it is the one worth providing to.\n const withDepth = await Promise.all(\n matches.map(async (pool) => ({ pool, liquidity: (await client.getSlot({ poolKey: pool.key }))?.liquidity ?? 0n })),\n )\n withDepth.sort((x, y) => (y.liquidity > x.liquidity ? 1 : y.liquidity < x.liquidity ? -1 : 0))\n poolKey = withDepth[0]!.pool.key\n if (matches.length > 1) done(`${matches.length} fee tiers pair them — taking the deepest`)\n }\n\n const pool = await client.getPool({ poolKey })\n if (!pool) throw new Error(`no pool ${poolKey} on ${network} — check the key with \\`shield-swap pools\\`.`)\n\n // A mint the control gates would reject reverts on finalize and still costs a\n // fee, so the gates are read before the plan rather than discovered after it.\n const controls = await client.getTradeControls({ poolKey })\n if (!controls.tradeable) {\n throw new Error(\n `pool ${poolKey} is gated on chain right now (global pause ${controls.globalPaused}, ` +\n `pool enabled ${controls.poolEnabled}, pair paused ${controls.pairPaused}) — a mint would revert.`,\n )\n }\n\n const tokens = await client.listTokens()\n const infoOf = (id: string) => tokens.find((token) => token.id === id)\n const token0 = infoOf(pool.token0)\n const token1 = infoOf(pool.token1)\n if (!token0 || !token1) throw new Error(`the registry does not describe both tokens of pool ${poolKey}.`)\n\n // Private records fund a deposit; the public balance cannot be minted from.\n step('reading private balances for both sides')\n const balances = await client.getBalances({ tokens: [token0.id, token1.id] })\n const held0 = balances[token0.id]?.private ?? 0n\n const held1 = balances[token1.id]?.private ?? 0n\n if (held0 === 0n || held1 === 0n) {\n throw new Error(\n `an in-range position needs both sides, and this account holds ` +\n `${formatAmount(held0, token0.decimals, token0.symbol)} and ` +\n `${formatAmount(held1, token1.decimals, token1.symbol)} privately. Fund the empty side first.`,\n )\n }\n\n // The budget is a ceiling, not the deposit: the preview below reports what the\n // range consumes out of it.\n const named = namedAmounts({\n entries: bySymbol,\n indexed: [args.amount0 as string | undefined, args.amount1 as string | undefined],\n tokens: [token0, token1],\n })\n const budget0 = percent ? share(held0, percent) : (named.amount0 ?? held0)\n const budget1 = percent ? share(held1, percent) : (named.amount1 ?? held1)\n if (budget0 > held0 || budget1 > held1) {\n throw new Error(\n `asked to commit ${formatAmount(budget0, token0.decimals, token0.symbol)} / ` +\n `${formatAmount(budget1, token1.decimals, token1.symbol)} but the account holds ` +\n `${formatAmount(held0, token0.decimals, token0.symbol)} / ` +\n `${formatAmount(held1, token1.decimals, token1.symbol)} privately.`,\n )\n }\n\n const rangePercent = args['range-pct'] ? Number(args['range-pct']) : 5\n step(`pricing a ±${rangePercent}% range against the pool's live price`)\n const preview = await client.previewMint({\n poolKey,\n amount0Desired: budget0,\n amount1Desired: budget1,\n rangePercent,\n })\n if (preview.liquidity === 0n) {\n throw new Error(\n `that budget backs no liquidity over ticks ${preview.tickLower}…${preview.tickUpper} — ` +\n 'commit more, or narrow the range with --range-pct. A mint would cost a fee and open nothing.',\n )\n }\n if (!preview.inRange) {\n warn(\n `the pool trades at tick ${preview.tickCurrent}, outside ${preview.tickLower}…${preview.tickUpper}: ` +\n 'this position earns nothing until the price moves into its range, and is funded from one side only',\n )\n }\n if (preview.feeTierSpacing !== null && preview.feeTierSpacing !== preview.tickSpacing) {\n warn(\n `the pool's tick spacing (${preview.tickSpacing}) differs from what fee tier ${preview.fee} binds ` +\n `(${preview.feeTierSpacing}) — the bounds follow the pool, which is what the contract aligns to`,\n )\n }\n\n // Each side is funded from ONE record, not the sum of several, so a balance\n // large enough in total can still be too fragmented to mint from.\n const perSide = [\n { info: token0, needed: preview.amount0, held: held0 },\n { info: token1, needed: preview.amount1, held: held1 },\n ]\n for (const side of perSide) {\n if (side.needed > side.held) {\n throw new Error(\n `the range needs ${formatAmount(side.needed, side.info.decimals, side.info.symbol)} but only ` +\n `${formatAmount(side.held, side.info.decimals, side.info.symbol)} is held privately.`,\n )\n }\n }\n\n const planLines: Array<readonly [string, string]> = [\n ['pool', `${token0.symbol}/${token1.symbol} fee ${preview.fee} spacing ${preview.tickSpacing}`],\n [\n 'range',\n `ticks ${preview.tickLower}…${preview.tickUpper} (±${rangePercent}%, price at tick ${preview.tickCurrent})`,\n ],\n ['status', preview.inRange ? 'in range — earns fees immediately' : 'OUT OF RANGE — earns nothing yet'],\n ['deposit', formatAmount(preview.amount0, token0.decimals, token0.symbol)],\n // Empty label: the second side of the same deposit, not a separate step.\n ['', formatAmount(preview.amount1, token1.decimals, token1.symbol)],\n [\n 'unused',\n `${formatAmount(budget0 - preview.amount0, token0.decimals, token0.symbol)} / ` +\n `${formatAmount(budget1 - preview.amount1, token1.decimals, token1.symbol)} of the budget stays in the account`,\n ],\n ['owner', `${account.address} (also the withdrawal address collect pays)`],\n ]\n if (!confirmed({ execute: args.execute as boolean | undefined, network, plan: planLines })) {\n output({ network, submitted: false, poolKey, preview }, () => {})\n return\n }\n\n // Both token programs' sources: the prover cannot discover the dynamically\n // dispatched IARC20 callees on its own.\n const imports = await client.resolveDexImports({\n tokenPrograms: [token0.ammTokenProgram, token1.ammTokenProgram].filter((program): program is string => !!program),\n })\n\n step('proving and submitting the mint — this takes a minute or two')\n // No amount0Min/amount1Min: the contract takes at most the desired amounts, so\n // a price that moves between the preview and the finalize deposits slightly\n // less rather than more. A min would turn that into a revert.\n const minted = await client.mint({\n poolKey,\n tickLower: preview.tickLower,\n tickUpper: preview.tickUpper,\n amount0Desired: preview.amount0,\n amount1Desired: preview.amount1,\n recipient: account.address,\n withdrawal: account.address,\n imports,\n })\n done(`mint landed: tx ${minted.transactionId}`)\n if (!minted.positionTokenId) {\n // Only reachable on a wallet signer without the WASM peer; a local key\n // always gets the id back as a public output.\n warn('the position id was not returned — find it with `shield-swap positions`')\n output({ network, submitted: true, poolKey, transactionId: minted.transactionId, preview }, () => {})\n return\n }\n step(`position ${minted.positionTokenId} — waiting for the positions mapping to catch up`)\n\n // Mapping writes propagate to reads asynchronously, so the entry is expected\n // to be absent for a few seconds after the transaction confirms.\n let onchain: NonNullable<Awaited<ReturnType<typeof client.getPosition>>> | undefined\n const appeared = await pollUntil(\n async () => {\n const position = await client.getPosition({ positionTokenId: minted.positionTokenId! })\n if (position) onchain = position\n return position !== null\n },\n 20,\n 3_000,\n )\n if (appeared) done(`chain carries the position with liquidity ${onchain!.liquidity}`)\n else warn('the position has not appeared in the positions mapping yet — check `shield-swap positions` shortly')\n\n output(\n {\n network,\n submitted: true,\n poolKey,\n positionTokenId: minted.positionTokenId,\n transactionId: minted.transactionId,\n tickLower: preview.tickLower,\n tickUpper: preview.tickUpper,\n deposited0: preview.amount0,\n deposited1: preview.amount1,\n predictedLiquidity: preview.liquidity,\n liquidity: onchain?.liquidity ?? null,\n },\n (data) => {\n console.log(`\\nPosition ${data.positionTokenId} open on ${token0.symbol}/${token1.symbol}.`)\n console.log(\n `Deposited ${formatAmount(data.deposited0, token0.decimals, token0.symbol)} and ` +\n `${formatAmount(data.deposited1, token1.decimals, token1.symbol)} over ticks ` +\n `${data.tickLower}…${data.tickUpper}.`,\n )\n console.log('Track it with `shield-swap positions`; collect earnings with `shield-swap collect`.')\n },\n )\n })\n}\n"],"mappings":"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","names":["pool"]}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
done,
|
|
3
|
+
flags,
|
|
4
|
+
output,
|
|
5
|
+
run,
|
|
6
|
+
step,
|
|
7
|
+
table,
|
|
8
|
+
warn
|
|
9
|
+
} from "./chunk-IBVZHLUT.js";
|
|
10
|
+
import {
|
|
11
|
+
green,
|
|
12
|
+
yellow
|
|
13
|
+
} from "./chunk-IHYFMX5A.js";
|
|
14
|
+
import {
|
|
15
|
+
loadSession
|
|
16
|
+
} from "./chunk-2OT6LZPW.js";
|
|
17
|
+
|
|
18
|
+
// src/commands/pools.ts
|
|
19
|
+
function formatFee(pips) {
|
|
20
|
+
return `${(pips / 1e4).toFixed(4).replace(/\.?0+$/, "")}%`;
|
|
21
|
+
}
|
|
22
|
+
var USAGE = `shield-swap pools \u2014 discover tradeable pools
|
|
23
|
+
|
|
24
|
+
--network <testnet|mainnet> default testnet
|
|
25
|
+
--token <symbol|id> only pools holding this token
|
|
26
|
+
--positions add this account's stake in each pool
|
|
27
|
+
--sort <liquidity|fee> default liquidity
|
|
28
|
+
--limit <n> pools to inspect, default 50
|
|
29
|
+
--json machine-readable output`;
|
|
30
|
+
async function stakePerPool(client, pools) {
|
|
31
|
+
const owned = await client.getOwnedPositions();
|
|
32
|
+
const byKey = new Map(pools.map((pool) => [pool.poolKey, pool]));
|
|
33
|
+
const stakes = /* @__PURE__ */ new Map();
|
|
34
|
+
let elsewhere = 0;
|
|
35
|
+
for (const position of owned) {
|
|
36
|
+
const pool = byKey.get(position.poolKey);
|
|
37
|
+
if (!pool) {
|
|
38
|
+
elsewhere += 1;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const stake = stakes.get(position.poolKey) ?? { positions: 0, outOfRange: 0, noChainEntry: 0, liquidity: 0n, shareBps: null };
|
|
42
|
+
stake.positions += 1;
|
|
43
|
+
if (position.state === null) {
|
|
44
|
+
stake.noChainEntry += 1;
|
|
45
|
+
} else if (position.tickLower <= pool.tick && pool.tick < position.tickUpper) {
|
|
46
|
+
stake.liquidity += position.state.liquidity;
|
|
47
|
+
} else stake.outOfRange += 1;
|
|
48
|
+
stakes.set(position.poolKey, stake);
|
|
49
|
+
}
|
|
50
|
+
for (const [poolKey, stake] of stakes) {
|
|
51
|
+
const active = byKey.get(poolKey)?.liquidity ?? 0n;
|
|
52
|
+
stake.shareBps = active > 0n ? Number(stake.liquidity * 10000n / active) : null;
|
|
53
|
+
}
|
|
54
|
+
return { stakes, elsewhere };
|
|
55
|
+
}
|
|
56
|
+
async function discoverPools(client, options = {}) {
|
|
57
|
+
const filter = options.token ? await client.tokenData(options.token) : void 0;
|
|
58
|
+
if (filter) step(`filtering to ${filter.symbol} (${filter.id})`);
|
|
59
|
+
step("reading the pool index");
|
|
60
|
+
const listed = (await client.api.getPools({ limit: options.limit ?? 50 })).data;
|
|
61
|
+
const tokens = await client.listTokens();
|
|
62
|
+
const infoOf = (id) => tokens.find((token) => token.id === id);
|
|
63
|
+
const relevant = filter ? listed.filter((pool) => pool.token0 === filter.id || pool.token1 === filter.id) : listed;
|
|
64
|
+
step(`inspecting ${relevant.length} of ${listed.length} pools on chain`);
|
|
65
|
+
const summaries = [];
|
|
66
|
+
for (const pool of relevant) {
|
|
67
|
+
const [onchain, slot, controls] = await Promise.all([
|
|
68
|
+
client.getPool({ poolKey: pool.key }),
|
|
69
|
+
client.getSlot({ poolKey: pool.key }),
|
|
70
|
+
client.getTradeControls({ poolKey: pool.key })
|
|
71
|
+
]);
|
|
72
|
+
if (!onchain || !slot) continue;
|
|
73
|
+
const t0 = infoOf(pool.token0);
|
|
74
|
+
const t1 = infoOf(pool.token1);
|
|
75
|
+
summaries.push({
|
|
76
|
+
poolKey: pool.key,
|
|
77
|
+
token0: { id: pool.token0, symbol: t0?.symbol ?? "?", decimals: t0?.decimals ?? 0 },
|
|
78
|
+
token1: { id: pool.token1, symbol: t1?.symbol ?? "?", decimals: t1?.decimals ?? 0 },
|
|
79
|
+
fee: onchain.fee,
|
|
80
|
+
tickSpacing: slot.tick_spacing,
|
|
81
|
+
tick: slot.tick,
|
|
82
|
+
liquidity: slot.liquidity,
|
|
83
|
+
tradeable: controls.tradeable && slot.liquidity > 0n,
|
|
84
|
+
...controls.tradeable ? slot.liquidity > 0n ? {} : { reason: "no liquidity" } : { reason: "paused or gated on chain" }
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return summaries;
|
|
88
|
+
}
|
|
89
|
+
async function main(argv) {
|
|
90
|
+
const args = flags(
|
|
91
|
+
{
|
|
92
|
+
token: { type: "string" },
|
|
93
|
+
positions: { type: "boolean" },
|
|
94
|
+
sort: { type: "string" },
|
|
95
|
+
limit: { type: "string" }
|
|
96
|
+
},
|
|
97
|
+
USAGE,
|
|
98
|
+
argv
|
|
99
|
+
);
|
|
100
|
+
await run(async () => {
|
|
101
|
+
const { client, network } = await loadSession({ network: args.network });
|
|
102
|
+
done(`session on ${network}`);
|
|
103
|
+
const pools = await discoverPools(client, {
|
|
104
|
+
...args.token ? { token: args.token } : {},
|
|
105
|
+
...args.limit ? { limit: Number(args.limit) } : {}
|
|
106
|
+
});
|
|
107
|
+
const sorted = args.sort === "fee" ? [...pools].sort((a, b) => a.fee - b.fee) : [...pools].sort((a, b) => b.liquidity > a.liquidity ? 1 : b.liquidity < a.liquidity ? -1 : 0);
|
|
108
|
+
let stakes;
|
|
109
|
+
if (args.positions) {
|
|
110
|
+
step("scanning position records to attribute a stake to each pool");
|
|
111
|
+
const mine = await stakePerPool(client, sorted);
|
|
112
|
+
stakes = mine.stakes;
|
|
113
|
+
if (mine.elsewhere) {
|
|
114
|
+
warn(
|
|
115
|
+
`${mine.elsewhere} position(s) are in pools outside this listing \u2014 widen --limit or drop --token to see them`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
output(
|
|
120
|
+
{
|
|
121
|
+
network,
|
|
122
|
+
pools: sorted.map((pool) => ({
|
|
123
|
+
...pool,
|
|
124
|
+
...stakes ? { stake: stakes.get(pool.poolKey) ?? null } : {}
|
|
125
|
+
}))
|
|
126
|
+
},
|
|
127
|
+
(data) => {
|
|
128
|
+
if (!data.pools.length) {
|
|
129
|
+
console.log(
|
|
130
|
+
args.token ? `
|
|
131
|
+
No pools hold ${args.token} on ${data.network}. Drop --token to see every pool.` : `
|
|
132
|
+
No pools on ${data.network}.`
|
|
133
|
+
);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const headers = ["PAIR", "FEE", "SPACING", "TICK", "LIQUIDITY", "STATUS", "POOL KEY"];
|
|
137
|
+
const align = ["left", "right", "right", "right", "right", "left", "left"];
|
|
138
|
+
if (stakes) {
|
|
139
|
+
headers.splice(5, 0, "POSITIONS", "MY LIQUIDITY", "SHARE");
|
|
140
|
+
align.splice(5, 0, "right", "right", "right");
|
|
141
|
+
}
|
|
142
|
+
table(
|
|
143
|
+
headers,
|
|
144
|
+
data.pools.map((pool) => {
|
|
145
|
+
const row = [
|
|
146
|
+
`${pool.token0.symbol}/${pool.token1.symbol}`,
|
|
147
|
+
formatFee(pool.fee),
|
|
148
|
+
String(pool.tickSpacing),
|
|
149
|
+
String(pool.tick),
|
|
150
|
+
// Liquidity stays raw on purpose: it is not denominated in either
|
|
151
|
+
// token, so rendering it with a token's decimals would misstate it.
|
|
152
|
+
pool.liquidity.toString(),
|
|
153
|
+
// Green reads as "you can trade this now"; a reason is always a
|
|
154
|
+
// reason it is unavailable, so it takes the warning colour.
|
|
155
|
+
pool.reason ? yellow(pool.reason) : green("tradeable"),
|
|
156
|
+
pool.poolKey
|
|
157
|
+
];
|
|
158
|
+
if (stakes) {
|
|
159
|
+
const stake = stakes.get(pool.poolKey);
|
|
160
|
+
const notes = stake ? [
|
|
161
|
+
...stake.outOfRange ? [`${stake.outOfRange} out`] : [],
|
|
162
|
+
...stake.noChainEntry ? [`${stake.noChainEntry} no entry`] : []
|
|
163
|
+
] : [];
|
|
164
|
+
row.splice(
|
|
165
|
+
5,
|
|
166
|
+
0,
|
|
167
|
+
stake ? `${stake.positions}${notes.length ? ` (${notes.join(", ")})` : ""}` : "\u2014",
|
|
168
|
+
stake && stake.liquidity > 0n ? stake.liquidity.toString() : "\u2014",
|
|
169
|
+
stake?.shareBps == null ? "\u2014" : `${(stake.shareBps / 100).toFixed(2)}%`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return row;
|
|
173
|
+
}),
|
|
174
|
+
align
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
export {
|
|
181
|
+
discoverPools,
|
|
182
|
+
main
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=pools-NWQNFJ7W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/pools.ts"],"sourcesContent":["/**\n * Pool discovery — what can be traded, and how deep it is.\n *\n * Lists the pools the API knows, then checks each against chain: the API can\n * list a pool the contract refuses to trade, so the tradeable flag and the live\n * liquidity come from the mappings rather than the index.\n *\n * Reads only. Spends nothing, needs no funded account, and works before setup\n * has run if a state file already carries a key. `--positions` is the exception:\n * it scans the account's own records, which needs a working record scanner.\n *\n * Usage:\n * shield-swap pools # testnet, human table\n * shield-swap pools --network mainnet # mainnet\n * shield-swap pools --token USDCx # only pools holding this token\n * shield-swap pools --positions # add this account's stake per pool\n * shield-swap pools --sort liquidity # deepest first (default)\n * shield-swap pools --limit 10 --json # machine-readable\n */\nimport { loadSession } from '../session.js'\nimport { flags, step, done, warn, output, run, table } from '../shared.js'\nimport { green, yellow } from '../color.js'\n\n/**\n * Renders a pool's fee tier as a percentage.\n *\n * The contract counts fees in pips out of 1,000,000 (`fee_pips`), so the raw\n * `800` a pool reports is 0.08% rather than 800 of anything a trader recognises.\n * Trailing zeros are dropped so the thinnest tiers stay legible. Pure and local.\n *\n * @param pips The pool's fee, as stored on chain.\n * @returns The tier as a percentage string, e.g. `'0.08%'`.\n */\nfunction formatFee(pips: number): string {\n return `${(pips / 10_000).toFixed(4).replace(/\\.?0+$/, '')}%`\n}\n\nconst USAGE = `shield-swap pools — discover tradeable pools\n\n --network <testnet|mainnet> default testnet\n --token <symbol|id> only pools holding this token\n --positions add this account's stake in each pool\n --sort <liquidity|fee> default liquidity\n --limit <n> pools to inspect, default 50\n --json machine-readable output`\n\n/** A pool with the chain's opinion of it, not just the index's. */\nexport type PoolSummary = {\n poolKey: string\n token0: { id: string; symbol: string; decimals: number }\n token1: { id: string; symbol: string; decimals: number }\n fee: number\n tickSpacing: number\n tick: number\n liquidity: bigint\n tradeable: boolean\n reason?: string\n}\n\n/**\n * What the account holds in one pool, measured the way the pool measures it.\n *\n * @property positions Position records the account holds in this pool, including\n * any counted by `noChainEntry`.\n * @property outOfRange How many sit outside the current price. Those earn nothing\n * and contribute nothing to the pool's active liquidity.\n * @property noChainEntry How many have no entry in the positions mapping — a mint\n * still finalizing, or one already burned whose record the scanner still serves.\n * Neither holds liquidity, so they are classified as neither in nor out of range.\n * @property liquidity In-range liquidity only, so it is comparable to the pool's\n * own figure.\n * @property shareBps In-range liquidity as basis points of the pool's active\n * liquidity, or `null` when the pool has none to divide by.\n */\nexport type PoolStake = {\n positions: number\n outOfRange: number\n noChainEntry: number\n liquidity: bigint\n shareBps: number | null\n}\n\n/**\n * Sums the account's positions per pool, counting only what the pool counts.\n *\n * A position's liquidity is part of a pool's active liquidity only while the\n * price sits inside its range — outside it the position is idle. Summing every\n * position regardless would overstate the account's share of a pool it currently\n * earns nothing from, so out-of-range positions are counted separately rather\n * than added in.\n *\n * Scans the account's records, which needs a record scanner. One scan covers\n * every pool.\n *\n * @param client A composed shield-swap client.\n * @param pools The pools to attribute stakes to. Positions in any other pool are\n * ignored, and the count of those is returned so the caller can say so.\n * @returns Stakes keyed by pool key, and how many positions fell outside `pools`.\n */\nasync function stakePerPool(\n client: Awaited<ReturnType<typeof loadSession>>['client'],\n pools: PoolSummary[],\n): Promise<{ stakes: Map<string, PoolStake>; elsewhere: number }> {\n const owned = await client.getOwnedPositions()\n const byKey = new Map(pools.map((pool) => [pool.poolKey, pool]))\n const stakes = new Map<string, PoolStake>()\n let elsewhere = 0\n\n for (const position of owned) {\n const pool = byKey.get(position.poolKey)\n if (!pool) {\n elsewhere += 1\n continue\n }\n const stake =\n stakes.get(position.poolKey) ??\n { positions: 0, outOfRange: 0, noChainEntry: 0, liquidity: 0n, shareBps: null }\n stake.positions += 1\n if (position.state === null) {\n // No mapping entry means it holds no liquidity at all, so calling it\n // in-range would imply it is earning and calling it out-of-range would\n // imply it could be brought back in. It is neither.\n stake.noChainEntry += 1\n } else if (position.tickLower <= pool.tick && pool.tick < position.tickUpper) {\n // Upper bound exclusive, matching the contract: at exactly tick_upper the\n // position is already out of range.\n stake.liquidity += position.state.liquidity\n } else stake.outOfRange += 1\n stakes.set(position.poolKey, stake)\n }\n\n for (const [poolKey, stake] of stakes) {\n const active = byKey.get(poolKey)?.liquidity ?? 0n\n stake.shareBps = active > 0n ? Number((stake.liquidity * 10_000n) / active) : null\n }\n return { stakes, elsewhere }\n}\n\n/**\n * Discovers pools and joins each with its chain state.\n *\n * @param client A composed shield-swap client.\n * @param options.token Symbol or id to filter by.\n * @param options.limit Pools to inspect.\n * @returns One summary per pool, deepest first unless sorted otherwise.\n */\nexport async function discoverPools(\n client: Awaited<ReturnType<typeof loadSession>>['client'],\n options: { token?: string; limit?: number } = {},\n): Promise<PoolSummary[]> {\n const filter = options.token ? await client.tokenData(options.token) : undefined\n if (filter) step(`filtering to ${filter.symbol} (${filter.id})`)\n\n step('reading the pool index')\n const listed = (await client.api.getPools({ limit: options.limit ?? 50 })).data as Array<{\n key: string\n token0: string\n token1: string\n }>\n const tokens = await client.listTokens()\n const infoOf = (id: string) => tokens.find((token) => token.id === id)\n\n const relevant = filter\n ? listed.filter((pool) => pool.token0 === filter.id || pool.token1 === filter.id)\n : listed\n step(`inspecting ${relevant.length} of ${listed.length} pools on chain`)\n\n const summaries: PoolSummary[] = []\n for (const pool of relevant) {\n // Both gates matter and neither is in the index: a pool can be listed and\n // paused, or listed with no liquidity to trade against.\n const [onchain, slot, controls] = await Promise.all([\n client.getPool({ poolKey: pool.key }),\n client.getSlot({ poolKey: pool.key }),\n client.getTradeControls({ poolKey: pool.key }),\n ])\n if (!onchain || !slot) continue\n\n const t0 = infoOf(pool.token0)\n const t1 = infoOf(pool.token1)\n summaries.push({\n poolKey: pool.key,\n token0: { id: pool.token0, symbol: t0?.symbol ?? '?', decimals: t0?.decimals ?? 0 },\n token1: { id: pool.token1, symbol: t1?.symbol ?? '?', decimals: t1?.decimals ?? 0 },\n fee: onchain.fee,\n tickSpacing: slot.tick_spacing,\n tick: slot.tick,\n liquidity: slot.liquidity,\n tradeable: controls.tradeable && slot.liquidity > 0n,\n ...(controls.tradeable\n ? slot.liquidity > 0n\n ? {}\n : { reason: 'no liquidity' }\n : { reason: 'paused or gated on chain' }),\n })\n }\n return summaries\n}\n\n/**\n * Runs the `pools` subcommand.\n *\n * @param argv Arguments after the subcommand name, as the dispatcher supplies them.\n */\nexport async function main(argv: string[]): Promise<void> {\n const args = flags(\n {\n token: { type: 'string' },\n positions: { type: 'boolean' },\n sort: { type: 'string' },\n limit: { type: 'string' },\n },\n USAGE,\n argv,\n )\n\n await run(async () => {\n const { client, network } = await loadSession({ network: args.network as string | undefined })\n done(`session on ${network}`)\n\n const pools = await discoverPools(client, {\n ...(args.token ? { token: args.token as string } : {}),\n ...(args.limit ? { limit: Number(args.limit) } : {}),\n })\n const sorted =\n args.sort === 'fee'\n ? [...pools].sort((a, b) => a.fee - b.fee)\n : [...pools].sort((a, b) => (b.liquidity > a.liquidity ? 1 : b.liquidity < a.liquidity ? -1 : 0))\n\n let stakes: Map<string, PoolStake> | undefined\n if (args.positions) {\n step('scanning position records to attribute a stake to each pool')\n const mine = await stakePerPool(client, sorted)\n stakes = mine.stakes\n // Said rather than swallowed: a position in a pool this listing filtered out\n // is invisible here, and a zero stake would otherwise read as \"none held\".\n if (mine.elsewhere) {\n warn(\n `${mine.elsewhere} position(s) are in pools outside this listing — widen --limit or drop --token to see them`,\n )\n }\n }\n\n output(\n {\n network,\n pools: sorted.map((pool) => ({\n ...pool,\n ...(stakes ? { stake: stakes.get(pool.poolKey) ?? null } : {}),\n })),\n },\n (data) => {\n if (!data.pools.length) {\n // A bare header and rule reads as a rendering fault rather than an empty\n // result, and the filter is the likeliest reason there is nothing to show.\n console.log(\n args.token\n ? `\\nNo pools hold ${args.token as string} on ${data.network}. Drop --token to see every pool.`\n : `\\nNo pools on ${data.network}.`,\n )\n return\n }\n const headers = ['PAIR', 'FEE', 'SPACING', 'TICK', 'LIQUIDITY', 'STATUS', 'POOL KEY']\n const align: Array<'left' | 'right'> = ['left', 'right', 'right', 'right', 'right', 'left', 'left']\n if (stakes) {\n // Inserted before POOL KEY, which is long enough to push anything after\n // it off a narrow terminal.\n headers.splice(5, 0, 'POSITIONS', 'MY LIQUIDITY', 'SHARE')\n align.splice(5, 0, 'right', 'right', 'right')\n }\n table(\n headers,\n data.pools.map((pool) => {\n const row = [\n `${pool.token0.symbol}/${pool.token1.symbol}`,\n formatFee(pool.fee),\n String(pool.tickSpacing),\n String(pool.tick),\n // Liquidity stays raw on purpose: it is not denominated in either\n // token, so rendering it with a token's decimals would misstate it.\n pool.liquidity.toString(),\n // Green reads as \"you can trade this now\"; a reason is always a\n // reason it is unavailable, so it takes the warning colour.\n pool.reason ? yellow(pool.reason) : green('tradeable'),\n pool.poolKey,\n ]\n if (stakes) {\n const stake = stakes.get(pool.poolKey)\n // Both qualifiers are named in the cell rather than a footnote: a bare\n // count next to a share of 0.11% invites the reading that every\n // position is working, when one may be idle and another not on chain.\n const notes = stake\n ? [\n ...(stake.outOfRange ? [`${stake.outOfRange} out`] : []),\n ...(stake.noChainEntry ? [`${stake.noChainEntry} no entry`] : []),\n ]\n : []\n row.splice(\n 5,\n 0,\n stake ? `${stake.positions}${notes.length ? ` (${notes.join(', ')})` : ''}` : '—',\n stake && stake.liquidity > 0n ? stake.liquidity.toString() : '—',\n stake?.shareBps == null ? '—' : `${(stake.shareBps / 100).toFixed(2)}%`,\n )\n }\n return row\n }),\n align,\n )\n },\n )\n })\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {
|
|
2
|
+
done,
|
|
3
|
+
flags,
|
|
4
|
+
output,
|
|
5
|
+
run,
|
|
6
|
+
step,
|
|
7
|
+
table,
|
|
8
|
+
warn
|
|
9
|
+
} from "./chunk-IBVZHLUT.js";
|
|
10
|
+
import {
|
|
11
|
+
dim,
|
|
12
|
+
green,
|
|
13
|
+
red,
|
|
14
|
+
yellow
|
|
15
|
+
} from "./chunk-IHYFMX5A.js";
|
|
16
|
+
import {
|
|
17
|
+
formatAmount,
|
|
18
|
+
loadSession
|
|
19
|
+
} from "./chunk-2OT6LZPW.js";
|
|
20
|
+
|
|
21
|
+
// src/commands/positions.ts
|
|
22
|
+
var USAGE = `shield-swap positions \u2014 owned liquidity positions and what they are owed
|
|
23
|
+
|
|
24
|
+
--network <testnet|mainnet> default testnet
|
|
25
|
+
--pool <poolKey> only positions in this pool
|
|
26
|
+
--all include positions with no entry in the positions
|
|
27
|
+
mapping \u2014 a mint still finalizing, or one already
|
|
28
|
+
burned whose record the scanner still serves
|
|
29
|
+
--json machine-readable output`;
|
|
30
|
+
async function main(argv) {
|
|
31
|
+
const args = flags({ pool: { type: "string" }, all: { type: "boolean" } }, USAGE, argv);
|
|
32
|
+
await run(async () => {
|
|
33
|
+
const { client, network } = await loadSession({ network: args.network });
|
|
34
|
+
done(`session on ${network}`);
|
|
35
|
+
step(
|
|
36
|
+
args.all ? "scanning unspent and spent position records, then joining chain state" : "scanning position records and joining chain state"
|
|
37
|
+
);
|
|
38
|
+
const owned = await client.getOwnedPositions({
|
|
39
|
+
...args.pool ? { poolKey: args.pool } : {},
|
|
40
|
+
// The spent scan is what proves a burn, so it is only worth its cost when
|
|
41
|
+
// closed positions are going to be shown.
|
|
42
|
+
...args.all ? { includeClosed: true } : {}
|
|
43
|
+
});
|
|
44
|
+
const tokens = await client.listTokens();
|
|
45
|
+
const infoOf = (id) => tokens.find((token) => token.id === id);
|
|
46
|
+
const all = owned.map((position) => {
|
|
47
|
+
const t0 = infoOf(position.token0Id);
|
|
48
|
+
const t1 = infoOf(position.token1Id);
|
|
49
|
+
return {
|
|
50
|
+
positionTokenId: position.positionTokenId,
|
|
51
|
+
poolKey: position.poolKey,
|
|
52
|
+
pair: `${t0?.symbol ?? "?"}/${t1?.symbol ?? "?"}`,
|
|
53
|
+
decimals0: t0?.decimals ?? 0,
|
|
54
|
+
decimals1: t1?.decimals ?? 0,
|
|
55
|
+
tickLower: position.tickLower,
|
|
56
|
+
tickUpper: position.tickUpper,
|
|
57
|
+
frozen: position.frozen,
|
|
58
|
+
// A null state means the positions mapping has no entry, which happens at
|
|
59
|
+
// both ends of a position's life: a mint that has not finalized, or one
|
|
60
|
+
// already burned whose record the scanner is still serving. The two are
|
|
61
|
+
// indistinguishable from here, so the label must not claim either.
|
|
62
|
+
noChainEntry: position.state === null,
|
|
63
|
+
// Proven from records, not inferred from the mapping's silence: the burn
|
|
64
|
+
// consumed the last PositionNFT and re-issued nothing.
|
|
65
|
+
closed: position.closed,
|
|
66
|
+
// Left null rather than defaulted to zero. Everything above comes off the
|
|
67
|
+
// record; these five come only from the positions mapping, and a zero here
|
|
68
|
+
// would read as a drained position instead of one with nothing to read.
|
|
69
|
+
state: position.state ? {
|
|
70
|
+
liquidity: position.state.liquidity,
|
|
71
|
+
amount0: position.state.amount0,
|
|
72
|
+
amount1: position.state.amount1,
|
|
73
|
+
collectable0: position.state.uncollectedFees0,
|
|
74
|
+
collectable1: position.state.uncollectedFees1
|
|
75
|
+
} : null
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
const hidden = args.all ? 0 : all.filter((row) => row.noChainEntry).length;
|
|
79
|
+
const rows = args.all ? all : all.filter((row) => !row.noChainEntry);
|
|
80
|
+
const closed = all.filter((row) => row.closed).length;
|
|
81
|
+
output({ network, positions: rows, hidden, closed }, (data) => {
|
|
82
|
+
if (!data.positions.length) {
|
|
83
|
+
console.log(
|
|
84
|
+
data.hidden ? `
|
|
85
|
+
No operable positions. ${data.hidden} record(s) have no entry in the positions mapping \u2014 pass --all to list them.` : "\nNo positions. Open one with `shield-swap mint --help`."
|
|
86
|
+
);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
table(
|
|
90
|
+
["PAIR", "RANGE", "LIQUIDITY", "BACKING", "COLLECTABLE", "STATE", "POSITION ID"],
|
|
91
|
+
data.positions.map((row) => [
|
|
92
|
+
row.pair,
|
|
93
|
+
`${row.tickLower}\u2026${row.tickUpper}`,
|
|
94
|
+
// An em dash, not a zero: with no mapping entry there is no figure to
|
|
95
|
+
// report, and printing 0 would be indistinguishable from a drained
|
|
96
|
+
// position that still has one.
|
|
97
|
+
row.state ? row.state.liquidity.toString() : dim("\u2014"),
|
|
98
|
+
// Both sides in one cell, in the pool's own token order: a position is
|
|
99
|
+
// backed by a pair, and splitting them into four columns of bare numbers
|
|
100
|
+
// loses which token each belongs to.
|
|
101
|
+
row.state ? `${formatAmount(row.state.amount0, row.decimals0)} / ${formatAmount(row.state.amount1, row.decimals1)}` : dim("\u2014"),
|
|
102
|
+
row.state ? `${formatAmount(row.state.collectable0, row.decimals0)} / ${formatAmount(row.state.collectable1, row.decimals1)}` : dim("\u2014"),
|
|
103
|
+
// `closed` is proven, so it wins over the mapping's silence. Only a
|
|
104
|
+
// record that is still unspent AND has no entry is genuinely undecided:
|
|
105
|
+
// a mint mid-finalize, or a burn the scanner has not caught up with.
|
|
106
|
+
row.closed ? dim("closed") : [row.frozen ? red("FROZEN") : "", row.noChainEntry ? yellow("pending") : ""].filter(Boolean).join(", ") || green("open"),
|
|
107
|
+
row.positionTokenId
|
|
108
|
+
]),
|
|
109
|
+
["left", "right", "right", "right", "right", "left", "left"]
|
|
110
|
+
);
|
|
111
|
+
const owed = data.positions.filter(
|
|
112
|
+
(row) => (row.state?.collectable0 ?? 0n) > 0n || (row.state?.collectable1 ?? 0n) > 0n
|
|
113
|
+
);
|
|
114
|
+
if (owed.length) {
|
|
115
|
+
console.log(`
|
|
116
|
+
${owed.length} position(s) have something to collect \u2014 run \`shield-swap collect\`.`);
|
|
117
|
+
}
|
|
118
|
+
if (data.positions.some((row) => row.frozen)) {
|
|
119
|
+
warn("a frozen position blocks every liquidity operation until an admin unfreezes it");
|
|
120
|
+
}
|
|
121
|
+
if (data.closed) console.log(`${data.closed} closed position(s) listed \u2014 burned, nothing left to operate on.`);
|
|
122
|
+
const pending = data.positions.filter((row) => row.noChainEntry && !row.closed).length;
|
|
123
|
+
if (pending || data.hidden) {
|
|
124
|
+
warn(
|
|
125
|
+
`${pending || data.hidden} position(s) hold an unspent record with no entry in the positions mapping \u2014 a mint still finalizing, or a burn the record scanner has not caught up with (it can serve a burned record for minutes). Neither can be operated on` + (data.hidden ? ", and they are not listed above \u2014 pass --all to see them." : ".")
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
export {
|
|
132
|
+
main
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=positions-MILT3BRU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/positions.ts"],"sourcesContent":["/**\n * Position discovery — every liquidity position the account holds, summarized.\n *\n * Discovered from the account's own records, not from a local list: each\n * position is a private NFT, and `getOwnedPositions` joins it with chain state\n * to report the range, the tokens currently backing it, and what `collect`\n * would pay right now.\n *\n * Only positions with an entry in the positions mapping are listed by default —\n * those are the ones that can be increased, decreased, collected from, or burned.\n * A record whose mapping entry is gone holds nothing and cannot be operated on;\n * `--all` lists those too, which is what to reach for when a mint seems missing.\n *\n * Reads only. Spends nothing.\n *\n * Usage:\n * shield-swap positions # testnet, operable positions\n * shield-swap positions --all # include ones with no chain entry\n * shield-swap positions --network mainnet\n * shield-swap positions --pool <poolKey> # one pool\n * shield-swap positions --json\n */\nimport { loadSession, formatAmount } from '../session.js'\nimport { flags, step, done, warn, output, run, table } from '../shared.js'\nimport { dim, green, red, yellow } from '../color.js'\n\nconst USAGE = `shield-swap positions — owned liquidity positions and what they are owed\n\n --network <testnet|mainnet> default testnet\n --pool <poolKey> only positions in this pool\n --all include positions with no entry in the positions\n mapping — a mint still finalizing, or one already\n burned whose record the scanner still serves\n --json machine-readable output`\n\n/**\n * Runs the `positions` subcommand.\n *\n * @param argv Arguments after the subcommand name, as the dispatcher supplies them.\n */\nexport async function main(argv: string[]): Promise<void> {\n const args = flags({ pool: { type: 'string' }, all: { type: 'boolean' } }, USAGE, argv)\n\n await run(async () => {\n const { client, network } = await loadSession({ network: args.network as string | undefined })\n done(`session on ${network}`)\n\n step(\n args.all\n ? 'scanning unspent and spent position records, then joining chain state'\n : 'scanning position records and joining chain state',\n )\n const owned = await client.getOwnedPositions({\n ...(args.pool ? { poolKey: args.pool as string } : {}),\n // The spent scan is what proves a burn, so it is only worth its cost when\n // closed positions are going to be shown.\n ...(args.all ? { includeClosed: true } : {}),\n })\n const tokens = await client.listTokens()\n const infoOf = (id: string) => tokens.find((token) => token.id === id)\n\n const all = owned.map((position) => {\n const t0 = infoOf(position.token0Id)\n const t1 = infoOf(position.token1Id)\n return {\n positionTokenId: position.positionTokenId,\n poolKey: position.poolKey,\n pair: `${t0?.symbol ?? '?'}/${t1?.symbol ?? '?'}`,\n decimals0: t0?.decimals ?? 0,\n decimals1: t1?.decimals ?? 0,\n tickLower: position.tickLower,\n tickUpper: position.tickUpper,\n frozen: position.frozen,\n // A null state means the positions mapping has no entry, which happens at\n // both ends of a position's life: a mint that has not finalized, or one\n // already burned whose record the scanner is still serving. The two are\n // indistinguishable from here, so the label must not claim either.\n noChainEntry: position.state === null,\n // Proven from records, not inferred from the mapping's silence: the burn\n // consumed the last PositionNFT and re-issued nothing.\n closed: position.closed,\n // Left null rather than defaulted to zero. Everything above comes off the\n // record; these five come only from the positions mapping, and a zero here\n // would read as a drained position instead of one with nothing to read.\n state: position.state\n ? {\n liquidity: position.state.liquidity,\n amount0: position.state.amount0,\n amount1: position.state.amount1,\n collectable0: position.state.uncollectedFees0,\n collectable1: position.state.uncollectedFees1,\n }\n : null,\n }\n })\n\n // Hidden rather than dropped: the count is reported below so a position that\n // exists but cannot be operated on never reads as one the account never had.\n const hidden = args.all ? 0 : all.filter((row) => row.noChainEntry).length\n const rows = args.all ? all : all.filter((row) => !row.noChainEntry)\n const closed = all.filter((row) => row.closed).length\n\n output({ network, positions: rows, hidden, closed }, (data) => {\n if (!data.positions.length) {\n console.log(\n data.hidden\n ? `\\nNo operable positions. ${data.hidden} record(s) have no entry in the positions ` +\n 'mapping — pass --all to list them.'\n : '\\nNo positions. Open one with `shield-swap mint --help`.',\n )\n return\n }\n table(\n ['PAIR', 'RANGE', 'LIQUIDITY', 'BACKING', 'COLLECTABLE', 'STATE', 'POSITION ID'],\n data.positions.map((row) => [\n row.pair,\n `${row.tickLower}…${row.tickUpper}`,\n // An em dash, not a zero: with no mapping entry there is no figure to\n // report, and printing 0 would be indistinguishable from a drained\n // position that still has one.\n row.state ? row.state.liquidity.toString() : dim('—'),\n // Both sides in one cell, in the pool's own token order: a position is\n // backed by a pair, and splitting them into four columns of bare numbers\n // loses which token each belongs to.\n row.state\n ? `${formatAmount(row.state.amount0, row.decimals0)} / ${formatAmount(row.state.amount1, row.decimals1)}`\n : dim('—'),\n row.state\n ? `${formatAmount(row.state.collectable0, row.decimals0)} / ${formatAmount(row.state.collectable1, row.decimals1)}`\n : dim('—'),\n // `closed` is proven, so it wins over the mapping's silence. Only a\n // record that is still unspent AND has no entry is genuinely undecided:\n // a mint mid-finalize, or a burn the scanner has not caught up with.\n row.closed\n ? dim('closed')\n : [row.frozen ? red('FROZEN') : '', row.noChainEntry ? yellow('pending') : '']\n .filter(Boolean)\n .join(', ') || green('open'),\n row.positionTokenId,\n ]),\n ['left', 'right', 'right', 'right', 'right', 'left', 'left'],\n )\n const owed = data.positions.filter(\n (row) => (row.state?.collectable0 ?? 0n) > 0n || (row.state?.collectable1 ?? 0n) > 0n,\n )\n if (owed.length) {\n console.log(`\\n${owed.length} position(s) have something to collect — run \\`shield-swap collect\\`.`)\n }\n if (data.positions.some((row) => row.frozen)) {\n warn('a frozen position blocks every liquidity operation until an admin unfreezes it')\n }\n if (data.closed) console.log(`${data.closed} closed position(s) listed — burned, nothing left to operate on.`)\n // Only the undecided ones need the caveat now that burns are proven from\n // records: an unspent record with no mapping entry is a mint mid-finalize, or\n // a burn whose record the scanner has not marked spent yet.\n const pending = data.positions.filter((row) => row.noChainEntry && !row.closed).length\n if (pending || data.hidden) {\n warn(\n `${pending || data.hidden} position(s) hold an unspent record with no entry in the ` +\n 'positions mapping — a mint still finalizing, or a burn the record scanner has not ' +\n 'caught up with (it can serve a burned record for minutes). Neither can be operated on' +\n (data.hidden ? ', and they are not listed above — pass --all to see them.' : '.'),\n )\n }\n })\n })\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as _provablehq_veil_aleo_sdk from '@provablehq/veil-aleo-sdk';
|
|
2
|
+
import * as _provablehq_veil_core from '@provablehq/veil-core';
|
|
3
|
+
import * as _provablehq_shield_swap_sdk from '@provablehq/shield-swap-sdk';
|
|
4
|
+
|
|
5
|
+
/** The networks these scripts run against. */
|
|
6
|
+
type Network = 'testnet' | 'mainnet';
|
|
7
|
+
/**
|
|
8
|
+
* Resolves the network from an explicit choice, the environment, or the default.
|
|
9
|
+
*
|
|
10
|
+
* Testnet is the default and mainnet is never reached by omission: a script has
|
|
11
|
+
* to be told, because everything downstream — the DEX API host, the prover, the
|
|
12
|
+
* scanner, the token registry, and the identity store — is per-network, and the
|
|
13
|
+
* mainnet ones move real value.
|
|
14
|
+
*
|
|
15
|
+
* @param explicit A `--network` value, when a script parsed one.
|
|
16
|
+
* @throws When the value is neither network, rather than silently using testnet.
|
|
17
|
+
*/
|
|
18
|
+
declare function resolveNetwork(explicit?: string): Network;
|
|
19
|
+
declare const NETWORK_URL = "https://api.provable.com/v2";
|
|
20
|
+
/** Everything that must survive between agent sessions. */
|
|
21
|
+
type ShieldSwapState = {
|
|
22
|
+
network: string;
|
|
23
|
+
/**
|
|
24
|
+
* DEX API origin this session targets. Unset means the SDK's default
|
|
25
|
+
* hosted deployment. Set by `shield-swap setup` (`--api-url` / SHIELD_SWAP_API_URL);
|
|
26
|
+
* the access grant, API token, and airdrop job are scoped to one
|
|
27
|
+
* deployment, so setup clears them when this changes.
|
|
28
|
+
*/
|
|
29
|
+
apiUrl?: string;
|
|
30
|
+
privateKey?: string;
|
|
31
|
+
address?: string;
|
|
32
|
+
dexApiToken?: string;
|
|
33
|
+
accessRedeemed?: boolean;
|
|
34
|
+
/** Faucet job already requested for this account — prevents double-drawing on re-runs. */
|
|
35
|
+
airdropJobId?: string;
|
|
36
|
+
};
|
|
37
|
+
/** Per-network state directory. Nothing is shared between networks. */
|
|
38
|
+
declare function stateDir(network: Network): string;
|
|
39
|
+
/**
|
|
40
|
+
* Where the blinded identity store lives for a network.
|
|
41
|
+
*
|
|
42
|
+
* Scoped by network because a reservation is only meaningful against the chain
|
|
43
|
+
* it was checked on: counters reserved against testnet's
|
|
44
|
+
* `used_blinded_addresses` say nothing about mainnet's, and one shared file
|
|
45
|
+
* would hand out identities the other chain has already consumed.
|
|
46
|
+
*/
|
|
47
|
+
declare function blindedStorePath(network: Network): string;
|
|
48
|
+
/**
|
|
49
|
+
* Where Provable API credentials live for a network.
|
|
50
|
+
*
|
|
51
|
+
* Separate from the state file because the SDK owns the format: `shield-swap setup`
|
|
52
|
+
* hands `fileCredentialStore` this path and the client reads and writes it
|
|
53
|
+
* directly, including registering a consumer when the file is absent.
|
|
54
|
+
*/
|
|
55
|
+
declare function credentialsPath(network: Network): string;
|
|
56
|
+
/**
|
|
57
|
+
* Reads a network's state file, or returns a fresh empty state.
|
|
58
|
+
*
|
|
59
|
+
* Falls back to the pre-network layout for testnet only, so an existing
|
|
60
|
+
* `.shield-swap/state.json` keeps working; the next save writes it to the
|
|
61
|
+
* network-scoped path.
|
|
62
|
+
*/
|
|
63
|
+
declare function loadState(network: Network): ShieldSwapState;
|
|
64
|
+
/**
|
|
65
|
+
* Writes the state file atomically (temp file + rename, 0600 — it holds the
|
|
66
|
+
* private key). A crash mid-write can never truncate the only copy of the
|
|
67
|
+
* key and the open swap handles.
|
|
68
|
+
*/
|
|
69
|
+
declare function saveState(state: ShieldSwapState): void;
|
|
70
|
+
/**
|
|
71
|
+
* Renders a raw base-unit amount in human units ("0.0534 ETH"), the ONLY
|
|
72
|
+
* format that should ever reach the user. Raw units (wei-style integers)
|
|
73
|
+
* are SDK-facing; showing them to a person misstates their balances by
|
|
74
|
+
* orders of magnitude.
|
|
75
|
+
*/
|
|
76
|
+
declare function formatAmount(amount: bigint, decimals: number, symbol?: string): string;
|
|
77
|
+
/** What {@link namedAmounts} needs of a pool's token to place an amount. */
|
|
78
|
+
type AmountToken = {
|
|
79
|
+
id: string;
|
|
80
|
+
symbol: string;
|
|
81
|
+
decimals: number;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Places caller-named amounts into a pool's own token order.
|
|
85
|
+
*
|
|
86
|
+
* A pool orders its tokens by id, not by anything a caller types, so `--amount0`
|
|
87
|
+
* is unknowable without reading the pool first: naming a pair `USDCx:ETH` does
|
|
88
|
+
* not make USDCx side 0. `--amount USDCx:0.5` names the token instead and is
|
|
89
|
+
* matched here, while `--amount0`/`--amount1` stay available for callers who know
|
|
90
|
+
* the order. Parsing is the inverse of {@link formatAmount}: human decimals in,
|
|
91
|
+
* raw base units out. Pure and local.
|
|
92
|
+
*
|
|
93
|
+
* @param params.entries `--amount` values, each `<symbol|id>:<decimal>`.
|
|
94
|
+
* @param params.indexed The raw `--amount0` and `--amount1` strings, in that
|
|
95
|
+
* order, `undefined` where the flag was absent.
|
|
96
|
+
* @param params.tokens The pool's tokens in ITS order — `[token0, token1]`.
|
|
97
|
+
* @returns Raw base units per side, `undefined` where nothing named that side.
|
|
98
|
+
* @throws When an entry is malformed, names a token outside the pair, or names a
|
|
99
|
+
* side twice — including once by symbol and once by index, where preferring
|
|
100
|
+
* either would commit an amount the caller did not ask for.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* const { amount0, amount1 } = namedAmounts({
|
|
104
|
+
* entries: ['USDCx:0.5'],
|
|
105
|
+
* indexed: [undefined, undefined],
|
|
106
|
+
* tokens: [token0, token1],
|
|
107
|
+
* })
|
|
108
|
+
*/
|
|
109
|
+
declare function namedAmounts(params: {
|
|
110
|
+
entries: string[];
|
|
111
|
+
indexed: readonly [string | undefined, string | undefined];
|
|
112
|
+
tokens: readonly [AmountToken, AmountToken];
|
|
113
|
+
}): {
|
|
114
|
+
amount0: bigint | undefined;
|
|
115
|
+
amount1: bigint | undefined;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Builds the fully wired, authenticated session from the state file.
|
|
119
|
+
*
|
|
120
|
+
* Requires `shield-swap setup` to have run (key material in the state file).
|
|
121
|
+
* Authenticates with the DEX API on every call — the session JWT covers
|
|
122
|
+
* everything including access/token management, and auto-renews on expiry.
|
|
123
|
+
*
|
|
124
|
+
* Provable API credentials are not required up front: the client registers a
|
|
125
|
+
* consumer through the credential file on first prove or scan when it holds
|
|
126
|
+
* none — though `shield-swap setup` registers and verifies eagerly, so a session built
|
|
127
|
+
* after setup has working credentials rather than untested ones.
|
|
128
|
+
*/
|
|
129
|
+
declare function loadSession(options?: {
|
|
130
|
+
network?: string;
|
|
131
|
+
}): Promise<{
|
|
132
|
+
client: _provablehq_veil_core.Client<_provablehq_shield_swap_sdk.ShieldSwapActions & _provablehq_veil_core.WalletActions & {
|
|
133
|
+
recordProvider: _provablehq_veil_core.RecordProvider | undefined;
|
|
134
|
+
} & _provablehq_veil_aleo_sdk.ProvableApiActions>;
|
|
135
|
+
account: _provablehq_veil_core.LocalAccount<"privateKey">;
|
|
136
|
+
scanner: _provablehq_veil_core.RecordProvider & {
|
|
137
|
+
setSession: (session: _provablehq_veil_aleo_sdk.ProvableSession) => void;
|
|
138
|
+
};
|
|
139
|
+
state: ShieldSwapState;
|
|
140
|
+
aleo: _provablehq_veil_aleo_sdk.AleoSdk;
|
|
141
|
+
network: Network;
|
|
142
|
+
blindedIdentities: _provablehq_shield_swap_sdk.BlindedIdentityStore;
|
|
143
|
+
}>;
|
|
144
|
+
/** Polls a predicate until it returns true or attempts run out. */
|
|
145
|
+
declare function pollUntil(fn: () => Promise<boolean>, attempts: number, intervalMs: number): Promise<boolean>;
|
|
146
|
+
/**
|
|
147
|
+
* Resolves key material and stores it. Priority: existing state → imported
|
|
148
|
+
* key (`importKey`) → fresh generation, but only when `allowGenerate` is
|
|
149
|
+
* true. Returning users keep their account; a fresh key is never created
|
|
150
|
+
* silently.
|
|
151
|
+
*/
|
|
152
|
+
declare function ensureKeyMaterial(state: ShieldSwapState, options?: {
|
|
153
|
+
importKey?: string;
|
|
154
|
+
allowGenerate?: boolean;
|
|
155
|
+
}): Promise<ShieldSwapState>;
|
|
156
|
+
/** Signals that setup must ask the user about existing config before creating anything. */
|
|
157
|
+
declare class NeedsConfigDecisionError extends Error {
|
|
158
|
+
constructor();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { type AmountToken, NETWORK_URL, NeedsConfigDecisionError, type Network, type ShieldSwapState, blindedStorePath, credentialsPath, ensureKeyMaterial, formatAmount, loadSession, loadState, namedAmounts, pollUntil, resolveNetwork, saveState, stateDir };
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NETWORK_URL,
|
|
3
|
+
NeedsConfigDecisionError,
|
|
4
|
+
blindedStorePath,
|
|
5
|
+
credentialsPath,
|
|
6
|
+
ensureKeyMaterial,
|
|
7
|
+
formatAmount,
|
|
8
|
+
loadSession,
|
|
9
|
+
loadState,
|
|
10
|
+
namedAmounts,
|
|
11
|
+
pollUntil,
|
|
12
|
+
resolveNetwork,
|
|
13
|
+
saveState,
|
|
14
|
+
stateDir
|
|
15
|
+
} from "./chunk-2OT6LZPW.js";
|
|
16
|
+
export {
|
|
17
|
+
NETWORK_URL,
|
|
18
|
+
NeedsConfigDecisionError,
|
|
19
|
+
blindedStorePath,
|
|
20
|
+
credentialsPath,
|
|
21
|
+
ensureKeyMaterial,
|
|
22
|
+
formatAmount,
|
|
23
|
+
loadSession,
|
|
24
|
+
loadState,
|
|
25
|
+
namedAmounts,
|
|
26
|
+
pollUntil,
|
|
27
|
+
resolveNetwork,
|
|
28
|
+
saveState,
|
|
29
|
+
stateDir
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|