@sherwoodagent/cli 0.17.3 → 0.18.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/index.js +93 -376
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
import { config as loadDotenv } from "dotenv";
|
|
109
109
|
import { createRequire } from "module";
|
|
110
110
|
import { Command, Option } from "commander";
|
|
111
|
-
import { parseUnits as
|
|
111
|
+
import { parseUnits as parseUnits5, isAddress as isAddress5 } from "viem";
|
|
112
112
|
import chalk7 from "chalk";
|
|
113
113
|
import ora7 from "ora";
|
|
114
114
|
import { input, confirm, select } from "@inquirer/prompts";
|
|
@@ -1060,223 +1060,10 @@ async function getActiveSyndicates2(creator) {
|
|
|
1060
1060
|
}
|
|
1061
1061
|
|
|
1062
1062
|
// src/commands/venice.ts
|
|
1063
|
-
import {
|
|
1063
|
+
import { formatUnits as formatUnits2, isAddress as isAddress2 } from "viem";
|
|
1064
1064
|
import chalk2 from "chalk";
|
|
1065
1065
|
import ora2 from "ora";
|
|
1066
1066
|
|
|
1067
|
-
// src/lib/quote.ts
|
|
1068
|
-
import { encodeFunctionData as encodeFunctionData5, decodeFunctionResult, concat as concat2, pad, numberToHex } from "viem";
|
|
1069
|
-
async function getQuote(params) {
|
|
1070
|
-
const client = getPublicClient();
|
|
1071
|
-
const calldata = encodeFunctionData5({
|
|
1072
|
-
abi: UNISWAP_QUOTER_V2_ABI,
|
|
1073
|
-
functionName: "quoteExactInputSingle",
|
|
1074
|
-
args: [
|
|
1075
|
-
{
|
|
1076
|
-
tokenIn: params.tokenIn,
|
|
1077
|
-
tokenOut: params.tokenOut,
|
|
1078
|
-
amountIn: params.amountIn,
|
|
1079
|
-
fee: params.fee,
|
|
1080
|
-
sqrtPriceLimitX96: 0n
|
|
1081
|
-
}
|
|
1082
|
-
]
|
|
1083
|
-
});
|
|
1084
|
-
const { data } = await client.call({
|
|
1085
|
-
to: UNISWAP().QUOTER_V2,
|
|
1086
|
-
data: calldata
|
|
1087
|
-
});
|
|
1088
|
-
if (!data) {
|
|
1089
|
-
throw new Error("Quoter returned no data \u2014 pool may not exist for this pair/fee");
|
|
1090
|
-
}
|
|
1091
|
-
const [amountOut, sqrtPriceX96After, , gasEstimate] = decodeFunctionResult({
|
|
1092
|
-
abi: UNISWAP_QUOTER_V2_ABI,
|
|
1093
|
-
functionName: "quoteExactInputSingle",
|
|
1094
|
-
data
|
|
1095
|
-
});
|
|
1096
|
-
return { amountOut, sqrtPriceX96After, gasEstimate };
|
|
1097
|
-
}
|
|
1098
|
-
function applySlippage(amountOut, slippageBps) {
|
|
1099
|
-
return amountOut * BigInt(1e4 - slippageBps) / 10000n;
|
|
1100
|
-
}
|
|
1101
|
-
function encodeSwapPath(tokens, fees) {
|
|
1102
|
-
if (tokens.length < 2 || fees.length !== tokens.length - 1) {
|
|
1103
|
-
throw new Error("Invalid path: need at least 2 tokens and (tokens-1) fees");
|
|
1104
|
-
}
|
|
1105
|
-
const parts = [];
|
|
1106
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
1107
|
-
parts.push(tokens[i].toLowerCase());
|
|
1108
|
-
if (i < fees.length) {
|
|
1109
|
-
parts.push(pad(numberToHex(fees[i]), { size: 3 }));
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
return concat2(parts);
|
|
1113
|
-
}
|
|
1114
|
-
async function getMultiHopQuote(params) {
|
|
1115
|
-
const client = getPublicClient();
|
|
1116
|
-
const calldata = encodeFunctionData5({
|
|
1117
|
-
abi: UNISWAP_QUOTER_V2_ABI,
|
|
1118
|
-
functionName: "quoteExactInput",
|
|
1119
|
-
args: [params.path, params.amountIn]
|
|
1120
|
-
});
|
|
1121
|
-
const { data } = await client.call({
|
|
1122
|
-
to: UNISWAP().QUOTER_V2,
|
|
1123
|
-
data: calldata
|
|
1124
|
-
});
|
|
1125
|
-
if (!data) {
|
|
1126
|
-
throw new Error("Quoter returned no data \u2014 pool may not exist for this path");
|
|
1127
|
-
}
|
|
1128
|
-
const [amountOut, , , gasEstimate] = decodeFunctionResult({
|
|
1129
|
-
abi: UNISWAP_QUOTER_V2_ABI,
|
|
1130
|
-
functionName: "quoteExactInput",
|
|
1131
|
-
data
|
|
1132
|
-
});
|
|
1133
|
-
return { amountOut, sqrtPriceX96After: 0n, gasEstimate };
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
// src/strategies/venice-fund.ts
|
|
1137
|
-
import { encodeFunctionData as encodeFunctionData6, parseUnits as parseUnits3 } from "viem";
|
|
1138
|
-
var ERC20_ABI2 = [
|
|
1139
|
-
{
|
|
1140
|
-
name: "approve",
|
|
1141
|
-
type: "function",
|
|
1142
|
-
inputs: [
|
|
1143
|
-
{ name: "spender", type: "address" },
|
|
1144
|
-
{ name: "amount", type: "uint256" }
|
|
1145
|
-
],
|
|
1146
|
-
outputs: [{ name: "", type: "bool" }]
|
|
1147
|
-
}
|
|
1148
|
-
];
|
|
1149
|
-
var SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI = [
|
|
1150
|
-
{
|
|
1151
|
-
name: "exactInputSingle",
|
|
1152
|
-
type: "function",
|
|
1153
|
-
inputs: [
|
|
1154
|
-
{
|
|
1155
|
-
name: "params",
|
|
1156
|
-
type: "tuple",
|
|
1157
|
-
components: [
|
|
1158
|
-
{ name: "tokenIn", type: "address" },
|
|
1159
|
-
{ name: "tokenOut", type: "address" },
|
|
1160
|
-
{ name: "fee", type: "uint24" },
|
|
1161
|
-
{ name: "recipient", type: "address" },
|
|
1162
|
-
{ name: "amountIn", type: "uint256" },
|
|
1163
|
-
{ name: "amountOutMinimum", type: "uint256" },
|
|
1164
|
-
{ name: "sqrtPriceLimitX96", type: "uint160" }
|
|
1165
|
-
]
|
|
1166
|
-
}
|
|
1167
|
-
],
|
|
1168
|
-
outputs: [{ name: "amountOut", type: "uint256" }]
|
|
1169
|
-
}
|
|
1170
|
-
];
|
|
1171
|
-
var SWAP_ROUTER_EXACT_INPUT_ABI = [
|
|
1172
|
-
{
|
|
1173
|
-
name: "exactInput",
|
|
1174
|
-
type: "function",
|
|
1175
|
-
inputs: [
|
|
1176
|
-
{
|
|
1177
|
-
name: "params",
|
|
1178
|
-
type: "tuple",
|
|
1179
|
-
components: [
|
|
1180
|
-
{ name: "path", type: "bytes" },
|
|
1181
|
-
{ name: "recipient", type: "address" },
|
|
1182
|
-
{ name: "amountIn", type: "uint256" },
|
|
1183
|
-
{ name: "amountOutMinimum", type: "uint256" }
|
|
1184
|
-
]
|
|
1185
|
-
}
|
|
1186
|
-
],
|
|
1187
|
-
outputs: [{ name: "amountOut", type: "uint256" }]
|
|
1188
|
-
}
|
|
1189
|
-
];
|
|
1190
|
-
var STAKING_ABI = [
|
|
1191
|
-
{
|
|
1192
|
-
name: "stake",
|
|
1193
|
-
type: "function",
|
|
1194
|
-
inputs: [
|
|
1195
|
-
{ name: "recipient", type: "address" },
|
|
1196
|
-
{ name: "amount", type: "uint256" }
|
|
1197
|
-
],
|
|
1198
|
-
outputs: []
|
|
1199
|
-
}
|
|
1200
|
-
];
|
|
1201
|
-
function buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minVVV, swapPath) {
|
|
1202
|
-
const ZERO2 = "0x0000000000000000000000000000000000000000";
|
|
1203
|
-
if (VENICE().VVV === ZERO2 || VENICE().STAKING === ZERO2) {
|
|
1204
|
-
throw new Error("Venice (VVV/sVVV) is not deployed on this network \u2014 venice fund requires Venice staking contracts");
|
|
1205
|
-
}
|
|
1206
|
-
const assetAmount = parseUnits3(config.amount, assetDecimals);
|
|
1207
|
-
const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
|
|
1208
|
-
const calls = [];
|
|
1209
|
-
calls.push({
|
|
1210
|
-
target: assetAddress,
|
|
1211
|
-
data: encodeFunctionData6({
|
|
1212
|
-
abi: ERC20_ABI2,
|
|
1213
|
-
functionName: "approve",
|
|
1214
|
-
args: [UNISWAP().SWAP_ROUTER, assetAmount]
|
|
1215
|
-
}),
|
|
1216
|
-
value: 0n
|
|
1217
|
-
});
|
|
1218
|
-
if (isWeth) {
|
|
1219
|
-
calls.push({
|
|
1220
|
-
target: UNISWAP().SWAP_ROUTER,
|
|
1221
|
-
data: encodeFunctionData6({
|
|
1222
|
-
abi: SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
|
|
1223
|
-
functionName: "exactInputSingle",
|
|
1224
|
-
args: [
|
|
1225
|
-
{
|
|
1226
|
-
tokenIn: TOKENS().WETH,
|
|
1227
|
-
tokenOut: VENICE().VVV,
|
|
1228
|
-
fee: config.fee2,
|
|
1229
|
-
recipient: vaultAddress,
|
|
1230
|
-
amountIn: assetAmount,
|
|
1231
|
-
amountOutMinimum: minVVV,
|
|
1232
|
-
sqrtPriceLimitX96: 0n
|
|
1233
|
-
}
|
|
1234
|
-
]
|
|
1235
|
-
}),
|
|
1236
|
-
value: 0n
|
|
1237
|
-
});
|
|
1238
|
-
} else {
|
|
1239
|
-
calls.push({
|
|
1240
|
-
target: UNISWAP().SWAP_ROUTER,
|
|
1241
|
-
data: encodeFunctionData6({
|
|
1242
|
-
abi: SWAP_ROUTER_EXACT_INPUT_ABI,
|
|
1243
|
-
functionName: "exactInput",
|
|
1244
|
-
args: [
|
|
1245
|
-
{
|
|
1246
|
-
path: swapPath,
|
|
1247
|
-
recipient: vaultAddress,
|
|
1248
|
-
amountIn: assetAmount,
|
|
1249
|
-
amountOutMinimum: minVVV
|
|
1250
|
-
}
|
|
1251
|
-
]
|
|
1252
|
-
}),
|
|
1253
|
-
value: 0n
|
|
1254
|
-
});
|
|
1255
|
-
}
|
|
1256
|
-
calls.push({
|
|
1257
|
-
target: VENICE().VVV,
|
|
1258
|
-
data: encodeFunctionData6({
|
|
1259
|
-
abi: ERC20_ABI2,
|
|
1260
|
-
functionName: "approve",
|
|
1261
|
-
args: [VENICE().STAKING, minVVV]
|
|
1262
|
-
}),
|
|
1263
|
-
value: 0n
|
|
1264
|
-
});
|
|
1265
|
-
const perAgent = minVVV / BigInt(agents.length);
|
|
1266
|
-
for (const agent of agents) {
|
|
1267
|
-
calls.push({
|
|
1268
|
-
target: VENICE().STAKING,
|
|
1269
|
-
data: encodeFunctionData6({
|
|
1270
|
-
abi: STAKING_ABI,
|
|
1271
|
-
functionName: "stake",
|
|
1272
|
-
args: [agent, perAgent]
|
|
1273
|
-
}),
|
|
1274
|
-
value: 0n
|
|
1275
|
-
});
|
|
1276
|
-
}
|
|
1277
|
-
return calls;
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
1067
|
// src/lib/venice.ts
|
|
1281
1068
|
var VENICE_API_BASE = "https://api.venice.ai/api/v1";
|
|
1282
1069
|
async function provisionApiKey() {
|
|
@@ -1391,148 +1178,9 @@ async function listModels() {
|
|
|
1391
1178
|
}
|
|
1392
1179
|
|
|
1393
1180
|
// src/commands/venice.ts
|
|
1394
|
-
import { readFileSync
|
|
1395
|
-
var VALID_FEES = [500, 3e3, 1e4];
|
|
1181
|
+
import { readFileSync } from "fs";
|
|
1396
1182
|
function registerVeniceCommands(program2) {
|
|
1397
|
-
const venice = program2.command("venice").description("Venice private inference \u2014
|
|
1398
|
-
venice.command("fund").description("Swap vault profits \u2192 VVV \u2192 stake \u2192 distribute sVVV to agents").requiredOption("--vault <address>", "Vault address").requiredOption("--amount <amount>", "Deposit token amount to convert (e.g. 500)").option("--fee1 <tier>", "Fee tier for asset \u2192 WETH hop (500, 3000, 10000)", "3000").option("--fee2 <tier>", "Fee tier for WETH \u2192 VVV hop", "10000").option("--slippage <bps>", "Slippage tolerance in bps", "100").option("--execute", "Execute on-chain (default: simulate only)", false).option("--write-calls <path>", "Write batch calls to JSON file for proposal create (skips execution)").action(async (opts) => {
|
|
1399
|
-
const vaultAddress = opts.vault;
|
|
1400
|
-
if (!isAddress2(vaultAddress)) {
|
|
1401
|
-
console.error(chalk2.red(`Invalid vault address: ${opts.vault}`));
|
|
1402
|
-
process.exit(1);
|
|
1403
|
-
}
|
|
1404
|
-
const fee1 = Number(opts.fee1);
|
|
1405
|
-
const fee2 = Number(opts.fee2);
|
|
1406
|
-
if (!VALID_FEES.includes(fee1) || !VALID_FEES.includes(fee2)) {
|
|
1407
|
-
console.error(chalk2.red(`Invalid fee tier. Valid: ${VALID_FEES.join(", ")}`));
|
|
1408
|
-
process.exit(1);
|
|
1409
|
-
}
|
|
1410
|
-
const slippageBps = Number(opts.slippage);
|
|
1411
|
-
const client = getPublicClient();
|
|
1412
|
-
const spinner = ora2("Reading vault state...").start();
|
|
1413
|
-
let assetAddress;
|
|
1414
|
-
let assetDecimals;
|
|
1415
|
-
let assetSymbol;
|
|
1416
|
-
let totalDeposited;
|
|
1417
|
-
let assetBalance;
|
|
1418
|
-
let agents;
|
|
1419
|
-
try {
|
|
1420
|
-
[assetAddress, totalDeposited, agents] = await Promise.all([
|
|
1421
|
-
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
|
|
1422
|
-
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
|
|
1423
|
-
client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
|
|
1424
|
-
]);
|
|
1425
|
-
[assetDecimals, assetSymbol, assetBalance] = await Promise.all([
|
|
1426
|
-
client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
|
|
1427
|
-
client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "symbol" }),
|
|
1428
|
-
client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "balanceOf", args: [vaultAddress] })
|
|
1429
|
-
]);
|
|
1430
|
-
spinner.stop();
|
|
1431
|
-
} catch (err) {
|
|
1432
|
-
spinner.fail("Failed to read vault state");
|
|
1433
|
-
console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
|
|
1434
|
-
process.exit(1);
|
|
1435
|
-
}
|
|
1436
|
-
if (agents.length === 0) {
|
|
1437
|
-
console.error(chalk2.red("No agents registered in vault. Register agents first."));
|
|
1438
|
-
process.exit(1);
|
|
1439
|
-
}
|
|
1440
|
-
const requestedAmount = parseUnits4(opts.amount, assetDecimals);
|
|
1441
|
-
const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
|
|
1442
|
-
const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
|
|
1443
|
-
console.log();
|
|
1444
|
-
console.log(chalk2.bold("Venice Fund"));
|
|
1445
|
-
console.log(chalk2.dim("\u2500".repeat(40)));
|
|
1446
|
-
console.log(` Asset: ${assetSymbol} (${assetDecimals} decimals)`);
|
|
1447
|
-
console.log(` Amount: ${opts.amount} ${assetSymbol}`);
|
|
1448
|
-
console.log(` Vault balance: ${formatUnits2(assetBalance, assetDecimals)} ${assetSymbol}`);
|
|
1449
|
-
console.log(` Deposited: ${formatUnits2(totalDeposited, assetDecimals)} ${assetSymbol}`);
|
|
1450
|
-
console.log(` Profit: ${formatUnits2(profit, assetDecimals)} ${assetSymbol}`);
|
|
1451
|
-
console.log(` Agents: ${agents.length} (sVVV will be split equally)`);
|
|
1452
|
-
console.log(` Routing: ${isWeth ? `WETH \u2192 VVV (fee ${fee2})` : `${assetSymbol} \u2192 WETH (fee ${fee1}) \u2192 VVV (fee ${fee2})`}`);
|
|
1453
|
-
console.log(` Slippage: ${(slippageBps / 100).toFixed(2)}%`);
|
|
1454
|
-
console.log(` Vault: ${vaultAddress}`);
|
|
1455
|
-
console.log();
|
|
1456
|
-
if (requestedAmount > profit) {
|
|
1457
|
-
console.warn(chalk2.yellow(` Warning: amount (${opts.amount}) exceeds available profit (${formatUnits2(profit, assetDecimals)})`));
|
|
1458
|
-
console.warn(chalk2.yellow(" This will use deposited capital, not just profits."));
|
|
1459
|
-
console.log();
|
|
1460
|
-
}
|
|
1461
|
-
const quoteSpinner = ora2("Fetching Uniswap quote...").start();
|
|
1462
|
-
let amountOut;
|
|
1463
|
-
let minOut;
|
|
1464
|
-
let swapPath = null;
|
|
1465
|
-
try {
|
|
1466
|
-
if (isWeth) {
|
|
1467
|
-
const quote = await getQuote({
|
|
1468
|
-
tokenIn: TOKENS().WETH,
|
|
1469
|
-
tokenOut: VENICE().VVV,
|
|
1470
|
-
amountIn: requestedAmount,
|
|
1471
|
-
fee: fee2
|
|
1472
|
-
});
|
|
1473
|
-
amountOut = quote.amountOut;
|
|
1474
|
-
} else {
|
|
1475
|
-
swapPath = encodeSwapPath(
|
|
1476
|
-
[assetAddress, TOKENS().WETH, VENICE().VVV],
|
|
1477
|
-
[fee1, fee2]
|
|
1478
|
-
);
|
|
1479
|
-
const quote = await getMultiHopQuote({
|
|
1480
|
-
path: swapPath,
|
|
1481
|
-
amountIn: requestedAmount
|
|
1482
|
-
});
|
|
1483
|
-
amountOut = quote.amountOut;
|
|
1484
|
-
}
|
|
1485
|
-
minOut = applySlippage(amountOut, slippageBps);
|
|
1486
|
-
quoteSpinner.succeed(
|
|
1487
|
-
`Quote: ${formatUnits2(amountOut, 18)} VVV (min: ${formatUnits2(minOut, 18)}, per agent: ${formatUnits2(minOut / BigInt(agents.length), 18)})`
|
|
1488
|
-
);
|
|
1489
|
-
} catch (err) {
|
|
1490
|
-
quoteSpinner.fail("Failed to fetch quote");
|
|
1491
|
-
console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
|
|
1492
|
-
process.exit(1);
|
|
1493
|
-
}
|
|
1494
|
-
const config = {
|
|
1495
|
-
amount: opts.amount,
|
|
1496
|
-
fee1,
|
|
1497
|
-
fee2,
|
|
1498
|
-
slippageBps
|
|
1499
|
-
};
|
|
1500
|
-
const calls = buildFundBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minOut, swapPath);
|
|
1501
|
-
console.log();
|
|
1502
|
-
console.log(chalk2.bold(`Batch calls (${calls.length}):`));
|
|
1503
|
-
console.log(formatBatch(calls));
|
|
1504
|
-
console.log();
|
|
1505
|
-
if (opts.writeCalls) {
|
|
1506
|
-
const callsJson = calls.map((c) => ({
|
|
1507
|
-
target: c.target,
|
|
1508
|
-
data: c.data,
|
|
1509
|
-
value: c.value.toString()
|
|
1510
|
-
}));
|
|
1511
|
-
writeFileSync2(opts.writeCalls, JSON.stringify(callsJson, null, 2));
|
|
1512
|
-
const settlePath = `${opts.writeCalls}.settle.json`;
|
|
1513
|
-
writeFileSync2(settlePath, "[]");
|
|
1514
|
-
console.log(chalk2.green(`Execute calls written to: ${opts.writeCalls}`));
|
|
1515
|
-
console.log(chalk2.green(`Settlement calls written to: ${settlePath}`));
|
|
1516
|
-
console.log();
|
|
1517
|
-
console.log(chalk2.dim("Use with: sherwood proposal create --execute-calls <path> --settle-calls <path>"));
|
|
1518
|
-
return;
|
|
1519
|
-
}
|
|
1520
|
-
if (!opts.execute) {
|
|
1521
|
-
console.log();
|
|
1522
|
-
console.log(chalk2.yellow("Dry run complete. Add --execute to submit on-chain, or --write-calls <path> to export for proposals."));
|
|
1523
|
-
return;
|
|
1524
|
-
}
|
|
1525
|
-
const execSpinner = ora2("Executing batch via vault...").start();
|
|
1526
|
-
try {
|
|
1527
|
-
const txHash = await executeBatch(calls);
|
|
1528
|
-
execSpinner.succeed(`Batch executed: ${txHash}`);
|
|
1529
|
-
console.log(chalk2.dim(` ${getExplorerUrl(txHash)}`));
|
|
1530
|
-
} catch (err) {
|
|
1531
|
-
execSpinner.fail("Execution failed");
|
|
1532
|
-
console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
|
|
1533
|
-
process.exit(1);
|
|
1534
|
-
}
|
|
1535
|
-
});
|
|
1183
|
+
const venice = program2.command("venice").description("Venice private inference \u2014 provision API keys, run inference");
|
|
1536
1184
|
venice.command("provision").description("Self-provision a Venice API key (requires sVVV in wallet)").action(async () => {
|
|
1537
1185
|
const account = getAccount();
|
|
1538
1186
|
const client = getPublicClient();
|
|
@@ -1547,7 +1195,7 @@ function registerVeniceCommands(program2) {
|
|
|
1547
1195
|
if (sVvvBalance === 0n) {
|
|
1548
1196
|
checkSpinner.fail("No sVVV found in wallet");
|
|
1549
1197
|
console.log(chalk2.yellow(" Your wallet must hold staked VVV (sVVV) to provision a Venice API key."));
|
|
1550
|
-
console.log(chalk2.yellow("
|
|
1198
|
+
console.log(chalk2.yellow(" Use the VeniceInferenceStrategy via a proposal to distribute sVVV to agents."));
|
|
1551
1199
|
process.exit(1);
|
|
1552
1200
|
}
|
|
1553
1201
|
checkSpinner.succeed(`sVVV balance: ${formatUnits2(sVvvBalance, 18)}`);
|
|
@@ -1714,13 +1362,82 @@ ${opts.prompt}`;
|
|
|
1714
1362
|
}
|
|
1715
1363
|
|
|
1716
1364
|
// src/commands/allowance.ts
|
|
1717
|
-
import { parseUnits as
|
|
1365
|
+
import { parseUnits as parseUnits4, formatUnits as formatUnits3, isAddress as isAddress3 } from "viem";
|
|
1718
1366
|
import chalk3 from "chalk";
|
|
1719
1367
|
import ora3 from "ora";
|
|
1720
1368
|
|
|
1369
|
+
// src/lib/quote.ts
|
|
1370
|
+
import { encodeFunctionData as encodeFunctionData5, decodeFunctionResult, concat as concat2, pad, numberToHex } from "viem";
|
|
1371
|
+
async function getQuote(params) {
|
|
1372
|
+
const client = getPublicClient();
|
|
1373
|
+
const calldata = encodeFunctionData5({
|
|
1374
|
+
abi: UNISWAP_QUOTER_V2_ABI,
|
|
1375
|
+
functionName: "quoteExactInputSingle",
|
|
1376
|
+
args: [
|
|
1377
|
+
{
|
|
1378
|
+
tokenIn: params.tokenIn,
|
|
1379
|
+
tokenOut: params.tokenOut,
|
|
1380
|
+
amountIn: params.amountIn,
|
|
1381
|
+
fee: params.fee,
|
|
1382
|
+
sqrtPriceLimitX96: 0n
|
|
1383
|
+
}
|
|
1384
|
+
]
|
|
1385
|
+
});
|
|
1386
|
+
const { data } = await client.call({
|
|
1387
|
+
to: UNISWAP().QUOTER_V2,
|
|
1388
|
+
data: calldata
|
|
1389
|
+
});
|
|
1390
|
+
if (!data) {
|
|
1391
|
+
throw new Error("Quoter returned no data \u2014 pool may not exist for this pair/fee");
|
|
1392
|
+
}
|
|
1393
|
+
const [amountOut, sqrtPriceX96After, , gasEstimate] = decodeFunctionResult({
|
|
1394
|
+
abi: UNISWAP_QUOTER_V2_ABI,
|
|
1395
|
+
functionName: "quoteExactInputSingle",
|
|
1396
|
+
data
|
|
1397
|
+
});
|
|
1398
|
+
return { amountOut, sqrtPriceX96After, gasEstimate };
|
|
1399
|
+
}
|
|
1400
|
+
function applySlippage(amountOut, slippageBps) {
|
|
1401
|
+
return amountOut * BigInt(1e4 - slippageBps) / 10000n;
|
|
1402
|
+
}
|
|
1403
|
+
function encodeSwapPath(tokens, fees) {
|
|
1404
|
+
if (tokens.length < 2 || fees.length !== tokens.length - 1) {
|
|
1405
|
+
throw new Error("Invalid path: need at least 2 tokens and (tokens-1) fees");
|
|
1406
|
+
}
|
|
1407
|
+
const parts = [];
|
|
1408
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
1409
|
+
parts.push(tokens[i].toLowerCase());
|
|
1410
|
+
if (i < fees.length) {
|
|
1411
|
+
parts.push(pad(numberToHex(fees[i]), { size: 3 }));
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
return concat2(parts);
|
|
1415
|
+
}
|
|
1416
|
+
async function getMultiHopQuote(params) {
|
|
1417
|
+
const client = getPublicClient();
|
|
1418
|
+
const calldata = encodeFunctionData5({
|
|
1419
|
+
abi: UNISWAP_QUOTER_V2_ABI,
|
|
1420
|
+
functionName: "quoteExactInput",
|
|
1421
|
+
args: [params.path, params.amountIn]
|
|
1422
|
+
});
|
|
1423
|
+
const { data } = await client.call({
|
|
1424
|
+
to: UNISWAP().QUOTER_V2,
|
|
1425
|
+
data: calldata
|
|
1426
|
+
});
|
|
1427
|
+
if (!data) {
|
|
1428
|
+
throw new Error("Quoter returned no data \u2014 pool may not exist for this path");
|
|
1429
|
+
}
|
|
1430
|
+
const [amountOut, , , gasEstimate] = decodeFunctionResult({
|
|
1431
|
+
abi: UNISWAP_QUOTER_V2_ABI,
|
|
1432
|
+
functionName: "quoteExactInput",
|
|
1433
|
+
data
|
|
1434
|
+
});
|
|
1435
|
+
return { amountOut, sqrtPriceX96After: 0n, gasEstimate };
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1721
1438
|
// src/strategies/allowance-disburse.ts
|
|
1722
|
-
import { encodeFunctionData as
|
|
1723
|
-
var
|
|
1439
|
+
import { encodeFunctionData as encodeFunctionData6, parseUnits as parseUnits3 } from "viem";
|
|
1440
|
+
var ERC20_ABI2 = [
|
|
1724
1441
|
{
|
|
1725
1442
|
name: "approve",
|
|
1726
1443
|
type: "function",
|
|
@@ -1740,7 +1457,7 @@ var ERC20_ABI3 = [
|
|
|
1740
1457
|
outputs: [{ name: "", type: "bool" }]
|
|
1741
1458
|
}
|
|
1742
1459
|
];
|
|
1743
|
-
var
|
|
1460
|
+
var SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI = [
|
|
1744
1461
|
{
|
|
1745
1462
|
name: "exactInputSingle",
|
|
1746
1463
|
type: "function",
|
|
@@ -1762,7 +1479,7 @@ var SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI2 = [
|
|
|
1762
1479
|
outputs: [{ name: "amountOut", type: "uint256" }]
|
|
1763
1480
|
}
|
|
1764
1481
|
];
|
|
1765
|
-
var
|
|
1482
|
+
var SWAP_ROUTER_EXACT_INPUT_ABI = [
|
|
1766
1483
|
{
|
|
1767
1484
|
name: "exactInput",
|
|
1768
1485
|
type: "function",
|
|
@@ -1782,15 +1499,15 @@ var SWAP_ROUTER_EXACT_INPUT_ABI2 = [
|
|
|
1782
1499
|
}
|
|
1783
1500
|
];
|
|
1784
1501
|
function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDecimals, minUsdc, swapPath) {
|
|
1785
|
-
const assetAmount =
|
|
1502
|
+
const assetAmount = parseUnits3(config.amount, assetDecimals);
|
|
1786
1503
|
const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
|
|
1787
1504
|
const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
|
|
1788
1505
|
const calls = [];
|
|
1789
1506
|
if (!isUsdc) {
|
|
1790
1507
|
calls.push({
|
|
1791
1508
|
target: assetAddress,
|
|
1792
|
-
data:
|
|
1793
|
-
abi:
|
|
1509
|
+
data: encodeFunctionData6({
|
|
1510
|
+
abi: ERC20_ABI2,
|
|
1794
1511
|
functionName: "approve",
|
|
1795
1512
|
args: [UNISWAP().SWAP_ROUTER, assetAmount]
|
|
1796
1513
|
}),
|
|
@@ -1799,8 +1516,8 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
|
|
|
1799
1516
|
if (isWeth) {
|
|
1800
1517
|
calls.push({
|
|
1801
1518
|
target: UNISWAP().SWAP_ROUTER,
|
|
1802
|
-
data:
|
|
1803
|
-
abi:
|
|
1519
|
+
data: encodeFunctionData6({
|
|
1520
|
+
abi: SWAP_ROUTER_EXACT_INPUT_SINGLE_ABI,
|
|
1804
1521
|
functionName: "exactInputSingle",
|
|
1805
1522
|
args: [
|
|
1806
1523
|
{
|
|
@@ -1819,8 +1536,8 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
|
|
|
1819
1536
|
} else {
|
|
1820
1537
|
calls.push({
|
|
1821
1538
|
target: UNISWAP().SWAP_ROUTER,
|
|
1822
|
-
data:
|
|
1823
|
-
abi:
|
|
1539
|
+
data: encodeFunctionData6({
|
|
1540
|
+
abi: SWAP_ROUTER_EXACT_INPUT_ABI,
|
|
1824
1541
|
functionName: "exactInput",
|
|
1825
1542
|
args: [
|
|
1826
1543
|
{
|
|
@@ -1839,8 +1556,8 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
|
|
|
1839
1556
|
for (const agent of agents) {
|
|
1840
1557
|
calls.push({
|
|
1841
1558
|
target: TOKENS().USDC,
|
|
1842
|
-
data:
|
|
1843
|
-
abi:
|
|
1559
|
+
data: encodeFunctionData6({
|
|
1560
|
+
abi: ERC20_ABI2,
|
|
1844
1561
|
functionName: "transfer",
|
|
1845
1562
|
args: [agent, perAgent]
|
|
1846
1563
|
}),
|
|
@@ -1851,7 +1568,7 @@ function buildDisburseBatch(config, vaultAddress, agents, assetAddress, assetDec
|
|
|
1851
1568
|
}
|
|
1852
1569
|
|
|
1853
1570
|
// src/commands/allowance.ts
|
|
1854
|
-
var
|
|
1571
|
+
var VALID_FEES = [500, 3e3, 1e4];
|
|
1855
1572
|
function registerAllowanceCommands(program2) {
|
|
1856
1573
|
const allowance = program2.command("allowance").description("Disburse vault profits to agent wallets");
|
|
1857
1574
|
allowance.command("disburse").description("Swap vault profits \u2192 USDC \u2192 distribute to all agent operator wallets").requiredOption("--vault <address>", "Vault address").requiredOption("--amount <amount>", "Deposit token amount to convert & distribute (e.g. 500)").option("--fee <tier>", "Fee tier for asset \u2192 USDC swap (500, 3000, 10000)", "3000").option("--slippage <bps>", "Slippage tolerance in bps", "100").option("--execute", "Execute on-chain (default: simulate only)", false).action(async (opts) => {
|
|
@@ -1861,8 +1578,8 @@ function registerAllowanceCommands(program2) {
|
|
|
1861
1578
|
process.exit(1);
|
|
1862
1579
|
}
|
|
1863
1580
|
const fee = Number(opts.fee);
|
|
1864
|
-
if (!
|
|
1865
|
-
console.error(chalk3.red(`Invalid fee tier. Valid: ${
|
|
1581
|
+
if (!VALID_FEES.includes(fee)) {
|
|
1582
|
+
console.error(chalk3.red(`Invalid fee tier. Valid: ${VALID_FEES.join(", ")}`));
|
|
1866
1583
|
process.exit(1);
|
|
1867
1584
|
}
|
|
1868
1585
|
const slippageBps = Number(opts.slippage);
|
|
@@ -1895,7 +1612,7 @@ function registerAllowanceCommands(program2) {
|
|
|
1895
1612
|
console.error(chalk3.red("No agents registered in vault. Register agents first."));
|
|
1896
1613
|
process.exit(1);
|
|
1897
1614
|
}
|
|
1898
|
-
const requestedAmount =
|
|
1615
|
+
const requestedAmount = parseUnits4(opts.amount, assetDecimals);
|
|
1899
1616
|
const profit = assetBalance > totalDeposited ? assetBalance - totalDeposited : 0n;
|
|
1900
1617
|
const isUsdc = assetAddress.toLowerCase() === TOKENS().USDC.toLowerCase();
|
|
1901
1618
|
const isWeth = assetAddress.toLowerCase() === TOKENS().WETH.toLowerCase();
|
|
@@ -3412,7 +3129,7 @@ var vaultCmd = program.command("vault");
|
|
|
3412
3129
|
vaultCmd.command("deposit").description("Deposit into a vault").option("--vault <address>", "Vault address (default: from config)").requiredOption("--amount <amount>", "Amount to deposit (in asset units)").action(async (opts) => {
|
|
3413
3130
|
resolveVault(opts);
|
|
3414
3131
|
const decimals = await getAssetDecimals();
|
|
3415
|
-
const amount =
|
|
3132
|
+
const amount = parseUnits5(opts.amount, decimals);
|
|
3416
3133
|
const spinner = ora7(`Depositing ${opts.amount}...`).start();
|
|
3417
3134
|
try {
|
|
3418
3135
|
const hash = await deposit(amount);
|