pandora-cli-skills 1.1.11 → 1.1.13

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/cli/pandora.cjs CHANGED
@@ -27,6 +27,8 @@ const {
27
27
  runPolymarketApprove,
28
28
  runPolymarketPreflight,
29
29
  } = require('./lib/polymarket_ops_service.cjs');
30
+ const { runResolve, runLp, runLpPositions } = require('./lib/market_admin_service.cjs');
31
+ const { decodeContractError, formatDecodedContractError } = require('./lib/contract_error_decoder.cjs');
30
32
  const {
31
33
  defaultManifestFile: defaultMirrorManifestFile,
32
34
  findPair: findMirrorPair,
@@ -59,6 +61,8 @@ const COMMAND_TARGETS = {
59
61
  const OUTPUT_MODES = new Set(['table', 'json']);
60
62
  const DEFAULT_RPC_TIMEOUT_MS = 12_000;
61
63
  const DEFAULT_INDEXER_TIMEOUT_MS = 12_000;
64
+ const DEFAULT_POLYMARKET_HOST = 'https://clob.polymarket.com';
65
+ const DEFAULT_POLYMARKET_RPC_URL = 'https://polygon-bor-rpc.publicnode.com';
62
66
  const POSITIONS_ORDER_BY_FIELDS = ['lastTradeAt', 'id', 'chainId', 'marketAddress', 'user'];
63
67
  const POSITIONS_ORDER_BY_FIELD_SET = new Set(POSITIONS_ORDER_BY_FIELDS);
64
68
  const MARKET_LIFECYCLE_FILTERS = new Set(['all', 'active', 'resolved', 'expiring-soon']);
@@ -215,8 +219,8 @@ pandora - Prediction market CLI
215
219
  Usage:
216
220
  pandora [--output table|json] help
217
221
  pandora [--output table|json] init-env [--force] [--dotenv-path <path>] [--example <path>]
218
- pandora [--output table|json] doctor [--dotenv-path <path>] [--skip-dotenv] [--check-usdc-code] [--rpc-timeout-ms <ms>]
219
- pandora [--output table|json] setup [--force] [--dotenv-path <path>] [--example <path>] [--check-usdc-code] [--rpc-timeout-ms <ms>]
222
+ pandora [--output table|json] doctor [--dotenv-path <path>] [--skip-dotenv] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
223
+ pandora [--output table|json] setup [--force] [--dotenv-path <path>] [--example <path>] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
220
224
  pandora [--output table|json] markets list [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--active|--resolved|--expiring-soon] [--expiring-hours <n>] [--expand] [--with-odds]
221
225
  pandora [--output table|json] markets get [--id <id> ...] [--stdin]
222
226
  pandora [--output table|json] polls list [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--status <int>] [--category <int>] [--question-contains <text>] [--where-json <json>]
@@ -224,7 +228,7 @@ Usage:
224
228
  pandora [--output table|json] events list [--type all|liquidity|oracle-fee|claim] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-direction asc|desc] [--chain-id <id>] [--wallet <address>] [--market-address <address>] [--poll-address <address>] [--tx-hash <hash>]
225
229
  pandora [--output table|json] events get --id <id> [--type all|liquidity|oracle-fee|claim]
226
230
  pandora [--output table|json] positions list [--wallet <address>] [--market-address <address>] [--chain-id <id>] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--where-json <json>]
227
- pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events]
231
+ pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events] [--with-lp] [--rpc-url <url>]
228
232
  pandora [--output table|json] watch [--wallet <address>] [--market-address <address>] [--side yes|no] [--amount-usdc <amount>] [--iterations <n>] [--interval-ms <ms>] [--chain-id <id>] [--include-events|--no-events] [--yes-pct <0-100>] [--alert-yes-below <0-100>] [--alert-yes-above <0-100>] [--alert-net-liquidity-below <amount>] [--alert-net-liquidity-above <amount>] [--fail-on-alert]
229
233
  pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--expand] [--with-odds]
230
234
  pandora [--output table|json] quote [--indexer-url <url>] [--timeout-ms <ms>] --market-address <address> --side yes|no --amount-usdc <amount> [--yes-pct <0-100>] [--slippage-bps <0-10000>]
@@ -239,20 +243,20 @@ Usage:
239
243
  pandora [--output table|json] leaderboard [--metric profit|volume|win-rate] [--chain-id <id>] [--limit <n>] [--min-trades <n>]
240
244
  pandora [--output table|json] analyze --market-address <address> [--provider <name>] [--model <id>] [--max-cost-usd <n>] [--temperature <n>] [--timeout-ms <ms>]
241
245
  pandora [--output table|json] suggest --wallet <address> --risk low|medium|high --budget <amount> [--count <n>] [--include-venues pandora,polymarket]
242
- pandora [--output table|json] resolve --poll-address <address> --answer yes|no|invalid --reason <text> --dry-run|--execute
243
- pandora [--output table|json] lp add|remove|positions ...
246
+ pandora [--output table|json] resolve --poll-address <address> --answer yes|no --reason <text> --dry-run|--execute [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>]
247
+ pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n>] [--dry-run|--execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>] [--deadline-seconds <n>] [--indexer-url <url>] [--timeout-ms <ms>]
244
248
  pandora launch [--dotenv-path <path>] [--skip-dotenv] [script args...]
245
249
  pandora clone-bet [--dotenv-path <path>] [--skip-dotenv] [script args...]
246
250
 
247
251
  Examples:
248
252
  pandora setup
249
- pandora --output json doctor --check-usdc-code
253
+ pandora --output json doctor --check-usdc-code --check-polymarket
250
254
  pandora markets list --active --with-odds --limit 10
251
255
  pandora markets get --id market-1 --id market-2
252
256
  pandora polls get --id 0xabc...
253
257
  pandora events list --type all --limit 25
254
258
  pandora positions list --wallet 0x1234...
255
- pandora portfolio --wallet 0x1234... --chain-id 1
259
+ pandora portfolio --wallet 0x1234... --chain-id 1 --with-lp
256
260
  pandora watch --market-address 0xabc... --side yes --amount-usdc 10 --iterations 5 --interval-ms 2000
257
261
  pandora scan --limit 25 --chain-id 1 --with-odds
258
262
  pandora quote --market-address 0xabc... --side yes --amount-usdc 50
@@ -266,9 +270,9 @@ Examples:
266
270
  pandora mirror verify --pandora-market-address 0xabc... --polymarket-market-id 0xdef... --include-similarity
267
271
  pandora mirror go --polymarket-slug nba-mia-phi-2026-02-28 --liquidity-usdc 10 --paper
268
272
  pandora mirror sync once --pandora-market-address 0xabc... --polymarket-market-id 0xdef... --paper --hedge-ratio 1.0
269
- pandora polymarket check --rpc-url https://polygon-rpc.com --private-key 0x... --funder 0xproxy...
270
- pandora polymarket approve --dry-run --rpc-url https://polygon-rpc.com --private-key 0x... --funder 0xproxy...
271
- pandora polymarket preflight --rpc-url https://polygon-rpc.com --private-key 0x... --funder 0xproxy...
273
+ pandora polymarket check --rpc-url https://polygon-bor-rpc.publicnode.com --private-key 0x... --funder 0xproxy...
274
+ pandora polymarket approve --dry-run --rpc-url https://polygon-bor-rpc.publicnode.com --private-key 0x... --funder 0xproxy...
275
+ pandora polymarket preflight --rpc-url https://polygon-bor-rpc.publicnode.com --private-key 0x... --funder 0xproxy...
272
276
  pandora mirror close --pandora-market-address 0xabc... --polymarket-market-id 0xdef... --dry-run
273
277
  pandora webhook test --webhook-url https://example.com/hook --webhook-template '{\"text\":\"{{message}}\"}'
274
278
  pandora leaderboard --metric profit --limit 20
@@ -884,6 +888,7 @@ function parseDoctorFlags(args) {
884
888
  let envFile = DEFAULT_ENV_FILE;
885
889
  let useEnvFile = true;
886
890
  let checkUsdcCode = false;
891
+ let checkPolymarket = false;
887
892
  let rpcTimeoutMs = DEFAULT_RPC_TIMEOUT_MS;
888
893
 
889
894
  for (let i = 0; i < args.length; i += 1) {
@@ -906,6 +911,11 @@ function parseDoctorFlags(args) {
906
911
  continue;
907
912
  }
908
913
 
914
+ if (token === '--check-polymarket') {
915
+ checkPolymarket = true;
916
+ continue;
917
+ }
918
+
909
919
  if (token === '--rpc-timeout-ms') {
910
920
  const next = requireFlagValue(args, i, '--rpc-timeout-ms');
911
921
  rpcTimeoutMs = parsePositiveInteger(next, '--rpc-timeout-ms');
@@ -916,7 +926,7 @@ function parseDoctorFlags(args) {
916
926
  throw new CliError('UNKNOWN_FLAG', `Unknown flag for doctor: ${token}`);
917
927
  }
918
928
 
919
- return { envFile, useEnvFile, checkUsdcCode, rpcTimeoutMs };
929
+ return { envFile, useEnvFile, checkUsdcCode, checkPolymarket, rpcTimeoutMs };
920
930
  }
921
931
 
922
932
  function parseSetupFlags(args) {
@@ -924,6 +934,7 @@ function parseSetupFlags(args) {
924
934
  let exampleFile = DEFAULT_ENV_EXAMPLE;
925
935
  let force = false;
926
936
  let checkUsdcCode = false;
937
+ let checkPolymarket = false;
927
938
  let rpcTimeoutMs = DEFAULT_RPC_TIMEOUT_MS;
928
939
 
929
940
  for (let i = 0; i < args.length; i += 1) {
@@ -953,6 +964,11 @@ function parseSetupFlags(args) {
953
964
  continue;
954
965
  }
955
966
 
967
+ if (token === '--check-polymarket') {
968
+ checkPolymarket = true;
969
+ continue;
970
+ }
971
+
956
972
  if (token === '--rpc-timeout-ms') {
957
973
  const next = requireFlagValue(args, i, '--rpc-timeout-ms');
958
974
  rpcTimeoutMs = parsePositiveInteger(next, '--rpc-timeout-ms');
@@ -963,7 +979,7 @@ function parseSetupFlags(args) {
963
979
  throw new CliError('UNKNOWN_FLAG', `Unknown flag for setup: ${token}`);
964
980
  }
965
981
 
966
- return { envFile, exampleFile, force, checkUsdcCode, rpcTimeoutMs };
982
+ return { envFile, exampleFile, force, checkUsdcCode, checkPolymarket, rpcTimeoutMs };
967
983
  }
968
984
 
969
985
  function parseInitEnvFlags(args) {
@@ -1648,6 +1664,8 @@ function parsePortfolioFlags(args) {
1648
1664
  chainId: null,
1649
1665
  limit: 100,
1650
1666
  includeEvents: true,
1667
+ withLp: false,
1668
+ rpcUrl: null,
1651
1669
  };
1652
1670
 
1653
1671
  for (let i = 0; i < args.length; i += 1) {
@@ -1681,6 +1699,17 @@ function parsePortfolioFlags(args) {
1681
1699
  continue;
1682
1700
  }
1683
1701
 
1702
+ if (token === '--with-lp') {
1703
+ options.withLp = true;
1704
+ continue;
1705
+ }
1706
+
1707
+ if (token === '--rpc-url') {
1708
+ options.rpcUrl = requireFlagValue(args, i, '--rpc-url');
1709
+ i += 1;
1710
+ continue;
1711
+ }
1712
+
1684
1713
  throw new CliError('UNKNOWN_FLAG', `Unknown flag for portfolio: ${token}`);
1685
1714
  }
1686
1715
 
@@ -3207,6 +3236,7 @@ function parseMirrorSyncFlags(args) {
3207
3236
  maxTradesPerDay: null,
3208
3237
  cooldownMs: 60_000,
3209
3238
  depthSlippageBps: 100,
3239
+ minTimeToCloseSec: 1800,
3210
3240
  iterations: null,
3211
3241
  stream: false,
3212
3242
  stateFile: null,
@@ -3334,6 +3364,17 @@ function parseMirrorSyncFlags(args) {
3334
3364
  i += 1;
3335
3365
  continue;
3336
3366
  }
3367
+ if (token === '--min-time-to-close-sec') {
3368
+ options.minTimeToCloseSec = parsePositiveInteger(
3369
+ requireFlagValue(rest, i, '--min-time-to-close-sec'),
3370
+ '--min-time-to-close-sec',
3371
+ );
3372
+ if (options.minTimeToCloseSec < 60) {
3373
+ throw new CliError('INVALID_FLAG_VALUE', '--min-time-to-close-sec must be >= 60.');
3374
+ }
3375
+ i += 1;
3376
+ continue;
3377
+ }
3337
3378
  if (token === '--iterations') {
3338
3379
  options.iterations = parsePositiveInteger(requireFlagValue(rest, i, '--iterations'), '--iterations');
3339
3380
  i += 1;
@@ -3564,6 +3605,9 @@ function buildMirrorSyncDaemonCliArgs(options, shared) {
3564
3605
  }
3565
3606
  args.push('--cooldown-ms', String(options.cooldownMs));
3566
3607
  args.push('--depth-slippage-bps', String(options.depthSlippageBps));
3608
+ if (options.minTimeToCloseSec !== 1800) {
3609
+ args.push('--min-time-to-close-sec', String(options.minTimeToCloseSec));
3610
+ }
3567
3611
  if (Number.isFinite(options.iterations) && options.iterations > 0) {
3568
3612
  args.push('--iterations', String(options.iterations));
3569
3613
  }
@@ -3577,7 +3621,6 @@ function buildMirrorSyncDaemonCliArgs(options, shared) {
3577
3621
  args.push('--chain-id', String(options.chainId));
3578
3622
  }
3579
3623
  if (options.rpcUrl) args.push('--rpc-url', options.rpcUrl);
3580
- if (options.privateKey) args.push('--private-key', options.privateKey);
3581
3624
  if (options.funder) args.push('--funder', options.funder);
3582
3625
  if (options.usdc) args.push('--usdc', options.usdc);
3583
3626
  if (options.polymarketHost) args.push('--polymarket-host', options.polymarketHost);
@@ -4203,6 +4246,9 @@ function parseResolveFlags(args) {
4203
4246
  reason: null,
4204
4247
  dryRun: false,
4205
4248
  execute: false,
4249
+ chainId: null,
4250
+ rpcUrl: null,
4251
+ privateKey: null,
4206
4252
  };
4207
4253
 
4208
4254
  for (let i = 0; i < args.length; i += 1) {
@@ -4234,6 +4280,25 @@ function parseResolveFlags(args) {
4234
4280
  options.execute = true;
4235
4281
  continue;
4236
4282
  }
4283
+ if (token === '--chain-id') {
4284
+ options.chainId = parseInteger(requireFlagValue(args, i, '--chain-id'), '--chain-id');
4285
+ i += 1;
4286
+ continue;
4287
+ }
4288
+ if (token === '--rpc-url') {
4289
+ options.rpcUrl = requireFlagValue(args, i, '--rpc-url');
4290
+ i += 1;
4291
+ continue;
4292
+ }
4293
+ if (token === '--private-key') {
4294
+ const value = requireFlagValue(args, i, '--private-key');
4295
+ if (!isValidPrivateKey(value)) {
4296
+ throw new CliError('INVALID_FLAG_VALUE', '--private-key must be 0x + 64 hex chars.');
4297
+ }
4298
+ options.privateKey = value;
4299
+ i += 1;
4300
+ continue;
4301
+ }
4237
4302
  throw new CliError('UNKNOWN_FLAG', `Unknown flag for resolve: ${token}`);
4238
4303
  }
4239
4304
 
@@ -4269,6 +4334,12 @@ function parseLpFlags(args) {
4269
4334
  chainId: null,
4270
4335
  dryRun: false,
4271
4336
  execute: false,
4337
+ rpcUrl: null,
4338
+ privateKey: null,
4339
+ usdc: null,
4340
+ deadlineSeconds: 1800,
4341
+ indexerUrl: null,
4342
+ timeoutMs: DEFAULT_INDEXER_TIMEOUT_MS,
4272
4343
  };
4273
4344
 
4274
4345
  for (let i = 0; i < rest.length; i += 1) {
@@ -4298,6 +4369,43 @@ function parseLpFlags(args) {
4298
4369
  i += 1;
4299
4370
  continue;
4300
4371
  }
4372
+ if (token === '--rpc-url') {
4373
+ options.rpcUrl = requireFlagValue(rest, i, '--rpc-url');
4374
+ i += 1;
4375
+ continue;
4376
+ }
4377
+ if (token === '--private-key') {
4378
+ const value = requireFlagValue(rest, i, '--private-key');
4379
+ if (!isValidPrivateKey(value)) {
4380
+ throw new CliError('INVALID_FLAG_VALUE', '--private-key must be 0x + 64 hex chars.');
4381
+ }
4382
+ options.privateKey = value;
4383
+ i += 1;
4384
+ continue;
4385
+ }
4386
+ if (token === '--usdc') {
4387
+ options.usdc = parseAddressFlag(requireFlagValue(rest, i, '--usdc'), '--usdc');
4388
+ i += 1;
4389
+ continue;
4390
+ }
4391
+ if (token === '--deadline-seconds') {
4392
+ options.deadlineSeconds = parsePositiveInteger(requireFlagValue(rest, i, '--deadline-seconds'), '--deadline-seconds');
4393
+ if (options.deadlineSeconds < 60) {
4394
+ throw new CliError('INVALID_FLAG_VALUE', '--deadline-seconds must be >= 60.');
4395
+ }
4396
+ i += 1;
4397
+ continue;
4398
+ }
4399
+ if (token === '--indexer-url') {
4400
+ options.indexerUrl = requireFlagValue(rest, i, '--indexer-url');
4401
+ i += 1;
4402
+ continue;
4403
+ }
4404
+ if (token === '--timeout-ms') {
4405
+ options.timeoutMs = parsePositiveInteger(requireFlagValue(rest, i, '--timeout-ms'), '--timeout-ms');
4406
+ i += 1;
4407
+ continue;
4408
+ }
4301
4409
  if (token === '--dry-run') {
4302
4410
  options.dryRun = true;
4303
4411
  continue;
@@ -4378,6 +4486,42 @@ async function rpcRequest(rpcUrl, method, params, timeoutMs) {
4378
4486
  return payload.result;
4379
4487
  }
4380
4488
 
4489
+ async function probeHttpEndpoint(url, timeoutMs, method = 'HEAD') {
4490
+ const controller = new AbortController();
4491
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
4492
+ try {
4493
+ const response = await fetch(url, {
4494
+ method,
4495
+ signal: controller.signal,
4496
+ });
4497
+ return {
4498
+ ok: response.ok,
4499
+ status: response.status,
4500
+ error: response.ok ? null : `HTTP ${response.status}`,
4501
+ };
4502
+ } catch (err) {
4503
+ if (err && err.name === 'AbortError') {
4504
+ return { ok: false, status: null, error: `Request timed out after ${timeoutMs}ms.` };
4505
+ }
4506
+ return { ok: false, status: null, error: err && err.message ? err.message : String(err) };
4507
+ } finally {
4508
+ clearTimeout(timeout);
4509
+ }
4510
+ }
4511
+
4512
+ function hasPolymarketDoctorInputs() {
4513
+ const keys = [
4514
+ 'POLYMARKET_HOST',
4515
+ 'POLYMARKET_RPC_URL',
4516
+ 'POLYMARKET_FUNDER',
4517
+ 'POLYMARKET_PRIVATE_KEY',
4518
+ 'POLYMARKET_API_KEY',
4519
+ 'POLYMARKET_API_SECRET',
4520
+ 'POLYMARKET_API_PASSPHRASE',
4521
+ ];
4522
+ return keys.some((key) => String(process.env[key] || '').trim().length > 0);
4523
+ }
4524
+
4381
4525
  function validateEnvValues() {
4382
4526
  const missing = REQUIRED_ENV_KEYS.filter((key) => !process.env[key] || !String(process.env[key]).trim());
4383
4527
  const missingSet = new Set(missing);
@@ -4449,6 +4593,7 @@ async function buildDoctorReport(options) {
4449
4593
  }
4450
4594
 
4451
4595
  const envState = validateEnvValues();
4596
+ const shouldCheckPolymarket = options.checkPolymarket || hasPolymarketDoctorInputs();
4452
4597
  const report = {
4453
4598
  env: {
4454
4599
  envFile: options.envFile,
@@ -4472,6 +4617,19 @@ async function buildDoctorReport(options) {
4472
4617
  error: null,
4473
4618
  },
4474
4619
  codeChecks: [],
4620
+ polymarket: {
4621
+ checked: shouldCheckPolymarket,
4622
+ host: String(process.env.POLYMARKET_HOST || DEFAULT_POLYMARKET_HOST).trim() || DEFAULT_POLYMARKET_HOST,
4623
+ rpcUrl: String(process.env.POLYMARKET_RPC_URL || DEFAULT_POLYMARKET_RPC_URL).trim() || DEFAULT_POLYMARKET_RPC_URL,
4624
+ hostReachability: {
4625
+ ok: null,
4626
+ status: null,
4627
+ error: null,
4628
+ },
4629
+ check: null,
4630
+ failures: [],
4631
+ warnings: [],
4632
+ },
4475
4633
  summary: {
4476
4634
  ok: false,
4477
4635
  errorCount: 0,
@@ -4548,6 +4706,53 @@ async function buildDoctorReport(options) {
4548
4706
  report.codeChecks.push(check);
4549
4707
  }
4550
4708
 
4709
+ if (shouldCheckPolymarket) {
4710
+ const hostProbe = await probeHttpEndpoint(report.polymarket.host, options.rpcTimeoutMs, 'HEAD');
4711
+ report.polymarket.hostReachability = hostProbe;
4712
+ if (!hostProbe.ok) {
4713
+ report.polymarket.failures.push(`Polymarket host reachability failed: ${hostProbe.error || 'unknown error'}`);
4714
+ }
4715
+
4716
+ let polyCheck = null;
4717
+ try {
4718
+ polyCheck = await runPolymarketCheck({
4719
+ rpcUrl: report.polymarket.rpcUrl,
4720
+ privateKey: process.env.POLYMARKET_PRIVATE_KEY || null,
4721
+ funder: process.env.POLYMARKET_FUNDER || null,
4722
+ host: process.env.POLYMARKET_HOST || DEFAULT_POLYMARKET_HOST,
4723
+ });
4724
+ report.polymarket.check = polyCheck;
4725
+ } catch (err) {
4726
+ const message = err && err.message ? err.message : String(err);
4727
+ report.polymarket.failures.push(`Polymarket check failed: ${message}`);
4728
+ report.polymarket.check = null;
4729
+ }
4730
+
4731
+ if (polyCheck) {
4732
+ if (!polyCheck.chainOk || polyCheck.chainId !== 137) {
4733
+ report.polymarket.failures.push(
4734
+ `Polymarket RPC chain must be Polygon (137). Received: ${polyCheck.chainId === null ? 'unknown' : polyCheck.chainId}.`,
4735
+ );
4736
+ }
4737
+ if (!polyCheck.runtime || !polyCheck.runtime.funderAddress) {
4738
+ report.polymarket.failures.push('POLYMARKET_FUNDER is not configured.');
4739
+ } else if (!polyCheck.ownership || polyCheck.ownership.funderCodePresent !== true) {
4740
+ report.polymarket.failures.push('POLYMARKET_FUNDER does not appear to be a contract/proxy wallet.');
4741
+ }
4742
+ if (!polyCheck.ownership || polyCheck.ownership.ok !== true) {
4743
+ report.polymarket.failures.push('Signer does not match/own configured POLYMARKET_FUNDER.');
4744
+ }
4745
+ if (!polyCheck.apiKeySanity || polyCheck.apiKeySanity.ok !== true) {
4746
+ report.polymarket.failures.push(
4747
+ `Polymarket API-key derivation/sanity failed (${polyCheck.apiKeySanity ? polyCheck.apiKeySanity.status : 'unknown'}).`,
4748
+ );
4749
+ }
4750
+ if (Array.isArray(polyCheck.diagnostics) && polyCheck.diagnostics.length) {
4751
+ report.polymarket.warnings.push(...polyCheck.diagnostics);
4752
+ }
4753
+ }
4754
+ }
4755
+
4551
4756
  const failures = [];
4552
4757
  if (!report.env.required.ok) {
4553
4758
  failures.push(...report.env.required.missing.map((name) => `Missing required env var: ${name}`));
@@ -4566,6 +4771,10 @@ async function buildDoctorReport(options) {
4566
4771
  report.summary.warningCount += 1;
4567
4772
  }
4568
4773
  }
4774
+ if (shouldCheckPolymarket) {
4775
+ failures.push(...report.polymarket.failures);
4776
+ report.summary.warningCount += report.polymarket.warnings.length;
4777
+ }
4569
4778
 
4570
4779
  report.summary.errorCount = failures.length;
4571
4780
  report.summary.ok = failures.length === 0;
@@ -4640,6 +4849,38 @@ function renderDoctorReportTable(report) {
4640
4849
  statusRows.push([`code:${check.key}`, status, detail]);
4641
4850
  }
4642
4851
 
4852
+ if (report.polymarket && report.polymarket.checked) {
4853
+ const host = report.polymarket.hostReachability || {};
4854
+ statusRows.push([
4855
+ 'polymarket:host',
4856
+ host.ok ? 'PASS' : 'FAIL',
4857
+ host.ok ? `${report.polymarket.host} (${host.status})` : host.error || 'Unreachable',
4858
+ ]);
4859
+ const polyCheck = report.polymarket.check;
4860
+ statusRows.push([
4861
+ 'polymarket:chain',
4862
+ polyCheck && polyCheck.chainOk && polyCheck.chainId === 137 ? 'PASS' : 'FAIL',
4863
+ polyCheck ? `chainId=${polyCheck.chainId} expected=137` : 'Unavailable',
4864
+ ]);
4865
+ statusRows.push([
4866
+ 'polymarket:funder',
4867
+ polyCheck && polyCheck.ownership && polyCheck.ownership.funderCodePresent === true ? 'PASS' : 'FAIL',
4868
+ polyCheck && polyCheck.runtime ? polyCheck.runtime.funderAddress || 'missing' : 'Unavailable',
4869
+ ]);
4870
+ statusRows.push([
4871
+ 'polymarket:ownership',
4872
+ polyCheck && polyCheck.ownership && polyCheck.ownership.ok ? 'PASS' : 'FAIL',
4873
+ polyCheck && polyCheck.ownership && polyCheck.ownership.ownerCheckError
4874
+ ? polyCheck.ownership.ownerCheckError
4875
+ : '',
4876
+ ]);
4877
+ statusRows.push([
4878
+ 'polymarket:api-key',
4879
+ polyCheck && polyCheck.apiKeySanity && polyCheck.apiKeySanity.ok ? 'PASS' : 'FAIL',
4880
+ polyCheck && polyCheck.apiKeySanity ? polyCheck.apiKeySanity.status : 'Unavailable',
4881
+ ]);
4882
+ }
4883
+
4643
4884
  printTable(['Check', 'Status', 'Details'], statusRows);
4644
4885
 
4645
4886
  if (report.summary.ok) {
@@ -4652,6 +4893,13 @@ function renderDoctorReportTable(report) {
4652
4893
  }
4653
4894
  }
4654
4895
  }
4896
+
4897
+ if (report.polymarket && report.polymarket.checked && Array.isArray(report.polymarket.warnings) && report.polymarket.warnings.length) {
4898
+ console.log('Polymarket diagnostics:');
4899
+ for (const warning of report.polymarket.warnings) {
4900
+ console.log(`- ${warning}`);
4901
+ }
4902
+ }
4655
4903
  }
4656
4904
 
4657
4905
  function renderSetupTable(data) {
@@ -4788,7 +5036,14 @@ function renderTradeTable(data) {
4788
5036
  ['quoteAvailable', data.quote && data.quote.quoteAvailable ? 'yes' : 'no'],
4789
5037
  ['account', data.account || ''],
4790
5038
  ['approveTxHash', data.approveTxHash || ''],
5039
+ ['approveTxUrl', data.approveTxUrl || ''],
5040
+ ['approveGasEstimate', data.approveGasEstimate || ''],
5041
+ ['approveStatus', data.approveStatus || ''],
4791
5042
  ['buyTxHash', data.buyTxHash || ''],
5043
+ ['buyTxUrl', data.buyTxUrl || ''],
5044
+ ['buyGasEstimate', data.buyGasEstimate || ''],
5045
+ ['buyStatus', data.buyStatus || ''],
5046
+ ['finalStatus', data.finalStatus || ''],
4792
5047
  ['status', data.status || ''],
4793
5048
  ];
4794
5049
  printTable(['Field', 'Value'], rows);
@@ -4862,6 +5117,10 @@ function renderPortfolioTable(data) {
4862
5117
  ['cashflowNet', data.summary.cashflowNet],
4863
5118
  ['pnlProxy', data.summary.pnlProxy],
4864
5119
  ['eventsIncluded', data.summary.eventsIncluded ? 'yes' : 'no'],
5120
+ ['lpIncluded', data.summary.lpIncluded ? 'yes' : 'no'],
5121
+ ['lpPositionCount', data.summary.lpPositionCount === undefined ? '' : data.summary.lpPositionCount],
5122
+ ['lpMarketsWithBalance', data.summary.lpMarketsWithBalance === undefined ? '' : data.summary.lpMarketsWithBalance],
5123
+ ['lpEstimatedCollateralOutUsdc', data.summary.lpEstimatedCollateralOutUsdc === undefined ? '' : data.summary.lpEstimatedCollateralOutUsdc],
4865
5124
  ['diagnostic', data.summary.diagnostic || ''],
4866
5125
  ];
4867
5126
 
@@ -4878,6 +5137,19 @@ function renderPortfolioTable(data) {
4878
5137
  ]),
4879
5138
  );
4880
5139
  }
5140
+
5141
+ if (Array.isArray(data.lpPositions) && data.lpPositions.length) {
5142
+ console.log('');
5143
+ printTable(
5144
+ ['LP Market', 'LP Tokens', 'Est. Collateral Out (USDC)', 'Diagnostics'],
5145
+ data.lpPositions.map((item) => [
5146
+ short(item.marketAddress, 18),
5147
+ item.lpTokenBalance || '',
5148
+ item.preview && item.preview.collateralOutUsdc ? item.preview.collateralOutUsdc : '',
5149
+ short(Array.isArray(item.diagnostics) ? item.diagnostics.join('; ') : '', 56),
5150
+ ]),
5151
+ );
5152
+ }
4881
5153
  }
4882
5154
 
4883
5155
  function renderWatchTable(data) {
@@ -5081,6 +5353,15 @@ function renderMirrorPlanTable(data) {
5081
5353
  function renderMirrorBrowseTable(data) {
5082
5354
  if (!Array.isArray(data.items) || !data.items.length) {
5083
5355
  console.log('No mirrorable markets found for current filters.');
5356
+ if (data.gammaApiError) {
5357
+ console.log(`Gamma API error: ${data.gammaApiError}`);
5358
+ }
5359
+ if (Array.isArray(data.diagnostics) && data.diagnostics.length) {
5360
+ console.log('Diagnostics:');
5361
+ for (const diagnostic of data.diagnostics) {
5362
+ console.log(`- ${diagnostic}`);
5363
+ }
5364
+ }
5084
5365
  return;
5085
5366
  }
5086
5367
 
@@ -5095,6 +5376,18 @@ function renderMirrorBrowseTable(data) {
5095
5376
  short(item.question || '', 72),
5096
5377
  ]),
5097
5378
  );
5379
+
5380
+ if (Array.isArray(data.diagnostics) && data.diagnostics.length) {
5381
+ console.log('');
5382
+ console.log('Diagnostics:');
5383
+ for (const diagnostic of data.diagnostics) {
5384
+ console.log(`- ${diagnostic}`);
5385
+ }
5386
+ }
5387
+ if (data.gammaApiError) {
5388
+ console.log('');
5389
+ console.log(`Gamma API error: ${data.gammaApiError}`);
5390
+ }
5098
5391
  }
5099
5392
 
5100
5393
  function renderMirrorDeployTable(data) {
@@ -6379,34 +6672,107 @@ async function executeTradeOnchain(options) {
6379
6672
  const publicClient = createPublicClient({ chain: runtime.chain, transport: http(runtime.rpcUrl) });
6380
6673
  const walletClient = createWalletClient({ account, chain: runtime.chain, transport: http(runtime.rpcUrl) });
6381
6674
 
6675
+ const marketCode = await publicClient.getBytecode({ address: options.marketAddress });
6676
+ if (!marketCode || marketCode === '0x' || marketCode === '0x0') {
6677
+ throw new CliError(
6678
+ 'MARKET_ADDRESS_NO_CODE',
6679
+ `--market-address has no bytecode on chain ${runtime.chainId}: ${options.marketAddress}`,
6680
+ {
6681
+ chainId: runtime.chainId,
6682
+ marketAddress: options.marketAddress,
6683
+ },
6684
+ );
6685
+ }
6686
+
6382
6687
  const amountRaw = parseUnits(String(options.amountUsdc), 6);
6383
6688
  const minSharesOutRaw = options.minSharesOutRaw === null ? 0n : options.minSharesOutRaw;
6689
+ const explorerBase = runtime.chainId === 1 ? 'https://etherscan.io/tx/' : 'https://sonicscan.org/tx/';
6690
+ const toExplorerUrl = (hash) => (hash ? `${explorerBase}${hash}` : null);
6691
+ const decodeTradeError = async (error, code, fallbackMessage, details = {}) => {
6692
+ const decoded = await decodeContractError(error);
6693
+ const decodedMessage = formatDecodedContractError(decoded);
6694
+ const causeMessage =
6695
+ (error && (error.shortMessage || error.message)) || formatErrorValue(error) || fallbackMessage;
6696
+ throw new CliError(code, decodedMessage || causeMessage || fallbackMessage, {
6697
+ ...details,
6698
+ decodedError: decoded,
6699
+ cause: causeMessage,
6700
+ });
6701
+ };
6384
6702
 
6385
- const allowance = await publicClient.readContract({
6386
- address: runtime.usdcAddress,
6387
- abi: ERC20_ABI,
6388
- functionName: 'allowance',
6389
- args: [account.address, options.marketAddress],
6390
- });
6391
-
6392
- let approveTxHash = null;
6393
- if (allowance < amountRaw) {
6394
- approveTxHash = await walletClient.writeContract({
6703
+ let allowance;
6704
+ try {
6705
+ allowance = await publicClient.readContract({
6395
6706
  address: runtime.usdcAddress,
6396
6707
  abi: ERC20_ABI,
6397
- functionName: 'approve',
6398
- args: [options.marketAddress, amountRaw],
6708
+ functionName: 'allowance',
6709
+ args: [account.address, options.marketAddress],
6710
+ });
6711
+ } catch (error) {
6712
+ await decodeTradeError(error, 'ALLOWANCE_READ_FAILED', 'Failed to read USDC allowance.', {
6713
+ stage: 'allowance-read',
6714
+ usdc: runtime.usdcAddress,
6399
6715
  });
6400
- await publicClient.waitForTransactionReceipt({ hash: approveTxHash });
6401
6716
  }
6402
6717
 
6403
- const buyTxHash = await walletClient.writeContract({
6404
- address: options.marketAddress,
6405
- abi: PARI_MUTUEL_ABI,
6406
- functionName: 'buy',
6407
- args: [options.side === 'yes', amountRaw, minSharesOutRaw],
6408
- });
6409
- await publicClient.waitForTransactionReceipt({ hash: buyTxHash });
6718
+ let approveTxHash = null;
6719
+ let approveGasEstimate = null;
6720
+ let approveStatus = null;
6721
+ if (allowance < amountRaw) {
6722
+ let approveSimulation;
6723
+ try {
6724
+ approveSimulation = await publicClient.simulateContract({
6725
+ account,
6726
+ address: runtime.usdcAddress,
6727
+ abi: ERC20_ABI,
6728
+ functionName: 'approve',
6729
+ args: [options.marketAddress, amountRaw],
6730
+ });
6731
+ approveGasEstimate =
6732
+ approveSimulation && approveSimulation.request && approveSimulation.request.gas
6733
+ ? approveSimulation.request.gas.toString()
6734
+ : null;
6735
+ } catch (error) {
6736
+ await decodeTradeError(error, 'APPROVE_SIMULATION_FAILED', 'USDC approve simulation failed.', {
6737
+ stage: 'approve-simulate',
6738
+ });
6739
+ }
6740
+ try {
6741
+ approveTxHash = await walletClient.writeContract(approveSimulation.request);
6742
+ const approveReceipt = await publicClient.waitForTransactionReceipt({ hash: approveTxHash });
6743
+ approveStatus = approveReceipt && approveReceipt.status ? approveReceipt.status : null;
6744
+ } catch (error) {
6745
+ await decodeTradeError(error, 'APPROVE_EXECUTION_FAILED', 'USDC approve transaction failed.', {
6746
+ stage: 'approve-execute',
6747
+ approveTxHash,
6748
+ });
6749
+ }
6750
+ }
6751
+
6752
+ let buyTxHash = null;
6753
+ let buyGasEstimate = null;
6754
+ let buyStatus = null;
6755
+ try {
6756
+ const buySimulation = await publicClient.simulateContract({
6757
+ account,
6758
+ address: options.marketAddress,
6759
+ abi: PARI_MUTUEL_ABI,
6760
+ functionName: 'buy',
6761
+ args: [options.side === 'yes', amountRaw, minSharesOutRaw],
6762
+ });
6763
+ buyGasEstimate =
6764
+ buySimulation && buySimulation.request && buySimulation.request.gas
6765
+ ? buySimulation.request.gas.toString()
6766
+ : null;
6767
+ buyTxHash = await walletClient.writeContract(buySimulation.request);
6768
+ const buyReceipt = await publicClient.waitForTransactionReceipt({ hash: buyTxHash });
6769
+ buyStatus = buyReceipt && buyReceipt.status ? buyReceipt.status : null;
6770
+ } catch (error) {
6771
+ await decodeTradeError(error, 'TRADE_EXECUTION_FAILED', 'Buy transaction failed.', {
6772
+ stage: 'buy',
6773
+ buyTxHash,
6774
+ });
6775
+ }
6410
6776
 
6411
6777
  return {
6412
6778
  chainId: runtime.chainId,
@@ -6415,7 +6781,14 @@ async function executeTradeOnchain(options) {
6415
6781
  amountRaw: amountRaw.toString(),
6416
6782
  minSharesOutRaw: minSharesOutRaw.toString(),
6417
6783
  approveTxHash,
6784
+ approveTxUrl: toExplorerUrl(approveTxHash),
6785
+ approveGasEstimate,
6786
+ approveStatus,
6418
6787
  buyTxHash,
6788
+ buyTxUrl: toExplorerUrl(buyTxHash),
6789
+ buyGasEstimate,
6790
+ buyStatus,
6791
+ status: buyStatus || 'confirmed',
6419
6792
  };
6420
6793
  }
6421
6794
 
@@ -6492,7 +6865,14 @@ async function runTradeCommand(args, context) {
6492
6865
  account: execution.account,
6493
6866
  usdc: execution.usdc,
6494
6867
  approveTxHash: execution.approveTxHash,
6868
+ approveTxUrl: execution.approveTxUrl,
6869
+ approveGasEstimate: execution.approveGasEstimate,
6870
+ approveStatus: execution.approveStatus,
6495
6871
  buyTxHash: execution.buyTxHash,
6872
+ buyTxUrl: execution.buyTxUrl,
6873
+ buyGasEstimate: execution.buyGasEstimate,
6874
+ buyStatus: execution.buyStatus,
6875
+ finalStatus: execution.status,
6496
6876
  quote,
6497
6877
  };
6498
6878
 
@@ -7023,6 +7403,7 @@ async function collectPortfolioSnapshot(indexerUrl, options, timeoutMs) {
7023
7403
  const positionsPage = await fetchPortfolioPositions(indexerUrl, options, timeoutMs);
7024
7404
  let liquidityPage = { items: [] };
7025
7405
  let claimPage = { items: [] };
7406
+ let lpPayload = null;
7026
7407
 
7027
7408
  if (options.includeEvents) {
7028
7409
  [liquidityPage, claimPage] = await Promise.all([
@@ -7031,21 +7412,37 @@ async function collectPortfolioSnapshot(indexerUrl, options, timeoutMs) {
7031
7412
  ]);
7032
7413
  }
7033
7414
 
7415
+ if (options.withLp) {
7416
+ lpPayload = await runLpPositions({
7417
+ wallet: options.wallet,
7418
+ chainId: options.chainId,
7419
+ rpcUrl: options.rpcUrl || null,
7420
+ indexerUrl,
7421
+ timeoutMs,
7422
+ });
7423
+ }
7424
+
7034
7425
  const positions = Array.isArray(positionsPage.items) ? positionsPage.items : [];
7035
7426
  const liquidityEvents = Array.isArray(liquidityPage.items) ? liquidityPage.items : [];
7036
7427
  const claimEvents = Array.isArray(claimPage.items) ? claimPage.items : [];
7428
+ const lpPositions = Array.isArray(lpPayload && lpPayload.items) ? lpPayload.items : [];
7429
+ const lpDiagnostics = Array.isArray(lpPayload && lpPayload.diagnostics) ? lpPayload.diagnostics : [];
7037
7430
 
7038
7431
  return {
7039
- summary: summarizePortfolio(options, positions, liquidityEvents, claimEvents),
7432
+ summary: summarizePortfolio(options, positions, liquidityEvents, claimEvents, lpPositions, lpDiagnostics),
7040
7433
  positions,
7434
+ lpPositions,
7041
7435
  events: {
7042
7436
  liquidity: liquidityEvents,
7043
7437
  claims: claimEvents,
7044
7438
  },
7439
+ diagnostics: {
7440
+ lp: lpDiagnostics,
7441
+ },
7045
7442
  };
7046
7443
  }
7047
7444
 
7048
- function summarizePortfolio(options, positions, liquidityEvents, claimEvents) {
7445
+ function summarizePortfolio(options, positions, liquidityEvents, claimEvents, lpPositions = [], lpDiagnostics = []) {
7049
7446
  const uniqueMarkets = new Set(
7050
7447
  positions
7051
7448
  .map((item) => normalizeLookupKey(item && item.marketAddress))
@@ -7079,6 +7476,28 @@ function summarizePortfolio(options, positions, liquidityEvents, claimEvents) {
7079
7476
  if (options.chainId !== null && options.includeEvents) {
7080
7477
  diagnostics.push('Claim events are not chain-filtered by indexer schema; values may span chains.');
7081
7478
  }
7479
+ if (options.withLp && !lpPositions.length) {
7480
+ diagnostics.push('No LP positions found for this wallet/filter.');
7481
+ }
7482
+ if (options.withLp && lpDiagnostics.length) {
7483
+ diagnostics.push(...lpDiagnostics);
7484
+ }
7485
+
7486
+ const lpPositionCount = Array.isArray(lpPositions) ? lpPositions.length : 0;
7487
+ const lpMarketsWithBalance = lpPositions.filter((item) => {
7488
+ try {
7489
+ return BigInt(item && item.lpTokenBalanceRaw ? item.lpTokenBalanceRaw : '0') > 0n;
7490
+ } catch {
7491
+ return false;
7492
+ }
7493
+ }).length;
7494
+ const lpEstimatedCollateralOutUsdc = toFixedNumber(
7495
+ lpPositions.reduce((sum, item) => {
7496
+ const value = Number(item && item.preview && item.preview.collateralOutUsdc);
7497
+ return Number.isFinite(value) ? sum + value : sum;
7498
+ }, 0),
7499
+ 6,
7500
+ );
7082
7501
 
7083
7502
  return {
7084
7503
  positionCount: positions.length,
@@ -7090,6 +7509,10 @@ function summarizePortfolio(options, positions, liquidityEvents, claimEvents) {
7090
7509
  cashflowNet,
7091
7510
  pnlProxy: cashflowNet,
7092
7511
  eventsIncluded: options.includeEvents,
7512
+ lpIncluded: Boolean(options.withLp),
7513
+ lpPositionCount,
7514
+ lpMarketsWithBalance,
7515
+ lpEstimatedCollateralOutUsdc,
7093
7516
  diagnostic: diagnostics.join(' '),
7094
7517
  };
7095
7518
  }
@@ -7189,9 +7612,12 @@ async function runPortfolioCommand(args, context) {
7189
7612
  wallet: options.wallet,
7190
7613
  chainId: options.chainId,
7191
7614
  limit: options.limit,
7615
+ withLp: options.withLp,
7192
7616
  summary: snapshot.summary,
7193
7617
  positions: snapshot.positions,
7618
+ lpPositions: snapshot.lpPositions,
7194
7619
  events: snapshot.events,
7620
+ diagnostics: snapshot.diagnostics,
7195
7621
  };
7196
7622
 
7197
7623
  emitSuccess(context.outputMode, 'portfolio', payload, renderPortfolioTable);
@@ -8288,7 +8714,7 @@ async function runMirrorCommand(args, context) {
8288
8714
  'mirror.sync.help',
8289
8715
  {
8290
8716
  usage:
8291
- 'pandora [--output table|json] mirror sync run|once|start|stop|status --pandora-market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--execute-live] [--private-key <hex>] [--funder <address>] [--trust-deploy] [--daemon] [--stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--state-file <path>] [--kill-switch-file <path>]',
8717
+ 'pandora [--output table|json] mirror sync run|once|start|stop|status --pandora-market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--execute-live] [--private-key <hex>] [--funder <address>] [--trust-deploy] [--daemon] [--stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--min-time-to-close-sec <n>] [--state-file <path>] [--kill-switch-file <path>]',
8292
8718
  daemonLifecycle: {
8293
8719
  start:
8294
8720
  'pandora [--output table|json] mirror sync start --pandora-market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [run flags]',
@@ -8317,7 +8743,7 @@ async function runMirrorCommand(args, context) {
8317
8743
  );
8318
8744
  } else {
8319
8745
  console.log(
8320
- 'Usage: pandora [--output table|json] mirror sync run|once|start|stop|status --pandora-market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--execute-live] [--private-key <hex>] [--funder <address>] [--trust-deploy] [--daemon] [--stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--state-file <path>] [--kill-switch-file <path>]',
8746
+ 'Usage: pandora [--output table|json] mirror sync run|once|start|stop|status --pandora-market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--execute-live] [--private-key <hex>] [--funder <address>] [--trust-deploy] [--daemon] [--stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--min-time-to-close-sec <n>] [--state-file <path>] [--kill-switch-file <path>]',
8321
8747
  );
8322
8748
  console.log('Daemon stop: pandora mirror sync stop --pid-file <path>|--strategy-hash <hash>');
8323
8749
  console.log('Daemon status: pandora mirror sync status --pid-file <path>|--strategy-hash <hash>');
@@ -8391,6 +8817,15 @@ async function runMirrorCommand(args, context) {
8391
8817
  },
8392
8818
  shared,
8393
8819
  );
8820
+ const daemonEnv = {
8821
+ ...process.env,
8822
+ };
8823
+ if (options.privateKey) {
8824
+ daemonEnv.POLYMARKET_PRIVATE_KEY = options.privateKey;
8825
+ }
8826
+ if (options.funder) {
8827
+ daemonEnv.POLYMARKET_FUNDER = options.funder;
8828
+ }
8394
8829
 
8395
8830
  let payload;
8396
8831
  try {
@@ -8399,7 +8834,7 @@ async function runMirrorCommand(args, context) {
8399
8834
  cliPath: __filename,
8400
8835
  cliArgs: daemonCliArgs,
8401
8836
  cwd: process.cwd(),
8402
- env: process.env,
8837
+ env: daemonEnv,
8403
8838
  mode: 'run',
8404
8839
  executeLive: options.executeLive,
8405
8840
  stateFile: options.stateFile,
@@ -8867,48 +9302,64 @@ async function runSuggestCommand(args, context) {
8867
9302
  emitSuccess(context.outputMode, 'suggest', payload, renderSuggestTable);
8868
9303
  }
8869
9304
 
8870
- function runResolveCommand(args, context) {
9305
+ async function runResolveCommand(args, context) {
8871
9306
  if (includesHelpFlag(args)) {
8872
9307
  if (context.outputMode === 'json') {
8873
9308
  emitSuccess(
8874
9309
  context.outputMode,
8875
9310
  'resolve.help',
8876
9311
  commandHelpPayload(
8877
- 'pandora [--output table|json] resolve --poll-address <address> --answer yes|no|invalid --reason <text> --dry-run|--execute',
9312
+ 'pandora [--output table|json] resolve --poll-address <address> --answer yes|no --reason <text> --dry-run|--execute [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>]',
8878
9313
  ),
8879
9314
  );
8880
9315
  } else {
8881
9316
  console.log(
8882
- 'Usage: pandora [--output table|json] resolve --poll-address <address> --answer yes|no|invalid --reason <text> --dry-run|--execute',
9317
+ 'Usage: pandora [--output table|json] resolve --poll-address <address> --answer yes|no --reason <text> --dry-run|--execute [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>]',
8883
9318
  );
8884
9319
  }
8885
9320
  return;
8886
9321
  }
8887
- parseResolveFlags(args);
8888
- throw new CliError(
8889
- 'ABI_READY_REQUIRED',
8890
- 'resolve is ABI-gated and not available yet. Commit verified ABI signatures/events and tests first.',
8891
- { command: 'resolve' },
8892
- );
9322
+ const options = parseResolveFlags(args);
9323
+ let payload;
9324
+ try {
9325
+ payload = await runResolve(options);
9326
+ } catch (err) {
9327
+ if (err && err.code) {
9328
+ throw new CliError(err.code, err.message || 'resolve command failed.', err.details);
9329
+ }
9330
+ throw err;
9331
+ }
9332
+ emitSuccess(context.outputMode, 'resolve', payload, renderSingleEntityTable);
8893
9333
  }
8894
9334
 
8895
- function runLpCommand(args, context) {
9335
+ async function runLpCommand(args, context) {
8896
9336
  if (includesHelpFlag(args)) {
8897
9337
  if (context.outputMode === 'json') {
8898
- emitSuccess(context.outputMode, 'lp.help', commandHelpPayload('pandora [--output table|json] lp add|remove|positions ...'));
9338
+ emitSuccess(
9339
+ context.outputMode,
9340
+ 'lp.help',
9341
+ commandHelpPayload(
9342
+ 'pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n>] [--dry-run|--execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>] [--deadline-seconds <n>] [--indexer-url <url>] [--timeout-ms <ms>]',
9343
+ ),
9344
+ );
8899
9345
  } else {
8900
9346
  console.log(
8901
- 'Usage: pandora [--output table|json] lp add|remove|positions ...',
9347
+ 'Usage: pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n>] [--dry-run|--execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>] [--deadline-seconds <n>] [--indexer-url <url>] [--timeout-ms <ms>]',
8902
9348
  );
8903
9349
  }
8904
9350
  return;
8905
9351
  }
8906
- parseLpFlags(args);
8907
- throw new CliError(
8908
- 'ABI_READY_REQUIRED',
8909
- 'lp is ABI-gated and not available yet. Commit verified ABI signatures/events and tests first.',
8910
- { command: 'lp' },
8911
- );
9352
+ const options = parseLpFlags(args);
9353
+ let payload;
9354
+ try {
9355
+ payload = await runLp(options);
9356
+ } catch (err) {
9357
+ if (err && err.code) {
9358
+ throw new CliError(err.code, err.message || 'lp command failed.', err.details);
9359
+ }
9360
+ throw err;
9361
+ }
9362
+ emitSuccess(context.outputMode, 'lp', payload, renderSingleEntityTable);
8912
9363
  }
8913
9364
 
8914
9365
  function runInitEnv(args, outputMode) {
@@ -9003,6 +9454,7 @@ async function runSetup(args, outputMode) {
9003
9454
  envFile: options.envFile,
9004
9455
  useEnvFile: true,
9005
9456
  checkUsdcCode: options.checkUsdcCode,
9457
+ checkPolymarket: options.checkPolymarket,
9006
9458
  rpcTimeoutMs: options.rpcTimeoutMs,
9007
9459
  });
9008
9460
 
@@ -9146,12 +9598,12 @@ async function dispatch(command, args, context) {
9146
9598
  }
9147
9599
 
9148
9600
  if (command === 'resolve') {
9149
- runResolveCommand(args, context);
9601
+ await runResolveCommand(args, context);
9150
9602
  return;
9151
9603
  }
9152
9604
 
9153
9605
  if (command === 'lp') {
9154
- runLpCommand(args, context);
9606
+ await runLpCommand(args, context);
9155
9607
  return;
9156
9608
  }
9157
9609
 
@@ -9185,6 +9637,10 @@ async function main() {
9185
9637
  return;
9186
9638
  }
9187
9639
 
9640
+ if (args[0] === 'pandora') {
9641
+ args = args.slice(1);
9642
+ }
9643
+
9188
9644
  const command = args[0];
9189
9645
  const commandArgs = args.slice(1);
9190
9646