arc402-cli 1.4.17 → 1.4.19

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.
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -1224,6 +1257,216 @@ function registerWalletCommands(program) {
1224
1257
  printOpenShellHint();
1225
1258
  }
1226
1259
  });
1260
+ // ─── onboard ───────────────────────────────────────────────────────────────
1261
+ //
1262
+ // Post-deploy ceremony: register on PolicyEngine, set velocity limit,
1263
+ // 5 category spend limits, and whitelist 4 protocol contracts.
1264
+ // Runs in ONE WalletConnect session. Idempotent — skips steps already done.
1265
+ wallet.command("onboard")
1266
+ .description("Post-deploy onboarding: PolicyEngine registration, velocity limit, category limits, and contract whitelisting in one WalletConnect session")
1267
+ .option("--velocity-limit <eth>", "Velocity limit in ETH (rolling window)", "0.05")
1268
+ .option("--hardware", "Hardware wallet mode: show raw wc: URI only")
1269
+ .action(async (opts) => {
1270
+ const config = (0, config_1.loadConfig)();
1271
+ if (!config.walletContractAddress) {
1272
+ console.error(colors_1.c.failure + " " + colors_1.c.red("No walletContractAddress in config. Run `arc402 wallet deploy` first."));
1273
+ process.exit(1);
1274
+ }
1275
+ if (!config.walletConnectProjectId) {
1276
+ console.error(colors_1.c.failure + " " + colors_1.c.red("WalletConnect not configured. Run `arc402 config set walletConnectProjectId <id>`."));
1277
+ process.exit(1);
1278
+ }
1279
+ const walletAddress = config.walletContractAddress;
1280
+ const policyAddress = config.policyEngineAddress ?? POLICY_ENGINE_DEFAULT;
1281
+ const chainId = config.network === "base-mainnet" ? 8453 : 84532;
1282
+ const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1283
+ // Resolve protocol contracts to whitelist
1284
+ const handshakeAddress = config.handshakeAddress ??
1285
+ config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ??
1286
+ "0x4F5A38Bb746d7E5d49d8fd26CA6beD141Ec2DDb3";
1287
+ const protocolContracts = [
1288
+ { address: config.serviceAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.serviceAgreementAddress ?? "", name: "ServiceAgreement" },
1289
+ { address: config.computeAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.computeAgreementAddress ?? "", name: "ComputeAgreement" },
1290
+ { address: config.subscriptionAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.subscriptionAgreementAddress ?? "", name: "SubscriptionAgreement" },
1291
+ { address: handshakeAddress, name: "Handshake" },
1292
+ ].filter(pc => pc.address && pc.address !== "");
1293
+ // Pre-check on-chain state to count pending txs and decide what to skip
1294
+ const checkSpin = (0, spinner_1.startSpinner)("Checking onboarding state…");
1295
+ let needsRegister = true;
1296
+ let needsDefiAccess = true;
1297
+ let needsVelocity = true;
1298
+ const categoryNeeds = {};
1299
+ const whitelistNeeds = {};
1300
+ try {
1301
+ const govContract = new ethers_1.ethers.Contract(policyAddress, abis_1.POLICY_ENGINE_GOVERNANCE_ABI, provider);
1302
+ const limitsContract = new ethers_1.ethers.Contract(policyAddress, abis_1.POLICY_ENGINE_LIMITS_ABI, provider);
1303
+ const ownerContract = new ethers_1.ethers.Contract(walletAddress, abis_1.ARC402_WALLET_OWNER_ABI, provider);
1304
+ try {
1305
+ const registeredOwner = await govContract.walletOwners(walletAddress);
1306
+ needsRegister = registeredOwner === ethers_1.ethers.ZeroAddress;
1307
+ }
1308
+ catch { /* assume needs */ }
1309
+ try {
1310
+ const defiEnabled = await govContract.defiAccessEnabled(walletAddress);
1311
+ needsDefiAccess = !defiEnabled;
1312
+ }
1313
+ catch { /* assume needs */ }
1314
+ try {
1315
+ const velocity = await ownerContract.velocityLimit();
1316
+ needsVelocity = velocity === 0n;
1317
+ }
1318
+ catch { /* assume needs */ }
1319
+ for (const { name } of ONBOARDING_CATEGORIES) {
1320
+ try {
1321
+ const existing = await limitsContract.categoryLimits(walletAddress, name);
1322
+ categoryNeeds[name] = existing === 0n;
1323
+ }
1324
+ catch {
1325
+ categoryNeeds[name] = true;
1326
+ }
1327
+ }
1328
+ for (const pc of protocolContracts) {
1329
+ try {
1330
+ const isWL = await govContract.isContractWhitelisted(walletAddress, pc.address);
1331
+ whitelistNeeds[pc.name] = !isWL;
1332
+ }
1333
+ catch {
1334
+ whitelistNeeds[pc.name] = true;
1335
+ }
1336
+ }
1337
+ checkSpin.succeed("State checked");
1338
+ }
1339
+ catch (e) {
1340
+ checkSpin.fail(`State check failed: ${e instanceof Error ? e.message : String(e)}`);
1341
+ process.exit(1);
1342
+ }
1343
+ // Count pending transactions
1344
+ let pendingTxCount = 0;
1345
+ if (needsRegister)
1346
+ pendingTxCount++;
1347
+ if (needsDefiAccess)
1348
+ pendingTxCount++;
1349
+ if (needsVelocity)
1350
+ pendingTxCount++;
1351
+ for (const { name } of ONBOARDING_CATEGORIES) {
1352
+ if (categoryNeeds[name])
1353
+ pendingTxCount++;
1354
+ }
1355
+ for (const pc of protocolContracts) {
1356
+ if (whitelistNeeds[pc.name])
1357
+ pendingTxCount++;
1358
+ }
1359
+ if (pendingTxCount === 0) {
1360
+ console.log(" " + colors_1.c.success + " Wallet already fully onboarded — nothing to do.");
1361
+ return;
1362
+ }
1363
+ console.log(" " + colors_1.c.dim(`◈ ${pendingTxCount} transaction(s) pending — your phone will need to approve each.`));
1364
+ // Telegram notification before first tx
1365
+ const telegramOpts = config.telegramBotToken && config.telegramChatId
1366
+ ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1367
+ : undefined;
1368
+ // Connect WalletConnect ONCE — reuse session for all txs
1369
+ const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: `Approve ${pendingTxCount} wallet onboarding transaction(s)`, hardware: !!opts.hardware });
1370
+ const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
1371
+ const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
1372
+ console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
1373
+ if (telegramOpts) {
1374
+ await (0, telegram_notify_1.sendTelegramMessage)({
1375
+ botToken: telegramOpts.botToken,
1376
+ chatId: telegramOpts.chatId,
1377
+ threadId: telegramOpts.threadId,
1378
+ text: `◈ arc402 wallet onboard: ${pendingTxCount} txs pending for ${shortAddr}`,
1379
+ });
1380
+ }
1381
+ const sendTx = async (call, description) => {
1382
+ const txSpin = (0, spinner_1.startSpinner)(description);
1383
+ const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, call);
1384
+ await provider.waitForTransaction(hash, 1);
1385
+ txSpin.succeed(description);
1386
+ return hash;
1387
+ };
1388
+ const executeIface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_EXECUTE_ABI);
1389
+ const govIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_GOVERNANCE_ABI);
1390
+ const limitsIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
1391
+ const ownerIface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_OWNER_ABI);
1392
+ console.log("\n" + colors_1.c.dim("── Onboarding ceremony ────────────────────────────────────────"));
1393
+ // 1. registerWallet
1394
+ if (needsRegister) {
1395
+ const registerCalldata = govIface.encodeFunctionData("registerWallet", [walletAddress, account]);
1396
+ await sendTx({
1397
+ to: walletAddress,
1398
+ data: executeIface.encodeFunctionData("executeContractCall", [{
1399
+ target: policyAddress,
1400
+ data: registerCalldata,
1401
+ value: 0n,
1402
+ minReturnValue: 0n,
1403
+ maxApprovalAmount: 0n,
1404
+ approvalToken: ethers_1.ethers.ZeroAddress,
1405
+ }]),
1406
+ value: "0x0",
1407
+ }, "registerWallet on PolicyEngine");
1408
+ }
1409
+ else {
1410
+ console.log(" " + colors_1.c.success + colors_1.c.dim(" registerWallet — already done"));
1411
+ }
1412
+ // 2. enableDefiAccess
1413
+ if (needsDefiAccess) {
1414
+ await sendTx({
1415
+ to: policyAddress,
1416
+ data: govIface.encodeFunctionData("enableDefiAccess", [walletAddress]),
1417
+ value: "0x0",
1418
+ }, "enableDefiAccess on PolicyEngine");
1419
+ }
1420
+ else {
1421
+ console.log(" " + colors_1.c.success + colors_1.c.dim(" enableDefiAccess — already done"));
1422
+ }
1423
+ // 3. setVelocityLimit
1424
+ if (needsVelocity) {
1425
+ const velocityEth = opts.velocityLimit || "0.05";
1426
+ await sendTx({
1427
+ to: walletAddress,
1428
+ data: ownerIface.encodeFunctionData("setVelocityLimit", [ethers_1.ethers.parseEther(velocityEth)]),
1429
+ value: "0x0",
1430
+ }, `setVelocityLimit: ${velocityEth} ETH`);
1431
+ }
1432
+ else {
1433
+ console.log(" " + colors_1.c.success + colors_1.c.dim(" setVelocityLimit — already set"));
1434
+ }
1435
+ // 4–8. Category limits
1436
+ for (const { name, amountEth } of ONBOARDING_CATEGORIES) {
1437
+ if (categoryNeeds[name]) {
1438
+ await sendTx({
1439
+ to: policyAddress,
1440
+ data: limitsIface.encodeFunctionData("setCategoryLimitFor", [walletAddress, name, ethers_1.ethers.parseEther(amountEth)]),
1441
+ value: "0x0",
1442
+ }, `setCategoryLimitFor: ${name} → ${amountEth} ETH`);
1443
+ }
1444
+ else {
1445
+ console.log(" " + colors_1.c.success + colors_1.c.dim(` setCategoryLimitFor(${name}) — already set`));
1446
+ }
1447
+ }
1448
+ // 9+. Whitelist protocol contracts
1449
+ for (const pc of protocolContracts) {
1450
+ if (whitelistNeeds[pc.name]) {
1451
+ await sendTx({
1452
+ to: policyAddress,
1453
+ data: govIface.encodeFunctionData("whitelistContract", [walletAddress, pc.address]),
1454
+ value: "0x0",
1455
+ }, `whitelistContract: ${pc.name}`);
1456
+ }
1457
+ else {
1458
+ console.log(" " + colors_1.c.success + colors_1.c.dim(` whitelistContract(${pc.name}) — already done`));
1459
+ }
1460
+ }
1461
+ console.log(colors_1.c.dim("── Onboarding complete ─────────────────────────────────────────"));
1462
+ console.log("\n " + colors_1.c.success + colors_1.c.white(" Wallet onboarding complete"));
1463
+ (0, tree_1.renderTree)([
1464
+ { label: "Wallet", value: walletAddress },
1465
+ { label: "Owner", value: account },
1466
+ { label: "Velocity Limit", value: (opts.velocityLimit || "0.05") + " ETH" },
1467
+ { label: "Contracts Whitelisted", value: protocolContracts.length.toString(), last: true },
1468
+ ]);
1469
+ });
1227
1470
  // ─── send ──────────────────────────────────────────────────────────────────
1228
1471
  wallet.command("send <address> <amount>")
1229
1472
  .description("Send ETH from configured wallet (amount: '0.001eth' or wei)")
@@ -1355,6 +1598,64 @@ function registerWalletCommands(program) {
1355
1598
  console.log(`Daily limit for ${opts.category} set to ${opts.amount} ETH (12/24h rolling window)`);
1356
1599
  }
1357
1600
  });
1601
+ walletPolicy.command("init")
1602
+ .description("Set all 5 default spending limits in one WalletConnect session (1 phone connection, 5 approvals)")
1603
+ .option("--general <eth>", "General limit", "0.001")
1604
+ .option("--hire <eth>", "Hire limit", "0.1")
1605
+ .option("--compute <eth>", "Compute limit", "0.05")
1606
+ .option("--research <eth>", "Research limit", "0.05")
1607
+ .option("--protocol <eth>", "Protocol limit", "0.1")
1608
+ .action(async (opts) => {
1609
+ const config = (0, config_1.loadConfig)();
1610
+ const policyAddress = config.policyEngineAddress ?? POLICY_ENGINE_DEFAULT;
1611
+ const chainId = config.network === "base-mainnet" ? 8453 : 84532;
1612
+ const walletAddr = config.walletContractAddress;
1613
+ if (!walletAddr) {
1614
+ console.error("walletContractAddress not set. Run `arc402 wallet deploy` first.");
1615
+ process.exit(1);
1616
+ }
1617
+ if (!config.walletConnectProjectId) {
1618
+ console.error("walletConnectProjectId not set.");
1619
+ process.exit(1);
1620
+ }
1621
+ const categories = [
1622
+ { name: "general", amount: opts.general },
1623
+ { name: "hire", amount: opts.hire },
1624
+ { name: "compute", amount: opts.compute },
1625
+ { name: "research", amount: opts.research },
1626
+ { name: "protocol", amount: opts.protocol },
1627
+ ];
1628
+ const policyInterface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
1629
+ const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1630
+ const telegramOpts = config.telegramBotToken && config.telegramChatId ? {
1631
+ botToken: config.telegramBotToken,
1632
+ chatId: config.telegramChatId,
1633
+ threadId: config.telegramThreadId,
1634
+ } : undefined;
1635
+ console.log(colors_1.c.white("\n◈ Spend limit init — 1 connection, 5 approvals\n"));
1636
+ console.log(colors_1.c.dim(" Connect once — MetaMask will prompt 5 times in sequence."));
1637
+ // Connect once, reuse session for all 5 txs
1638
+ const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: "Connect wallet for spend limit setup (5 approvals)" });
1639
+ for (const cat of categories) {
1640
+ const amount = ethers_1.ethers.parseEther(cat.amount);
1641
+ const data = policyInterface.encodeFunctionData("setCategoryLimitFor", [walletAddr, cat.name, amount]);
1642
+ console.log(colors_1.c.dim(`\n Setting ${cat.name} → ${cat.amount} ETH...`));
1643
+ // Send Telegram notification for each
1644
+ if (telegramOpts) {
1645
+ const { sendTelegramMessage: sendTg } = await Promise.resolve().then(() => __importStar(require("../telegram-notify.js")));
1646
+ await sendTg({ ...telegramOpts, text: `Approve spend limit: ${cat.name} → ${cat.amount} ETH` });
1647
+ }
1648
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
1649
+ to: policyAddress,
1650
+ data,
1651
+ value: "0x0",
1652
+ });
1653
+ console.log(colors_1.c.dim(` Submitted: ${txHash}`));
1654
+ await provider.waitForTransaction(txHash);
1655
+ console.log(colors_1.c.success + colors_1.c.white(` ${cat.name} → ${cat.amount} ETH`));
1656
+ }
1657
+ console.log(colors_1.c.white("\n✓ All 5 spend limits set.\n"));
1658
+ });
1358
1659
  walletPolicy.command("set <policyId>")
1359
1660
  .description("Set the active policy ID on ARC402Wallet (phone wallet signs via WalletConnect)")
1360
1661
  .action(async (policyId) => {