@spectratools/aborean-cli 0.3.0 → 0.4.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/dist/cli.js +884 -154
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
import { readFileSync } from "fs";
|
|
5
5
|
import { dirname, resolve } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
-
import { Cli as
|
|
7
|
+
import { Cli as Cli5, z as z5 } from "incur";
|
|
8
8
|
|
|
9
|
-
// src/commands/
|
|
9
|
+
// src/commands/cl.ts
|
|
10
|
+
import { checksumAddress, isAddress } from "@spectratools/cli-shared";
|
|
10
11
|
import { Cli, z } from "incur";
|
|
12
|
+
import { formatUnits, parseUnits } from "viem";
|
|
11
13
|
|
|
12
14
|
// src/contracts/abis/CLFactory.abi.json
|
|
13
15
|
var CLFactory_abi_default = [
|
|
@@ -122,6 +124,73 @@ var CLFactory_abi_default = [
|
|
|
122
124
|
}
|
|
123
125
|
];
|
|
124
126
|
|
|
127
|
+
// src/contracts/abis/CLPool.abi.json
|
|
128
|
+
var CLPool_abi_default = [
|
|
129
|
+
{
|
|
130
|
+
type: "function",
|
|
131
|
+
name: "slot0",
|
|
132
|
+
inputs: [],
|
|
133
|
+
outputs: [
|
|
134
|
+
{ name: "sqrtPriceX96", type: "uint160", internalType: "uint160" },
|
|
135
|
+
{ name: "tick", type: "int24", internalType: "int24" },
|
|
136
|
+
{ name: "observationIndex", type: "uint16", internalType: "uint16" },
|
|
137
|
+
{ name: "observationCardinality", type: "uint16", internalType: "uint16" },
|
|
138
|
+
{ name: "observationCardinalityNext", type: "uint16", internalType: "uint16" },
|
|
139
|
+
{ name: "unlocked", type: "bool", internalType: "bool" }
|
|
140
|
+
],
|
|
141
|
+
stateMutability: "view"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
type: "function",
|
|
145
|
+
name: "liquidity",
|
|
146
|
+
inputs: [],
|
|
147
|
+
outputs: [{ name: "", type: "uint128", internalType: "uint128" }],
|
|
148
|
+
stateMutability: "view"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: "function",
|
|
152
|
+
name: "token0",
|
|
153
|
+
inputs: [],
|
|
154
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
155
|
+
stateMutability: "view"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: "function",
|
|
159
|
+
name: "token1",
|
|
160
|
+
inputs: [],
|
|
161
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
162
|
+
stateMutability: "view"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
type: "function",
|
|
166
|
+
name: "tickSpacing",
|
|
167
|
+
inputs: [],
|
|
168
|
+
outputs: [{ name: "", type: "int24", internalType: "int24" }],
|
|
169
|
+
stateMutability: "view"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: "function",
|
|
173
|
+
name: "factory",
|
|
174
|
+
inputs: [],
|
|
175
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
176
|
+
stateMutability: "view"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
type: "function",
|
|
180
|
+
name: "gauge",
|
|
181
|
+
inputs: [],
|
|
182
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
183
|
+
stateMutability: "view"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: "function",
|
|
187
|
+
name: "fee",
|
|
188
|
+
inputs: [],
|
|
189
|
+
outputs: [{ name: "", type: "uint24", internalType: "uint24" }],
|
|
190
|
+
stateMutability: "view"
|
|
191
|
+
}
|
|
192
|
+
];
|
|
193
|
+
|
|
125
194
|
// src/contracts/abis/Gauge.abi.json
|
|
126
195
|
var Gauge_abi_default = [
|
|
127
196
|
{
|
|
@@ -256,6 +325,68 @@ var Minter_abi_default = [
|
|
|
256
325
|
}
|
|
257
326
|
];
|
|
258
327
|
|
|
328
|
+
// src/contracts/abis/NonfungiblePositionManager.abi.json
|
|
329
|
+
var NonfungiblePositionManager_abi_default = [
|
|
330
|
+
{
|
|
331
|
+
type: "function",
|
|
332
|
+
name: "positions",
|
|
333
|
+
inputs: [{ name: "tokenId", type: "uint256", internalType: "uint256" }],
|
|
334
|
+
outputs: [
|
|
335
|
+
{ name: "nonce", type: "uint96", internalType: "uint96" },
|
|
336
|
+
{ name: "operator", type: "address", internalType: "address" },
|
|
337
|
+
{ name: "token0", type: "address", internalType: "address" },
|
|
338
|
+
{ name: "token1", type: "address", internalType: "address" },
|
|
339
|
+
{ name: "tickSpacing", type: "int24", internalType: "int24" },
|
|
340
|
+
{ name: "tickLower", type: "int24", internalType: "int24" },
|
|
341
|
+
{ name: "tickUpper", type: "int24", internalType: "int24" },
|
|
342
|
+
{ name: "liquidity", type: "uint128", internalType: "uint128" },
|
|
343
|
+
{ name: "feeGrowthInside0LastX128", type: "uint256", internalType: "uint256" },
|
|
344
|
+
{ name: "feeGrowthInside1LastX128", type: "uint256", internalType: "uint256" },
|
|
345
|
+
{ name: "tokensOwed0", type: "uint128", internalType: "uint128" },
|
|
346
|
+
{ name: "tokensOwed1", type: "uint128", internalType: "uint128" }
|
|
347
|
+
],
|
|
348
|
+
stateMutability: "view"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
type: "function",
|
|
352
|
+
name: "balanceOf",
|
|
353
|
+
inputs: [{ name: "owner", type: "address", internalType: "address" }],
|
|
354
|
+
outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
|
|
355
|
+
stateMutability: "view"
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
type: "function",
|
|
359
|
+
name: "tokenOfOwnerByIndex",
|
|
360
|
+
inputs: [
|
|
361
|
+
{ name: "owner", type: "address", internalType: "address" },
|
|
362
|
+
{ name: "index", type: "uint256", internalType: "uint256" }
|
|
363
|
+
],
|
|
364
|
+
outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
|
|
365
|
+
stateMutability: "view"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
type: "function",
|
|
369
|
+
name: "totalSupply",
|
|
370
|
+
inputs: [],
|
|
371
|
+
outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
|
|
372
|
+
stateMutability: "view"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
type: "function",
|
|
376
|
+
name: "factory",
|
|
377
|
+
inputs: [],
|
|
378
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
379
|
+
stateMutability: "view"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
type: "function",
|
|
383
|
+
name: "WETH9",
|
|
384
|
+
inputs: [],
|
|
385
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
386
|
+
stateMutability: "view"
|
|
387
|
+
}
|
|
388
|
+
];
|
|
389
|
+
|
|
259
390
|
// src/contracts/abis/PoolFactory.abi.json
|
|
260
391
|
var PoolFactory_abi_default = [
|
|
261
392
|
{
|
|
@@ -316,6 +447,64 @@ var PoolFactory_abi_default = [
|
|
|
316
447
|
}
|
|
317
448
|
];
|
|
318
449
|
|
|
450
|
+
// src/contracts/abis/QuoterV2.abi.json
|
|
451
|
+
var QuoterV2_abi_default = [
|
|
452
|
+
{
|
|
453
|
+
type: "function",
|
|
454
|
+
name: "factory",
|
|
455
|
+
inputs: [],
|
|
456
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
457
|
+
stateMutability: "view"
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
type: "function",
|
|
461
|
+
name: "WETH9",
|
|
462
|
+
inputs: [],
|
|
463
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
464
|
+
stateMutability: "view"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
type: "function",
|
|
468
|
+
name: "quoteExactInputSingle",
|
|
469
|
+
inputs: [
|
|
470
|
+
{
|
|
471
|
+
name: "params",
|
|
472
|
+
type: "tuple",
|
|
473
|
+
internalType: "struct IQuoterV2.QuoteExactInputSingleParams",
|
|
474
|
+
components: [
|
|
475
|
+
{ name: "tokenIn", type: "address", internalType: "address" },
|
|
476
|
+
{ name: "tokenOut", type: "address", internalType: "address" },
|
|
477
|
+
{ name: "amountIn", type: "uint256", internalType: "uint256" },
|
|
478
|
+
{ name: "tickSpacing", type: "int24", internalType: "int24" },
|
|
479
|
+
{ name: "sqrtPriceLimitX96", type: "uint160", internalType: "uint160" }
|
|
480
|
+
]
|
|
481
|
+
}
|
|
482
|
+
],
|
|
483
|
+
outputs: [
|
|
484
|
+
{ name: "amountOut", type: "uint256", internalType: "uint256" },
|
|
485
|
+
{ name: "sqrtPriceX96After", type: "uint160", internalType: "uint160" },
|
|
486
|
+
{ name: "initializedTicksCrossed", type: "uint32", internalType: "uint32" },
|
|
487
|
+
{ name: "gasEstimate", type: "uint256", internalType: "uint256" }
|
|
488
|
+
],
|
|
489
|
+
stateMutability: "nonpayable"
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
type: "function",
|
|
493
|
+
name: "quoteExactInput",
|
|
494
|
+
inputs: [
|
|
495
|
+
{ name: "path", type: "bytes", internalType: "bytes" },
|
|
496
|
+
{ name: "amountIn", type: "uint256", internalType: "uint256" }
|
|
497
|
+
],
|
|
498
|
+
outputs: [
|
|
499
|
+
{ name: "amountOut", type: "uint256", internalType: "uint256" },
|
|
500
|
+
{ name: "sqrtPriceX96AfterList", type: "uint160[]", internalType: "uint160[]" },
|
|
501
|
+
{ name: "initializedTicksCrossedList", type: "uint32[]", internalType: "uint32[]" },
|
|
502
|
+
{ name: "gasEstimate", type: "uint256", internalType: "uint256" }
|
|
503
|
+
],
|
|
504
|
+
stateMutability: "nonpayable"
|
|
505
|
+
}
|
|
506
|
+
];
|
|
507
|
+
|
|
319
508
|
// src/contracts/abis/RewardsDistributor.abi.json
|
|
320
509
|
var RewardsDistributor_abi_default = [
|
|
321
510
|
{
|
|
@@ -668,9 +857,12 @@ var VotingReward_abi_default = [
|
|
|
668
857
|
|
|
669
858
|
// src/contracts/abis.ts
|
|
670
859
|
var clFactoryAbi = CLFactory_abi_default;
|
|
860
|
+
var clPoolAbi = CLPool_abi_default;
|
|
671
861
|
var gaugeAbi = Gauge_abi_default;
|
|
672
862
|
var minterAbi = Minter_abi_default;
|
|
863
|
+
var nonfungiblePositionManagerAbi = NonfungiblePositionManager_abi_default;
|
|
673
864
|
var poolFactoryAbi = PoolFactory_abi_default;
|
|
865
|
+
var quoterV2Abi = QuoterV2_abi_default;
|
|
674
866
|
var rewardsDistributorAbi = RewardsDistributor_abi_default;
|
|
675
867
|
var voterAbi = Voter_abi_default;
|
|
676
868
|
var votingEscrowAbi = VotingEscrow_abi_default;
|
|
@@ -761,12 +953,549 @@ function createAboreanPublicClient(rpcUrl) {
|
|
|
761
953
|
});
|
|
762
954
|
}
|
|
763
955
|
|
|
956
|
+
// src/commands/cl.ts
|
|
957
|
+
var Q96 = 2n ** 96n;
|
|
958
|
+
var MULTICALL_BATCH_SIZE = 100;
|
|
959
|
+
var env = z.object({
|
|
960
|
+
ABSTRACT_RPC_URL: z.string().optional().describe("Abstract RPC URL override")
|
|
961
|
+
});
|
|
962
|
+
var tokenSchema = z.object({
|
|
963
|
+
address: z.string(),
|
|
964
|
+
symbol: z.string(),
|
|
965
|
+
decimals: z.number()
|
|
966
|
+
});
|
|
967
|
+
var poolRowSchema = z.object({
|
|
968
|
+
pool: z.string(),
|
|
969
|
+
pair: z.string(),
|
|
970
|
+
token0: tokenSchema,
|
|
971
|
+
token1: tokenSchema,
|
|
972
|
+
fee: z.number(),
|
|
973
|
+
feePercent: z.number(),
|
|
974
|
+
tickSpacing: z.number(),
|
|
975
|
+
liquidity: z.string(),
|
|
976
|
+
currentTick: z.number(),
|
|
977
|
+
sqrtPriceX96: z.string(),
|
|
978
|
+
activeLiquidityEstimate: z.object({
|
|
979
|
+
token0: z.string(),
|
|
980
|
+
token1: z.string(),
|
|
981
|
+
totalInToken0: z.number().nullable(),
|
|
982
|
+
totalInToken1: z.number().nullable()
|
|
983
|
+
}),
|
|
984
|
+
price: z.object({
|
|
985
|
+
token1PerToken0: z.number().nullable(),
|
|
986
|
+
token0PerToken1: z.number().nullable()
|
|
987
|
+
})
|
|
988
|
+
});
|
|
989
|
+
var quoteOutputSchema = z.object({
|
|
990
|
+
pool: z.string(),
|
|
991
|
+
selectedFee: z.number(),
|
|
992
|
+
selectedTickSpacing: z.number(),
|
|
993
|
+
tokenIn: tokenSchema,
|
|
994
|
+
tokenOut: tokenSchema,
|
|
995
|
+
amountIn: z.object({
|
|
996
|
+
raw: z.string(),
|
|
997
|
+
decimal: z.string()
|
|
998
|
+
}),
|
|
999
|
+
amountOut: z.object({
|
|
1000
|
+
raw: z.string(),
|
|
1001
|
+
decimal: z.string()
|
|
1002
|
+
}),
|
|
1003
|
+
execution: z.object({
|
|
1004
|
+
sqrtPriceX96After: z.string(),
|
|
1005
|
+
initializedTicksCrossed: z.number(),
|
|
1006
|
+
gasEstimate: z.string()
|
|
1007
|
+
}),
|
|
1008
|
+
prices: z.object({
|
|
1009
|
+
poolMidPriceOutPerIn: z.number().nullable(),
|
|
1010
|
+
quotePriceOutPerIn: z.number().nullable(),
|
|
1011
|
+
priceImpactPct: z.number().nullable()
|
|
1012
|
+
})
|
|
1013
|
+
});
|
|
1014
|
+
var erc20MetadataAbi = [
|
|
1015
|
+
{
|
|
1016
|
+
type: "function",
|
|
1017
|
+
name: "symbol",
|
|
1018
|
+
stateMutability: "view",
|
|
1019
|
+
inputs: [],
|
|
1020
|
+
outputs: [{ type: "string" }]
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
type: "function",
|
|
1024
|
+
name: "decimals",
|
|
1025
|
+
stateMutability: "view",
|
|
1026
|
+
inputs: [],
|
|
1027
|
+
outputs: [{ type: "uint8" }]
|
|
1028
|
+
}
|
|
1029
|
+
];
|
|
1030
|
+
function shortAddress(address) {
|
|
1031
|
+
return `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
1032
|
+
}
|
|
1033
|
+
function finiteOrNull(value) {
|
|
1034
|
+
return Number.isFinite(value) ? value : null;
|
|
1035
|
+
}
|
|
1036
|
+
function toTokenMetaFallback(address) {
|
|
1037
|
+
return {
|
|
1038
|
+
address,
|
|
1039
|
+
symbol: shortAddress(checksumAddress(address)),
|
|
1040
|
+
decimals: 18
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
function chunk(items, size) {
|
|
1044
|
+
const out = [];
|
|
1045
|
+
for (let i = 0; i < items.length; i += size) {
|
|
1046
|
+
out.push(items.slice(i, i + size));
|
|
1047
|
+
}
|
|
1048
|
+
return out;
|
|
1049
|
+
}
|
|
1050
|
+
async function multicallAllowFailure(client, contracts) {
|
|
1051
|
+
const batches = chunk(contracts, MULTICALL_BATCH_SIZE);
|
|
1052
|
+
const out = [];
|
|
1053
|
+
for (const batch of batches) {
|
|
1054
|
+
const res = await client.multicall({
|
|
1055
|
+
allowFailure: true,
|
|
1056
|
+
contracts: batch
|
|
1057
|
+
});
|
|
1058
|
+
out.push(...res);
|
|
1059
|
+
}
|
|
1060
|
+
return out;
|
|
1061
|
+
}
|
|
1062
|
+
async function multicallStrict(client, contracts) {
|
|
1063
|
+
const batches = chunk(contracts, MULTICALL_BATCH_SIZE);
|
|
1064
|
+
const out = [];
|
|
1065
|
+
for (const batch of batches) {
|
|
1066
|
+
const res = await client.multicall({
|
|
1067
|
+
allowFailure: false,
|
|
1068
|
+
contracts: batch
|
|
1069
|
+
});
|
|
1070
|
+
out.push(...res);
|
|
1071
|
+
}
|
|
1072
|
+
return out;
|
|
1073
|
+
}
|
|
1074
|
+
function derivePrices(sqrtPriceX96, token0Decimals, token1Decimals) {
|
|
1075
|
+
const sqrtRatio = Number(sqrtPriceX96) / 2 ** 96;
|
|
1076
|
+
const rawPrice = sqrtRatio * sqrtRatio;
|
|
1077
|
+
const decimalScale = 10 ** (token0Decimals - token1Decimals);
|
|
1078
|
+
const token1PerToken0 = finiteOrNull(rawPrice * decimalScale);
|
|
1079
|
+
const token0PerToken1 = token1PerToken0 === null || token1PerToken0 === 0 ? null : finiteOrNull(1 / token1PerToken0);
|
|
1080
|
+
return {
|
|
1081
|
+
token1PerToken0,
|
|
1082
|
+
token0PerToken1
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
function estimateActiveLiquidity(liquidity, sqrtPriceX96, token0Decimals, token1Decimals, prices) {
|
|
1086
|
+
if (sqrtPriceX96 === 0n) {
|
|
1087
|
+
return {
|
|
1088
|
+
token0: "0",
|
|
1089
|
+
token1: "0",
|
|
1090
|
+
totalInToken0: null,
|
|
1091
|
+
totalInToken1: null
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const reserve0Raw = liquidity * Q96 / sqrtPriceX96;
|
|
1095
|
+
const reserve1Raw = liquidity * sqrtPriceX96 / Q96;
|
|
1096
|
+
const reserve0 = Number(formatUnits(reserve0Raw, token0Decimals));
|
|
1097
|
+
const reserve1 = Number(formatUnits(reserve1Raw, token1Decimals));
|
|
1098
|
+
const totalInToken1 = prices.token1PerToken0 === null ? null : finiteOrNull(reserve1 + reserve0 * prices.token1PerToken0);
|
|
1099
|
+
const totalInToken0 = prices.token0PerToken1 === null ? null : finiteOrNull(reserve0 + reserve1 * prices.token0PerToken1);
|
|
1100
|
+
return {
|
|
1101
|
+
token0: formatUnits(reserve0Raw, token0Decimals),
|
|
1102
|
+
token1: formatUnits(reserve1Raw, token1Decimals),
|
|
1103
|
+
totalInToken0,
|
|
1104
|
+
totalInToken1
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
async function readTokenMetadata(client, tokenAddresses) {
|
|
1108
|
+
const uniqueTokens = [...new Set(tokenAddresses.map((x) => x.toLowerCase()))];
|
|
1109
|
+
if (uniqueTokens.length === 0) {
|
|
1110
|
+
return /* @__PURE__ */ new Map();
|
|
1111
|
+
}
|
|
1112
|
+
const contracts = uniqueTokens.flatMap((address) => [
|
|
1113
|
+
{
|
|
1114
|
+
abi: erc20MetadataAbi,
|
|
1115
|
+
address,
|
|
1116
|
+
functionName: "symbol"
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
abi: erc20MetadataAbi,
|
|
1120
|
+
address,
|
|
1121
|
+
functionName: "decimals"
|
|
1122
|
+
}
|
|
1123
|
+
]);
|
|
1124
|
+
const results = await multicallAllowFailure(client, contracts);
|
|
1125
|
+
const out = /* @__PURE__ */ new Map();
|
|
1126
|
+
for (let i = 0; i < uniqueTokens.length; i += 1) {
|
|
1127
|
+
const address = uniqueTokens[i];
|
|
1128
|
+
const symbolResult = results[i * 2];
|
|
1129
|
+
const decimalsResult = results[i * 2 + 1];
|
|
1130
|
+
const fallback = toTokenMetaFallback(address);
|
|
1131
|
+
const symbol = symbolResult && symbolResult.status === "success" && typeof symbolResult.result === "string" ? symbolResult.result : fallback.symbol;
|
|
1132
|
+
const decimals = decimalsResult && decimalsResult.status === "success" && typeof decimalsResult.result === "number" ? decimalsResult.result : fallback.decimals;
|
|
1133
|
+
out.set(address, {
|
|
1134
|
+
address: checksumAddress(address),
|
|
1135
|
+
symbol,
|
|
1136
|
+
decimals
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
return out;
|
|
1140
|
+
}
|
|
1141
|
+
async function listPoolAddresses(client) {
|
|
1142
|
+
const count = await client.readContract({
|
|
1143
|
+
abi: clFactoryAbi,
|
|
1144
|
+
address: ABOREAN_CL_ADDRESSES.clFactory,
|
|
1145
|
+
functionName: "allPoolsLength"
|
|
1146
|
+
});
|
|
1147
|
+
if (count === 0n) {
|
|
1148
|
+
return [];
|
|
1149
|
+
}
|
|
1150
|
+
const poolIndexContracts = Array.from({ length: Number(count) }, (_, i) => ({
|
|
1151
|
+
abi: clFactoryAbi,
|
|
1152
|
+
address: ABOREAN_CL_ADDRESSES.clFactory,
|
|
1153
|
+
functionName: "allPools",
|
|
1154
|
+
args: [BigInt(i)]
|
|
1155
|
+
}));
|
|
1156
|
+
const poolAddresses = await multicallStrict(client, poolIndexContracts);
|
|
1157
|
+
return poolAddresses;
|
|
1158
|
+
}
|
|
1159
|
+
async function readPoolStates(client, poolAddresses) {
|
|
1160
|
+
if (poolAddresses.length === 0) {
|
|
1161
|
+
return [];
|
|
1162
|
+
}
|
|
1163
|
+
const poolContracts = poolAddresses.flatMap((pool) => [
|
|
1164
|
+
{
|
|
1165
|
+
abi: clPoolAbi,
|
|
1166
|
+
address: pool,
|
|
1167
|
+
functionName: "token0"
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
abi: clPoolAbi,
|
|
1171
|
+
address: pool,
|
|
1172
|
+
functionName: "token1"
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
abi: clPoolAbi,
|
|
1176
|
+
address: pool,
|
|
1177
|
+
functionName: "tickSpacing"
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
abi: clPoolAbi,
|
|
1181
|
+
address: pool,
|
|
1182
|
+
functionName: "fee"
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
abi: clPoolAbi,
|
|
1186
|
+
address: pool,
|
|
1187
|
+
functionName: "liquidity"
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
abi: clPoolAbi,
|
|
1191
|
+
address: pool,
|
|
1192
|
+
functionName: "slot0"
|
|
1193
|
+
}
|
|
1194
|
+
]);
|
|
1195
|
+
const values = await multicallStrict(client, poolContracts);
|
|
1196
|
+
return poolAddresses.map((pool, i) => ({
|
|
1197
|
+
pool,
|
|
1198
|
+
token0: values[i * 6],
|
|
1199
|
+
token1: values[i * 6 + 1],
|
|
1200
|
+
tickSpacing: Number(values[i * 6 + 2]),
|
|
1201
|
+
fee: Number(values[i * 6 + 3]),
|
|
1202
|
+
liquidity: values[i * 6 + 4],
|
|
1203
|
+
slot0: values[i * 6 + 5]
|
|
1204
|
+
}));
|
|
1205
|
+
}
|
|
1206
|
+
function toPoolRow(pool, tokenMeta) {
|
|
1207
|
+
const token0 = tokenMeta.get(pool.token0) ?? toTokenMetaFallback(pool.token0);
|
|
1208
|
+
const token1 = tokenMeta.get(pool.token1) ?? toTokenMetaFallback(pool.token1);
|
|
1209
|
+
const prices = derivePrices(pool.slot0[0], token0.decimals, token1.decimals);
|
|
1210
|
+
const activeLiquidityEstimate = estimateActiveLiquidity(
|
|
1211
|
+
pool.liquidity,
|
|
1212
|
+
pool.slot0[0],
|
|
1213
|
+
token0.decimals,
|
|
1214
|
+
token1.decimals,
|
|
1215
|
+
prices
|
|
1216
|
+
);
|
|
1217
|
+
return {
|
|
1218
|
+
pool: checksumAddress(pool.pool),
|
|
1219
|
+
pair: `${token0.symbol}/${token1.symbol}`,
|
|
1220
|
+
token0,
|
|
1221
|
+
token1,
|
|
1222
|
+
fee: pool.fee,
|
|
1223
|
+
feePercent: pool.fee / 1e4,
|
|
1224
|
+
tickSpacing: pool.tickSpacing,
|
|
1225
|
+
liquidity: pool.liquidity.toString(),
|
|
1226
|
+
currentTick: pool.slot0[1],
|
|
1227
|
+
sqrtPriceX96: pool.slot0[0].toString(),
|
|
1228
|
+
activeLiquidityEstimate,
|
|
1229
|
+
price: prices
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
function normalizeAddress(address) {
|
|
1233
|
+
return address.toLowerCase();
|
|
1234
|
+
}
|
|
1235
|
+
var cl = Cli.create("cl", {
|
|
1236
|
+
description: "Concentrated liquidity (Slipstream) pools, positions, and quotes."
|
|
1237
|
+
});
|
|
1238
|
+
cl.command("pools", {
|
|
1239
|
+
description: "List Slipstream pools with current state, prices, and active liquidity estimate.",
|
|
1240
|
+
env,
|
|
1241
|
+
output: z.object({
|
|
1242
|
+
count: z.number(),
|
|
1243
|
+
pools: z.array(poolRowSchema)
|
|
1244
|
+
}),
|
|
1245
|
+
async run(c) {
|
|
1246
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
1247
|
+
const pools = await listPoolAddresses(client);
|
|
1248
|
+
const poolStates = await readPoolStates(client, pools);
|
|
1249
|
+
const tokenMeta = await readTokenMetadata(
|
|
1250
|
+
client,
|
|
1251
|
+
poolStates.flatMap((pool) => [pool.token0, pool.token1])
|
|
1252
|
+
);
|
|
1253
|
+
const rows = poolStates.map((pool) => toPoolRow(pool, tokenMeta));
|
|
1254
|
+
return c.ok({
|
|
1255
|
+
count: rows.length,
|
|
1256
|
+
pools: rows
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
cl.command("pool", {
|
|
1261
|
+
description: "Get detailed state for a Slipstream pool address.",
|
|
1262
|
+
args: z.object({
|
|
1263
|
+
pool: z.string().describe("Pool address")
|
|
1264
|
+
}),
|
|
1265
|
+
env,
|
|
1266
|
+
output: z.object({
|
|
1267
|
+
pool: poolRowSchema
|
|
1268
|
+
}),
|
|
1269
|
+
async run(c) {
|
|
1270
|
+
if (!isAddress(c.args.pool)) {
|
|
1271
|
+
return c.error({
|
|
1272
|
+
code: "INVALID_ADDRESS",
|
|
1273
|
+
message: `Invalid pool address: "${c.args.pool}". Use a valid 0x-prefixed 20-byte hex address.`
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
1277
|
+
const checksummedPool = checksumAddress(c.args.pool);
|
|
1278
|
+
const [poolState] = await readPoolStates(client, [checksummedPool]);
|
|
1279
|
+
const tokenMeta = await readTokenMetadata(client, [poolState.token0, poolState.token1]);
|
|
1280
|
+
return c.ok({
|
|
1281
|
+
pool: toPoolRow(poolState, tokenMeta)
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
});
|
|
1285
|
+
cl.command("positions", {
|
|
1286
|
+
description: "List concentrated liquidity NFT positions for an owner.",
|
|
1287
|
+
args: z.object({
|
|
1288
|
+
owner: z.string().describe("Owner wallet address")
|
|
1289
|
+
}),
|
|
1290
|
+
env,
|
|
1291
|
+
output: z.object({
|
|
1292
|
+
owner: z.string(),
|
|
1293
|
+
count: z.number(),
|
|
1294
|
+
positions: z.array(
|
|
1295
|
+
z.object({
|
|
1296
|
+
tokenId: z.string(),
|
|
1297
|
+
pair: z.string(),
|
|
1298
|
+
token0: tokenSchema,
|
|
1299
|
+
token1: tokenSchema,
|
|
1300
|
+
tickSpacing: z.number(),
|
|
1301
|
+
tickLower: z.number(),
|
|
1302
|
+
tickUpper: z.number(),
|
|
1303
|
+
liquidity: z.string(),
|
|
1304
|
+
tokensOwed0: z.object({ raw: z.string(), decimal: z.string() }),
|
|
1305
|
+
tokensOwed1: z.object({ raw: z.string(), decimal: z.string() })
|
|
1306
|
+
})
|
|
1307
|
+
)
|
|
1308
|
+
}),
|
|
1309
|
+
async run(c) {
|
|
1310
|
+
if (!isAddress(c.args.owner)) {
|
|
1311
|
+
return c.error({
|
|
1312
|
+
code: "INVALID_ADDRESS",
|
|
1313
|
+
message: `Invalid owner address: "${c.args.owner}". Use a valid 0x-prefixed 20-byte hex address.`
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
1317
|
+
const owner = checksumAddress(c.args.owner);
|
|
1318
|
+
const balance = await client.readContract({
|
|
1319
|
+
abi: nonfungiblePositionManagerAbi,
|
|
1320
|
+
address: ABOREAN_CL_ADDRESSES.nonfungiblePositionManager,
|
|
1321
|
+
functionName: "balanceOf",
|
|
1322
|
+
args: [owner]
|
|
1323
|
+
});
|
|
1324
|
+
if (balance === 0n) {
|
|
1325
|
+
return c.ok({ owner, count: 0, positions: [] });
|
|
1326
|
+
}
|
|
1327
|
+
const tokenIdContracts = Array.from({ length: Number(balance) }, (_, i) => ({
|
|
1328
|
+
abi: nonfungiblePositionManagerAbi,
|
|
1329
|
+
address: ABOREAN_CL_ADDRESSES.nonfungiblePositionManager,
|
|
1330
|
+
functionName: "tokenOfOwnerByIndex",
|
|
1331
|
+
args: [owner, BigInt(i)]
|
|
1332
|
+
}));
|
|
1333
|
+
const tokenIds = await multicallStrict(client, tokenIdContracts);
|
|
1334
|
+
const positionContracts = tokenIds.map((tokenId) => ({
|
|
1335
|
+
abi: nonfungiblePositionManagerAbi,
|
|
1336
|
+
address: ABOREAN_CL_ADDRESSES.nonfungiblePositionManager,
|
|
1337
|
+
functionName: "positions",
|
|
1338
|
+
args: [tokenId]
|
|
1339
|
+
}));
|
|
1340
|
+
const positionsRaw = await multicallStrict(client, positionContracts);
|
|
1341
|
+
const tokenMeta = await readTokenMetadata(
|
|
1342
|
+
client,
|
|
1343
|
+
positionsRaw.flatMap((position) => [position[2], position[3]])
|
|
1344
|
+
);
|
|
1345
|
+
const positions = tokenIds.map((tokenId, i) => {
|
|
1346
|
+
const position = positionsRaw[i];
|
|
1347
|
+
const token0 = tokenMeta.get(position[2]) ?? toTokenMetaFallback(position[2]);
|
|
1348
|
+
const token1 = tokenMeta.get(position[3]) ?? toTokenMetaFallback(position[3]);
|
|
1349
|
+
return {
|
|
1350
|
+
tokenId: tokenId.toString(),
|
|
1351
|
+
pair: `${token0.symbol}/${token1.symbol}`,
|
|
1352
|
+
token0,
|
|
1353
|
+
token1,
|
|
1354
|
+
tickSpacing: position[4],
|
|
1355
|
+
tickLower: position[5],
|
|
1356
|
+
tickUpper: position[6],
|
|
1357
|
+
liquidity: position[7].toString(),
|
|
1358
|
+
tokensOwed0: {
|
|
1359
|
+
raw: position[10].toString(),
|
|
1360
|
+
decimal: formatUnits(position[10], token0.decimals)
|
|
1361
|
+
},
|
|
1362
|
+
tokensOwed1: {
|
|
1363
|
+
raw: position[11].toString(),
|
|
1364
|
+
decimal: formatUnits(position[11], token1.decimals)
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
});
|
|
1368
|
+
return c.ok({
|
|
1369
|
+
owner,
|
|
1370
|
+
count: positions.length,
|
|
1371
|
+
positions
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
cl.command("quote", {
|
|
1376
|
+
description: "Quote a single-hop Slipstream swap via QuoterV2.",
|
|
1377
|
+
args: z.object({
|
|
1378
|
+
tokenIn: z.string().describe("Input token address"),
|
|
1379
|
+
tokenOut: z.string().describe("Output token address"),
|
|
1380
|
+
amountIn: z.string().describe("Input amount in human-readable decimal units")
|
|
1381
|
+
}),
|
|
1382
|
+
options: z.object({
|
|
1383
|
+
fee: z.coerce.number().int().positive().optional().describe("Optional fee tier filter")
|
|
1384
|
+
}),
|
|
1385
|
+
env,
|
|
1386
|
+
output: quoteOutputSchema,
|
|
1387
|
+
async run(c) {
|
|
1388
|
+
const { tokenIn, tokenOut, amountIn } = c.args;
|
|
1389
|
+
if (!isAddress(tokenIn) || !isAddress(tokenOut)) {
|
|
1390
|
+
return c.error({
|
|
1391
|
+
code: "INVALID_ADDRESS",
|
|
1392
|
+
message: "tokenIn and tokenOut must both be valid 0x-prefixed 20-byte addresses."
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
1396
|
+
const inAddress = checksumAddress(tokenIn);
|
|
1397
|
+
const outAddress = checksumAddress(tokenOut);
|
|
1398
|
+
const allPools = await listPoolAddresses(client);
|
|
1399
|
+
const poolStates = await readPoolStates(client, allPools);
|
|
1400
|
+
const pairPools = poolStates.filter((pool) => {
|
|
1401
|
+
const a = normalizeAddress(pool.token0);
|
|
1402
|
+
const b = normalizeAddress(pool.token1);
|
|
1403
|
+
const tokenInNorm = normalizeAddress(inAddress);
|
|
1404
|
+
const tokenOutNorm = normalizeAddress(outAddress);
|
|
1405
|
+
return a === tokenInNorm && b === tokenOutNorm || a === tokenOutNorm && b === tokenInNorm;
|
|
1406
|
+
});
|
|
1407
|
+
const filteredPools = typeof c.options.fee === "number" ? pairPools.filter((pool) => pool.fee === c.options.fee) : pairPools;
|
|
1408
|
+
if (filteredPools.length === 0) {
|
|
1409
|
+
return c.error({
|
|
1410
|
+
code: "POOL_NOT_FOUND",
|
|
1411
|
+
message: typeof c.options.fee === "number" ? `No Slipstream pool found for pair ${inAddress}/${outAddress} at fee tier ${c.options.fee}.` : `No Slipstream pool found for pair ${inAddress}/${outAddress}.`
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
const selectedPool = [...filteredPools].sort((a, b) => {
|
|
1415
|
+
if (a.liquidity === b.liquidity) return 0;
|
|
1416
|
+
return a.liquidity > b.liquidity ? -1 : 1;
|
|
1417
|
+
})[0];
|
|
1418
|
+
const tokenMeta = await readTokenMetadata(client, [inAddress, outAddress]);
|
|
1419
|
+
const inMeta = tokenMeta.get(inAddress) ?? toTokenMetaFallback(inAddress);
|
|
1420
|
+
const outMeta = tokenMeta.get(outAddress) ?? toTokenMetaFallback(outAddress);
|
|
1421
|
+
let amountInRaw;
|
|
1422
|
+
try {
|
|
1423
|
+
amountInRaw = parseUnits(amountIn, inMeta.decimals);
|
|
1424
|
+
} catch {
|
|
1425
|
+
return c.error({
|
|
1426
|
+
code: "INVALID_AMOUNT",
|
|
1427
|
+
message: `Invalid amountIn: "${amountIn}" for token ${inMeta.symbol} (${inMeta.decimals} decimals).`
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
const quote = await client.readContract({
|
|
1431
|
+
abi: quoterV2Abi,
|
|
1432
|
+
address: ABOREAN_CL_ADDRESSES.quoterV2,
|
|
1433
|
+
functionName: "quoteExactInputSingle",
|
|
1434
|
+
args: [
|
|
1435
|
+
{
|
|
1436
|
+
tokenIn: inAddress,
|
|
1437
|
+
tokenOut: outAddress,
|
|
1438
|
+
amountIn: amountInRaw,
|
|
1439
|
+
tickSpacing: selectedPool.tickSpacing,
|
|
1440
|
+
sqrtPriceLimitX96: 0n
|
|
1441
|
+
}
|
|
1442
|
+
]
|
|
1443
|
+
});
|
|
1444
|
+
const amountOutRaw = quote[0];
|
|
1445
|
+
const amountOutDecimal = formatUnits(amountOutRaw, outMeta.decimals);
|
|
1446
|
+
const amountInDecimal = formatUnits(amountInRaw, inMeta.decimals);
|
|
1447
|
+
const quotePriceOutPerIn = Number(amountInDecimal) === 0 ? null : finiteOrNull(Number(amountOutDecimal) / Number(amountInDecimal));
|
|
1448
|
+
const poolTokenMeta = await readTokenMetadata(client, [
|
|
1449
|
+
selectedPool.token0,
|
|
1450
|
+
selectedPool.token1
|
|
1451
|
+
]);
|
|
1452
|
+
const poolToken0Meta = poolTokenMeta.get(selectedPool.token0) ?? toTokenMetaFallback(selectedPool.token0);
|
|
1453
|
+
const poolToken1Meta = poolTokenMeta.get(selectedPool.token1) ?? toTokenMetaFallback(selectedPool.token1);
|
|
1454
|
+
const poolPrices = derivePrices(
|
|
1455
|
+
selectedPool.slot0[0],
|
|
1456
|
+
poolToken0Meta.decimals,
|
|
1457
|
+
poolToken1Meta.decimals
|
|
1458
|
+
);
|
|
1459
|
+
const inIsToken0 = normalizeAddress(inAddress) === normalizeAddress(selectedPool.token0);
|
|
1460
|
+
const poolMidPriceOutPerIn = inIsToken0 ? poolPrices.token1PerToken0 : poolPrices.token0PerToken1;
|
|
1461
|
+
const priceImpactPct = quotePriceOutPerIn === null || poolMidPriceOutPerIn === null || poolMidPriceOutPerIn === 0 ? null : finiteOrNull((poolMidPriceOutPerIn - quotePriceOutPerIn) / poolMidPriceOutPerIn * 100);
|
|
1462
|
+
return c.ok({
|
|
1463
|
+
pool: checksumAddress(selectedPool.pool),
|
|
1464
|
+
selectedFee: selectedPool.fee,
|
|
1465
|
+
selectedTickSpacing: selectedPool.tickSpacing,
|
|
1466
|
+
tokenIn: inMeta,
|
|
1467
|
+
tokenOut: outMeta,
|
|
1468
|
+
amountIn: {
|
|
1469
|
+
raw: amountInRaw.toString(),
|
|
1470
|
+
decimal: amountInDecimal
|
|
1471
|
+
},
|
|
1472
|
+
amountOut: {
|
|
1473
|
+
raw: amountOutRaw.toString(),
|
|
1474
|
+
decimal: amountOutDecimal
|
|
1475
|
+
},
|
|
1476
|
+
execution: {
|
|
1477
|
+
sqrtPriceX96After: quote[1].toString(),
|
|
1478
|
+
initializedTicksCrossed: quote[2],
|
|
1479
|
+
gasEstimate: quote[3].toString()
|
|
1480
|
+
},
|
|
1481
|
+
prices: {
|
|
1482
|
+
poolMidPriceOutPerIn,
|
|
1483
|
+
quotePriceOutPerIn,
|
|
1484
|
+
priceImpactPct
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
// src/commands/gauges.ts
|
|
1491
|
+
import { Cli as Cli2, z as z2 } from "incur";
|
|
1492
|
+
|
|
764
1493
|
// src/commands/_common.ts
|
|
765
|
-
import { checksumAddress, weiToEth } from "@spectratools/cli-shared";
|
|
1494
|
+
import { checksumAddress as checksumAddress2, weiToEth } from "@spectratools/cli-shared";
|
|
766
1495
|
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
767
1496
|
function toChecksum(address) {
|
|
768
1497
|
try {
|
|
769
|
-
return
|
|
1498
|
+
return checksumAddress2(address);
|
|
770
1499
|
} catch {
|
|
771
1500
|
return address;
|
|
772
1501
|
}
|
|
@@ -789,8 +1518,8 @@ function clampPositive(seconds) {
|
|
|
789
1518
|
}
|
|
790
1519
|
|
|
791
1520
|
// src/commands/gauges.ts
|
|
792
|
-
var
|
|
793
|
-
ABSTRACT_RPC_URL:
|
|
1521
|
+
var env2 = z2.object({
|
|
1522
|
+
ABSTRACT_RPC_URL: z2.string().optional().describe("Abstract RPC URL override")
|
|
794
1523
|
});
|
|
795
1524
|
async function discoverGaugePools(client) {
|
|
796
1525
|
const [v2PoolCount, clPoolCount] = await Promise.all([
|
|
@@ -840,26 +1569,26 @@ async function discoverGaugePools(client) {
|
|
|
840
1569
|
});
|
|
841
1570
|
return pools.map((pool, index) => ({ pool, gauge: gauges2[index] })).filter(({ gauge }) => gauge.toLowerCase() !== ZERO_ADDRESS.toLowerCase());
|
|
842
1571
|
}
|
|
843
|
-
var gauges =
|
|
1572
|
+
var gauges = Cli2.create("gauges", {
|
|
844
1573
|
description: "Inspect Aborean gauge emissions, staking, and user positions."
|
|
845
1574
|
});
|
|
846
1575
|
gauges.command("list", {
|
|
847
1576
|
description: "List active gauges with pool, emissions, and staking stats.",
|
|
848
|
-
env,
|
|
849
|
-
output:
|
|
850
|
-
gauges:
|
|
851
|
-
|
|
852
|
-
pool:
|
|
853
|
-
gauge:
|
|
854
|
-
rewardToken:
|
|
855
|
-
rewardRate:
|
|
856
|
-
totalStaked:
|
|
857
|
-
claimableEmissions:
|
|
858
|
-
periodFinish:
|
|
859
|
-
periodFinishRelative:
|
|
1577
|
+
env: env2,
|
|
1578
|
+
output: z2.object({
|
|
1579
|
+
gauges: z2.array(
|
|
1580
|
+
z2.object({
|
|
1581
|
+
pool: z2.string(),
|
|
1582
|
+
gauge: z2.string(),
|
|
1583
|
+
rewardToken: z2.string(),
|
|
1584
|
+
rewardRate: z2.string(),
|
|
1585
|
+
totalStaked: z2.string(),
|
|
1586
|
+
claimableEmissions: z2.string(),
|
|
1587
|
+
periodFinish: z2.number(),
|
|
1588
|
+
periodFinishRelative: z2.string()
|
|
860
1589
|
})
|
|
861
1590
|
),
|
|
862
|
-
count:
|
|
1591
|
+
count: z2.number()
|
|
863
1592
|
}),
|
|
864
1593
|
examples: [{ description: "List all active gauges and current emissions state" }],
|
|
865
1594
|
async run(c) {
|
|
@@ -925,27 +1654,27 @@ gauges.command("list", {
|
|
|
925
1654
|
});
|
|
926
1655
|
gauges.command("info", {
|
|
927
1656
|
description: "Get detailed state for one gauge address.",
|
|
928
|
-
args:
|
|
929
|
-
gauge:
|
|
1657
|
+
args: z2.object({
|
|
1658
|
+
gauge: z2.string().describe("Gauge contract address")
|
|
930
1659
|
}),
|
|
931
|
-
env,
|
|
932
|
-
output:
|
|
933
|
-
gauge:
|
|
934
|
-
pool:
|
|
935
|
-
isAlive:
|
|
936
|
-
stakingToken:
|
|
937
|
-
rewardToken:
|
|
938
|
-
totalStaked:
|
|
939
|
-
rewardRate:
|
|
940
|
-
rewardPerTokenStored:
|
|
941
|
-
fees0:
|
|
942
|
-
fees1:
|
|
943
|
-
left:
|
|
944
|
-
periodFinish:
|
|
945
|
-
periodFinishRelative:
|
|
946
|
-
lastUpdateTime:
|
|
947
|
-
bribeContract:
|
|
948
|
-
feeContract:
|
|
1660
|
+
env: env2,
|
|
1661
|
+
output: z2.object({
|
|
1662
|
+
gauge: z2.string(),
|
|
1663
|
+
pool: z2.string(),
|
|
1664
|
+
isAlive: z2.boolean(),
|
|
1665
|
+
stakingToken: z2.string(),
|
|
1666
|
+
rewardToken: z2.string(),
|
|
1667
|
+
totalStaked: z2.string(),
|
|
1668
|
+
rewardRate: z2.string(),
|
|
1669
|
+
rewardPerTokenStored: z2.string(),
|
|
1670
|
+
fees0: z2.string(),
|
|
1671
|
+
fees1: z2.string(),
|
|
1672
|
+
left: z2.string(),
|
|
1673
|
+
periodFinish: z2.number(),
|
|
1674
|
+
periodFinishRelative: z2.string(),
|
|
1675
|
+
lastUpdateTime: z2.number(),
|
|
1676
|
+
bribeContract: z2.string(),
|
|
1677
|
+
feeContract: z2.string()
|
|
949
1678
|
}),
|
|
950
1679
|
examples: [
|
|
951
1680
|
{
|
|
@@ -1069,22 +1798,22 @@ gauges.command("info", {
|
|
|
1069
1798
|
});
|
|
1070
1799
|
gauges.command("staked", {
|
|
1071
1800
|
description: "Show one address staking positions across all gauges.",
|
|
1072
|
-
args:
|
|
1073
|
-
address:
|
|
1801
|
+
args: z2.object({
|
|
1802
|
+
address: z2.string().describe("Wallet address to inspect")
|
|
1074
1803
|
}),
|
|
1075
|
-
env,
|
|
1076
|
-
output:
|
|
1077
|
-
address:
|
|
1078
|
-
positions:
|
|
1079
|
-
|
|
1080
|
-
pool:
|
|
1081
|
-
gauge:
|
|
1082
|
-
rewardToken:
|
|
1083
|
-
staked:
|
|
1084
|
-
earned:
|
|
1804
|
+
env: env2,
|
|
1805
|
+
output: z2.object({
|
|
1806
|
+
address: z2.string(),
|
|
1807
|
+
positions: z2.array(
|
|
1808
|
+
z2.object({
|
|
1809
|
+
pool: z2.string(),
|
|
1810
|
+
gauge: z2.string(),
|
|
1811
|
+
rewardToken: z2.string(),
|
|
1812
|
+
staked: z2.string(),
|
|
1813
|
+
earned: z2.string()
|
|
1085
1814
|
})
|
|
1086
1815
|
),
|
|
1087
|
-
count:
|
|
1816
|
+
count: z2.number()
|
|
1088
1817
|
}),
|
|
1089
1818
|
examples: [
|
|
1090
1819
|
{
|
|
@@ -1152,26 +1881,26 @@ gauges.command("staked", {
|
|
|
1152
1881
|
});
|
|
1153
1882
|
|
|
1154
1883
|
// src/commands/ve.ts
|
|
1155
|
-
import { Cli as
|
|
1156
|
-
var
|
|
1157
|
-
ABSTRACT_RPC_URL:
|
|
1884
|
+
import { Cli as Cli3, z as z3 } from "incur";
|
|
1885
|
+
var env3 = z3.object({
|
|
1886
|
+
ABSTRACT_RPC_URL: z3.string().optional().describe("Abstract RPC URL override")
|
|
1158
1887
|
});
|
|
1159
|
-
var ve =
|
|
1888
|
+
var ve = Cli3.create("ve", {
|
|
1160
1889
|
description: "Inspect Aborean VotingEscrow (veABX) global and per-NFT lock state."
|
|
1161
1890
|
});
|
|
1162
1891
|
ve.command("stats", {
|
|
1163
1892
|
description: "Get global VotingEscrow supply, locks, and decay checkpoint data.",
|
|
1164
|
-
env:
|
|
1165
|
-
output:
|
|
1166
|
-
token:
|
|
1167
|
-
totalVotingPower:
|
|
1168
|
-
totalLocked:
|
|
1169
|
-
permanentLocked:
|
|
1170
|
-
epoch:
|
|
1171
|
-
decayBias:
|
|
1172
|
-
decaySlope:
|
|
1173
|
-
lastCheckpointTimestamp:
|
|
1174
|
-
lastCheckpointBlock:
|
|
1893
|
+
env: env3,
|
|
1894
|
+
output: z3.object({
|
|
1895
|
+
token: z3.string(),
|
|
1896
|
+
totalVotingPower: z3.string(),
|
|
1897
|
+
totalLocked: z3.string(),
|
|
1898
|
+
permanentLocked: z3.string(),
|
|
1899
|
+
epoch: z3.number(),
|
|
1900
|
+
decayBias: z3.string(),
|
|
1901
|
+
decaySlope: z3.string(),
|
|
1902
|
+
lastCheckpointTimestamp: z3.number(),
|
|
1903
|
+
lastCheckpointBlock: z3.number()
|
|
1175
1904
|
}),
|
|
1176
1905
|
examples: [{ description: "Show global veABX state and decay metrics" }],
|
|
1177
1906
|
async run(c) {
|
|
@@ -1224,17 +1953,17 @@ ve.command("stats", {
|
|
|
1224
1953
|
});
|
|
1225
1954
|
ve.command("lock", {
|
|
1226
1955
|
description: "Get lock details and voting power for one veNFT token id.",
|
|
1227
|
-
args:
|
|
1228
|
-
tokenId:
|
|
1956
|
+
args: z3.object({
|
|
1957
|
+
tokenId: z3.coerce.number().int().nonnegative().describe("veNFT token id")
|
|
1229
1958
|
}),
|
|
1230
|
-
env:
|
|
1231
|
-
output:
|
|
1232
|
-
tokenId:
|
|
1233
|
-
owner:
|
|
1234
|
-
amount:
|
|
1235
|
-
unlockTime:
|
|
1236
|
-
isPermanent:
|
|
1237
|
-
votingPower:
|
|
1959
|
+
env: env3,
|
|
1960
|
+
output: z3.object({
|
|
1961
|
+
tokenId: z3.number(),
|
|
1962
|
+
owner: z3.string(),
|
|
1963
|
+
amount: z3.string(),
|
|
1964
|
+
unlockTime: z3.number(),
|
|
1965
|
+
isPermanent: z3.boolean(),
|
|
1966
|
+
votingPower: z3.string()
|
|
1238
1967
|
}),
|
|
1239
1968
|
examples: [{ args: { tokenId: 1 }, description: "Inspect lock details for veNFT #1" }],
|
|
1240
1969
|
async run(c) {
|
|
@@ -1272,22 +2001,22 @@ ve.command("lock", {
|
|
|
1272
2001
|
});
|
|
1273
2002
|
ve.command("locks", {
|
|
1274
2003
|
description: "List all veNFT locks owned by an address.",
|
|
1275
|
-
args:
|
|
1276
|
-
address:
|
|
2004
|
+
args: z3.object({
|
|
2005
|
+
address: z3.string().describe("Owner address")
|
|
1277
2006
|
}),
|
|
1278
|
-
env:
|
|
1279
|
-
output:
|
|
1280
|
-
address:
|
|
1281
|
-
locks:
|
|
1282
|
-
|
|
1283
|
-
tokenId:
|
|
1284
|
-
amount:
|
|
1285
|
-
unlockTime:
|
|
1286
|
-
isPermanent:
|
|
1287
|
-
votingPower:
|
|
2007
|
+
env: env3,
|
|
2008
|
+
output: z3.object({
|
|
2009
|
+
address: z3.string(),
|
|
2010
|
+
locks: z3.array(
|
|
2011
|
+
z3.object({
|
|
2012
|
+
tokenId: z3.string(),
|
|
2013
|
+
amount: z3.string(),
|
|
2014
|
+
unlockTime: z3.number(),
|
|
2015
|
+
isPermanent: z3.boolean(),
|
|
2016
|
+
votingPower: z3.string()
|
|
1288
2017
|
})
|
|
1289
2018
|
),
|
|
1290
|
-
count:
|
|
2019
|
+
count: z3.number()
|
|
1291
2020
|
}),
|
|
1292
2021
|
examples: [
|
|
1293
2022
|
{
|
|
@@ -1359,13 +2088,13 @@ ve.command("locks", {
|
|
|
1359
2088
|
});
|
|
1360
2089
|
ve.command("voting-power", {
|
|
1361
2090
|
description: "Get current voting power for one veNFT token id.",
|
|
1362
|
-
args:
|
|
1363
|
-
tokenId:
|
|
2091
|
+
args: z3.object({
|
|
2092
|
+
tokenId: z3.coerce.number().int().nonnegative().describe("veNFT token id")
|
|
1364
2093
|
}),
|
|
1365
|
-
env:
|
|
1366
|
-
output:
|
|
1367
|
-
tokenId:
|
|
1368
|
-
votingPower:
|
|
2094
|
+
env: env3,
|
|
2095
|
+
output: z3.object({
|
|
2096
|
+
tokenId: z3.number(),
|
|
2097
|
+
votingPower: z3.string()
|
|
1369
2098
|
}),
|
|
1370
2099
|
examples: [{ args: { tokenId: 1 }, description: "Get current voting power for veNFT #1" }],
|
|
1371
2100
|
async run(c) {
|
|
@@ -1384,9 +2113,9 @@ ve.command("voting-power", {
|
|
|
1384
2113
|
});
|
|
1385
2114
|
|
|
1386
2115
|
// src/commands/voter.ts
|
|
1387
|
-
import { Cli as
|
|
1388
|
-
var
|
|
1389
|
-
ABSTRACT_RPC_URL:
|
|
2116
|
+
import { Cli as Cli4, z as z4 } from "incur";
|
|
2117
|
+
var env4 = z4.object({
|
|
2118
|
+
ABSTRACT_RPC_URL: z4.string().optional().describe("Abstract RPC URL override")
|
|
1390
2119
|
});
|
|
1391
2120
|
async function discoverPools(client) {
|
|
1392
2121
|
const [v2PoolCount, clPoolCount] = await Promise.all([
|
|
@@ -1425,20 +2154,20 @@ async function discoverPools(client) {
|
|
|
1425
2154
|
]);
|
|
1426
2155
|
return [...v2Pools, ...clPools];
|
|
1427
2156
|
}
|
|
1428
|
-
var voter =
|
|
2157
|
+
var voter = Cli4.create("voter", {
|
|
1429
2158
|
description: "Inspect Aborean voter epoch, pool weights, and claimable rewards context."
|
|
1430
2159
|
});
|
|
1431
2160
|
voter.command("epoch", {
|
|
1432
2161
|
description: "Show current emissions epoch timing from Minter.",
|
|
1433
|
-
env:
|
|
1434
|
-
output:
|
|
1435
|
-
activePeriod:
|
|
1436
|
-
epochEnd:
|
|
1437
|
-
secondsRemaining:
|
|
1438
|
-
timeRemaining:
|
|
1439
|
-
weekSeconds:
|
|
1440
|
-
epochCount:
|
|
1441
|
-
weeklyEmission:
|
|
2162
|
+
env: env4,
|
|
2163
|
+
output: z4.object({
|
|
2164
|
+
activePeriod: z4.number(),
|
|
2165
|
+
epochEnd: z4.number(),
|
|
2166
|
+
secondsRemaining: z4.number(),
|
|
2167
|
+
timeRemaining: z4.string(),
|
|
2168
|
+
weekSeconds: z4.number(),
|
|
2169
|
+
epochCount: z4.number(),
|
|
2170
|
+
weeklyEmission: z4.string()
|
|
1442
2171
|
}),
|
|
1443
2172
|
examples: [{ description: "Inspect current voter epoch boundaries" }],
|
|
1444
2173
|
async run(c) {
|
|
@@ -1480,17 +2209,17 @@ voter.command("epoch", {
|
|
|
1480
2209
|
});
|
|
1481
2210
|
voter.command("weights", {
|
|
1482
2211
|
description: "Show current pool voting weight distribution.",
|
|
1483
|
-
env:
|
|
1484
|
-
output:
|
|
1485
|
-
totalWeight:
|
|
1486
|
-
pools:
|
|
1487
|
-
|
|
1488
|
-
pool:
|
|
1489
|
-
gauge:
|
|
1490
|
-
weight:
|
|
2212
|
+
env: env4,
|
|
2213
|
+
output: z4.object({
|
|
2214
|
+
totalWeight: z4.string(),
|
|
2215
|
+
pools: z4.array(
|
|
2216
|
+
z4.object({
|
|
2217
|
+
pool: z4.string(),
|
|
2218
|
+
gauge: z4.string(),
|
|
2219
|
+
weight: z4.string()
|
|
1491
2220
|
})
|
|
1492
2221
|
),
|
|
1493
|
-
count:
|
|
2222
|
+
count: z4.number()
|
|
1494
2223
|
}),
|
|
1495
2224
|
examples: [{ description: "List all pools with non-zero voting weight" }],
|
|
1496
2225
|
async run(c) {
|
|
@@ -1548,19 +2277,19 @@ voter.command("weights", {
|
|
|
1548
2277
|
});
|
|
1549
2278
|
voter.command("rewards", {
|
|
1550
2279
|
description: "Show claimable rebase rewards and voting context for a veNFT.",
|
|
1551
|
-
args:
|
|
1552
|
-
tokenId:
|
|
2280
|
+
args: z4.object({
|
|
2281
|
+
tokenId: z4.coerce.number().int().nonnegative().describe("veNFT token id")
|
|
1553
2282
|
}),
|
|
1554
|
-
env:
|
|
1555
|
-
output:
|
|
1556
|
-
tokenId:
|
|
1557
|
-
rewardToken:
|
|
1558
|
-
claimableRebase:
|
|
1559
|
-
timeCursor:
|
|
1560
|
-
lastTokenTime:
|
|
1561
|
-
distributorStartTime:
|
|
1562
|
-
usedWeight:
|
|
1563
|
-
lastVoted:
|
|
2283
|
+
env: env4,
|
|
2284
|
+
output: z4.object({
|
|
2285
|
+
tokenId: z4.number(),
|
|
2286
|
+
rewardToken: z4.string(),
|
|
2287
|
+
claimableRebase: z4.string(),
|
|
2288
|
+
timeCursor: z4.number(),
|
|
2289
|
+
lastTokenTime: z4.number(),
|
|
2290
|
+
distributorStartTime: z4.number(),
|
|
2291
|
+
usedWeight: z4.string(),
|
|
2292
|
+
lastVoted: z4.number()
|
|
1564
2293
|
}),
|
|
1565
2294
|
examples: [{ args: { tokenId: 1 }, description: "Check claimable voter/distributor rewards" }],
|
|
1566
2295
|
async run(c) {
|
|
@@ -1629,22 +2358,22 @@ voter.command("rewards", {
|
|
|
1629
2358
|
});
|
|
1630
2359
|
voter.command("bribes", {
|
|
1631
2360
|
description: "Show active bribe reward tokens and current-epoch amounts for a pool.",
|
|
1632
|
-
args:
|
|
1633
|
-
pool:
|
|
2361
|
+
args: z4.object({
|
|
2362
|
+
pool: z4.string().describe("Pool address")
|
|
1634
2363
|
}),
|
|
1635
|
-
env:
|
|
1636
|
-
output:
|
|
1637
|
-
pool:
|
|
1638
|
-
gauge:
|
|
1639
|
-
bribeContract:
|
|
1640
|
-
epochStart:
|
|
1641
|
-
rewardTokens:
|
|
1642
|
-
|
|
1643
|
-
token:
|
|
1644
|
-
epochAmount:
|
|
2364
|
+
env: env4,
|
|
2365
|
+
output: z4.object({
|
|
2366
|
+
pool: z4.string(),
|
|
2367
|
+
gauge: z4.string(),
|
|
2368
|
+
bribeContract: z4.string(),
|
|
2369
|
+
epochStart: z4.number(),
|
|
2370
|
+
rewardTokens: z4.array(
|
|
2371
|
+
z4.object({
|
|
2372
|
+
token: z4.string(),
|
|
2373
|
+
epochAmount: z4.string()
|
|
1645
2374
|
})
|
|
1646
2375
|
),
|
|
1647
|
-
count:
|
|
2376
|
+
count: z4.number()
|
|
1648
2377
|
}),
|
|
1649
2378
|
examples: [
|
|
1650
2379
|
{
|
|
@@ -1870,26 +2599,27 @@ function applyFriendlyErrorHandling(cli2) {
|
|
|
1870
2599
|
// src/cli.ts
|
|
1871
2600
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1872
2601
|
var pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf8"));
|
|
1873
|
-
var cli =
|
|
2602
|
+
var cli = Cli5.create("aborean", {
|
|
1874
2603
|
version: pkg.version,
|
|
1875
2604
|
description: "Aborean Finance DEX CLI for Abstract chain."
|
|
1876
2605
|
});
|
|
1877
2606
|
cli.command(gauges);
|
|
1878
2607
|
cli.command(ve);
|
|
1879
2608
|
cli.command(voter);
|
|
1880
|
-
var rootEnv =
|
|
1881
|
-
ABSTRACT_RPC_URL:
|
|
2609
|
+
var rootEnv = z5.object({
|
|
2610
|
+
ABSTRACT_RPC_URL: z5.string().optional().describe("Abstract RPC URL override")
|
|
1882
2611
|
});
|
|
2612
|
+
cli.command(cl);
|
|
1883
2613
|
cli.command("status", {
|
|
1884
2614
|
description: "Get a cross-contract Aborean protocol snapshot (pool counts, gauge count, veABX supply).",
|
|
1885
2615
|
env: rootEnv,
|
|
1886
|
-
output:
|
|
1887
|
-
v2PoolCount:
|
|
1888
|
-
clPoolCount:
|
|
1889
|
-
gaugeCount:
|
|
1890
|
-
totalVotingWeight:
|
|
1891
|
-
veABXTotalSupply:
|
|
1892
|
-
veABXLockedSupply:
|
|
2616
|
+
output: z5.object({
|
|
2617
|
+
v2PoolCount: z5.number().describe("Number of V2 AMM pools"),
|
|
2618
|
+
clPoolCount: z5.number().describe("Number of Slipstream (CL) pools"),
|
|
2619
|
+
gaugeCount: z5.number().describe("Number of pools with gauges"),
|
|
2620
|
+
totalVotingWeight: z5.string().describe("Total voting weight (wei)"),
|
|
2621
|
+
veABXTotalSupply: z5.string().describe("Total veABX supply (wei)"),
|
|
2622
|
+
veABXLockedSupply: z5.string().describe("Total ABX locked in VotingEscrow (wei)")
|
|
1893
2623
|
}),
|
|
1894
2624
|
examples: [{ description: "Fetch the current Aborean protocol status" }],
|
|
1895
2625
|
async run(c) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spectratools/aborean-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI for Aborean Finance DEX on Abstract (pools, swaps, gauges, voting escrow).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"aborean-cli": "./dist/cli.js"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"incur": "^0.
|
|
33
|
+
"incur": "^0.3.0",
|
|
34
34
|
"ox": "^0.14.0",
|
|
35
35
|
"viem": "^2.47.0",
|
|
36
36
|
"@spectratools/cli-shared": "0.1.1"
|