@spectratools/aborean-cli 0.4.0 → 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/dist/cli.js +2571 -255
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
import { readFileSync } from "fs";
|
|
5
5
|
import { dirname, resolve } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
-
import {
|
|
7
|
+
import { checksumAddress as checksumAddress6 } from "@spectratools/cli-shared";
|
|
8
|
+
import { Cli as Cli8, z as z8 } from "incur";
|
|
9
|
+
import { formatUnits as formatUnits4 } from "viem";
|
|
8
10
|
|
|
9
11
|
// src/commands/cl.ts
|
|
10
12
|
import { checksumAddress, isAddress } from "@spectratools/cli-shared";
|
|
@@ -558,6 +560,87 @@ var RewardsDistributor_abi_default = [
|
|
|
558
560
|
}
|
|
559
561
|
];
|
|
560
562
|
|
|
563
|
+
// src/contracts/abis/V2Pool.abi.json
|
|
564
|
+
var V2Pool_abi_default = [
|
|
565
|
+
{
|
|
566
|
+
type: "function",
|
|
567
|
+
name: "token0",
|
|
568
|
+
inputs: [],
|
|
569
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
570
|
+
stateMutability: "view"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
type: "function",
|
|
574
|
+
name: "token1",
|
|
575
|
+
inputs: [],
|
|
576
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
577
|
+
stateMutability: "view"
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
type: "function",
|
|
581
|
+
name: "stable",
|
|
582
|
+
inputs: [],
|
|
583
|
+
outputs: [{ name: "", type: "bool", internalType: "bool" }],
|
|
584
|
+
stateMutability: "view"
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
type: "function",
|
|
588
|
+
name: "getReserves",
|
|
589
|
+
inputs: [],
|
|
590
|
+
outputs: [
|
|
591
|
+
{ name: "_reserve0", type: "uint256", internalType: "uint256" },
|
|
592
|
+
{ name: "_reserve1", type: "uint256", internalType: "uint256" },
|
|
593
|
+
{ name: "_blockTimestampLast", type: "uint256", internalType: "uint256" }
|
|
594
|
+
],
|
|
595
|
+
stateMutability: "view"
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
type: "function",
|
|
599
|
+
name: "totalSupply",
|
|
600
|
+
inputs: [],
|
|
601
|
+
outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
|
|
602
|
+
stateMutability: "view"
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
type: "function",
|
|
606
|
+
name: "poolFees",
|
|
607
|
+
inputs: [],
|
|
608
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
609
|
+
stateMutability: "view"
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
type: "function",
|
|
613
|
+
name: "factory",
|
|
614
|
+
inputs: [],
|
|
615
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
616
|
+
stateMutability: "view"
|
|
617
|
+
}
|
|
618
|
+
];
|
|
619
|
+
|
|
620
|
+
// src/contracts/abis/V2Router.abi.json
|
|
621
|
+
var V2Router_abi_default = [
|
|
622
|
+
{
|
|
623
|
+
type: "function",
|
|
624
|
+
name: "getAmountsOut",
|
|
625
|
+
inputs: [
|
|
626
|
+
{ name: "amountIn", type: "uint256", internalType: "uint256" },
|
|
627
|
+
{
|
|
628
|
+
name: "routes",
|
|
629
|
+
type: "tuple[]",
|
|
630
|
+
internalType: "struct IRouter.Route[]",
|
|
631
|
+
components: [
|
|
632
|
+
{ name: "from", type: "address", internalType: "address" },
|
|
633
|
+
{ name: "to", type: "address", internalType: "address" },
|
|
634
|
+
{ name: "stable", type: "bool", internalType: "bool" },
|
|
635
|
+
{ name: "factory", type: "address", internalType: "address" }
|
|
636
|
+
]
|
|
637
|
+
}
|
|
638
|
+
],
|
|
639
|
+
outputs: [{ name: "amounts", type: "uint256[]", internalType: "uint256[]" }],
|
|
640
|
+
stateMutability: "view"
|
|
641
|
+
}
|
|
642
|
+
];
|
|
643
|
+
|
|
561
644
|
// src/contracts/abis/Voter.abi.json
|
|
562
645
|
var Voter_abi_default = [
|
|
563
646
|
{
|
|
@@ -864,6 +947,8 @@ var nonfungiblePositionManagerAbi = NonfungiblePositionManager_abi_default;
|
|
|
864
947
|
var poolFactoryAbi = PoolFactory_abi_default;
|
|
865
948
|
var quoterV2Abi = QuoterV2_abi_default;
|
|
866
949
|
var rewardsDistributorAbi = RewardsDistributor_abi_default;
|
|
950
|
+
var v2PoolAbi = V2Pool_abi_default;
|
|
951
|
+
var v2RouterAbi = V2Router_abi_default;
|
|
867
952
|
var voterAbi = Voter_abi_default;
|
|
868
953
|
var votingEscrowAbi = VotingEscrow_abi_default;
|
|
869
954
|
var votingRewardAbi = VotingReward_abi_default;
|
|
@@ -925,9 +1010,25 @@ var ABOREAN_CL_ADDRESSES = {
|
|
|
925
1010
|
/** CL swap router */
|
|
926
1011
|
swapRouter: "0xAda5d0E79681038A9547fe6a59f1413F3E720839"
|
|
927
1012
|
};
|
|
1013
|
+
var ABOREAN_VAULT_ADDRESSES = {
|
|
1014
|
+
/** Factory for AutoCompounder relay vaults */
|
|
1015
|
+
autoCompounderFactory: "0x35b320599C1434291a0003E40Fcd1e40fA9E0222",
|
|
1016
|
+
/** Factory for AutoConverter relay vaults */
|
|
1017
|
+
autoConverterFactory: "0xf114D2aCF8aAFDde833e0a8ba2dc5A3946614354",
|
|
1018
|
+
/** veABX Maxi relay vault */
|
|
1019
|
+
veAbxMaxiRelay: "0xcbeB1A72A31670AE5ba27798c124Fcf3Ca1971df",
|
|
1020
|
+
/** ABX rewards relay vault */
|
|
1021
|
+
abxRewardsRelay: "0x3E8D887Bba5D4A757FaE757883CA35882AB4a0ee"
|
|
1022
|
+
};
|
|
1023
|
+
var ABOREAN_LENDING_ADDRESSES = {
|
|
1024
|
+
/** Morpho Blue core contract on Abstract */
|
|
1025
|
+
morphoBlue: "0xc85CE8ffdA27b646D269516B8d0Fa6ec2E958B55"
|
|
1026
|
+
};
|
|
928
1027
|
var ABOREAN_ADDRESSES = {
|
|
929
1028
|
...ABOREAN_V2_ADDRESSES,
|
|
930
|
-
...ABOREAN_CL_ADDRESSES
|
|
1029
|
+
...ABOREAN_CL_ADDRESSES,
|
|
1030
|
+
...ABOREAN_VAULT_ADDRESSES,
|
|
1031
|
+
...ABOREAN_LENDING_ADDRESSES
|
|
931
1032
|
};
|
|
932
1033
|
|
|
933
1034
|
// src/contracts/client.ts
|
|
@@ -1244,17 +1345,39 @@ cl.command("pools", {
|
|
|
1244
1345
|
}),
|
|
1245
1346
|
async run(c) {
|
|
1246
1347
|
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
1247
|
-
const
|
|
1248
|
-
const poolStates = await readPoolStates(client,
|
|
1348
|
+
const pools2 = await listPoolAddresses(client);
|
|
1349
|
+
const poolStates = await readPoolStates(client, pools2);
|
|
1249
1350
|
const tokenMeta = await readTokenMetadata(
|
|
1250
1351
|
client,
|
|
1251
1352
|
poolStates.flatMap((pool) => [pool.token0, pool.token1])
|
|
1252
1353
|
);
|
|
1253
1354
|
const rows = poolStates.map((pool) => toPoolRow(pool, tokenMeta));
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1355
|
+
const firstPool = rows[0];
|
|
1356
|
+
return c.ok(
|
|
1357
|
+
{
|
|
1358
|
+
count: rows.length,
|
|
1359
|
+
pools: rows
|
|
1360
|
+
},
|
|
1361
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
1362
|
+
cta: {
|
|
1363
|
+
description: "Explore CL pools:",
|
|
1364
|
+
commands: [
|
|
1365
|
+
...firstPool ? [
|
|
1366
|
+
{
|
|
1367
|
+
command: "cl pool",
|
|
1368
|
+
args: { pool: firstPool.pool },
|
|
1369
|
+
description: `Inspect ${firstPool.pair}`
|
|
1370
|
+
}
|
|
1371
|
+
] : [],
|
|
1372
|
+
{
|
|
1373
|
+
command: "cl positions",
|
|
1374
|
+
args: { owner: "<address>" },
|
|
1375
|
+
description: "List CL positions for an address"
|
|
1376
|
+
}
|
|
1377
|
+
]
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1258
1381
|
}
|
|
1259
1382
|
});
|
|
1260
1383
|
cl.command("pool", {
|
|
@@ -1277,9 +1400,33 @@ cl.command("pool", {
|
|
|
1277
1400
|
const checksummedPool = checksumAddress(c.args.pool);
|
|
1278
1401
|
const [poolState] = await readPoolStates(client, [checksummedPool]);
|
|
1279
1402
|
const tokenMeta = await readTokenMetadata(client, [poolState.token0, poolState.token1]);
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1403
|
+
const row = toPoolRow(poolState, tokenMeta);
|
|
1404
|
+
return c.ok(
|
|
1405
|
+
{
|
|
1406
|
+
pool: row
|
|
1407
|
+
},
|
|
1408
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
1409
|
+
cta: {
|
|
1410
|
+
description: "Next steps:",
|
|
1411
|
+
commands: [
|
|
1412
|
+
{
|
|
1413
|
+
command: "cl quote",
|
|
1414
|
+
args: {
|
|
1415
|
+
tokenIn: row.token0.address,
|
|
1416
|
+
tokenOut: row.token1.address,
|
|
1417
|
+
amountIn: "1"
|
|
1418
|
+
},
|
|
1419
|
+
description: `Quote a ${row.token0.symbol} \u2192 ${row.token1.symbol} swap`
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
command: "cl positions",
|
|
1423
|
+
args: { owner: "<address>" },
|
|
1424
|
+
description: "List positions in this pool"
|
|
1425
|
+
}
|
|
1426
|
+
]
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
);
|
|
1283
1430
|
}
|
|
1284
1431
|
});
|
|
1285
1432
|
cl.command("positions", {
|
|
@@ -1365,11 +1512,35 @@ cl.command("positions", {
|
|
|
1365
1512
|
}
|
|
1366
1513
|
};
|
|
1367
1514
|
});
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1515
|
+
const firstPosition = positions[0];
|
|
1516
|
+
return c.ok(
|
|
1517
|
+
{
|
|
1518
|
+
owner,
|
|
1519
|
+
count: positions.length,
|
|
1520
|
+
positions
|
|
1521
|
+
},
|
|
1522
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
1523
|
+
cta: {
|
|
1524
|
+
description: "Related commands:",
|
|
1525
|
+
commands: firstPosition ? [
|
|
1526
|
+
{
|
|
1527
|
+
command: "cl pool",
|
|
1528
|
+
args: { pool: "<poolAddress>" },
|
|
1529
|
+
description: `Inspect pool for ${firstPosition.pair}`
|
|
1530
|
+
},
|
|
1531
|
+
{
|
|
1532
|
+
command: "cl quote",
|
|
1533
|
+
args: {
|
|
1534
|
+
tokenIn: firstPosition.token0.address,
|
|
1535
|
+
tokenOut: firstPosition.token1.address,
|
|
1536
|
+
amountIn: "1"
|
|
1537
|
+
},
|
|
1538
|
+
description: `Quote a ${firstPosition.token0.symbol} \u2192 ${firstPosition.token1.symbol} swap`
|
|
1539
|
+
}
|
|
1540
|
+
] : []
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
);
|
|
1373
1544
|
}
|
|
1374
1545
|
});
|
|
1375
1546
|
cl.command("quote", {
|
|
@@ -1459,31 +1630,54 @@ cl.command("quote", {
|
|
|
1459
1630
|
const inIsToken0 = normalizeAddress(inAddress) === normalizeAddress(selectedPool.token0);
|
|
1460
1631
|
const poolMidPriceOutPerIn = inIsToken0 ? poolPrices.token1PerToken0 : poolPrices.token0PerToken1;
|
|
1461
1632
|
const priceImpactPct = quotePriceOutPerIn === null || poolMidPriceOutPerIn === null || poolMidPriceOutPerIn === 0 ? null : finiteOrNull((poolMidPriceOutPerIn - quotePriceOutPerIn) / poolMidPriceOutPerIn * 100);
|
|
1462
|
-
return c.ok(
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1633
|
+
return c.ok(
|
|
1634
|
+
{
|
|
1635
|
+
pool: checksumAddress(selectedPool.pool),
|
|
1636
|
+
selectedFee: selectedPool.fee,
|
|
1637
|
+
selectedTickSpacing: selectedPool.tickSpacing,
|
|
1638
|
+
tokenIn: inMeta,
|
|
1639
|
+
tokenOut: outMeta,
|
|
1640
|
+
amountIn: {
|
|
1641
|
+
raw: amountInRaw.toString(),
|
|
1642
|
+
decimal: amountInDecimal
|
|
1643
|
+
},
|
|
1644
|
+
amountOut: {
|
|
1645
|
+
raw: amountOutRaw.toString(),
|
|
1646
|
+
decimal: amountOutDecimal
|
|
1647
|
+
},
|
|
1648
|
+
execution: {
|
|
1649
|
+
sqrtPriceX96After: quote[1].toString(),
|
|
1650
|
+
initializedTicksCrossed: quote[2],
|
|
1651
|
+
gasEstimate: quote[3].toString()
|
|
1652
|
+
},
|
|
1653
|
+
prices: {
|
|
1654
|
+
poolMidPriceOutPerIn,
|
|
1655
|
+
quotePriceOutPerIn,
|
|
1656
|
+
priceImpactPct
|
|
1657
|
+
}
|
|
1480
1658
|
},
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1659
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
1660
|
+
cta: {
|
|
1661
|
+
description: "Related commands:",
|
|
1662
|
+
commands: [
|
|
1663
|
+
{
|
|
1664
|
+
command: "cl pool",
|
|
1665
|
+
args: { pool: checksumAddress(selectedPool.pool) },
|
|
1666
|
+
description: "Inspect the pool used for this quote"
|
|
1667
|
+
},
|
|
1668
|
+
{
|
|
1669
|
+
command: "cl quote",
|
|
1670
|
+
args: {
|
|
1671
|
+
tokenIn: outMeta.address,
|
|
1672
|
+
tokenOut: inMeta.address,
|
|
1673
|
+
amountIn: amountOutDecimal
|
|
1674
|
+
},
|
|
1675
|
+
description: `Reverse quote ${outMeta.symbol} \u2192 ${inMeta.symbol}`
|
|
1676
|
+
}
|
|
1677
|
+
]
|
|
1678
|
+
}
|
|
1485
1679
|
}
|
|
1486
|
-
|
|
1680
|
+
);
|
|
1487
1681
|
}
|
|
1488
1682
|
});
|
|
1489
1683
|
|
|
@@ -1556,18 +1750,18 @@ async function discoverGaugePools(client) {
|
|
|
1556
1750
|
}))
|
|
1557
1751
|
}) : Promise.resolve([])
|
|
1558
1752
|
]);
|
|
1559
|
-
const
|
|
1560
|
-
if (!
|
|
1753
|
+
const pools2 = [...v2Pools, ...clPools];
|
|
1754
|
+
if (!pools2.length) return [];
|
|
1561
1755
|
const gauges2 = await client.multicall({
|
|
1562
1756
|
allowFailure: false,
|
|
1563
|
-
contracts:
|
|
1757
|
+
contracts: pools2.map((pool) => ({
|
|
1564
1758
|
abi: voterAbi,
|
|
1565
1759
|
address: ABOREAN_V2_ADDRESSES.voter,
|
|
1566
1760
|
functionName: "gauges",
|
|
1567
1761
|
args: [pool]
|
|
1568
1762
|
}))
|
|
1569
1763
|
});
|
|
1570
|
-
return
|
|
1764
|
+
return pools2.map((pool, index) => ({ pool, gauge: gauges2[index] })).filter(({ gauge }) => gauge.toLowerCase() !== ZERO_ADDRESS.toLowerCase());
|
|
1571
1765
|
}
|
|
1572
1766
|
var gauges = Cli2.create("gauges", {
|
|
1573
1767
|
description: "Inspect Aborean gauge emissions, staking, and user positions."
|
|
@@ -1646,10 +1840,25 @@ gauges.command("list", {
|
|
|
1646
1840
|
periodFinishRelative: relTime(periodFinish)
|
|
1647
1841
|
};
|
|
1648
1842
|
});
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1843
|
+
const firstGauge = items[0];
|
|
1844
|
+
return c.ok(
|
|
1845
|
+
{
|
|
1846
|
+
gauges: items,
|
|
1847
|
+
count: items.length
|
|
1848
|
+
},
|
|
1849
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
1850
|
+
cta: {
|
|
1851
|
+
description: "Explore gauges:",
|
|
1852
|
+
commands: firstGauge ? [
|
|
1853
|
+
{
|
|
1854
|
+
command: "gauges info",
|
|
1855
|
+
args: { gauge: firstGauge.gauge },
|
|
1856
|
+
description: "Inspect top gauge details"
|
|
1857
|
+
}
|
|
1858
|
+
] : []
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
);
|
|
1653
1862
|
}
|
|
1654
1863
|
});
|
|
1655
1864
|
gauges.command("info", {
|
|
@@ -1776,24 +1985,46 @@ gauges.command("info", {
|
|
|
1776
1985
|
functionName: "left"
|
|
1777
1986
|
})
|
|
1778
1987
|
]);
|
|
1779
|
-
return c.ok(
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1988
|
+
return c.ok(
|
|
1989
|
+
{
|
|
1990
|
+
gauge: toChecksum(gauge),
|
|
1991
|
+
pool: toChecksum(pool),
|
|
1992
|
+
isAlive,
|
|
1993
|
+
stakingToken: toChecksum(stakingToken),
|
|
1994
|
+
rewardToken: toChecksum(rewardToken),
|
|
1995
|
+
totalStaked: totalStaked.toString(),
|
|
1996
|
+
rewardRate: rewardRate.toString(),
|
|
1997
|
+
rewardPerTokenStored: rewardPerTokenStored.toString(),
|
|
1998
|
+
fees0: fees0.toString(),
|
|
1999
|
+
fees1: fees1.toString(),
|
|
2000
|
+
left: left.toString(),
|
|
2001
|
+
periodFinish: asNum(periodFinish),
|
|
2002
|
+
periodFinishRelative: relTime(periodFinish),
|
|
2003
|
+
lastUpdateTime: asNum(lastUpdateTime),
|
|
2004
|
+
bribeContract: toChecksum(bribeContract),
|
|
2005
|
+
feeContract: toChecksum(feeContract)
|
|
2006
|
+
},
|
|
2007
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
2008
|
+
cta: {
|
|
2009
|
+
description: "Related commands:",
|
|
2010
|
+
commands: [
|
|
2011
|
+
{
|
|
2012
|
+
command: "ve stats",
|
|
2013
|
+
description: "View veABX global stats"
|
|
2014
|
+
},
|
|
2015
|
+
{
|
|
2016
|
+
command: "voter weights",
|
|
2017
|
+
description: "Check pool voting weight distribution"
|
|
2018
|
+
},
|
|
2019
|
+
{
|
|
2020
|
+
command: "voter bribes",
|
|
2021
|
+
args: { pool: toChecksum(pool) },
|
|
2022
|
+
description: "View bribe rewards for this pool"
|
|
2023
|
+
}
|
|
2024
|
+
]
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
);
|
|
1797
2028
|
}
|
|
1798
2029
|
});
|
|
1799
2030
|
gauges.command("staked", {
|
|
@@ -1872,47 +2103,1624 @@ gauges.command("staked", {
|
|
|
1872
2103
|
staked: position.staked.toString(),
|
|
1873
2104
|
earned: position.earned.toString()
|
|
1874
2105
|
}));
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
2106
|
+
const firstPosition = positions[0];
|
|
2107
|
+
return c.ok(
|
|
2108
|
+
{
|
|
2109
|
+
address: toChecksum(c.args.address),
|
|
2110
|
+
positions,
|
|
2111
|
+
count: positions.length
|
|
2112
|
+
},
|
|
2113
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
2114
|
+
cta: {
|
|
2115
|
+
description: "Related commands:",
|
|
2116
|
+
commands: [
|
|
2117
|
+
...firstPosition ? [
|
|
2118
|
+
{
|
|
2119
|
+
command: "gauges info",
|
|
2120
|
+
args: { gauge: firstPosition.gauge },
|
|
2121
|
+
description: "Inspect gauge details"
|
|
2122
|
+
}
|
|
2123
|
+
] : [],
|
|
2124
|
+
{
|
|
2125
|
+
command: "ve locks",
|
|
2126
|
+
args: { address: toChecksum(c.args.address) },
|
|
2127
|
+
description: "View veNFT locks for this address"
|
|
2128
|
+
}
|
|
2129
|
+
]
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
);
|
|
1880
2133
|
}
|
|
1881
2134
|
});
|
|
1882
2135
|
|
|
1883
|
-
// src/commands/
|
|
2136
|
+
// src/commands/lending.ts
|
|
2137
|
+
import { checksumAddress as checksumAddress3, isAddress as isAddress2 } from "@spectratools/cli-shared";
|
|
1884
2138
|
import { Cli as Cli3, z as z3 } from "incur";
|
|
2139
|
+
import { formatUnits as formatUnits2 } from "viem";
|
|
2140
|
+
var MORPHO_DEPLOY_BLOCK = 13947713n;
|
|
1885
2141
|
var env3 = z3.object({
|
|
1886
2142
|
ABSTRACT_RPC_URL: z3.string().optional().describe("Abstract RPC URL override")
|
|
1887
2143
|
});
|
|
1888
|
-
var
|
|
1889
|
-
|
|
2144
|
+
var morphoAbi = [
|
|
2145
|
+
{
|
|
2146
|
+
type: "event",
|
|
2147
|
+
name: "CreateMarket",
|
|
2148
|
+
inputs: [
|
|
2149
|
+
{ type: "bytes32", name: "id", indexed: true },
|
|
2150
|
+
{
|
|
2151
|
+
type: "tuple",
|
|
2152
|
+
name: "marketParams",
|
|
2153
|
+
indexed: false,
|
|
2154
|
+
components: [
|
|
2155
|
+
{ type: "address", name: "loanToken" },
|
|
2156
|
+
{ type: "address", name: "collateralToken" },
|
|
2157
|
+
{ type: "address", name: "oracle" },
|
|
2158
|
+
{ type: "address", name: "irm" },
|
|
2159
|
+
{ type: "uint256", name: "lltv" }
|
|
2160
|
+
]
|
|
2161
|
+
}
|
|
2162
|
+
],
|
|
2163
|
+
anonymous: false
|
|
2164
|
+
},
|
|
2165
|
+
{
|
|
2166
|
+
type: "function",
|
|
2167
|
+
name: "idToMarketParams",
|
|
2168
|
+
stateMutability: "view",
|
|
2169
|
+
inputs: [{ type: "bytes32", name: "id" }],
|
|
2170
|
+
outputs: [
|
|
2171
|
+
{ type: "address", name: "loanToken" },
|
|
2172
|
+
{ type: "address", name: "collateralToken" },
|
|
2173
|
+
{ type: "address", name: "oracle" },
|
|
2174
|
+
{ type: "address", name: "irm" },
|
|
2175
|
+
{ type: "uint256", name: "lltv" }
|
|
2176
|
+
]
|
|
2177
|
+
},
|
|
2178
|
+
{
|
|
2179
|
+
type: "function",
|
|
2180
|
+
name: "market",
|
|
2181
|
+
stateMutability: "view",
|
|
2182
|
+
inputs: [{ type: "bytes32", name: "id" }],
|
|
2183
|
+
outputs: [
|
|
2184
|
+
{ type: "uint128", name: "totalSupplyAssets" },
|
|
2185
|
+
{ type: "uint128", name: "totalSupplyShares" },
|
|
2186
|
+
{ type: "uint128", name: "totalBorrowAssets" },
|
|
2187
|
+
{ type: "uint128", name: "totalBorrowShares" },
|
|
2188
|
+
{ type: "uint128", name: "lastUpdate" },
|
|
2189
|
+
{ type: "uint128", name: "fee" }
|
|
2190
|
+
]
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
type: "function",
|
|
2194
|
+
name: "position",
|
|
2195
|
+
stateMutability: "view",
|
|
2196
|
+
inputs: [
|
|
2197
|
+
{ type: "bytes32", name: "id" },
|
|
2198
|
+
{ type: "address", name: "user" }
|
|
2199
|
+
],
|
|
2200
|
+
outputs: [
|
|
2201
|
+
{ type: "uint256", name: "supplyShares" },
|
|
2202
|
+
{ type: "uint128", name: "borrowShares" },
|
|
2203
|
+
{ type: "uint128", name: "collateral" }
|
|
2204
|
+
]
|
|
2205
|
+
}
|
|
2206
|
+
];
|
|
2207
|
+
var erc20MetadataAbi2 = [
|
|
2208
|
+
{
|
|
2209
|
+
type: "function",
|
|
2210
|
+
name: "symbol",
|
|
2211
|
+
stateMutability: "view",
|
|
2212
|
+
inputs: [],
|
|
2213
|
+
outputs: [{ type: "string" }]
|
|
2214
|
+
},
|
|
2215
|
+
{
|
|
2216
|
+
type: "function",
|
|
2217
|
+
name: "decimals",
|
|
2218
|
+
stateMutability: "view",
|
|
2219
|
+
inputs: [],
|
|
2220
|
+
outputs: [{ type: "uint8" }]
|
|
2221
|
+
}
|
|
2222
|
+
];
|
|
2223
|
+
var tokenMetaSchema = z3.object({
|
|
2224
|
+
address: z3.string(),
|
|
2225
|
+
symbol: z3.string(),
|
|
2226
|
+
decimals: z3.number()
|
|
1890
2227
|
});
|
|
1891
|
-
|
|
1892
|
-
|
|
2228
|
+
var lendingMarketRowSchema = z3.object({
|
|
2229
|
+
marketId: z3.string(),
|
|
2230
|
+
loanToken: tokenMetaSchema,
|
|
2231
|
+
collateralToken: tokenMetaSchema,
|
|
2232
|
+
oracle: z3.string(),
|
|
2233
|
+
irm: z3.string(),
|
|
2234
|
+
lltvBps: z3.number(),
|
|
2235
|
+
lltvPercent: z3.number(),
|
|
2236
|
+
totalSupplyAssets: z3.string(),
|
|
2237
|
+
totalBorrowAssets: z3.string(),
|
|
2238
|
+
totalSupplyShares: z3.string(),
|
|
2239
|
+
totalBorrowShares: z3.string(),
|
|
2240
|
+
availableLiquidityAssets: z3.string(),
|
|
2241
|
+
utilization: z3.number().nullable(),
|
|
2242
|
+
feeWad: z3.string(),
|
|
2243
|
+
lastUpdate: z3.number()
|
|
2244
|
+
});
|
|
2245
|
+
function isMarketId(value) {
|
|
2246
|
+
return /^0x[0-9a-fA-F]{64}$/.test(value);
|
|
2247
|
+
}
|
|
2248
|
+
function shortAddress2(address) {
|
|
2249
|
+
return `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
2250
|
+
}
|
|
2251
|
+
function finiteOrNull2(value) {
|
|
2252
|
+
return Number.isFinite(value) ? value : null;
|
|
2253
|
+
}
|
|
2254
|
+
function sharesToAssets(shares, totalShares, totalAssets) {
|
|
2255
|
+
if (shares === 0n || totalShares === 0n) return 0n;
|
|
2256
|
+
return shares * totalAssets / totalShares;
|
|
2257
|
+
}
|
|
2258
|
+
function normalizeMarketParams(value) {
|
|
2259
|
+
if (Array.isArray(value)) {
|
|
2260
|
+
return {
|
|
2261
|
+
loanToken: checksumAddress3(String(value[0])),
|
|
2262
|
+
collateralToken: checksumAddress3(String(value[1])),
|
|
2263
|
+
oracle: checksumAddress3(String(value[2])),
|
|
2264
|
+
irm: checksumAddress3(String(value[3])),
|
|
2265
|
+
lltv: BigInt(value[4])
|
|
2266
|
+
};
|
|
2267
|
+
}
|
|
2268
|
+
if (typeof value !== "object" || value === null) {
|
|
2269
|
+
throw new Error("invalid market params");
|
|
2270
|
+
}
|
|
2271
|
+
const v = value;
|
|
2272
|
+
return {
|
|
2273
|
+
loanToken: checksumAddress3(String(v.loanToken)),
|
|
2274
|
+
collateralToken: checksumAddress3(String(v.collateralToken)),
|
|
2275
|
+
oracle: checksumAddress3(String(v.oracle)),
|
|
2276
|
+
irm: checksumAddress3(String(v.irm)),
|
|
2277
|
+
lltv: BigInt(v.lltv)
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
function normalizeMarketState(value) {
|
|
2281
|
+
if (Array.isArray(value)) {
|
|
2282
|
+
return {
|
|
2283
|
+
totalSupplyAssets: BigInt(value[0]),
|
|
2284
|
+
totalSupplyShares: BigInt(value[1]),
|
|
2285
|
+
totalBorrowAssets: BigInt(value[2]),
|
|
2286
|
+
totalBorrowShares: BigInt(value[3]),
|
|
2287
|
+
lastUpdate: BigInt(value[4]),
|
|
2288
|
+
fee: BigInt(value[5])
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
if (typeof value !== "object" || value === null) {
|
|
2292
|
+
throw new Error("invalid market state");
|
|
2293
|
+
}
|
|
2294
|
+
const v = value;
|
|
2295
|
+
return {
|
|
2296
|
+
totalSupplyAssets: BigInt(v.totalSupplyAssets),
|
|
2297
|
+
totalSupplyShares: BigInt(v.totalSupplyShares),
|
|
2298
|
+
totalBorrowAssets: BigInt(v.totalBorrowAssets),
|
|
2299
|
+
totalBorrowShares: BigInt(v.totalBorrowShares),
|
|
2300
|
+
lastUpdate: BigInt(v.lastUpdate),
|
|
2301
|
+
fee: BigInt(v.fee)
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
function toMarketRow(marketId, params, state, tokenMeta) {
|
|
2305
|
+
const loanMeta = tokenMeta.get(params.loanToken.toLowerCase()) ?? {
|
|
2306
|
+
address: params.loanToken,
|
|
2307
|
+
symbol: shortAddress2(params.loanToken),
|
|
2308
|
+
decimals: 18
|
|
2309
|
+
};
|
|
2310
|
+
const collateralMeta = tokenMeta.get(params.collateralToken.toLowerCase()) ?? {
|
|
2311
|
+
address: params.collateralToken,
|
|
2312
|
+
symbol: shortAddress2(params.collateralToken),
|
|
2313
|
+
decimals: 18
|
|
2314
|
+
};
|
|
2315
|
+
const utilization = state.totalSupplyAssets === 0n ? null : finiteOrNull2(Number(state.totalBorrowAssets) / Number(state.totalSupplyAssets));
|
|
2316
|
+
const lltvPercent = Number(params.lltv) / 1e16;
|
|
2317
|
+
return {
|
|
2318
|
+
marketId,
|
|
2319
|
+
loanToken: {
|
|
2320
|
+
address: toChecksum(loanMeta.address),
|
|
2321
|
+
symbol: loanMeta.symbol,
|
|
2322
|
+
decimals: loanMeta.decimals
|
|
2323
|
+
},
|
|
2324
|
+
collateralToken: {
|
|
2325
|
+
address: toChecksum(collateralMeta.address),
|
|
2326
|
+
symbol: collateralMeta.symbol,
|
|
2327
|
+
decimals: collateralMeta.decimals
|
|
2328
|
+
},
|
|
2329
|
+
oracle: toChecksum(params.oracle),
|
|
2330
|
+
irm: toChecksum(params.irm),
|
|
2331
|
+
lltvBps: Math.round(lltvPercent * 100),
|
|
2332
|
+
lltvPercent,
|
|
2333
|
+
totalSupplyAssets: state.totalSupplyAssets.toString(),
|
|
2334
|
+
totalBorrowAssets: state.totalBorrowAssets.toString(),
|
|
2335
|
+
totalSupplyShares: state.totalSupplyShares.toString(),
|
|
2336
|
+
totalBorrowShares: state.totalBorrowShares.toString(),
|
|
2337
|
+
availableLiquidityAssets: (state.totalSupplyAssets - state.totalBorrowAssets).toString(),
|
|
2338
|
+
utilization,
|
|
2339
|
+
feeWad: state.fee.toString(),
|
|
2340
|
+
lastUpdate: asNum(state.lastUpdate)
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
async function readTokenMetadata2(client, addresses) {
|
|
2344
|
+
const unique = [...new Set(addresses.map((address) => address.toLowerCase()))];
|
|
2345
|
+
if (unique.length === 0) {
|
|
2346
|
+
return /* @__PURE__ */ new Map();
|
|
2347
|
+
}
|
|
2348
|
+
const contracts = unique.flatMap((address) => [
|
|
2349
|
+
{
|
|
2350
|
+
abi: erc20MetadataAbi2,
|
|
2351
|
+
address,
|
|
2352
|
+
functionName: "symbol"
|
|
2353
|
+
},
|
|
2354
|
+
{
|
|
2355
|
+
abi: erc20MetadataAbi2,
|
|
2356
|
+
address,
|
|
2357
|
+
functionName: "decimals"
|
|
2358
|
+
}
|
|
2359
|
+
]);
|
|
2360
|
+
const results = await client.multicall({
|
|
2361
|
+
allowFailure: true,
|
|
2362
|
+
contracts
|
|
2363
|
+
});
|
|
2364
|
+
const map = /* @__PURE__ */ new Map();
|
|
2365
|
+
for (let i = 0; i < unique.length; i += 1) {
|
|
2366
|
+
const address = unique[i];
|
|
2367
|
+
const symbolResult = results[i * 2];
|
|
2368
|
+
const decimalsResult = results[i * 2 + 1];
|
|
2369
|
+
const symbol = symbolResult && symbolResult.status === "success" && typeof symbolResult.result === "string" ? symbolResult.result : shortAddress2(address);
|
|
2370
|
+
const decimals = decimalsResult && decimalsResult.status === "success" && typeof decimalsResult.result === "number" ? decimalsResult.result : 18;
|
|
2371
|
+
map.set(address.toLowerCase(), {
|
|
2372
|
+
address: checksumAddress3(address),
|
|
2373
|
+
symbol,
|
|
2374
|
+
decimals
|
|
2375
|
+
});
|
|
2376
|
+
}
|
|
2377
|
+
return map;
|
|
2378
|
+
}
|
|
2379
|
+
async function discoverMarketIds(client) {
|
|
2380
|
+
const logs = await client.getContractEvents({
|
|
2381
|
+
address: ABOREAN_LENDING_ADDRESSES.morphoBlue,
|
|
2382
|
+
abi: morphoAbi,
|
|
2383
|
+
eventName: "CreateMarket",
|
|
2384
|
+
fromBlock: MORPHO_DEPLOY_BLOCK,
|
|
2385
|
+
toBlock: "latest"
|
|
2386
|
+
});
|
|
2387
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2388
|
+
const ids = [];
|
|
2389
|
+
for (const log of logs) {
|
|
2390
|
+
const id = log.args.id;
|
|
2391
|
+
if (!id) continue;
|
|
2392
|
+
const key = id.toLowerCase();
|
|
2393
|
+
if (seen.has(key)) continue;
|
|
2394
|
+
seen.add(key);
|
|
2395
|
+
ids.push(id);
|
|
2396
|
+
}
|
|
2397
|
+
return ids;
|
|
2398
|
+
}
|
|
2399
|
+
async function readMarkets(client, marketIds) {
|
|
2400
|
+
if (marketIds.length === 0) return [];
|
|
2401
|
+
const contracts = marketIds.flatMap((marketId) => [
|
|
2402
|
+
{
|
|
2403
|
+
abi: morphoAbi,
|
|
2404
|
+
address: ABOREAN_LENDING_ADDRESSES.morphoBlue,
|
|
2405
|
+
functionName: "idToMarketParams",
|
|
2406
|
+
args: [marketId]
|
|
2407
|
+
},
|
|
2408
|
+
{
|
|
2409
|
+
abi: morphoAbi,
|
|
2410
|
+
address: ABOREAN_LENDING_ADDRESSES.morphoBlue,
|
|
2411
|
+
functionName: "market",
|
|
2412
|
+
args: [marketId]
|
|
2413
|
+
}
|
|
2414
|
+
]);
|
|
2415
|
+
const values = await client.multicall({
|
|
2416
|
+
allowFailure: false,
|
|
2417
|
+
contracts
|
|
2418
|
+
});
|
|
2419
|
+
return marketIds.map((marketId, index) => {
|
|
2420
|
+
const paramsValue = values[index * 2];
|
|
2421
|
+
const stateValue = values[index * 2 + 1];
|
|
2422
|
+
return {
|
|
2423
|
+
marketId,
|
|
2424
|
+
params: normalizeMarketParams(paramsValue),
|
|
2425
|
+
state: normalizeMarketState(stateValue)
|
|
2426
|
+
};
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
function summarizeByLoanToken(markets, tokenMeta) {
|
|
2430
|
+
const byLoanToken = /* @__PURE__ */ new Map();
|
|
2431
|
+
for (const { params, state } of markets) {
|
|
2432
|
+
const key = params.loanToken.toLowerCase();
|
|
2433
|
+
const prev = byLoanToken.get(key);
|
|
2434
|
+
if (prev) {
|
|
2435
|
+
prev.totalSupply += state.totalSupplyAssets;
|
|
2436
|
+
prev.totalBorrow += state.totalBorrowAssets;
|
|
2437
|
+
continue;
|
|
2438
|
+
}
|
|
2439
|
+
byLoanToken.set(key, {
|
|
2440
|
+
token: params.loanToken,
|
|
2441
|
+
totalSupply: state.totalSupplyAssets,
|
|
2442
|
+
totalBorrow: state.totalBorrowAssets
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
return [...byLoanToken.values()].map((entry) => {
|
|
2446
|
+
const meta = tokenMeta.get(entry.token.toLowerCase()) ?? {
|
|
2447
|
+
address: entry.token,
|
|
2448
|
+
symbol: shortAddress2(entry.token),
|
|
2449
|
+
decimals: 18
|
|
2450
|
+
};
|
|
2451
|
+
return {
|
|
2452
|
+
token: toChecksum(entry.token),
|
|
2453
|
+
symbol: meta.symbol,
|
|
2454
|
+
decimals: meta.decimals,
|
|
2455
|
+
totalSupplyAssets: entry.totalSupply.toString(),
|
|
2456
|
+
totalBorrowAssets: entry.totalBorrow.toString()
|
|
2457
|
+
};
|
|
2458
|
+
});
|
|
2459
|
+
}
|
|
2460
|
+
async function readLendingSummary(client) {
|
|
2461
|
+
const marketIds = await discoverMarketIds(client);
|
|
2462
|
+
const markets = await readMarkets(client, marketIds);
|
|
2463
|
+
const tokenMeta = await readTokenMetadata2(
|
|
2464
|
+
client,
|
|
2465
|
+
markets.flatMap((market) => [market.params.loanToken, market.params.collateralToken])
|
|
2466
|
+
);
|
|
2467
|
+
return {
|
|
2468
|
+
available: true,
|
|
2469
|
+
morpho: toChecksum(ABOREAN_LENDING_ADDRESSES.morphoBlue),
|
|
2470
|
+
marketCount: marketIds.length,
|
|
2471
|
+
supplyByLoanToken: summarizeByLoanToken(markets, tokenMeta)
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
var lending = Cli3.create("lending", {
|
|
2475
|
+
description: "Inspect Morpho lending markets on Abstract."
|
|
2476
|
+
});
|
|
2477
|
+
lending.command("markets", {
|
|
2478
|
+
description: "List Morpho markets discovered from CreateMarket events.",
|
|
2479
|
+
args: z3.object({
|
|
2480
|
+
limit: z3.coerce.number().int().positive().max(200).default(25).describe("Max markets to return")
|
|
2481
|
+
}),
|
|
1893
2482
|
env: env3,
|
|
1894
2483
|
output: z3.object({
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
2484
|
+
morpho: z3.string(),
|
|
2485
|
+
marketCount: z3.number(),
|
|
2486
|
+
markets: z3.array(lendingMarketRowSchema),
|
|
2487
|
+
totalsByLoanToken: z3.array(
|
|
2488
|
+
z3.object({
|
|
2489
|
+
token: z3.string(),
|
|
2490
|
+
symbol: z3.string(),
|
|
2491
|
+
decimals: z3.number(),
|
|
2492
|
+
totalSupplyAssets: z3.string(),
|
|
2493
|
+
totalBorrowAssets: z3.string()
|
|
2494
|
+
})
|
|
2495
|
+
)
|
|
1904
2496
|
}),
|
|
1905
|
-
examples: [{ description: "
|
|
2497
|
+
examples: [{ description: "List active Morpho markets on Abstract" }],
|
|
1906
2498
|
async run(c) {
|
|
1907
2499
|
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
1908
|
-
const
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
2500
|
+
const marketIds = await discoverMarketIds(client);
|
|
2501
|
+
const markets = await readMarkets(client, marketIds);
|
|
2502
|
+
const tokenMeta = await readTokenMetadata2(
|
|
2503
|
+
client,
|
|
2504
|
+
markets.flatMap((market) => [market.params.loanToken, market.params.collateralToken])
|
|
2505
|
+
);
|
|
2506
|
+
const rows = markets.map((market) => toMarketRow(market.marketId, market.params, market.state, tokenMeta)).sort(
|
|
2507
|
+
(a, b) => BigInt(a.totalSupplyAssets) > BigInt(b.totalSupplyAssets) ? -1 : BigInt(a.totalSupplyAssets) < BigInt(b.totalSupplyAssets) ? 1 : 0
|
|
2508
|
+
).slice(0, c.args.limit);
|
|
2509
|
+
const firstMarket = rows[0];
|
|
2510
|
+
return c.ok(
|
|
2511
|
+
{
|
|
2512
|
+
morpho: toChecksum(ABOREAN_LENDING_ADDRESSES.morphoBlue),
|
|
2513
|
+
marketCount: marketIds.length,
|
|
2514
|
+
markets: rows,
|
|
2515
|
+
totalsByLoanToken: summarizeByLoanToken(markets, tokenMeta)
|
|
2516
|
+
},
|
|
2517
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
2518
|
+
cta: {
|
|
2519
|
+
description: "Explore lending:",
|
|
2520
|
+
commands: [
|
|
2521
|
+
...firstMarket ? [
|
|
2522
|
+
{
|
|
2523
|
+
command: "lending market",
|
|
2524
|
+
args: { marketId: firstMarket.marketId },
|
|
2525
|
+
description: `Inspect ${firstMarket.loanToken.symbol}/${firstMarket.collateralToken.symbol} market`
|
|
2526
|
+
}
|
|
2527
|
+
] : [],
|
|
2528
|
+
{
|
|
2529
|
+
command: "pools list",
|
|
2530
|
+
description: "View V2 AMM pools"
|
|
2531
|
+
}
|
|
2532
|
+
]
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
});
|
|
2538
|
+
lending.command("market", {
|
|
2539
|
+
description: "Get details for one Morpho market id (bytes32).",
|
|
2540
|
+
args: z3.object({
|
|
2541
|
+
marketId: z3.string().describe("Morpho market id (bytes32 hex)")
|
|
2542
|
+
}),
|
|
2543
|
+
env: env3,
|
|
2544
|
+
output: lendingMarketRowSchema,
|
|
2545
|
+
examples: [
|
|
2546
|
+
{
|
|
2547
|
+
args: { marketId: "0xfe1d7da2fbde85b1fee120c88df3e6b55164a2442dab97486d3d4f719a5ff1fb" },
|
|
2548
|
+
description: "Inspect one Morpho market by id"
|
|
2549
|
+
}
|
|
2550
|
+
],
|
|
2551
|
+
async run(c) {
|
|
2552
|
+
if (!isMarketId(c.args.marketId)) {
|
|
2553
|
+
return c.error({
|
|
2554
|
+
code: "INVALID_ARGUMENT",
|
|
2555
|
+
message: "marketId must be a 32-byte hex string (0x + 64 hex chars)"
|
|
2556
|
+
});
|
|
2557
|
+
}
|
|
2558
|
+
const marketId = c.args.marketId;
|
|
2559
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
2560
|
+
const [paramsRaw, stateRaw] = await Promise.all([
|
|
2561
|
+
client.readContract({
|
|
2562
|
+
abi: morphoAbi,
|
|
2563
|
+
address: ABOREAN_LENDING_ADDRESSES.morphoBlue,
|
|
2564
|
+
functionName: "idToMarketParams",
|
|
2565
|
+
args: [marketId]
|
|
2566
|
+
}),
|
|
2567
|
+
client.readContract({
|
|
2568
|
+
abi: morphoAbi,
|
|
2569
|
+
address: ABOREAN_LENDING_ADDRESSES.morphoBlue,
|
|
2570
|
+
functionName: "market",
|
|
2571
|
+
args: [marketId]
|
|
2572
|
+
})
|
|
2573
|
+
]);
|
|
2574
|
+
const params = normalizeMarketParams(paramsRaw);
|
|
2575
|
+
const state = normalizeMarketState(stateRaw);
|
|
2576
|
+
const tokenMeta = await readTokenMetadata2(client, [params.loanToken, params.collateralToken]);
|
|
2577
|
+
const row = toMarketRow(marketId, params, state, tokenMeta);
|
|
2578
|
+
return c.ok(
|
|
2579
|
+
row,
|
|
2580
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
2581
|
+
cta: {
|
|
2582
|
+
description: "Related commands:",
|
|
2583
|
+
commands: [
|
|
2584
|
+
{
|
|
2585
|
+
command: "lending position",
|
|
2586
|
+
args: { marketId, user: "<address>" },
|
|
2587
|
+
description: "Inspect a user position in this market"
|
|
2588
|
+
},
|
|
2589
|
+
{
|
|
2590
|
+
command: "lending markets",
|
|
2591
|
+
description: "List all Morpho markets"
|
|
2592
|
+
}
|
|
2593
|
+
]
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
);
|
|
2597
|
+
}
|
|
2598
|
+
});
|
|
2599
|
+
lending.command("position", {
|
|
2600
|
+
description: "Inspect one user position in a Morpho market.",
|
|
2601
|
+
args: z3.object({
|
|
2602
|
+
marketId: z3.string().describe("Morpho market id (bytes32 hex)"),
|
|
2603
|
+
user: z3.string().describe("Position owner address")
|
|
2604
|
+
}),
|
|
2605
|
+
env: env3,
|
|
2606
|
+
output: z3.object({
|
|
2607
|
+
marketId: z3.string(),
|
|
2608
|
+
user: z3.string(),
|
|
2609
|
+
loanToken: tokenMetaSchema,
|
|
2610
|
+
collateralToken: tokenMetaSchema,
|
|
2611
|
+
supplyShares: z3.string(),
|
|
2612
|
+
supplyAssetsEstimate: z3.object({
|
|
2613
|
+
raw: z3.string(),
|
|
2614
|
+
decimal: z3.string()
|
|
2615
|
+
}),
|
|
2616
|
+
borrowShares: z3.string(),
|
|
2617
|
+
borrowAssetsEstimate: z3.object({
|
|
2618
|
+
raw: z3.string(),
|
|
2619
|
+
decimal: z3.string()
|
|
2620
|
+
}),
|
|
2621
|
+
collateralAssets: z3.object({
|
|
2622
|
+
raw: z3.string(),
|
|
2623
|
+
decimal: z3.string()
|
|
2624
|
+
})
|
|
2625
|
+
}),
|
|
2626
|
+
examples: [
|
|
2627
|
+
{
|
|
2628
|
+
args: {
|
|
2629
|
+
marketId: "0xfe1d7da2fbde85b1fee120c88df3e6b55164a2442dab97486d3d4f719a5ff1fb",
|
|
2630
|
+
user: "0x0000000000000000000000000000000000000000"
|
|
2631
|
+
},
|
|
2632
|
+
description: "Inspect one user position in a market"
|
|
2633
|
+
}
|
|
2634
|
+
],
|
|
2635
|
+
async run(c) {
|
|
2636
|
+
if (!isMarketId(c.args.marketId)) {
|
|
2637
|
+
return c.error({
|
|
2638
|
+
code: "INVALID_ARGUMENT",
|
|
2639
|
+
message: "marketId must be a 32-byte hex string (0x + 64 hex chars)"
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
2642
|
+
if (!isAddress2(c.args.user)) {
|
|
2643
|
+
return c.error({
|
|
2644
|
+
code: "INVALID_ARGUMENT",
|
|
2645
|
+
message: "user must be a valid address"
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2648
|
+
const marketId = c.args.marketId;
|
|
2649
|
+
const user = checksumAddress3(c.args.user);
|
|
2650
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
2651
|
+
const [paramsRaw, stateRaw, positionRaw] = await Promise.all([
|
|
2652
|
+
client.readContract({
|
|
2653
|
+
abi: morphoAbi,
|
|
2654
|
+
address: ABOREAN_LENDING_ADDRESSES.morphoBlue,
|
|
2655
|
+
functionName: "idToMarketParams",
|
|
2656
|
+
args: [marketId]
|
|
2657
|
+
}),
|
|
2658
|
+
client.readContract({
|
|
2659
|
+
abi: morphoAbi,
|
|
2660
|
+
address: ABOREAN_LENDING_ADDRESSES.morphoBlue,
|
|
2661
|
+
functionName: "market",
|
|
2662
|
+
args: [marketId]
|
|
2663
|
+
}),
|
|
2664
|
+
client.readContract({
|
|
2665
|
+
abi: morphoAbi,
|
|
2666
|
+
address: ABOREAN_LENDING_ADDRESSES.morphoBlue,
|
|
2667
|
+
functionName: "position",
|
|
2668
|
+
args: [marketId, user]
|
|
2669
|
+
})
|
|
2670
|
+
]);
|
|
2671
|
+
const params = normalizeMarketParams(paramsRaw);
|
|
2672
|
+
const state = normalizeMarketState(stateRaw);
|
|
2673
|
+
const position = Array.isArray(positionRaw) ? {
|
|
2674
|
+
supplyShares: BigInt(positionRaw[0]),
|
|
2675
|
+
borrowShares: BigInt(positionRaw[1]),
|
|
2676
|
+
collateral: BigInt(positionRaw[2])
|
|
2677
|
+
} : {
|
|
2678
|
+
supplyShares: BigInt(positionRaw.supplyShares),
|
|
2679
|
+
borrowShares: BigInt(positionRaw.borrowShares),
|
|
2680
|
+
collateral: BigInt(positionRaw.collateral)
|
|
2681
|
+
};
|
|
2682
|
+
const tokenMeta = await readTokenMetadata2(client, [params.loanToken, params.collateralToken]);
|
|
2683
|
+
const loanMeta = tokenMeta.get(params.loanToken.toLowerCase()) ?? {
|
|
2684
|
+
address: params.loanToken,
|
|
2685
|
+
symbol: shortAddress2(params.loanToken),
|
|
2686
|
+
decimals: 18
|
|
2687
|
+
};
|
|
2688
|
+
const collateralMeta = tokenMeta.get(params.collateralToken.toLowerCase()) ?? {
|
|
2689
|
+
address: params.collateralToken,
|
|
2690
|
+
symbol: shortAddress2(params.collateralToken),
|
|
2691
|
+
decimals: 18
|
|
2692
|
+
};
|
|
2693
|
+
const supplyAssetsEstimate = sharesToAssets(
|
|
2694
|
+
BigInt(position.supplyShares),
|
|
2695
|
+
state.totalSupplyShares,
|
|
2696
|
+
state.totalSupplyAssets
|
|
2697
|
+
);
|
|
2698
|
+
const borrowAssetsEstimate = sharesToAssets(
|
|
2699
|
+
BigInt(position.borrowShares),
|
|
2700
|
+
state.totalBorrowShares,
|
|
2701
|
+
state.totalBorrowAssets
|
|
2702
|
+
);
|
|
2703
|
+
return c.ok(
|
|
2704
|
+
{
|
|
2705
|
+
marketId,
|
|
2706
|
+
user: toChecksum(user),
|
|
2707
|
+
loanToken: {
|
|
2708
|
+
address: toChecksum(loanMeta.address),
|
|
2709
|
+
symbol: loanMeta.symbol,
|
|
2710
|
+
decimals: loanMeta.decimals
|
|
2711
|
+
},
|
|
2712
|
+
collateralToken: {
|
|
2713
|
+
address: toChecksum(collateralMeta.address),
|
|
2714
|
+
symbol: collateralMeta.symbol,
|
|
2715
|
+
decimals: collateralMeta.decimals
|
|
2716
|
+
},
|
|
2717
|
+
supplyShares: position.supplyShares.toString(),
|
|
2718
|
+
supplyAssetsEstimate: {
|
|
2719
|
+
raw: supplyAssetsEstimate.toString(),
|
|
2720
|
+
decimal: formatUnits2(supplyAssetsEstimate, loanMeta.decimals)
|
|
2721
|
+
},
|
|
2722
|
+
borrowShares: position.borrowShares.toString(),
|
|
2723
|
+
borrowAssetsEstimate: {
|
|
2724
|
+
raw: borrowAssetsEstimate.toString(),
|
|
2725
|
+
decimal: formatUnits2(borrowAssetsEstimate, loanMeta.decimals)
|
|
2726
|
+
},
|
|
2727
|
+
collateralAssets: {
|
|
2728
|
+
raw: position.collateral.toString(),
|
|
2729
|
+
decimal: formatUnits2(BigInt(position.collateral), collateralMeta.decimals)
|
|
2730
|
+
}
|
|
2731
|
+
},
|
|
2732
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
2733
|
+
cta: {
|
|
2734
|
+
description: "Related commands:",
|
|
2735
|
+
commands: [
|
|
2736
|
+
{
|
|
2737
|
+
command: "lending market",
|
|
2738
|
+
args: { marketId },
|
|
2739
|
+
description: "View market details"
|
|
2740
|
+
},
|
|
2741
|
+
{
|
|
2742
|
+
command: "lending markets",
|
|
2743
|
+
description: "List all Morpho markets"
|
|
2744
|
+
}
|
|
2745
|
+
]
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
);
|
|
2749
|
+
}
|
|
2750
|
+
});
|
|
2751
|
+
|
|
2752
|
+
// src/commands/pools.ts
|
|
2753
|
+
import { checksumAddress as checksumAddress4, isAddress as isAddress3 } from "@spectratools/cli-shared";
|
|
2754
|
+
import { Cli as Cli4, z as z4 } from "incur";
|
|
2755
|
+
import { formatUnits as formatUnits3, parseUnits as parseUnits2 } from "viem";
|
|
2756
|
+
var MULTICALL_BATCH_SIZE2 = 100;
|
|
2757
|
+
var env4 = z4.object({
|
|
2758
|
+
ABSTRACT_RPC_URL: z4.string().optional().describe("Abstract RPC URL override")
|
|
2759
|
+
});
|
|
2760
|
+
var tokenSchema2 = z4.object({
|
|
2761
|
+
address: z4.string(),
|
|
2762
|
+
symbol: z4.string(),
|
|
2763
|
+
decimals: z4.number()
|
|
2764
|
+
});
|
|
2765
|
+
var amountSchema = z4.object({
|
|
2766
|
+
raw: z4.string(),
|
|
2767
|
+
decimal: z4.string()
|
|
2768
|
+
});
|
|
2769
|
+
var feeSchema = z4.object({
|
|
2770
|
+
feeBps: z4.number(),
|
|
2771
|
+
feePercent: z4.number()
|
|
2772
|
+
}).nullable();
|
|
2773
|
+
var poolSummarySchema = z4.object({
|
|
2774
|
+
pool: z4.string(),
|
|
2775
|
+
pair: z4.string(),
|
|
2776
|
+
stable: z4.boolean(),
|
|
2777
|
+
poolType: z4.enum(["stable", "volatile"]),
|
|
2778
|
+
token0: tokenSchema2,
|
|
2779
|
+
token1: tokenSchema2,
|
|
2780
|
+
reserves: z4.object({
|
|
2781
|
+
token0: amountSchema,
|
|
2782
|
+
token1: amountSchema,
|
|
2783
|
+
blockTimestampLast: z4.number()
|
|
2784
|
+
}),
|
|
2785
|
+
totalSupply: z4.string(),
|
|
2786
|
+
fee: feeSchema
|
|
2787
|
+
});
|
|
2788
|
+
var erc20MetadataAbi3 = [
|
|
2789
|
+
{
|
|
2790
|
+
type: "function",
|
|
2791
|
+
name: "symbol",
|
|
2792
|
+
stateMutability: "view",
|
|
2793
|
+
inputs: [],
|
|
2794
|
+
outputs: [{ type: "string" }]
|
|
2795
|
+
},
|
|
2796
|
+
{
|
|
2797
|
+
type: "function",
|
|
2798
|
+
name: "decimals",
|
|
2799
|
+
stateMutability: "view",
|
|
2800
|
+
inputs: [],
|
|
2801
|
+
outputs: [{ type: "uint8" }]
|
|
2802
|
+
}
|
|
2803
|
+
];
|
|
2804
|
+
function shortAddress3(address) {
|
|
2805
|
+
return `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
2806
|
+
}
|
|
2807
|
+
function toAddress(address) {
|
|
2808
|
+
return checksumAddress4(address);
|
|
2809
|
+
}
|
|
2810
|
+
function finiteOrNull3(value) {
|
|
2811
|
+
return Number.isFinite(value) ? value : null;
|
|
2812
|
+
}
|
|
2813
|
+
function toFeeInfo(value) {
|
|
2814
|
+
if (value === null) return null;
|
|
2815
|
+
const feeBps = Number(value);
|
|
2816
|
+
return {
|
|
2817
|
+
feeBps,
|
|
2818
|
+
feePercent: feeBps / 1e4
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
function chunk2(items, size) {
|
|
2822
|
+
const output = [];
|
|
2823
|
+
for (let i = 0; i < items.length; i += size) {
|
|
2824
|
+
output.push(items.slice(i, i + size));
|
|
2825
|
+
}
|
|
2826
|
+
return output;
|
|
2827
|
+
}
|
|
2828
|
+
async function multicallStrict2(client, contracts) {
|
|
2829
|
+
if (contracts.length === 0) return [];
|
|
2830
|
+
const batches = chunk2(contracts, MULTICALL_BATCH_SIZE2);
|
|
2831
|
+
const output = [];
|
|
2832
|
+
for (const batch of batches) {
|
|
2833
|
+
const values = await client.multicall({
|
|
2834
|
+
allowFailure: false,
|
|
2835
|
+
contracts: batch
|
|
2836
|
+
});
|
|
2837
|
+
output.push(...values);
|
|
2838
|
+
}
|
|
2839
|
+
return output;
|
|
2840
|
+
}
|
|
2841
|
+
async function multicallAllowFailure2(client, contracts) {
|
|
2842
|
+
if (contracts.length === 0) return [];
|
|
2843
|
+
const batches = chunk2(contracts, MULTICALL_BATCH_SIZE2);
|
|
2844
|
+
const output = [];
|
|
2845
|
+
for (const batch of batches) {
|
|
2846
|
+
const values = await client.multicall({
|
|
2847
|
+
allowFailure: true,
|
|
2848
|
+
contracts: batch
|
|
2849
|
+
});
|
|
2850
|
+
output.push(...values);
|
|
2851
|
+
}
|
|
2852
|
+
return output;
|
|
2853
|
+
}
|
|
2854
|
+
function fallbackTokenMeta(address) {
|
|
2855
|
+
const checksummed = checksumAddress4(address);
|
|
2856
|
+
return {
|
|
2857
|
+
address: checksummed,
|
|
2858
|
+
symbol: shortAddress3(checksummed),
|
|
2859
|
+
decimals: 18
|
|
2860
|
+
};
|
|
2861
|
+
}
|
|
2862
|
+
async function readTokenMetadata3(client, tokenAddresses) {
|
|
2863
|
+
const unique = [
|
|
2864
|
+
...new Set(tokenAddresses.map((address) => checksumAddress4(address).toLowerCase()))
|
|
2865
|
+
];
|
|
2866
|
+
if (unique.length === 0) {
|
|
2867
|
+
return /* @__PURE__ */ new Map();
|
|
2868
|
+
}
|
|
2869
|
+
const contracts = unique.flatMap((address) => [
|
|
2870
|
+
{
|
|
2871
|
+
abi: erc20MetadataAbi3,
|
|
2872
|
+
address,
|
|
2873
|
+
functionName: "symbol"
|
|
2874
|
+
},
|
|
2875
|
+
{
|
|
2876
|
+
abi: erc20MetadataAbi3,
|
|
2877
|
+
address,
|
|
2878
|
+
functionName: "decimals"
|
|
2879
|
+
}
|
|
2880
|
+
]);
|
|
2881
|
+
const values = await multicallAllowFailure2(client, contracts);
|
|
2882
|
+
const map = /* @__PURE__ */ new Map();
|
|
2883
|
+
for (let i = 0; i < unique.length; i += 1) {
|
|
2884
|
+
const address = unique[i];
|
|
2885
|
+
const symbolResult = values[i * 2];
|
|
2886
|
+
const decimalsResult = values[i * 2 + 1];
|
|
2887
|
+
const fallback = fallbackTokenMeta(address);
|
|
2888
|
+
const symbol = symbolResult && symbolResult.status === "success" && typeof symbolResult.result === "string" ? symbolResult.result : fallback.symbol;
|
|
2889
|
+
const decimals = decimalsResult && decimalsResult.status === "success" && typeof decimalsResult.result === "number" ? decimalsResult.result : fallback.decimals;
|
|
2890
|
+
map.set(address.toLowerCase(), {
|
|
2891
|
+
address: checksumAddress4(address),
|
|
2892
|
+
symbol,
|
|
2893
|
+
decimals
|
|
2894
|
+
});
|
|
2895
|
+
}
|
|
2896
|
+
return map;
|
|
2897
|
+
}
|
|
2898
|
+
async function readPoolStates2(client, pools2) {
|
|
2899
|
+
if (pools2.length === 0) {
|
|
2900
|
+
return [];
|
|
2901
|
+
}
|
|
2902
|
+
const contracts = pools2.flatMap((pool) => [
|
|
2903
|
+
{
|
|
2904
|
+
abi: v2PoolAbi,
|
|
2905
|
+
address: pool,
|
|
2906
|
+
functionName: "token0"
|
|
2907
|
+
},
|
|
2908
|
+
{
|
|
2909
|
+
abi: v2PoolAbi,
|
|
2910
|
+
address: pool,
|
|
2911
|
+
functionName: "token1"
|
|
2912
|
+
},
|
|
2913
|
+
{
|
|
2914
|
+
abi: v2PoolAbi,
|
|
2915
|
+
address: pool,
|
|
2916
|
+
functionName: "stable"
|
|
2917
|
+
},
|
|
2918
|
+
{
|
|
2919
|
+
abi: v2PoolAbi,
|
|
2920
|
+
address: pool,
|
|
2921
|
+
functionName: "getReserves"
|
|
2922
|
+
},
|
|
2923
|
+
{
|
|
2924
|
+
abi: v2PoolAbi,
|
|
2925
|
+
address: pool,
|
|
2926
|
+
functionName: "totalSupply"
|
|
2927
|
+
}
|
|
2928
|
+
]);
|
|
2929
|
+
const values = await multicallStrict2(client, contracts);
|
|
2930
|
+
return pools2.map((pool, index) => {
|
|
2931
|
+
const offset = index * 5;
|
|
2932
|
+
const reserves = values[offset + 3];
|
|
2933
|
+
return {
|
|
2934
|
+
pool,
|
|
2935
|
+
token0: values[offset],
|
|
2936
|
+
token1: values[offset + 1],
|
|
2937
|
+
stable: values[offset + 2],
|
|
2938
|
+
reserve0: reserves[0],
|
|
2939
|
+
reserve1: reserves[1],
|
|
2940
|
+
blockTimestampLast: reserves[2],
|
|
2941
|
+
totalSupply: values[offset + 4]
|
|
2942
|
+
};
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
async function readPoolFees(client, pools2) {
|
|
2946
|
+
if (pools2.length === 0) {
|
|
2947
|
+
return [];
|
|
2948
|
+
}
|
|
2949
|
+
const contracts = pools2.map(({ pool, stable }) => ({
|
|
2950
|
+
abi: poolFactoryAbi,
|
|
2951
|
+
address: ABOREAN_V2_ADDRESSES.poolFactory,
|
|
2952
|
+
functionName: "getFee",
|
|
2953
|
+
args: [pool, stable]
|
|
2954
|
+
}));
|
|
2955
|
+
const values = await multicallAllowFailure2(client, contracts);
|
|
2956
|
+
return values.map(
|
|
2957
|
+
(value) => value.status === "success" && typeof value.result === "bigint" ? value.result : null
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2960
|
+
function toAmount(raw, decimals) {
|
|
2961
|
+
return {
|
|
2962
|
+
raw: raw.toString(),
|
|
2963
|
+
decimal: formatUnits3(raw, decimals)
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
function toPoolSummary(state, tokens, fee) {
|
|
2967
|
+
const token0 = tokens.get(state.token0.toLowerCase()) ?? fallbackTokenMeta(state.token0);
|
|
2968
|
+
const token1 = tokens.get(state.token1.toLowerCase()) ?? fallbackTokenMeta(state.token1);
|
|
2969
|
+
return {
|
|
2970
|
+
pool: checksumAddress4(state.pool),
|
|
2971
|
+
pair: `${token0.symbol}/${token1.symbol}`,
|
|
2972
|
+
stable: state.stable,
|
|
2973
|
+
poolType: state.stable ? "stable" : "volatile",
|
|
2974
|
+
token0,
|
|
2975
|
+
token1,
|
|
2976
|
+
reserves: {
|
|
2977
|
+
token0: toAmount(state.reserve0, token0.decimals),
|
|
2978
|
+
token1: toAmount(state.reserve1, token1.decimals),
|
|
2979
|
+
blockTimestampLast: Number(state.blockTimestampLast)
|
|
2980
|
+
},
|
|
2981
|
+
totalSupply: state.totalSupply.toString(),
|
|
2982
|
+
fee: toFeeInfo(fee)
|
|
2983
|
+
};
|
|
2984
|
+
}
|
|
2985
|
+
var pools = Cli4.create("pools", {
|
|
2986
|
+
description: "Inspect V2 AMM pools, reserves, quotes, and fee configuration."
|
|
2987
|
+
});
|
|
2988
|
+
pools.command("list", {
|
|
2989
|
+
description: "List V2 pools with token pairs, reserves, and stable/volatile type.",
|
|
2990
|
+
options: z4.object({
|
|
2991
|
+
offset: z4.coerce.number().int().nonnegative().default(0).describe("Pool index offset"),
|
|
2992
|
+
limit: z4.coerce.number().int().positive().max(500).default(50).describe("Maximum pools to return (max 500)")
|
|
2993
|
+
}),
|
|
2994
|
+
env: env4,
|
|
2995
|
+
output: z4.object({
|
|
2996
|
+
total: z4.number(),
|
|
2997
|
+
offset: z4.number(),
|
|
2998
|
+
limit: z4.number(),
|
|
2999
|
+
count: z4.number(),
|
|
3000
|
+
pools: z4.array(poolSummarySchema)
|
|
3001
|
+
}),
|
|
3002
|
+
async run(c) {
|
|
3003
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
3004
|
+
const totalRaw = await client.readContract({
|
|
3005
|
+
abi: poolFactoryAbi,
|
|
3006
|
+
address: ABOREAN_V2_ADDRESSES.poolFactory,
|
|
3007
|
+
functionName: "allPoolsLength"
|
|
3008
|
+
});
|
|
3009
|
+
const total = Number(totalRaw);
|
|
3010
|
+
const offset = Math.min(c.options.offset, total);
|
|
3011
|
+
const end = Math.min(total, offset + c.options.limit);
|
|
3012
|
+
if (offset >= end) {
|
|
3013
|
+
return c.ok({
|
|
3014
|
+
total,
|
|
3015
|
+
offset,
|
|
3016
|
+
limit: c.options.limit,
|
|
3017
|
+
count: 0,
|
|
3018
|
+
pools: []
|
|
3019
|
+
});
|
|
3020
|
+
}
|
|
3021
|
+
const indices = Array.from({ length: end - offset }, (_, i) => BigInt(offset + i));
|
|
3022
|
+
const poolAddresses = await multicallStrict2(
|
|
3023
|
+
client,
|
|
3024
|
+
indices.map((index) => ({
|
|
3025
|
+
abi: poolFactoryAbi,
|
|
3026
|
+
address: ABOREAN_V2_ADDRESSES.poolFactory,
|
|
3027
|
+
functionName: "allPools",
|
|
3028
|
+
args: [index]
|
|
3029
|
+
}))
|
|
3030
|
+
);
|
|
3031
|
+
const poolStates = await readPoolStates2(client, poolAddresses);
|
|
3032
|
+
const tokenMeta = await readTokenMetadata3(
|
|
3033
|
+
client,
|
|
3034
|
+
poolStates.flatMap((pool) => [pool.token0, pool.token1])
|
|
3035
|
+
);
|
|
3036
|
+
const fees = await readPoolFees(
|
|
3037
|
+
client,
|
|
3038
|
+
poolStates.map((pool) => ({ pool: pool.pool, stable: pool.stable }))
|
|
3039
|
+
);
|
|
3040
|
+
const summaries = poolStates.map(
|
|
3041
|
+
(pool, index) => toPoolSummary(pool, tokenMeta, fees[index] ?? null)
|
|
3042
|
+
);
|
|
3043
|
+
const firstPool = summaries[0];
|
|
3044
|
+
return c.ok(
|
|
3045
|
+
{
|
|
3046
|
+
total,
|
|
3047
|
+
offset,
|
|
3048
|
+
limit: c.options.limit,
|
|
3049
|
+
count: summaries.length,
|
|
3050
|
+
pools: summaries
|
|
3051
|
+
},
|
|
3052
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3053
|
+
cta: {
|
|
3054
|
+
description: "Explore pools:",
|
|
3055
|
+
commands: [
|
|
3056
|
+
...firstPool ? [
|
|
3057
|
+
{
|
|
3058
|
+
command: "pools pool",
|
|
3059
|
+
args: { address: firstPool.pool },
|
|
3060
|
+
description: `Inspect ${firstPool.pair}`
|
|
3061
|
+
}
|
|
3062
|
+
] : [],
|
|
3063
|
+
...firstPool ? [
|
|
3064
|
+
{
|
|
3065
|
+
command: "pools fees",
|
|
3066
|
+
args: { pool: firstPool.pool },
|
|
3067
|
+
description: `Check fees for ${firstPool.pair}`
|
|
3068
|
+
}
|
|
3069
|
+
] : []
|
|
3070
|
+
]
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
);
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
3076
|
+
pools.command("pool", {
|
|
3077
|
+
description: "Get detailed state for one V2 pool.",
|
|
3078
|
+
args: z4.object({
|
|
3079
|
+
address: z4.string().describe("Pool address")
|
|
3080
|
+
}),
|
|
3081
|
+
env: env4,
|
|
3082
|
+
output: z4.object({
|
|
3083
|
+
pool: poolSummarySchema.extend({
|
|
3084
|
+
poolFees: z4.string(),
|
|
3085
|
+
factory: z4.string()
|
|
3086
|
+
})
|
|
3087
|
+
}),
|
|
3088
|
+
async run(c) {
|
|
3089
|
+
if (!isAddress3(c.args.address)) {
|
|
3090
|
+
return c.error({
|
|
3091
|
+
code: "INVALID_ADDRESS",
|
|
3092
|
+
message: `Invalid pool address: "${c.args.address}". Use a valid 0x-prefixed 20-byte hex address.`
|
|
3093
|
+
});
|
|
3094
|
+
}
|
|
3095
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
3096
|
+
const poolAddress = toAddress(c.args.address);
|
|
3097
|
+
const [token0, token1, stable, reserves, totalSupply, poolFees, factory] = await multicallStrict2(client, [
|
|
3098
|
+
{
|
|
3099
|
+
abi: v2PoolAbi,
|
|
3100
|
+
address: poolAddress,
|
|
3101
|
+
functionName: "token0"
|
|
3102
|
+
},
|
|
3103
|
+
{
|
|
3104
|
+
abi: v2PoolAbi,
|
|
3105
|
+
address: poolAddress,
|
|
3106
|
+
functionName: "token1"
|
|
3107
|
+
},
|
|
3108
|
+
{
|
|
3109
|
+
abi: v2PoolAbi,
|
|
3110
|
+
address: poolAddress,
|
|
3111
|
+
functionName: "stable"
|
|
3112
|
+
},
|
|
3113
|
+
{
|
|
3114
|
+
abi: v2PoolAbi,
|
|
3115
|
+
address: poolAddress,
|
|
3116
|
+
functionName: "getReserves"
|
|
3117
|
+
},
|
|
3118
|
+
{
|
|
3119
|
+
abi: v2PoolAbi,
|
|
3120
|
+
address: poolAddress,
|
|
3121
|
+
functionName: "totalSupply"
|
|
3122
|
+
},
|
|
3123
|
+
{
|
|
3124
|
+
abi: v2PoolAbi,
|
|
3125
|
+
address: poolAddress,
|
|
3126
|
+
functionName: "poolFees"
|
|
3127
|
+
},
|
|
3128
|
+
{
|
|
3129
|
+
abi: v2PoolAbi,
|
|
3130
|
+
address: poolAddress,
|
|
3131
|
+
functionName: "factory"
|
|
3132
|
+
}
|
|
3133
|
+
]);
|
|
3134
|
+
const tokenMeta = await readTokenMetadata3(client, [token0, token1]);
|
|
3135
|
+
const [fee] = await readPoolFees(client, [{ pool: poolAddress, stable }]);
|
|
3136
|
+
const summary = toPoolSummary(
|
|
3137
|
+
{
|
|
3138
|
+
pool: poolAddress,
|
|
3139
|
+
token0,
|
|
3140
|
+
token1,
|
|
3141
|
+
stable,
|
|
3142
|
+
reserve0: reserves[0],
|
|
3143
|
+
reserve1: reserves[1],
|
|
3144
|
+
blockTimestampLast: reserves[2],
|
|
3145
|
+
totalSupply
|
|
3146
|
+
},
|
|
3147
|
+
tokenMeta,
|
|
3148
|
+
fee ?? null
|
|
3149
|
+
);
|
|
3150
|
+
return c.ok(
|
|
3151
|
+
{
|
|
3152
|
+
pool: {
|
|
3153
|
+
...summary,
|
|
3154
|
+
poolFees: checksumAddress4(poolFees),
|
|
3155
|
+
factory: checksumAddress4(factory)
|
|
3156
|
+
}
|
|
3157
|
+
},
|
|
3158
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3159
|
+
cta: {
|
|
3160
|
+
description: "Next steps:",
|
|
3161
|
+
commands: [
|
|
3162
|
+
{
|
|
3163
|
+
command: "pools quote",
|
|
3164
|
+
args: {
|
|
3165
|
+
tokenIn: summary.token0.address,
|
|
3166
|
+
tokenOut: summary.token1.address,
|
|
3167
|
+
amountIn: "1"
|
|
3168
|
+
},
|
|
3169
|
+
description: `Quote a ${summary.token0.symbol} \u2192 ${summary.token1.symbol} swap`
|
|
3170
|
+
},
|
|
3171
|
+
{
|
|
3172
|
+
command: "pools fees",
|
|
3173
|
+
args: { pool: checksumAddress4(poolAddress) },
|
|
3174
|
+
description: "Check fee configuration"
|
|
3175
|
+
}
|
|
3176
|
+
]
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
);
|
|
3180
|
+
}
|
|
3181
|
+
});
|
|
3182
|
+
pools.command("quote", {
|
|
3183
|
+
description: "Quote a single-hop V2 swap between tokenIn and tokenOut.",
|
|
3184
|
+
args: z4.object({
|
|
3185
|
+
tokenIn: z4.string().describe("Input token address"),
|
|
3186
|
+
tokenOut: z4.string().describe("Output token address"),
|
|
3187
|
+
amountIn: z4.string().describe("Input amount in human-readable decimal units")
|
|
3188
|
+
}),
|
|
3189
|
+
options: z4.object({
|
|
3190
|
+
stable: z4.boolean().default(false).describe("Use stable pool route (default: volatile)")
|
|
3191
|
+
}),
|
|
3192
|
+
env: env4,
|
|
3193
|
+
output: z4.object({
|
|
3194
|
+
pool: z4.string(),
|
|
3195
|
+
stable: z4.boolean(),
|
|
3196
|
+
tokenIn: tokenSchema2,
|
|
3197
|
+
tokenOut: tokenSchema2,
|
|
3198
|
+
amountIn: amountSchema,
|
|
3199
|
+
amountOut: amountSchema,
|
|
3200
|
+
priceOutPerIn: z4.number().nullable()
|
|
3201
|
+
}),
|
|
3202
|
+
async run(c) {
|
|
3203
|
+
if (!isAddress3(c.args.tokenIn) || !isAddress3(c.args.tokenOut)) {
|
|
3204
|
+
return c.error({
|
|
3205
|
+
code: "INVALID_ADDRESS",
|
|
3206
|
+
message: "tokenIn and tokenOut must both be valid 0x-prefixed 20-byte addresses."
|
|
3207
|
+
});
|
|
3208
|
+
}
|
|
3209
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
3210
|
+
const tokenIn = toAddress(c.args.tokenIn);
|
|
3211
|
+
const tokenOut = toAddress(c.args.tokenOut);
|
|
3212
|
+
const tokenMeta = await readTokenMetadata3(client, [tokenIn, tokenOut]);
|
|
3213
|
+
const inMeta = tokenMeta.get(tokenIn.toLowerCase()) ?? fallbackTokenMeta(tokenIn);
|
|
3214
|
+
const outMeta = tokenMeta.get(tokenOut.toLowerCase()) ?? fallbackTokenMeta(tokenOut);
|
|
3215
|
+
let amountInRaw;
|
|
3216
|
+
try {
|
|
3217
|
+
amountInRaw = parseUnits2(c.args.amountIn, inMeta.decimals);
|
|
3218
|
+
} catch {
|
|
3219
|
+
return c.error({
|
|
3220
|
+
code: "INVALID_AMOUNT",
|
|
3221
|
+
message: `Invalid amountIn: "${c.args.amountIn}" for token ${inMeta.symbol} (${inMeta.decimals} decimals).`
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3224
|
+
const poolAddress = await client.readContract({
|
|
3225
|
+
abi: poolFactoryAbi,
|
|
3226
|
+
address: ABOREAN_V2_ADDRESSES.poolFactory,
|
|
3227
|
+
functionName: "getPool",
|
|
3228
|
+
args: [tokenIn, tokenOut, c.options.stable]
|
|
3229
|
+
});
|
|
3230
|
+
if (poolAddress.toLowerCase() === ZERO_ADDRESS.toLowerCase()) {
|
|
3231
|
+
return c.error({
|
|
3232
|
+
code: "POOL_NOT_FOUND",
|
|
3233
|
+
message: `No ${c.options.stable ? "stable" : "volatile"} V2 pool found for pair ${checksumAddress4(
|
|
3234
|
+
tokenIn
|
|
3235
|
+
)}/${checksumAddress4(tokenOut)}.`
|
|
3236
|
+
});
|
|
3237
|
+
}
|
|
3238
|
+
const amounts = await client.readContract({
|
|
3239
|
+
abi: v2RouterAbi,
|
|
3240
|
+
address: ABOREAN_V2_ADDRESSES.router,
|
|
3241
|
+
functionName: "getAmountsOut",
|
|
3242
|
+
args: [
|
|
3243
|
+
amountInRaw,
|
|
3244
|
+
[
|
|
3245
|
+
{
|
|
3246
|
+
from: tokenIn,
|
|
3247
|
+
to: tokenOut,
|
|
3248
|
+
stable: c.options.stable,
|
|
3249
|
+
factory: ABOREAN_V2_ADDRESSES.poolFactory
|
|
3250
|
+
}
|
|
3251
|
+
]
|
|
3252
|
+
]
|
|
3253
|
+
});
|
|
3254
|
+
const amountOutRaw = amounts[amounts.length - 1] ?? 0n;
|
|
3255
|
+
const amountInDecimal = formatUnits3(amountInRaw, inMeta.decimals);
|
|
3256
|
+
const amountOutDecimal = formatUnits3(amountOutRaw, outMeta.decimals);
|
|
3257
|
+
const ratio = Number(amountOutDecimal) / Number(amountInDecimal);
|
|
3258
|
+
return c.ok(
|
|
3259
|
+
{
|
|
3260
|
+
pool: checksumAddress4(poolAddress),
|
|
3261
|
+
stable: c.options.stable,
|
|
3262
|
+
tokenIn: inMeta,
|
|
3263
|
+
tokenOut: outMeta,
|
|
3264
|
+
amountIn: {
|
|
3265
|
+
raw: amountInRaw.toString(),
|
|
3266
|
+
decimal: amountInDecimal
|
|
3267
|
+
},
|
|
3268
|
+
amountOut: {
|
|
3269
|
+
raw: amountOutRaw.toString(),
|
|
3270
|
+
decimal: amountOutDecimal
|
|
3271
|
+
},
|
|
3272
|
+
priceOutPerIn: Number(amountInDecimal) === 0 ? null : finiteOrNull3(ratio)
|
|
3273
|
+
},
|
|
3274
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3275
|
+
cta: {
|
|
3276
|
+
description: "Related commands:",
|
|
3277
|
+
commands: [
|
|
3278
|
+
{
|
|
3279
|
+
command: "pools pool",
|
|
3280
|
+
args: { address: checksumAddress4(poolAddress) },
|
|
3281
|
+
description: "Inspect the pool used for this quote"
|
|
3282
|
+
},
|
|
3283
|
+
{
|
|
3284
|
+
command: "pools quote",
|
|
3285
|
+
args: {
|
|
3286
|
+
tokenIn: outMeta.address,
|
|
3287
|
+
tokenOut: inMeta.address,
|
|
3288
|
+
amountIn: amountOutDecimal
|
|
3289
|
+
},
|
|
3290
|
+
description: `Reverse quote ${outMeta.symbol} \u2192 ${inMeta.symbol}`
|
|
3291
|
+
}
|
|
3292
|
+
]
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3297
|
+
});
|
|
3298
|
+
pools.command("fees", {
|
|
3299
|
+
description: "Read V2 fee configuration for a pool address.",
|
|
3300
|
+
args: z4.object({
|
|
3301
|
+
pool: z4.string().describe("Pool address")
|
|
3302
|
+
}),
|
|
3303
|
+
env: env4,
|
|
3304
|
+
output: z4.object({
|
|
3305
|
+
pool: z4.string(),
|
|
3306
|
+
pair: z4.string(),
|
|
3307
|
+
stable: z4.boolean(),
|
|
3308
|
+
activeFee: feeSchema,
|
|
3309
|
+
stableFee: feeSchema,
|
|
3310
|
+
volatileFee: feeSchema
|
|
3311
|
+
}),
|
|
3312
|
+
async run(c) {
|
|
3313
|
+
if (!isAddress3(c.args.pool)) {
|
|
3314
|
+
return c.error({
|
|
3315
|
+
code: "INVALID_ADDRESS",
|
|
3316
|
+
message: `Invalid pool address: "${c.args.pool}". Use a valid 0x-prefixed 20-byte hex address.`
|
|
3317
|
+
});
|
|
3318
|
+
}
|
|
3319
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
3320
|
+
const pool = toAddress(c.args.pool);
|
|
3321
|
+
const [token0, token1, stable] = await multicallStrict2(client, [
|
|
3322
|
+
{
|
|
3323
|
+
abi: v2PoolAbi,
|
|
3324
|
+
address: pool,
|
|
3325
|
+
functionName: "token0"
|
|
3326
|
+
},
|
|
3327
|
+
{
|
|
3328
|
+
abi: v2PoolAbi,
|
|
3329
|
+
address: pool,
|
|
3330
|
+
functionName: "token1"
|
|
3331
|
+
},
|
|
3332
|
+
{
|
|
3333
|
+
abi: v2PoolAbi,
|
|
3334
|
+
address: pool,
|
|
3335
|
+
functionName: "stable"
|
|
3336
|
+
}
|
|
3337
|
+
]);
|
|
3338
|
+
const tokenMeta = await readTokenMetadata3(client, [token0, token1]);
|
|
3339
|
+
const token0Meta = tokenMeta.get(token0.toLowerCase()) ?? fallbackTokenMeta(token0);
|
|
3340
|
+
const token1Meta = tokenMeta.get(token1.toLowerCase()) ?? fallbackTokenMeta(token1);
|
|
3341
|
+
const [stableFeeRaw, volatileFeeRaw] = await readPoolFees(client, [
|
|
3342
|
+
{ pool, stable: true },
|
|
3343
|
+
{ pool, stable: false }
|
|
3344
|
+
]);
|
|
3345
|
+
const stableFee = toFeeInfo(stableFeeRaw ?? null);
|
|
3346
|
+
const volatileFee = toFeeInfo(volatileFeeRaw ?? null);
|
|
3347
|
+
return c.ok(
|
|
3348
|
+
{
|
|
3349
|
+
pool: checksumAddress4(pool),
|
|
3350
|
+
pair: `${token0Meta.symbol}/${token1Meta.symbol}`,
|
|
3351
|
+
stable,
|
|
3352
|
+
activeFee: stable ? stableFee : volatileFee,
|
|
3353
|
+
stableFee,
|
|
3354
|
+
volatileFee
|
|
3355
|
+
},
|
|
3356
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3357
|
+
cta: {
|
|
3358
|
+
description: "Related commands:",
|
|
3359
|
+
commands: [
|
|
3360
|
+
{
|
|
3361
|
+
command: "pools pool",
|
|
3362
|
+
args: { address: checksumAddress4(pool) },
|
|
3363
|
+
description: "View full pool state"
|
|
3364
|
+
},
|
|
3365
|
+
{
|
|
3366
|
+
command: "pools quote",
|
|
3367
|
+
args: {
|
|
3368
|
+
tokenIn: token0Meta.address,
|
|
3369
|
+
tokenOut: token1Meta.address,
|
|
3370
|
+
amountIn: "1"
|
|
3371
|
+
},
|
|
3372
|
+
description: `Quote a ${token0Meta.symbol} \u2192 ${token1Meta.symbol} swap`
|
|
3373
|
+
}
|
|
3374
|
+
]
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
);
|
|
3378
|
+
}
|
|
3379
|
+
});
|
|
3380
|
+
|
|
3381
|
+
// src/commands/vaults.ts
|
|
3382
|
+
import { checksumAddress as checksumAddress5, isAddress as isAddress4 } from "@spectratools/cli-shared";
|
|
3383
|
+
import { Cli as Cli5, z as z5 } from "incur";
|
|
3384
|
+
var env5 = z5.object({
|
|
3385
|
+
ABSTRACT_RPC_URL: z5.string().optional().describe("Abstract RPC URL override")
|
|
3386
|
+
});
|
|
3387
|
+
var relayAbi = [
|
|
3388
|
+
{
|
|
3389
|
+
type: "function",
|
|
3390
|
+
name: "name",
|
|
3391
|
+
stateMutability: "view",
|
|
3392
|
+
inputs: [],
|
|
3393
|
+
outputs: [{ type: "string" }]
|
|
3394
|
+
},
|
|
3395
|
+
{
|
|
3396
|
+
type: "function",
|
|
3397
|
+
name: "mTokenId",
|
|
3398
|
+
stateMutability: "view",
|
|
3399
|
+
inputs: [],
|
|
3400
|
+
outputs: [{ type: "uint256" }]
|
|
3401
|
+
},
|
|
3402
|
+
{
|
|
3403
|
+
type: "function",
|
|
3404
|
+
name: "token",
|
|
3405
|
+
stateMutability: "view",
|
|
3406
|
+
inputs: [],
|
|
3407
|
+
outputs: [{ type: "address" }]
|
|
3408
|
+
},
|
|
3409
|
+
{
|
|
3410
|
+
type: "function",
|
|
3411
|
+
name: "keeperLastRun",
|
|
3412
|
+
stateMutability: "view",
|
|
3413
|
+
inputs: [],
|
|
3414
|
+
outputs: [{ type: "uint256" }]
|
|
3415
|
+
}
|
|
3416
|
+
];
|
|
3417
|
+
var erc20Abi = [
|
|
3418
|
+
{
|
|
3419
|
+
type: "function",
|
|
3420
|
+
name: "symbol",
|
|
3421
|
+
stateMutability: "view",
|
|
3422
|
+
inputs: [],
|
|
3423
|
+
outputs: [{ type: "string" }]
|
|
3424
|
+
},
|
|
3425
|
+
{
|
|
3426
|
+
type: "function",
|
|
3427
|
+
name: "decimals",
|
|
3428
|
+
stateMutability: "view",
|
|
3429
|
+
inputs: [],
|
|
3430
|
+
outputs: [{ type: "uint8" }]
|
|
3431
|
+
},
|
|
3432
|
+
{
|
|
3433
|
+
type: "function",
|
|
3434
|
+
name: "balanceOf",
|
|
3435
|
+
stateMutability: "view",
|
|
3436
|
+
inputs: [{ type: "address", name: "owner" }],
|
|
3437
|
+
outputs: [{ type: "uint256" }]
|
|
3438
|
+
}
|
|
3439
|
+
];
|
|
3440
|
+
var votingEscrowLiteAbi = [
|
|
3441
|
+
{
|
|
3442
|
+
type: "function",
|
|
3443
|
+
name: "balanceOfNFT",
|
|
3444
|
+
stateMutability: "view",
|
|
3445
|
+
inputs: [{ type: "uint256", name: "tokenId" }],
|
|
3446
|
+
outputs: [{ type: "uint256" }]
|
|
3447
|
+
}
|
|
3448
|
+
];
|
|
3449
|
+
var relayRowSchema = z5.object({
|
|
3450
|
+
label: z5.string(),
|
|
3451
|
+
relay: z5.string(),
|
|
3452
|
+
name: z5.string(),
|
|
3453
|
+
factoryType: z5.enum(["autoCompounder", "autoConverter"]),
|
|
3454
|
+
managedTokenId: z5.string(),
|
|
3455
|
+
managedVotingPower: z5.string(),
|
|
3456
|
+
relayToken: z5.object({
|
|
3457
|
+
address: z5.string(),
|
|
3458
|
+
symbol: z5.string(),
|
|
3459
|
+
decimals: z5.number()
|
|
3460
|
+
}),
|
|
3461
|
+
relayTokenBalance: z5.string(),
|
|
3462
|
+
keeperLastRun: z5.number(),
|
|
3463
|
+
keeperLastRunRelative: z5.string(),
|
|
3464
|
+
secondsSinceKeeperRun: z5.number()
|
|
3465
|
+
});
|
|
3466
|
+
var KNOWN_RELAYS = [
|
|
3467
|
+
{
|
|
3468
|
+
label: "veABX Maxi Relay",
|
|
3469
|
+
relay: checksumAddress5(ABOREAN_VAULT_ADDRESSES.veAbxMaxiRelay),
|
|
3470
|
+
factoryType: "autoCompounder"
|
|
3471
|
+
},
|
|
3472
|
+
{
|
|
3473
|
+
label: "ABX Rewards Relay",
|
|
3474
|
+
relay: checksumAddress5(ABOREAN_VAULT_ADDRESSES.abxRewardsRelay),
|
|
3475
|
+
factoryType: "autoConverter"
|
|
3476
|
+
}
|
|
3477
|
+
];
|
|
3478
|
+
function shortAddress4(address) {
|
|
3479
|
+
return `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
3480
|
+
}
|
|
3481
|
+
async function readRelaySnapshot(client, relay) {
|
|
3482
|
+
const [name, managedTokenId, relayTokenAddress, keeperLastRun] = await Promise.all([
|
|
3483
|
+
client.readContract({
|
|
3484
|
+
abi: relayAbi,
|
|
3485
|
+
address: relay.relay,
|
|
3486
|
+
functionName: "name"
|
|
3487
|
+
}),
|
|
3488
|
+
client.readContract({
|
|
3489
|
+
abi: relayAbi,
|
|
3490
|
+
address: relay.relay,
|
|
3491
|
+
functionName: "mTokenId"
|
|
3492
|
+
}),
|
|
3493
|
+
client.readContract({
|
|
3494
|
+
abi: relayAbi,
|
|
3495
|
+
address: relay.relay,
|
|
3496
|
+
functionName: "token"
|
|
3497
|
+
}),
|
|
3498
|
+
client.readContract({
|
|
3499
|
+
abi: relayAbi,
|
|
3500
|
+
address: relay.relay,
|
|
3501
|
+
functionName: "keeperLastRun"
|
|
3502
|
+
})
|
|
3503
|
+
]);
|
|
3504
|
+
const tokenAddress = checksumAddress5(relayTokenAddress);
|
|
3505
|
+
const [symbol, decimals, relayTokenBalance, managedVotingPower] = await Promise.all([
|
|
3506
|
+
client.readContract({
|
|
3507
|
+
abi: erc20Abi,
|
|
3508
|
+
address: tokenAddress,
|
|
3509
|
+
functionName: "symbol"
|
|
3510
|
+
}),
|
|
3511
|
+
client.readContract({
|
|
3512
|
+
abi: erc20Abi,
|
|
3513
|
+
address: tokenAddress,
|
|
3514
|
+
functionName: "decimals"
|
|
3515
|
+
}),
|
|
3516
|
+
client.readContract({
|
|
3517
|
+
abi: erc20Abi,
|
|
3518
|
+
address: tokenAddress,
|
|
3519
|
+
functionName: "balanceOf",
|
|
3520
|
+
args: [relay.relay]
|
|
3521
|
+
}),
|
|
3522
|
+
client.readContract({
|
|
3523
|
+
abi: votingEscrowLiteAbi,
|
|
3524
|
+
address: ABOREAN_V2_ADDRESSES.votingEscrow,
|
|
3525
|
+
functionName: "balanceOfNFT",
|
|
3526
|
+
args: [managedTokenId]
|
|
3527
|
+
})
|
|
3528
|
+
]);
|
|
3529
|
+
const keeperTs = asNum(keeperLastRun);
|
|
3530
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3531
|
+
return {
|
|
3532
|
+
label: relay.label,
|
|
3533
|
+
relay: toChecksum(relay.relay),
|
|
3534
|
+
name,
|
|
3535
|
+
factoryType: relay.factoryType,
|
|
3536
|
+
managedTokenId: managedTokenId.toString(),
|
|
3537
|
+
managedVotingPower: managedVotingPower.toString(),
|
|
3538
|
+
relayToken: {
|
|
3539
|
+
address: toChecksum(tokenAddress),
|
|
3540
|
+
symbol,
|
|
3541
|
+
decimals
|
|
3542
|
+
},
|
|
3543
|
+
relayTokenBalance: relayTokenBalance.toString(),
|
|
3544
|
+
keeperLastRun: keeperTs,
|
|
3545
|
+
keeperLastRunRelative: relTime(keeperLastRun),
|
|
3546
|
+
secondsSinceKeeperRun: Math.max(0, now - keeperTs)
|
|
3547
|
+
};
|
|
3548
|
+
}
|
|
3549
|
+
async function readVaultSummary(client) {
|
|
3550
|
+
const relays = await Promise.all(KNOWN_RELAYS.map((relay) => readRelaySnapshot(client, relay)));
|
|
3551
|
+
const tokenTotals = /* @__PURE__ */ new Map();
|
|
3552
|
+
let managedVotingPowerTotal = 0n;
|
|
3553
|
+
for (const relay of relays) {
|
|
3554
|
+
managedVotingPowerTotal += BigInt(relay.managedVotingPower);
|
|
3555
|
+
const key = relay.relayToken.address.toLowerCase();
|
|
3556
|
+
const prev = tokenTotals.get(key);
|
|
3557
|
+
const balance = BigInt(relay.relayTokenBalance);
|
|
3558
|
+
if (prev) {
|
|
3559
|
+
prev.balance += balance;
|
|
3560
|
+
continue;
|
|
3561
|
+
}
|
|
3562
|
+
tokenTotals.set(key, {
|
|
3563
|
+
token: relay.relayToken.address,
|
|
3564
|
+
symbol: relay.relayToken.symbol,
|
|
3565
|
+
decimals: relay.relayToken.decimals,
|
|
3566
|
+
balance
|
|
3567
|
+
});
|
|
3568
|
+
}
|
|
3569
|
+
const relayTokenBalances = [...tokenTotals.values()].map((row) => ({
|
|
3570
|
+
token: row.token,
|
|
3571
|
+
symbol: row.symbol,
|
|
3572
|
+
decimals: row.decimals,
|
|
3573
|
+
balance: row.balance.toString()
|
|
3574
|
+
}));
|
|
3575
|
+
return {
|
|
3576
|
+
relayCount: relays.length,
|
|
3577
|
+
relays,
|
|
3578
|
+
totals: {
|
|
3579
|
+
managedVotingPower: managedVotingPowerTotal.toString(),
|
|
3580
|
+
relayTokenBalances
|
|
3581
|
+
}
|
|
3582
|
+
};
|
|
3583
|
+
}
|
|
3584
|
+
var vaults = Cli5.create("vaults", {
|
|
3585
|
+
description: "Inspect Aborean relay vaults (auto-compounder / auto-converter)."
|
|
3586
|
+
});
|
|
3587
|
+
vaults.command("list", {
|
|
3588
|
+
description: "List known Aborean relay vaults with keeper and veNFT state.",
|
|
3589
|
+
env: env5,
|
|
3590
|
+
output: z5.object({
|
|
3591
|
+
relayCount: z5.number(),
|
|
3592
|
+
relays: z5.array(relayRowSchema),
|
|
3593
|
+
totals: z5.object({
|
|
3594
|
+
managedVotingPower: z5.string(),
|
|
3595
|
+
relayTokenBalances: z5.array(
|
|
3596
|
+
z5.object({
|
|
3597
|
+
token: z5.string(),
|
|
3598
|
+
symbol: z5.string(),
|
|
3599
|
+
decimals: z5.number(),
|
|
3600
|
+
balance: z5.string()
|
|
3601
|
+
})
|
|
3602
|
+
)
|
|
3603
|
+
})
|
|
3604
|
+
}),
|
|
3605
|
+
examples: [{ description: "List all known vault relays on Abstract" }],
|
|
3606
|
+
async run(c) {
|
|
3607
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
3608
|
+
const snapshot = await readVaultSummary(client);
|
|
3609
|
+
const firstRelay = snapshot.relays[0];
|
|
3610
|
+
return c.ok(
|
|
3611
|
+
snapshot,
|
|
3612
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3613
|
+
cta: {
|
|
3614
|
+
description: "Related commands:",
|
|
3615
|
+
commands: [
|
|
3616
|
+
...firstRelay ? [
|
|
3617
|
+
{
|
|
3618
|
+
command: "vaults relay",
|
|
3619
|
+
args: { relay: firstRelay.relay },
|
|
3620
|
+
description: `Inspect ${firstRelay.label}`
|
|
3621
|
+
}
|
|
3622
|
+
] : [],
|
|
3623
|
+
{
|
|
3624
|
+
command: "lending markets",
|
|
3625
|
+
description: "View Morpho lending markets"
|
|
3626
|
+
},
|
|
3627
|
+
{
|
|
3628
|
+
command: "ve stats",
|
|
3629
|
+
description: "View veABX global stats"
|
|
3630
|
+
}
|
|
3631
|
+
]
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
);
|
|
3635
|
+
}
|
|
3636
|
+
});
|
|
3637
|
+
vaults.command("relay", {
|
|
3638
|
+
description: "Inspect one relay vault by address.",
|
|
3639
|
+
args: z5.object({
|
|
3640
|
+
relay: z5.string().describe("Relay vault contract address")
|
|
3641
|
+
}),
|
|
3642
|
+
env: env5,
|
|
3643
|
+
output: relayRowSchema,
|
|
3644
|
+
examples: [
|
|
3645
|
+
{
|
|
3646
|
+
args: { relay: ABOREAN_VAULT_ADDRESSES.veAbxMaxiRelay },
|
|
3647
|
+
description: "Inspect the veABX maxi relay"
|
|
3648
|
+
}
|
|
3649
|
+
],
|
|
3650
|
+
async run(c) {
|
|
3651
|
+
if (!isAddress4(c.args.relay)) {
|
|
3652
|
+
return c.error({
|
|
3653
|
+
code: "INVALID_ARGUMENT",
|
|
3654
|
+
message: "relay must be a valid address"
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
const relayAddress = checksumAddress5(c.args.relay);
|
|
3658
|
+
const known = KNOWN_RELAYS.find(
|
|
3659
|
+
(relay) => relay.relay.toLowerCase() === relayAddress.toLowerCase()
|
|
3660
|
+
);
|
|
3661
|
+
if (!known) {
|
|
3662
|
+
return c.error({
|
|
3663
|
+
code: "NOT_FOUND",
|
|
3664
|
+
message: `relay ${shortAddress4(relayAddress)} is not in the known Aborean vault set`
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
3668
|
+
const snapshot = await readRelaySnapshot(client, known);
|
|
3669
|
+
return c.ok(
|
|
3670
|
+
snapshot,
|
|
3671
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3672
|
+
cta: {
|
|
3673
|
+
description: "Related commands:",
|
|
3674
|
+
commands: [
|
|
3675
|
+
{
|
|
3676
|
+
command: "vaults list",
|
|
3677
|
+
description: "List all relay vaults"
|
|
3678
|
+
},
|
|
3679
|
+
{
|
|
3680
|
+
command: "ve lock",
|
|
3681
|
+
args: { tokenId: Number(snapshot.managedTokenId) },
|
|
3682
|
+
description: `Inspect managed veNFT #${snapshot.managedTokenId}`
|
|
3683
|
+
}
|
|
3684
|
+
]
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
);
|
|
3688
|
+
}
|
|
3689
|
+
});
|
|
3690
|
+
|
|
3691
|
+
// src/commands/ve.ts
|
|
3692
|
+
import { Cli as Cli6, z as z6 } from "incur";
|
|
3693
|
+
var env6 = z6.object({
|
|
3694
|
+
ABSTRACT_RPC_URL: z6.string().optional().describe("Abstract RPC URL override")
|
|
3695
|
+
});
|
|
3696
|
+
var ve = Cli6.create("ve", {
|
|
3697
|
+
description: "Inspect Aborean VotingEscrow (veABX) global and per-NFT lock state."
|
|
3698
|
+
});
|
|
3699
|
+
ve.command("stats", {
|
|
3700
|
+
description: "Get global VotingEscrow supply, locks, and decay checkpoint data.",
|
|
3701
|
+
env: env6,
|
|
3702
|
+
output: z6.object({
|
|
3703
|
+
token: z6.string(),
|
|
3704
|
+
totalVotingPower: z6.string(),
|
|
3705
|
+
totalLocked: z6.string(),
|
|
3706
|
+
permanentLocked: z6.string(),
|
|
3707
|
+
epoch: z6.number(),
|
|
3708
|
+
decayBias: z6.string(),
|
|
3709
|
+
decaySlope: z6.string(),
|
|
3710
|
+
lastCheckpointTimestamp: z6.number(),
|
|
3711
|
+
lastCheckpointBlock: z6.number()
|
|
3712
|
+
}),
|
|
3713
|
+
examples: [{ description: "Show global veABX state and decay metrics" }],
|
|
3714
|
+
async run(c) {
|
|
3715
|
+
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
3716
|
+
const [token, totalVotingPower, totalLocked, permanentLocked, epoch] = await Promise.all([
|
|
3717
|
+
client.readContract({
|
|
3718
|
+
abi: votingEscrowAbi,
|
|
3719
|
+
address: ABOREAN_V2_ADDRESSES.votingEscrow,
|
|
3720
|
+
functionName: "token"
|
|
3721
|
+
}),
|
|
3722
|
+
client.readContract({
|
|
3723
|
+
abi: votingEscrowAbi,
|
|
1916
3724
|
address: ABOREAN_V2_ADDRESSES.votingEscrow,
|
|
1917
3725
|
functionName: "totalSupply"
|
|
1918
3726
|
}),
|
|
@@ -1938,32 +3746,50 @@ ve.command("stats", {
|
|
|
1938
3746
|
functionName: "pointHistory",
|
|
1939
3747
|
args: [epoch]
|
|
1940
3748
|
});
|
|
1941
|
-
return c.ok(
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
3749
|
+
return c.ok(
|
|
3750
|
+
{
|
|
3751
|
+
token: toChecksum(token),
|
|
3752
|
+
totalVotingPower: totalVotingPower.toString(),
|
|
3753
|
+
totalLocked: totalLocked.toString(),
|
|
3754
|
+
permanentLocked: permanentLocked.toString(),
|
|
3755
|
+
epoch: asNum(epoch),
|
|
3756
|
+
decayBias: point.bias.toString(),
|
|
3757
|
+
decaySlope: point.slope.toString(),
|
|
3758
|
+
lastCheckpointTimestamp: asNum(point.ts),
|
|
3759
|
+
lastCheckpointBlock: asNum(point.blk)
|
|
3760
|
+
},
|
|
3761
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3762
|
+
cta: {
|
|
3763
|
+
description: "Explore veABX:",
|
|
3764
|
+
commands: [
|
|
3765
|
+
{
|
|
3766
|
+
command: "ve locks",
|
|
3767
|
+
args: { address: "<owner>" },
|
|
3768
|
+
description: "List veNFT locks for an address"
|
|
3769
|
+
},
|
|
3770
|
+
{
|
|
3771
|
+
command: "voter epoch",
|
|
3772
|
+
description: "View current emissions epoch timing"
|
|
3773
|
+
}
|
|
3774
|
+
]
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
);
|
|
1952
3778
|
}
|
|
1953
3779
|
});
|
|
1954
3780
|
ve.command("lock", {
|
|
1955
3781
|
description: "Get lock details and voting power for one veNFT token id.",
|
|
1956
|
-
args:
|
|
1957
|
-
tokenId:
|
|
3782
|
+
args: z6.object({
|
|
3783
|
+
tokenId: z6.coerce.number().int().nonnegative().describe("veNFT token id")
|
|
1958
3784
|
}),
|
|
1959
|
-
env:
|
|
1960
|
-
output:
|
|
1961
|
-
tokenId:
|
|
1962
|
-
owner:
|
|
1963
|
-
amount:
|
|
1964
|
-
unlockTime:
|
|
1965
|
-
isPermanent:
|
|
1966
|
-
votingPower:
|
|
3785
|
+
env: env6,
|
|
3786
|
+
output: z6.object({
|
|
3787
|
+
tokenId: z6.number(),
|
|
3788
|
+
owner: z6.string(),
|
|
3789
|
+
amount: z6.string(),
|
|
3790
|
+
unlockTime: z6.number(),
|
|
3791
|
+
isPermanent: z6.boolean(),
|
|
3792
|
+
votingPower: z6.string()
|
|
1967
3793
|
}),
|
|
1968
3794
|
examples: [{ args: { tokenId: 1 }, description: "Inspect lock details for veNFT #1" }],
|
|
1969
3795
|
async run(c) {
|
|
@@ -1989,34 +3815,53 @@ ve.command("lock", {
|
|
|
1989
3815
|
args: [tokenId]
|
|
1990
3816
|
})
|
|
1991
3817
|
]);
|
|
1992
|
-
return c.ok(
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
3818
|
+
return c.ok(
|
|
3819
|
+
{
|
|
3820
|
+
tokenId: c.args.tokenId,
|
|
3821
|
+
owner: toChecksum(owner),
|
|
3822
|
+
amount: locked.amount.toString(),
|
|
3823
|
+
unlockTime: asNum(locked.end),
|
|
3824
|
+
isPermanent: locked.isPermanent,
|
|
3825
|
+
votingPower: votingPower.toString()
|
|
3826
|
+
},
|
|
3827
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3828
|
+
cta: {
|
|
3829
|
+
description: "Related commands:",
|
|
3830
|
+
commands: [
|
|
3831
|
+
{
|
|
3832
|
+
command: "ve voting-power",
|
|
3833
|
+
args: { tokenId: c.args.tokenId },
|
|
3834
|
+
description: "Check current voting power"
|
|
3835
|
+
},
|
|
3836
|
+
{
|
|
3837
|
+
command: "voter rewards",
|
|
3838
|
+
args: { tokenId: c.args.tokenId },
|
|
3839
|
+
description: "Check claimable rewards for this veNFT"
|
|
3840
|
+
}
|
|
3841
|
+
]
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
);
|
|
2000
3845
|
}
|
|
2001
3846
|
});
|
|
2002
3847
|
ve.command("locks", {
|
|
2003
3848
|
description: "List all veNFT locks owned by an address.",
|
|
2004
|
-
args:
|
|
2005
|
-
address:
|
|
3849
|
+
args: z6.object({
|
|
3850
|
+
address: z6.string().describe("Owner address")
|
|
2006
3851
|
}),
|
|
2007
|
-
env:
|
|
2008
|
-
output:
|
|
2009
|
-
address:
|
|
2010
|
-
locks:
|
|
2011
|
-
|
|
2012
|
-
tokenId:
|
|
2013
|
-
amount:
|
|
2014
|
-
unlockTime:
|
|
2015
|
-
isPermanent:
|
|
2016
|
-
votingPower:
|
|
3852
|
+
env: env6,
|
|
3853
|
+
output: z6.object({
|
|
3854
|
+
address: z6.string(),
|
|
3855
|
+
locks: z6.array(
|
|
3856
|
+
z6.object({
|
|
3857
|
+
tokenId: z6.string(),
|
|
3858
|
+
amount: z6.string(),
|
|
3859
|
+
unlockTime: z6.number(),
|
|
3860
|
+
isPermanent: z6.boolean(),
|
|
3861
|
+
votingPower: z6.string()
|
|
2017
3862
|
})
|
|
2018
3863
|
),
|
|
2019
|
-
count:
|
|
3864
|
+
count: z6.number()
|
|
2020
3865
|
}),
|
|
2021
3866
|
examples: [
|
|
2022
3867
|
{
|
|
@@ -2079,22 +3924,42 @@ ve.command("locks", {
|
|
|
2079
3924
|
votingPower: votingPower.toString()
|
|
2080
3925
|
};
|
|
2081
3926
|
});
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
3927
|
+
const firstLock = locks[0];
|
|
3928
|
+
return c.ok(
|
|
3929
|
+
{
|
|
3930
|
+
address: toChecksum(c.args.address),
|
|
3931
|
+
locks,
|
|
3932
|
+
count: locks.length
|
|
3933
|
+
},
|
|
3934
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3935
|
+
cta: {
|
|
3936
|
+
description: "Explore locks:",
|
|
3937
|
+
commands: firstLock ? [
|
|
3938
|
+
{
|
|
3939
|
+
command: "ve lock",
|
|
3940
|
+
args: { tokenId: Number(firstLock.tokenId) },
|
|
3941
|
+
description: `Inspect veNFT #${firstLock.tokenId}`
|
|
3942
|
+
},
|
|
3943
|
+
{
|
|
3944
|
+
command: "voter rewards",
|
|
3945
|
+
args: { tokenId: Number(firstLock.tokenId) },
|
|
3946
|
+
description: `Check rewards for veNFT #${firstLock.tokenId}`
|
|
3947
|
+
}
|
|
3948
|
+
] : []
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
);
|
|
2087
3952
|
}
|
|
2088
3953
|
});
|
|
2089
3954
|
ve.command("voting-power", {
|
|
2090
3955
|
description: "Get current voting power for one veNFT token id.",
|
|
2091
|
-
args:
|
|
2092
|
-
tokenId:
|
|
3956
|
+
args: z6.object({
|
|
3957
|
+
tokenId: z6.coerce.number().int().nonnegative().describe("veNFT token id")
|
|
2093
3958
|
}),
|
|
2094
|
-
env:
|
|
2095
|
-
output:
|
|
2096
|
-
tokenId:
|
|
2097
|
-
votingPower:
|
|
3959
|
+
env: env6,
|
|
3960
|
+
output: z6.object({
|
|
3961
|
+
tokenId: z6.number(),
|
|
3962
|
+
votingPower: z6.string()
|
|
2098
3963
|
}),
|
|
2099
3964
|
examples: [{ args: { tokenId: 1 }, description: "Get current voting power for veNFT #1" }],
|
|
2100
3965
|
async run(c) {
|
|
@@ -2105,17 +3970,36 @@ ve.command("voting-power", {
|
|
|
2105
3970
|
functionName: "balanceOfNFT",
|
|
2106
3971
|
args: [BigInt(c.args.tokenId)]
|
|
2107
3972
|
});
|
|
2108
|
-
return c.ok(
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
3973
|
+
return c.ok(
|
|
3974
|
+
{
|
|
3975
|
+
tokenId: c.args.tokenId,
|
|
3976
|
+
votingPower: votingPower.toString()
|
|
3977
|
+
},
|
|
3978
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
3979
|
+
cta: {
|
|
3980
|
+
description: "Related commands:",
|
|
3981
|
+
commands: [
|
|
3982
|
+
{
|
|
3983
|
+
command: "ve lock",
|
|
3984
|
+
args: { tokenId: c.args.tokenId },
|
|
3985
|
+
description: "View full lock details"
|
|
3986
|
+
},
|
|
3987
|
+
{
|
|
3988
|
+
command: "voter rewards",
|
|
3989
|
+
args: { tokenId: c.args.tokenId },
|
|
3990
|
+
description: "Check claimable rewards"
|
|
3991
|
+
}
|
|
3992
|
+
]
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
);
|
|
2112
3996
|
}
|
|
2113
3997
|
});
|
|
2114
3998
|
|
|
2115
3999
|
// src/commands/voter.ts
|
|
2116
|
-
import { Cli as
|
|
2117
|
-
var
|
|
2118
|
-
ABSTRACT_RPC_URL:
|
|
4000
|
+
import { Cli as Cli7, z as z7 } from "incur";
|
|
4001
|
+
var env7 = z7.object({
|
|
4002
|
+
ABSTRACT_RPC_URL: z7.string().optional().describe("Abstract RPC URL override")
|
|
2119
4003
|
});
|
|
2120
4004
|
async function discoverPools(client) {
|
|
2121
4005
|
const [v2PoolCount, clPoolCount] = await Promise.all([
|
|
@@ -2154,20 +4038,20 @@ async function discoverPools(client) {
|
|
|
2154
4038
|
]);
|
|
2155
4039
|
return [...v2Pools, ...clPools];
|
|
2156
4040
|
}
|
|
2157
|
-
var voter =
|
|
4041
|
+
var voter = Cli7.create("voter", {
|
|
2158
4042
|
description: "Inspect Aborean voter epoch, pool weights, and claimable rewards context."
|
|
2159
4043
|
});
|
|
2160
4044
|
voter.command("epoch", {
|
|
2161
4045
|
description: "Show current emissions epoch timing from Minter.",
|
|
2162
|
-
env:
|
|
2163
|
-
output:
|
|
2164
|
-
activePeriod:
|
|
2165
|
-
epochEnd:
|
|
2166
|
-
secondsRemaining:
|
|
2167
|
-
timeRemaining:
|
|
2168
|
-
weekSeconds:
|
|
2169
|
-
epochCount:
|
|
2170
|
-
weeklyEmission:
|
|
4046
|
+
env: env7,
|
|
4047
|
+
output: z7.object({
|
|
4048
|
+
activePeriod: z7.number(),
|
|
4049
|
+
epochEnd: z7.number(),
|
|
4050
|
+
secondsRemaining: z7.number(),
|
|
4051
|
+
timeRemaining: z7.string(),
|
|
4052
|
+
weekSeconds: z7.number(),
|
|
4053
|
+
epochCount: z7.number(),
|
|
4054
|
+
weeklyEmission: z7.string()
|
|
2171
4055
|
}),
|
|
2172
4056
|
examples: [{ description: "Inspect current voter epoch boundaries" }],
|
|
2173
4057
|
async run(c) {
|
|
@@ -2196,36 +4080,53 @@ voter.command("epoch", {
|
|
|
2196
4080
|
]);
|
|
2197
4081
|
const now = Math.floor(Date.now() / 1e3);
|
|
2198
4082
|
const epochEnd = asNum(activePeriod + weekSeconds);
|
|
2199
|
-
return c.ok(
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
4083
|
+
return c.ok(
|
|
4084
|
+
{
|
|
4085
|
+
activePeriod: asNum(activePeriod),
|
|
4086
|
+
epochEnd,
|
|
4087
|
+
secondsRemaining: clampPositive(epochEnd - now),
|
|
4088
|
+
timeRemaining: relTime(activePeriod + weekSeconds),
|
|
4089
|
+
weekSeconds: asNum(weekSeconds),
|
|
4090
|
+
epochCount: asNum(epochCount),
|
|
4091
|
+
weeklyEmission: weeklyEmission.toString()
|
|
4092
|
+
},
|
|
4093
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
4094
|
+
cta: {
|
|
4095
|
+
description: "Related commands:",
|
|
4096
|
+
commands: [
|
|
4097
|
+
{
|
|
4098
|
+
command: "voter weights",
|
|
4099
|
+
description: "View pool voting weight distribution"
|
|
4100
|
+
},
|
|
4101
|
+
{
|
|
4102
|
+
command: "gauges list",
|
|
4103
|
+
description: "List active gauges"
|
|
4104
|
+
}
|
|
4105
|
+
]
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
);
|
|
2208
4109
|
}
|
|
2209
4110
|
});
|
|
2210
4111
|
voter.command("weights", {
|
|
2211
4112
|
description: "Show current pool voting weight distribution.",
|
|
2212
|
-
env:
|
|
2213
|
-
output:
|
|
2214
|
-
totalWeight:
|
|
2215
|
-
pools:
|
|
2216
|
-
|
|
2217
|
-
pool:
|
|
2218
|
-
gauge:
|
|
2219
|
-
weight:
|
|
4113
|
+
env: env7,
|
|
4114
|
+
output: z7.object({
|
|
4115
|
+
totalWeight: z7.string(),
|
|
4116
|
+
pools: z7.array(
|
|
4117
|
+
z7.object({
|
|
4118
|
+
pool: z7.string(),
|
|
4119
|
+
gauge: z7.string(),
|
|
4120
|
+
weight: z7.string()
|
|
2220
4121
|
})
|
|
2221
4122
|
),
|
|
2222
|
-
count:
|
|
4123
|
+
count: z7.number()
|
|
2223
4124
|
}),
|
|
2224
4125
|
examples: [{ description: "List all pools with non-zero voting weight" }],
|
|
2225
4126
|
async run(c) {
|
|
2226
4127
|
const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
|
|
2227
|
-
const
|
|
2228
|
-
if (!
|
|
4128
|
+
const pools2 = await discoverPools(client);
|
|
4129
|
+
if (!pools2.length) {
|
|
2229
4130
|
return c.ok({ totalWeight: "0", pools: [], count: 0 });
|
|
2230
4131
|
}
|
|
2231
4132
|
const [totalWeight, poolData] = await Promise.all([
|
|
@@ -2236,7 +4137,7 @@ voter.command("weights", {
|
|
|
2236
4137
|
}),
|
|
2237
4138
|
client.multicall({
|
|
2238
4139
|
allowFailure: false,
|
|
2239
|
-
contracts:
|
|
4140
|
+
contracts: pools2.flatMap((pool) => [
|
|
2240
4141
|
{
|
|
2241
4142
|
abi: voterAbi,
|
|
2242
4143
|
address: ABOREAN_V2_ADDRESSES.voter,
|
|
@@ -2252,7 +4153,7 @@ voter.command("weights", {
|
|
|
2252
4153
|
])
|
|
2253
4154
|
})
|
|
2254
4155
|
]);
|
|
2255
|
-
const entries =
|
|
4156
|
+
const entries = pools2.map((pool, index) => {
|
|
2256
4157
|
const offset = index * 2;
|
|
2257
4158
|
const gauge = poolData[offset] ?? ZERO_ADDRESS;
|
|
2258
4159
|
const weight = poolData[offset + 1] ?? 0n;
|
|
@@ -2268,28 +4169,49 @@ voter.command("weights", {
|
|
|
2268
4169
|
gauge: toChecksum(entry.gauge),
|
|
2269
4170
|
weight: entry.weight.toString()
|
|
2270
4171
|
}));
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
4172
|
+
const firstEntry = entries[0];
|
|
4173
|
+
return c.ok(
|
|
4174
|
+
{
|
|
4175
|
+
totalWeight: totalWeight.toString(),
|
|
4176
|
+
pools: entries,
|
|
4177
|
+
count: entries.length
|
|
4178
|
+
},
|
|
4179
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
4180
|
+
cta: {
|
|
4181
|
+
description: "Related commands:",
|
|
4182
|
+
commands: [
|
|
4183
|
+
{
|
|
4184
|
+
command: "voter epoch",
|
|
4185
|
+
description: "View current epoch timing"
|
|
4186
|
+
},
|
|
4187
|
+
...firstEntry ? [
|
|
4188
|
+
{
|
|
4189
|
+
command: "voter bribes",
|
|
4190
|
+
args: { pool: firstEntry.pool },
|
|
4191
|
+
description: "View bribe rewards for top-weighted pool"
|
|
4192
|
+
}
|
|
4193
|
+
] : []
|
|
4194
|
+
]
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
);
|
|
2276
4198
|
}
|
|
2277
4199
|
});
|
|
2278
4200
|
voter.command("rewards", {
|
|
2279
4201
|
description: "Show claimable rebase rewards and voting context for a veNFT.",
|
|
2280
|
-
args:
|
|
2281
|
-
tokenId:
|
|
4202
|
+
args: z7.object({
|
|
4203
|
+
tokenId: z7.coerce.number().int().nonnegative().describe("veNFT token id")
|
|
2282
4204
|
}),
|
|
2283
|
-
env:
|
|
2284
|
-
output:
|
|
2285
|
-
tokenId:
|
|
2286
|
-
rewardToken:
|
|
2287
|
-
claimableRebase:
|
|
2288
|
-
timeCursor:
|
|
2289
|
-
lastTokenTime:
|
|
2290
|
-
distributorStartTime:
|
|
2291
|
-
usedWeight:
|
|
2292
|
-
lastVoted:
|
|
4205
|
+
env: env7,
|
|
4206
|
+
output: z7.object({
|
|
4207
|
+
tokenId: z7.number(),
|
|
4208
|
+
rewardToken: z7.string(),
|
|
4209
|
+
claimableRebase: z7.string(),
|
|
4210
|
+
timeCursor: z7.number(),
|
|
4211
|
+
lastTokenTime: z7.number(),
|
|
4212
|
+
distributorStartTime: z7.number(),
|
|
4213
|
+
usedWeight: z7.string(),
|
|
4214
|
+
lastVoted: z7.number()
|
|
2293
4215
|
}),
|
|
2294
4216
|
examples: [{ args: { tokenId: 1 }, description: "Check claimable voter/distributor rewards" }],
|
|
2295
4217
|
async run(c) {
|
|
@@ -2344,36 +4266,54 @@ voter.command("rewards", {
|
|
|
2344
4266
|
args: [tokenId]
|
|
2345
4267
|
})
|
|
2346
4268
|
]);
|
|
2347
|
-
return c.ok(
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
4269
|
+
return c.ok(
|
|
4270
|
+
{
|
|
4271
|
+
tokenId: c.args.tokenId,
|
|
4272
|
+
rewardToken: toChecksum(rewardToken),
|
|
4273
|
+
claimableRebase: claimableRebase.toString(),
|
|
4274
|
+
timeCursor: asNum(timeCursor),
|
|
4275
|
+
lastTokenTime: asNum(lastTokenTime),
|
|
4276
|
+
distributorStartTime: asNum(distributorStartTime),
|
|
4277
|
+
usedWeight: usedWeight.toString(),
|
|
4278
|
+
lastVoted: asNum(lastVoted)
|
|
4279
|
+
},
|
|
4280
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
4281
|
+
cta: {
|
|
4282
|
+
description: "Related commands:",
|
|
4283
|
+
commands: [
|
|
4284
|
+
{
|
|
4285
|
+
command: "ve lock",
|
|
4286
|
+
args: { tokenId: c.args.tokenId },
|
|
4287
|
+
description: "View lock details for this veNFT"
|
|
4288
|
+
},
|
|
4289
|
+
{
|
|
4290
|
+
command: "voter weights",
|
|
4291
|
+
description: "Check pool voting weight distribution"
|
|
4292
|
+
}
|
|
4293
|
+
]
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
);
|
|
2357
4297
|
}
|
|
2358
4298
|
});
|
|
2359
4299
|
voter.command("bribes", {
|
|
2360
4300
|
description: "Show active bribe reward tokens and current-epoch amounts for a pool.",
|
|
2361
|
-
args:
|
|
2362
|
-
pool:
|
|
4301
|
+
args: z7.object({
|
|
4302
|
+
pool: z7.string().describe("Pool address")
|
|
2363
4303
|
}),
|
|
2364
|
-
env:
|
|
2365
|
-
output:
|
|
2366
|
-
pool:
|
|
2367
|
-
gauge:
|
|
2368
|
-
bribeContract:
|
|
2369
|
-
epochStart:
|
|
2370
|
-
rewardTokens:
|
|
2371
|
-
|
|
2372
|
-
token:
|
|
2373
|
-
epochAmount:
|
|
4304
|
+
env: env7,
|
|
4305
|
+
output: z7.object({
|
|
4306
|
+
pool: z7.string(),
|
|
4307
|
+
gauge: z7.string(),
|
|
4308
|
+
bribeContract: z7.string(),
|
|
4309
|
+
epochStart: z7.number(),
|
|
4310
|
+
rewardTokens: z7.array(
|
|
4311
|
+
z7.object({
|
|
4312
|
+
token: z7.string(),
|
|
4313
|
+
epochAmount: z7.string()
|
|
2374
4314
|
})
|
|
2375
4315
|
),
|
|
2376
|
-
count:
|
|
4316
|
+
count: z7.number()
|
|
2377
4317
|
}),
|
|
2378
4318
|
examples: [
|
|
2379
4319
|
{
|
|
@@ -2458,14 +4398,32 @@ voter.command("bribes", {
|
|
|
2458
4398
|
token: toChecksum(token),
|
|
2459
4399
|
epochAmount: epochAmounts[index].toString()
|
|
2460
4400
|
}));
|
|
2461
|
-
return c.ok(
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
4401
|
+
return c.ok(
|
|
4402
|
+
{
|
|
4403
|
+
pool: toChecksum(c.args.pool),
|
|
4404
|
+
gauge: toChecksum(gauge),
|
|
4405
|
+
bribeContract: toChecksum(bribeContract),
|
|
4406
|
+
epochStart: asNum(epochStart),
|
|
4407
|
+
rewardTokens: items,
|
|
4408
|
+
count: items.length
|
|
4409
|
+
},
|
|
4410
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
4411
|
+
cta: {
|
|
4412
|
+
description: "Related commands:",
|
|
4413
|
+
commands: [
|
|
4414
|
+
{
|
|
4415
|
+
command: "gauges info",
|
|
4416
|
+
args: { gauge: toChecksum(gauge) },
|
|
4417
|
+
description: "Inspect gauge details"
|
|
4418
|
+
},
|
|
4419
|
+
{
|
|
4420
|
+
command: "voter epoch",
|
|
4421
|
+
description: "View current epoch timing"
|
|
4422
|
+
}
|
|
4423
|
+
]
|
|
4424
|
+
}
|
|
4425
|
+
}
|
|
4426
|
+
);
|
|
2469
4427
|
}
|
|
2470
4428
|
});
|
|
2471
4429
|
|
|
@@ -2599,27 +4557,283 @@ function applyFriendlyErrorHandling(cli2) {
|
|
|
2599
4557
|
// src/cli.ts
|
|
2600
4558
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
2601
4559
|
var pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf8"));
|
|
2602
|
-
var cli =
|
|
4560
|
+
var cli = Cli8.create("aborean", {
|
|
2603
4561
|
version: pkg.version,
|
|
2604
4562
|
description: "Aborean Finance DEX CLI for Abstract chain."
|
|
2605
4563
|
});
|
|
2606
4564
|
cli.command(gauges);
|
|
4565
|
+
cli.command(pools);
|
|
2607
4566
|
cli.command(ve);
|
|
2608
4567
|
cli.command(voter);
|
|
2609
|
-
var rootEnv = z5.object({
|
|
2610
|
-
ABSTRACT_RPC_URL: z5.string().optional().describe("Abstract RPC URL override")
|
|
2611
|
-
});
|
|
2612
4568
|
cli.command(cl);
|
|
4569
|
+
cli.command(vaults);
|
|
4570
|
+
cli.command(lending);
|
|
4571
|
+
var rootEnv = z8.object({
|
|
4572
|
+
ABSTRACT_RPC_URL: z8.string().optional().describe("Abstract RPC URL override")
|
|
4573
|
+
});
|
|
4574
|
+
var erc20MetadataAbi4 = [
|
|
4575
|
+
{
|
|
4576
|
+
type: "function",
|
|
4577
|
+
name: "symbol",
|
|
4578
|
+
stateMutability: "view",
|
|
4579
|
+
inputs: [],
|
|
4580
|
+
outputs: [{ type: "string" }]
|
|
4581
|
+
},
|
|
4582
|
+
{
|
|
4583
|
+
type: "function",
|
|
4584
|
+
name: "decimals",
|
|
4585
|
+
stateMutability: "view",
|
|
4586
|
+
inputs: [],
|
|
4587
|
+
outputs: [{ type: "uint8" }]
|
|
4588
|
+
}
|
|
4589
|
+
];
|
|
4590
|
+
var v2PoolLiteAbi = [
|
|
4591
|
+
{
|
|
4592
|
+
type: "function",
|
|
4593
|
+
name: "token0",
|
|
4594
|
+
stateMutability: "view",
|
|
4595
|
+
inputs: [],
|
|
4596
|
+
outputs: [{ type: "address" }]
|
|
4597
|
+
},
|
|
4598
|
+
{
|
|
4599
|
+
type: "function",
|
|
4600
|
+
name: "token1",
|
|
4601
|
+
stateMutability: "view",
|
|
4602
|
+
inputs: [],
|
|
4603
|
+
outputs: [{ type: "address" }]
|
|
4604
|
+
},
|
|
4605
|
+
{
|
|
4606
|
+
type: "function",
|
|
4607
|
+
name: "stable",
|
|
4608
|
+
stateMutability: "view",
|
|
4609
|
+
inputs: [],
|
|
4610
|
+
outputs: [{ type: "bool" }]
|
|
4611
|
+
},
|
|
4612
|
+
{
|
|
4613
|
+
type: "function",
|
|
4614
|
+
name: "getReserves",
|
|
4615
|
+
stateMutability: "view",
|
|
4616
|
+
inputs: [],
|
|
4617
|
+
outputs: [
|
|
4618
|
+
{ type: "uint256", name: "reserve0" },
|
|
4619
|
+
{ type: "uint256", name: "reserve1" },
|
|
4620
|
+
{ type: "uint256", name: "blockTimestampLast" }
|
|
4621
|
+
]
|
|
4622
|
+
}
|
|
4623
|
+
];
|
|
4624
|
+
function finiteOrZero(value) {
|
|
4625
|
+
return Number.isFinite(value) ? value : 0;
|
|
4626
|
+
}
|
|
4627
|
+
function shortAddress5(address) {
|
|
4628
|
+
return `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
4629
|
+
}
|
|
4630
|
+
function normalizeReserveTuple(value) {
|
|
4631
|
+
if (Array.isArray(value)) {
|
|
4632
|
+
return {
|
|
4633
|
+
reserve0: BigInt(value[0]),
|
|
4634
|
+
reserve1: BigInt(value[1])
|
|
4635
|
+
};
|
|
4636
|
+
}
|
|
4637
|
+
const row = value;
|
|
4638
|
+
return {
|
|
4639
|
+
reserve0: BigInt(row.reserve0),
|
|
4640
|
+
reserve1: BigInt(row.reserve1)
|
|
4641
|
+
};
|
|
4642
|
+
}
|
|
4643
|
+
async function readTokenMetadata4(client, tokenAddresses) {
|
|
4644
|
+
const unique = [...new Set(tokenAddresses.map((address) => address.toLowerCase()))];
|
|
4645
|
+
if (!unique.length) {
|
|
4646
|
+
return /* @__PURE__ */ new Map();
|
|
4647
|
+
}
|
|
4648
|
+
const contracts = unique.flatMap((address) => [
|
|
4649
|
+
{
|
|
4650
|
+
abi: erc20MetadataAbi4,
|
|
4651
|
+
address,
|
|
4652
|
+
functionName: "symbol"
|
|
4653
|
+
},
|
|
4654
|
+
{
|
|
4655
|
+
abi: erc20MetadataAbi4,
|
|
4656
|
+
address,
|
|
4657
|
+
functionName: "decimals"
|
|
4658
|
+
}
|
|
4659
|
+
]);
|
|
4660
|
+
const results = await client.multicall({
|
|
4661
|
+
allowFailure: true,
|
|
4662
|
+
contracts
|
|
4663
|
+
});
|
|
4664
|
+
const map = /* @__PURE__ */ new Map();
|
|
4665
|
+
for (let i = 0; i < unique.length; i += 1) {
|
|
4666
|
+
const address = checksumAddress6(unique[i]);
|
|
4667
|
+
const symbolResult = results[i * 2];
|
|
4668
|
+
const decimalsResult = results[i * 2 + 1];
|
|
4669
|
+
const symbol = symbolResult && symbolResult.status === "success" && typeof symbolResult.result === "string" ? symbolResult.result : shortAddress5(address);
|
|
4670
|
+
const decimals = decimalsResult && decimalsResult.status === "success" && typeof decimalsResult.result === "number" ? decimalsResult.result : 18;
|
|
4671
|
+
map.set(address.toLowerCase(), {
|
|
4672
|
+
address,
|
|
4673
|
+
symbol,
|
|
4674
|
+
decimals
|
|
4675
|
+
});
|
|
4676
|
+
}
|
|
4677
|
+
return map;
|
|
4678
|
+
}
|
|
4679
|
+
async function readTopV2PoolsSnapshot(client, limit) {
|
|
4680
|
+
const v2PoolCount = await client.readContract({
|
|
4681
|
+
abi: poolFactoryAbi,
|
|
4682
|
+
address: ABOREAN_V2_ADDRESSES.poolFactory,
|
|
4683
|
+
functionName: "allPoolsLength"
|
|
4684
|
+
});
|
|
4685
|
+
if (v2PoolCount === 0n) {
|
|
4686
|
+
return { topPools: [], reserveUnitTvl: 0 };
|
|
4687
|
+
}
|
|
4688
|
+
const indices = Array.from({ length: Number(v2PoolCount) }, (_, i) => BigInt(i));
|
|
4689
|
+
const pools2 = await client.multicall({
|
|
4690
|
+
allowFailure: false,
|
|
4691
|
+
contracts: indices.map((index) => ({
|
|
4692
|
+
abi: poolFactoryAbi,
|
|
4693
|
+
address: ABOREAN_V2_ADDRESSES.poolFactory,
|
|
4694
|
+
functionName: "allPools",
|
|
4695
|
+
args: [index]
|
|
4696
|
+
}))
|
|
4697
|
+
});
|
|
4698
|
+
const poolData = await client.multicall({
|
|
4699
|
+
allowFailure: false,
|
|
4700
|
+
contracts: pools2.flatMap((pool) => [
|
|
4701
|
+
{
|
|
4702
|
+
abi: v2PoolLiteAbi,
|
|
4703
|
+
address: pool,
|
|
4704
|
+
functionName: "token0"
|
|
4705
|
+
},
|
|
4706
|
+
{
|
|
4707
|
+
abi: v2PoolLiteAbi,
|
|
4708
|
+
address: pool,
|
|
4709
|
+
functionName: "token1"
|
|
4710
|
+
},
|
|
4711
|
+
{
|
|
4712
|
+
abi: v2PoolLiteAbi,
|
|
4713
|
+
address: pool,
|
|
4714
|
+
functionName: "stable"
|
|
4715
|
+
},
|
|
4716
|
+
{
|
|
4717
|
+
abi: v2PoolLiteAbi,
|
|
4718
|
+
address: pool,
|
|
4719
|
+
functionName: "getReserves"
|
|
4720
|
+
}
|
|
4721
|
+
])
|
|
4722
|
+
});
|
|
4723
|
+
const tokenAddresses = [];
|
|
4724
|
+
for (let i = 0; i < pools2.length; i += 1) {
|
|
4725
|
+
tokenAddresses.push(checksumAddress6(poolData[i * 4]));
|
|
4726
|
+
tokenAddresses.push(checksumAddress6(poolData[i * 4 + 1]));
|
|
4727
|
+
}
|
|
4728
|
+
const tokenMeta = await readTokenMetadata4(client, tokenAddresses);
|
|
4729
|
+
const rows = pools2.map((pool, index) => {
|
|
4730
|
+
const token0Address = checksumAddress6(poolData[index * 4]);
|
|
4731
|
+
const token1Address = checksumAddress6(poolData[index * 4 + 1]);
|
|
4732
|
+
const stable = Boolean(poolData[index * 4 + 2]);
|
|
4733
|
+
const reserves = normalizeReserveTuple(poolData[index * 4 + 3]);
|
|
4734
|
+
const token0 = tokenMeta.get(token0Address.toLowerCase()) ?? {
|
|
4735
|
+
address: token0Address,
|
|
4736
|
+
symbol: shortAddress5(token0Address),
|
|
4737
|
+
decimals: 18
|
|
4738
|
+
};
|
|
4739
|
+
const token1 = tokenMeta.get(token1Address.toLowerCase()) ?? {
|
|
4740
|
+
address: token1Address,
|
|
4741
|
+
symbol: shortAddress5(token1Address),
|
|
4742
|
+
decimals: 18
|
|
4743
|
+
};
|
|
4744
|
+
const reserve0Decimal = finiteOrZero(Number(formatUnits4(reserves.reserve0, token0.decimals)));
|
|
4745
|
+
const reserve1Decimal = finiteOrZero(Number(formatUnits4(reserves.reserve1, token1.decimals)));
|
|
4746
|
+
const tvlUnits = reserve0Decimal + reserve1Decimal;
|
|
4747
|
+
return {
|
|
4748
|
+
pool: checksumAddress6(pool),
|
|
4749
|
+
pair: `${token0.symbol}/${token1.symbol}`,
|
|
4750
|
+
poolType: stable ? "stable" : "volatile",
|
|
4751
|
+
token0: {
|
|
4752
|
+
address: checksumAddress6(token0.address),
|
|
4753
|
+
symbol: token0.symbol,
|
|
4754
|
+
decimals: token0.decimals
|
|
4755
|
+
},
|
|
4756
|
+
token1: {
|
|
4757
|
+
address: checksumAddress6(token1.address),
|
|
4758
|
+
symbol: token1.symbol,
|
|
4759
|
+
decimals: token1.decimals
|
|
4760
|
+
},
|
|
4761
|
+
reserves: {
|
|
4762
|
+
token0: formatUnits4(reserves.reserve0, token0.decimals),
|
|
4763
|
+
token1: formatUnits4(reserves.reserve1, token1.decimals)
|
|
4764
|
+
},
|
|
4765
|
+
tvlEstimateUnits: tvlUnits
|
|
4766
|
+
};
|
|
4767
|
+
});
|
|
4768
|
+
const reserveUnitTvl = rows.reduce((sum, row) => sum + Number(row.tvlEstimateUnits), 0);
|
|
4769
|
+
return {
|
|
4770
|
+
topPools: rows.sort((a, b) => Number(b.tvlEstimateUnits) - Number(a.tvlEstimateUnits)).slice(0, limit),
|
|
4771
|
+
reserveUnitTvl
|
|
4772
|
+
};
|
|
4773
|
+
}
|
|
2613
4774
|
cli.command("status", {
|
|
2614
|
-
description: "
|
|
4775
|
+
description: "Cross-protocol Aborean snapshot (TVL estimates, epoch, top pools, ve lock, vaults, Morpho lending).",
|
|
2615
4776
|
env: rootEnv,
|
|
2616
|
-
output:
|
|
2617
|
-
v2PoolCount:
|
|
2618
|
-
clPoolCount:
|
|
2619
|
-
gaugeCount:
|
|
2620
|
-
totalVotingWeight:
|
|
2621
|
-
veABXTotalSupply:
|
|
2622
|
-
veABXLockedSupply:
|
|
4777
|
+
output: z8.object({
|
|
4778
|
+
v2PoolCount: z8.number().describe("Number of V2 AMM pools"),
|
|
4779
|
+
clPoolCount: z8.number().describe("Number of Slipstream (CL) pools"),
|
|
4780
|
+
gaugeCount: z8.number().describe("Number of pools with gauges"),
|
|
4781
|
+
totalVotingWeight: z8.string().describe("Total voting weight (wei)"),
|
|
4782
|
+
veABXTotalSupply: z8.string().describe("Total veABX supply (wei)"),
|
|
4783
|
+
veABXLockedSupply: z8.string().describe("Total ABX locked in VotingEscrow (wei)"),
|
|
4784
|
+
epoch: z8.object({
|
|
4785
|
+
activePeriod: z8.number(),
|
|
4786
|
+
epochEnd: z8.number(),
|
|
4787
|
+
secondsRemaining: z8.number(),
|
|
4788
|
+
epochCount: z8.number(),
|
|
4789
|
+
weeklyEmission: z8.string()
|
|
4790
|
+
}),
|
|
4791
|
+
topPools: z8.array(
|
|
4792
|
+
z8.object({
|
|
4793
|
+
pool: z8.string(),
|
|
4794
|
+
pair: z8.string(),
|
|
4795
|
+
poolType: z8.enum(["stable", "volatile"]),
|
|
4796
|
+
token0: z8.object({
|
|
4797
|
+
address: z8.string(),
|
|
4798
|
+
symbol: z8.string(),
|
|
4799
|
+
decimals: z8.number()
|
|
4800
|
+
}),
|
|
4801
|
+
token1: z8.object({
|
|
4802
|
+
address: z8.string(),
|
|
4803
|
+
symbol: z8.string(),
|
|
4804
|
+
decimals: z8.number()
|
|
4805
|
+
}),
|
|
4806
|
+
reserves: z8.object({
|
|
4807
|
+
token0: z8.string(),
|
|
4808
|
+
token1: z8.string()
|
|
4809
|
+
}),
|
|
4810
|
+
tvlEstimateUnits: z8.number()
|
|
4811
|
+
})
|
|
4812
|
+
),
|
|
4813
|
+
tvl: z8.object({
|
|
4814
|
+
v2ReserveUnitEstimate: z8.number(),
|
|
4815
|
+
vaultManagedVotingPower: z8.string()
|
|
4816
|
+
}),
|
|
4817
|
+
vaults: z8.object({
|
|
4818
|
+
relayCount: z8.number(),
|
|
4819
|
+
managedVotingPower: z8.string(),
|
|
4820
|
+
note: z8.string().nullable()
|
|
4821
|
+
}),
|
|
4822
|
+
lending: z8.object({
|
|
4823
|
+
available: z8.boolean(),
|
|
4824
|
+
morpho: z8.string(),
|
|
4825
|
+
marketCount: z8.number(),
|
|
4826
|
+
supplyByLoanToken: z8.array(
|
|
4827
|
+
z8.object({
|
|
4828
|
+
token: z8.string(),
|
|
4829
|
+
symbol: z8.string(),
|
|
4830
|
+
decimals: z8.number(),
|
|
4831
|
+
totalSupplyAssets: z8.string(),
|
|
4832
|
+
totalBorrowAssets: z8.string()
|
|
4833
|
+
})
|
|
4834
|
+
),
|
|
4835
|
+
note: z8.string().nullable()
|
|
4836
|
+
})
|
|
2623
4837
|
}),
|
|
2624
4838
|
examples: [{ description: "Fetch the current Aborean protocol status" }],
|
|
2625
4839
|
async run(c) {
|
|
@@ -2630,7 +4844,12 @@ cli.command("status", {
|
|
|
2630
4844
|
gaugeCount,
|
|
2631
4845
|
totalVotingWeight,
|
|
2632
4846
|
veABXTotalSupply,
|
|
2633
|
-
veABXLockedSupply
|
|
4847
|
+
veABXLockedSupply,
|
|
4848
|
+
activePeriod,
|
|
4849
|
+
weekSeconds,
|
|
4850
|
+
epochCount,
|
|
4851
|
+
weeklyEmission,
|
|
4852
|
+
v2PoolSnapshot
|
|
2634
4853
|
] = await Promise.all([
|
|
2635
4854
|
client.readContract({
|
|
2636
4855
|
abi: poolFactoryAbi,
|
|
@@ -2661,16 +4880,113 @@ cli.command("status", {
|
|
|
2661
4880
|
abi: votingEscrowAbi,
|
|
2662
4881
|
address: ABOREAN_V2_ADDRESSES.votingEscrow,
|
|
2663
4882
|
functionName: "supply"
|
|
2664
|
-
})
|
|
4883
|
+
}),
|
|
4884
|
+
client.readContract({
|
|
4885
|
+
abi: minterAbi,
|
|
4886
|
+
address: ABOREAN_V2_ADDRESSES.minter,
|
|
4887
|
+
functionName: "activePeriod"
|
|
4888
|
+
}),
|
|
4889
|
+
client.readContract({
|
|
4890
|
+
abi: minterAbi,
|
|
4891
|
+
address: ABOREAN_V2_ADDRESSES.minter,
|
|
4892
|
+
functionName: "WEEK"
|
|
4893
|
+
}),
|
|
4894
|
+
client.readContract({
|
|
4895
|
+
abi: minterAbi,
|
|
4896
|
+
address: ABOREAN_V2_ADDRESSES.minter,
|
|
4897
|
+
functionName: "epochCount"
|
|
4898
|
+
}),
|
|
4899
|
+
client.readContract({
|
|
4900
|
+
abi: minterAbi,
|
|
4901
|
+
address: ABOREAN_V2_ADDRESSES.minter,
|
|
4902
|
+
functionName: "weekly"
|
|
4903
|
+
}),
|
|
4904
|
+
readTopV2PoolsSnapshot(client, 5)
|
|
2665
4905
|
]);
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
})
|
|
4906
|
+
let vaultRelayCount = 0;
|
|
4907
|
+
let vaultManagedVotingPower = "0";
|
|
4908
|
+
let vaultNote = null;
|
|
4909
|
+
try {
|
|
4910
|
+
const vaultSnapshot = await readVaultSummary(client);
|
|
4911
|
+
vaultRelayCount = vaultSnapshot.relayCount;
|
|
4912
|
+
vaultManagedVotingPower = vaultSnapshot.totals.managedVotingPower;
|
|
4913
|
+
} catch (error) {
|
|
4914
|
+
vaultNote = error instanceof Error ? error.message : "vault snapshot unavailable";
|
|
4915
|
+
}
|
|
4916
|
+
let lendingAvailable = false;
|
|
4917
|
+
let lendingMarketCount = 0;
|
|
4918
|
+
let lendingMorpho = "";
|
|
4919
|
+
let lendingSupplyByLoanToken = [];
|
|
4920
|
+
let lendingNote = null;
|
|
4921
|
+
try {
|
|
4922
|
+
const lendingSnapshot = await readLendingSummary(client);
|
|
4923
|
+
lendingAvailable = lendingSnapshot.available;
|
|
4924
|
+
lendingMarketCount = lendingSnapshot.marketCount;
|
|
4925
|
+
lendingMorpho = lendingSnapshot.morpho;
|
|
4926
|
+
lendingSupplyByLoanToken = lendingSnapshot.supplyByLoanToken;
|
|
4927
|
+
} catch (error) {
|
|
4928
|
+
lendingMorpho = "";
|
|
4929
|
+
lendingNote = error instanceof Error ? error.message : "lending snapshot unavailable";
|
|
4930
|
+
}
|
|
4931
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
4932
|
+
const epochEnd = Number(activePeriod) + Number(weekSeconds);
|
|
4933
|
+
return c.ok(
|
|
4934
|
+
{
|
|
4935
|
+
v2PoolCount: Number(v2PoolCount),
|
|
4936
|
+
clPoolCount: Number(clPoolCount),
|
|
4937
|
+
gaugeCount: Number(gaugeCount),
|
|
4938
|
+
totalVotingWeight: String(totalVotingWeight),
|
|
4939
|
+
veABXTotalSupply: String(veABXTotalSupply),
|
|
4940
|
+
veABXLockedSupply: String(veABXLockedSupply),
|
|
4941
|
+
epoch: {
|
|
4942
|
+
activePeriod: Number(activePeriod),
|
|
4943
|
+
epochEnd,
|
|
4944
|
+
secondsRemaining: Math.max(0, epochEnd - now),
|
|
4945
|
+
epochCount: Number(epochCount),
|
|
4946
|
+
weeklyEmission: String(weeklyEmission)
|
|
4947
|
+
},
|
|
4948
|
+
topPools: v2PoolSnapshot.topPools,
|
|
4949
|
+
tvl: {
|
|
4950
|
+
v2ReserveUnitEstimate: v2PoolSnapshot.reserveUnitTvl,
|
|
4951
|
+
vaultManagedVotingPower
|
|
4952
|
+
},
|
|
4953
|
+
vaults: {
|
|
4954
|
+
relayCount: vaultRelayCount,
|
|
4955
|
+
managedVotingPower: vaultManagedVotingPower,
|
|
4956
|
+
note: vaultNote
|
|
4957
|
+
},
|
|
4958
|
+
lending: {
|
|
4959
|
+
available: lendingAvailable,
|
|
4960
|
+
morpho: lendingMorpho,
|
|
4961
|
+
marketCount: lendingMarketCount,
|
|
4962
|
+
supplyByLoanToken: lendingSupplyByLoanToken,
|
|
4963
|
+
note: lendingNote
|
|
4964
|
+
}
|
|
4965
|
+
},
|
|
4966
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
4967
|
+
cta: {
|
|
4968
|
+
description: "Drill down:",
|
|
4969
|
+
commands: [
|
|
4970
|
+
{
|
|
4971
|
+
command: "pools list",
|
|
4972
|
+
description: "List V2 AMM pools"
|
|
4973
|
+
},
|
|
4974
|
+
{
|
|
4975
|
+
command: "cl pools",
|
|
4976
|
+
description: "List Slipstream CL pools"
|
|
4977
|
+
},
|
|
4978
|
+
{
|
|
4979
|
+
command: "gauges list",
|
|
4980
|
+
description: "List active gauges"
|
|
4981
|
+
},
|
|
4982
|
+
{
|
|
4983
|
+
command: "ve stats",
|
|
4984
|
+
description: "View veABX global stats"
|
|
4985
|
+
}
|
|
4986
|
+
]
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
);
|
|
2674
4990
|
}
|
|
2675
4991
|
});
|
|
2676
4992
|
applyFriendlyErrorHandling(cli);
|