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/SKILL.md +2 -2
- package/cli/lib/contract_error_decoder.cjs +138 -0
- package/cli/lib/market_admin_service.cjs +868 -0
- package/cli/lib/mirror_service.cjs +1 -0
- package/cli/lib/mirror_sync_service.cjs +7 -1
- package/cli/lib/pandora_deploy_service.cjs +69 -27
- package/cli/lib/polymarket_trade_adapter.cjs +6 -1
- package/cli/pandora.cjs +514 -58
- package/package.json +1 -1
- package/references/creation-script.md +1 -1
- package/scripts/.env.example +2 -0
- package/scripts/create_market_launcher.ts +1 -1
- package/scripts/create_polymarket_clone_and_bet.ts +1 -1
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
|
|
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
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
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: '
|
|
6398
|
-
args: [options.marketAddress
|
|
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
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
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(
|
|
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
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
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
|
|