pandora-cli-skills 1.1.47 → 1.1.49
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/README.md +1 -0
- package/README_FOR_SHARING.md +2 -1
- package/SKILL.md +4 -3
- package/cli/lib/error_recovery_service.cjs +25 -1
- package/cli/lib/lp_command_service.cjs +13 -3
- package/cli/lib/market_admin_service.cjs +35 -2
- package/cli/lib/mirror_command_service.cjs +3 -3
- package/cli/lib/mirror_handlers/browse.cjs +2 -2
- package/cli/lib/mirror_handlers/go.cjs +127 -19
- package/cli/lib/mirror_handlers/sync.cjs +4 -2
- package/cli/lib/mirror_service.cjs +12 -1
- package/cli/lib/parsers/lp_flags.cjs +10 -2
- package/cli/lib/parsers/mirror_deploy_flags.cjs +10 -1
- package/cli/lib/parsers/mirror_go_flags.cjs +23 -5
- package/cli/lib/parsers/mirror_remaining_flags.cjs +29 -0
- package/cli/lib/parsers/mirror_sync_flags.cjs +21 -5
- package/cli/lib/polymarket_ops_service.cjs +29 -0
- package/cli/lib/polymarket_trade_adapter.cjs +167 -12
- package/cli/lib/schema_command_service.cjs +20 -0
- package/cli/pandora.cjs +38 -2
- package/package.json +1 -1
- package/tests/cli/cli.integration.test.cjs +99 -0
- package/tests/unit/lp_command_service.test.cjs +125 -0
- package/tests/unit/lp_flags.test.cjs +92 -0
- package/tests/unit/mirror_go_regressions.test.cjs +479 -0
- package/tests/unit/new-features.test.cjs +119 -0
package/README.md
CHANGED
|
@@ -122,6 +122,7 @@ pandora --output json lifecycle resolve --id <lifecycle-id> --confirm
|
|
|
122
122
|
- `pandora odds record|history`
|
|
123
123
|
- `pandora autopilot run|once`
|
|
124
124
|
- `pandora mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close`
|
|
125
|
+
- `mirror browse` supports `--polymarket-tag-id|--polymarket-tag-ids` (aliases `--sport-tag-id|--sport-tag-ids`) for sports-tagged Gamma event discovery.
|
|
125
126
|
- `pandora polymarket check|approve|preflight|trade`
|
|
126
127
|
- `pandora resolve`
|
|
127
128
|
- `pandora lp add|remove|positions`
|
package/README_FOR_SHARING.md
CHANGED
|
@@ -200,6 +200,7 @@ Mirror advanced flags (for operator tuning):
|
|
|
200
200
|
- `--sync-interval-ms <ms>` on `mirror go` to control auto-sync tick cadence.
|
|
201
201
|
- `--oracle <address>` / `--factory <address>` on `mirror deploy` and `mirror go` for explicit contract overrides.
|
|
202
202
|
- `--polymarket-gamma-mock-url <url>` on `mirror browse|plan|verify|go|sync|status` for deterministic mock-source testing.
|
|
203
|
+
- `--polymarket-tag-id <id>` / `--polymarket-tag-ids <csv>` on `mirror browse` (aliases: `--sport-tag-id`, `--sport-tag-ids`) to query sports-tagged Gamma events.
|
|
203
204
|
- `--no-stream` on `mirror sync` to disable per-tick stdout line streaming in run mode.
|
|
204
205
|
- `--pid-file <path>` on `mirror sync stop|status` for explicit daemon process selection.
|
|
205
206
|
|
|
@@ -219,7 +220,7 @@ Mirror advanced flags (for operator tuning):
|
|
|
219
220
|
- `pandora export --wallet <0x...> --format csv --out ./trades.csv`
|
|
220
221
|
- `pandora arbitrage --venues pandora,polymarket --min-spread-pct 2 --cross-venue-only --with-rules --include-similarity`
|
|
221
222
|
- `pandora autopilot once --market-address <0x...> --side no --amount-usdc 10 --trigger-yes-below 15 --paper`
|
|
222
|
-
- `pandora mirror browse --min-yes-pct 20 --max-yes-pct 80 --min-volume-24h 100000 --limit 10`
|
|
223
|
+
- `pandora mirror browse --polymarket-tag-id 82 --min-yes-pct 20 --max-yes-pct 80 --min-volume-24h 100000 --limit 10`
|
|
223
224
|
- `pandora mirror plan --source polymarket --polymarket-market-id <id> --with-rules --include-similarity`
|
|
224
225
|
- `pandora mirror go --polymarket-slug <slug> --liquidity-usdc 10 --paper`
|
|
225
226
|
- `pandora mirror verify --pandora-market-address <0x...> --polymarket-market-id <id> --include-similarity`
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pandora-cli-skills
|
|
3
3
|
summary: Canonical skill and operator guide for Pandora CLI including mirror, polymarket, resolve, and LP flows.
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.49
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Pandora CLI & Skills
|
|
@@ -317,7 +317,7 @@ pandora --output json history --wallet <0x...> --limit 50
|
|
|
317
317
|
pandora --output json export --wallet <0x...> --format csv --out ./trades.csv
|
|
318
318
|
pandora --output json arbitrage --venues pandora,polymarket --min-spread-pct 3 --cross-venue-only --with-rules --include-similarity
|
|
319
319
|
pandora --output json autopilot once --market-address <0x...> --side no --amount-usdc 10 --trigger-yes-below 15 --paper
|
|
320
|
-
pandora --output json mirror browse --min-yes-pct 20 --max-yes-pct 80 --min-volume-24h 100000 --limit 10
|
|
320
|
+
pandora --output json mirror browse --polymarket-tag-id 82 --min-yes-pct 20 --max-yes-pct 80 --min-volume-24h 100000 --limit 10
|
|
321
321
|
pandora --output json mirror plan --source polymarket --polymarket-market-id <id> --with-rules --include-similarity
|
|
322
322
|
pandora --output json mirror lp-explain --liquidity-usdc 10000 --source-yes-pct 58
|
|
323
323
|
pandora --output json mirror hedge-calc --reserve-yes-usdc 8 --reserve-no-usdc 12 --excess-no-usdc 2 --polymarket-yes-pct 60
|
|
@@ -562,7 +562,8 @@ Error envelope:
|
|
|
562
562
|
- `{ ok: true, command: "autopilot", data: { schemaVersion, generatedAt, strategyHash, mode, executeLive, stateFile, killSwitchFile, iterationsRequested, iterationsCompleted, stoppedReason?, parameters: { marketAddress, side, amountUsdc, triggerYesBelow?, triggerYesAbove?, intervalMs, cooldownMs, maxAmountUsdc?, maxOpenExposureUsdc, dailySpendCapUsdc, maxTradesPerDay }, state, actionCount, actions[], snapshots[], webhookReports[] } }`
|
|
563
563
|
- `mirror browse`:
|
|
564
564
|
- `{ ok: true, command: "mirror.browse", data: { schemaVersion, generatedAt, source, gammaApiError, filters, count, items[], diagnostics[] } }`
|
|
565
|
-
-
|
|
565
|
+
- `filters` can include `polymarketTagIds[]` when using `--polymarket-tag-id|--polymarket-tag-ids` (or `--sport-tag-id|--sport-tag-ids` aliases).
|
|
566
|
+
- each candidate row can include `eventId`, `eventSlug`, `eventTitle`, and `existingMirror: { marketAddress, similarity } | null`.
|
|
566
567
|
- `mirror go`:
|
|
567
568
|
- `{ ok: true, command: "mirror.go", data: { schemaVersion, generatedAt, mode, plan, deploy, verify, sync, polymarketPreflight, suggestedSyncCommand, trustManifest, diagnostics[] } }`
|
|
568
569
|
- `plan` is the same payload shape as `mirror.plan`; `deploy` is the same payload shape as `mirror.deploy`; `sync` is null unless `--auto-sync` is used.
|
|
@@ -43,6 +43,24 @@ function buildMirrorDeployRetryCommand(cliName) {
|
|
|
43
43
|
return `${cliName} mirror deploy --dry-run --plan-file <plan-file>`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function buildMirrorVerifyRetryCommand(cliName, details) {
|
|
47
|
+
const pandoraMarketAddress = toAddressOrPlaceholder(details && details.pandoraMarketAddress);
|
|
48
|
+
const polymarketMarketId = cleanToken(details && details.polymarketMarketId, '');
|
|
49
|
+
const polymarketSlug = cleanToken(details && details.polymarketSlug, '');
|
|
50
|
+
const manifestFile = cleanToken(details && details.manifestFile, '');
|
|
51
|
+
const command = [
|
|
52
|
+
`${cliName} mirror verify`,
|
|
53
|
+
`--market-address ${pandoraMarketAddress}`,
|
|
54
|
+
polymarketMarketId ? `--polymarket-market-id ${polymarketMarketId}` : null,
|
|
55
|
+
!polymarketMarketId && polymarketSlug ? `--polymarket-slug ${polymarketSlug}` : null,
|
|
56
|
+
'--trust-deploy',
|
|
57
|
+
manifestFile ? `--manifest-file ${manifestFile}` : null,
|
|
58
|
+
]
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.join(' ');
|
|
61
|
+
return command;
|
|
62
|
+
}
|
|
63
|
+
|
|
46
64
|
function buildMirrorSyncRetryCommand(cliName) {
|
|
47
65
|
return `${cliName} mirror sync once --paper --pandora-market-address <address> --polymarket-market-id <id>`;
|
|
48
66
|
}
|
|
@@ -197,13 +215,19 @@ function createErrorRecoveryService(options = {}) {
|
|
|
197
215
|
};
|
|
198
216
|
case 'MIRROR_DEPLOY_FAILED':
|
|
199
217
|
case 'MIRROR_GO_FAILED':
|
|
200
|
-
case 'MIRROR_GO_VERIFY_FAILED':
|
|
201
218
|
case 'MIRROR_GO_PREFLIGHT_FAILED':
|
|
202
219
|
return {
|
|
203
220
|
action: 'Re-run mirror deploy/verify in dry-run mode',
|
|
204
221
|
command: buildMirrorDeployRetryCommand(cliName),
|
|
205
222
|
retryable: true,
|
|
206
223
|
};
|
|
224
|
+
case 'MIRROR_GO_VERIFY_FAILED':
|
|
225
|
+
case 'MIRROR_GO_VERIFY_PENDING':
|
|
226
|
+
return {
|
|
227
|
+
action: 'Retry verification against the existing deployed market (do not redeploy)',
|
|
228
|
+
command: buildMirrorVerifyRetryCommand(cliName, details),
|
|
229
|
+
retryable: true,
|
|
230
|
+
};
|
|
207
231
|
case 'MIRROR_SYNC_FAILED':
|
|
208
232
|
case 'MIRROR_GO_SYNC_FAILED':
|
|
209
233
|
case 'MIRROR_SYNC_PREFLIGHT_FAILED':
|
|
@@ -14,6 +14,8 @@ function createRunLpCommand(deps) {
|
|
|
14
14
|
const includesHelpFlag = requireDep(deps, 'includesHelpFlag');
|
|
15
15
|
const emitSuccess = requireDep(deps, 'emitSuccess');
|
|
16
16
|
const commandHelpPayload = requireDep(deps, 'commandHelpPayload');
|
|
17
|
+
const parseIndexerSharedFlags = requireDep(deps, 'parseIndexerSharedFlags');
|
|
18
|
+
const maybeLoadTradeEnv = requireDep(deps, 'maybeLoadTradeEnv');
|
|
17
19
|
const parseLpFlags = requireDep(deps, 'parseLpFlags');
|
|
18
20
|
const runLp = requireDep(deps, 'runLp');
|
|
19
21
|
const renderSingleEntityTable = requireDep(deps, 'renderSingleEntityTable');
|
|
@@ -27,18 +29,26 @@ function createRunLpCommand(deps) {
|
|
|
27
29
|
context.outputMode,
|
|
28
30
|
'lp.help',
|
|
29
31
|
commandHelpPayload(
|
|
30
|
-
'pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n
|
|
32
|
+
'pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n>|--all] [--dry-run|--execute] [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>] [--deadline-seconds <n>] [--indexer-url <url>] [--timeout-ms <ms>]',
|
|
31
33
|
),
|
|
32
34
|
);
|
|
33
35
|
} else {
|
|
34
36
|
// eslint-disable-next-line no-console
|
|
35
37
|
console.log(
|
|
36
|
-
'Usage: pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n
|
|
38
|
+
'Usage: pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n>|--all] [--dry-run|--execute] [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>] [--deadline-seconds <n>] [--indexer-url <url>] [--timeout-ms <ms>]',
|
|
37
39
|
);
|
|
38
40
|
}
|
|
39
41
|
return;
|
|
40
42
|
}
|
|
41
|
-
const
|
|
43
|
+
const shared = parseIndexerSharedFlags(args);
|
|
44
|
+
maybeLoadTradeEnv(shared);
|
|
45
|
+
const options = parseLpFlags(shared.rest);
|
|
46
|
+
if (shared.indexerUrl) {
|
|
47
|
+
options.indexerUrl = shared.indexerUrl;
|
|
48
|
+
}
|
|
49
|
+
if (Number.isFinite(shared.timeoutMs)) {
|
|
50
|
+
options.timeoutMs = shared.timeoutMs;
|
|
51
|
+
}
|
|
42
52
|
if (options.execute && options.action !== 'positions' && assertLiveWriteAllowed) {
|
|
43
53
|
await assertLiveWriteAllowed(`lp.${options.action}.execute`, {
|
|
44
54
|
notionalUsdc: options.action === 'add' ? options.amountUsdc : null,
|
|
@@ -813,7 +813,7 @@ async function runLpRemove(options = {}) {
|
|
|
813
813
|
status: options.execute ? 'submitted' : 'planned',
|
|
814
814
|
action: 'remove',
|
|
815
815
|
marketAddress: options.marketAddress,
|
|
816
|
-
lpTokens: options.lpTokens,
|
|
816
|
+
lpTokens: options.lpAll ? 'all' : options.lpTokens,
|
|
817
817
|
deadlineSeconds,
|
|
818
818
|
txPlan: null,
|
|
819
819
|
preflight: null,
|
|
@@ -835,6 +835,20 @@ async function runLpRemove(options = {}) {
|
|
|
835
835
|
|
|
836
836
|
if (!options.execute) {
|
|
837
837
|
const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineSeconds);
|
|
838
|
+
if (options.lpAll) {
|
|
839
|
+
payload.txPlan = {
|
|
840
|
+
lpTokenDecimalsAssumed: 18,
|
|
841
|
+
sharesToBurnRaw: null,
|
|
842
|
+
sharesToBurnMode: 'all-onchain-balance-at-execution',
|
|
843
|
+
minCollateralOutRaw: '0',
|
|
844
|
+
deadline: deadline.toString(),
|
|
845
|
+
removeLiquidityArgOrder: 'sharesToBurn, minCollateralOut, deadline',
|
|
846
|
+
};
|
|
847
|
+
payload.diagnostics.push(
|
|
848
|
+
'Dry-run with --all defers LP token amount resolution to execution-time on-chain balance.',
|
|
849
|
+
);
|
|
850
|
+
return payload;
|
|
851
|
+
}
|
|
838
852
|
payload.txPlan = {
|
|
839
853
|
lpTokenDecimalsAssumed: 18,
|
|
840
854
|
sharesToBurnRaw: parseUnits(String(options.lpTokens), 18).toString(),
|
|
@@ -849,7 +863,26 @@ async function runLpRemove(options = {}) {
|
|
|
849
863
|
await ensureContractCode(publicClient, marketAddress, 'Market');
|
|
850
864
|
|
|
851
865
|
const lpTokenDecimals = await readDecimals(publicClient, marketAddress, 18);
|
|
852
|
-
|
|
866
|
+
let sharesToBurnRaw;
|
|
867
|
+
if (options.lpAll) {
|
|
868
|
+
const balanceRaw = await publicClient.readContract({
|
|
869
|
+
address: marketAddress,
|
|
870
|
+
abi: LP_TOKEN_ABI,
|
|
871
|
+
functionName: 'balanceOf',
|
|
872
|
+
args: [account.address],
|
|
873
|
+
});
|
|
874
|
+
if (typeof balanceRaw !== 'bigint' || balanceRaw <= 0n) {
|
|
875
|
+
throw createServiceError('LP_REMOVE_ZERO_BALANCE', 'No LP token balance available to remove with --all.', {
|
|
876
|
+
marketAddress,
|
|
877
|
+
account: account.address,
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
sharesToBurnRaw = balanceRaw;
|
|
881
|
+
payload.lpTokens = formatUnits(balanceRaw, lpTokenDecimals);
|
|
882
|
+
payload.diagnostics.push('Using full LP token balance (--all) from on-chain balanceOf(account).');
|
|
883
|
+
} else {
|
|
884
|
+
sharesToBurnRaw = parseUnits(String(options.lpTokens), lpTokenDecimals);
|
|
885
|
+
}
|
|
853
886
|
const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineSeconds);
|
|
854
887
|
const minCollateralOutRaw = 0n;
|
|
855
888
|
const preview = await readCalcRemoveLiquidity(publicClient, marketAddress, sharesToBurnRaw);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @type {string}
|
|
5
5
|
*/
|
|
6
6
|
const MIRROR_GO_USAGE =
|
|
7
|
-
'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--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>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--manifest-file <path>] [--trust-deploy] [--skip-gate] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>]';
|
|
7
|
+
'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--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>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--manifest-file <path>] [--trust-deploy] [--skip-gate] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>]';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Canonical usage string for `mirror sync`.
|
|
@@ -12,7 +12,7 @@ const MIRROR_GO_USAGE =
|
|
|
12
12
|
* @type {string}
|
|
13
13
|
*/
|
|
14
14
|
const MIRROR_SYNC_USAGE =
|
|
15
|
-
'pandora [--output table|json] mirror sync run|once|start --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--trust-deploy] [--manifest-file <path>] [--skip-gate] [--daemon] [--stream|--no-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>] [--depth-slippage-bps <n>] [--min-time-to-close-sec <n>] [--iterations <n>] [--state-file <path>] [--kill-switch-file <path>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--webhook-url <url>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>]';
|
|
15
|
+
'pandora [--output table|json] mirror sync run|once|start --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--trust-deploy] [--manifest-file <path>] [--skip-gate] [--daemon] [--stream|--no-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>] [--depth-slippage-bps <n>] [--min-time-to-close-sec <n>] [--iterations <n>] [--state-file <path>] [--kill-switch-file <path>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-rpc-url <url>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--webhook-url <url>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>]';
|
|
16
16
|
|
|
17
17
|
const INVALID_SUBCOMMAND_MESSAGE =
|
|
18
18
|
'mirror requires subcommand: browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close';
|
|
@@ -91,7 +91,7 @@ function createRunMirrorCommand(deps) {
|
|
|
91
91
|
console.log('');
|
|
92
92
|
console.log('Subcommands:');
|
|
93
93
|
console.log(
|
|
94
|
-
' browse --min-yes-pct <n> --max-yes-pct <n> --min-volume-24h <n> [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>] [--chain-id <id>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
94
|
+
' browse --min-yes-pct <n> --max-yes-pct <n> --min-volume-24h <n> [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>] [--chain-id <id>] [--polymarket-tag-id <id>] [--polymarket-tag-ids <csv>] [--sport-tag-id <id>] [--sport-tag-ids <csv>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
95
95
|
);
|
|
96
96
|
console.log(
|
|
97
97
|
' plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
@@ -22,12 +22,12 @@ module.exports = async function handleMirrorBrowse({ shared, context, deps }) {
|
|
|
22
22
|
context.outputMode,
|
|
23
23
|
'mirror.browse.help',
|
|
24
24
|
commandHelpPayload(
|
|
25
|
-
'pandora [--output table|json] mirror browse [--min-yes-pct <n>] [--max-yes-pct <n>] [--min-volume-24h <n>] [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>]',
|
|
25
|
+
'pandora [--output table|json] mirror browse [--min-yes-pct <n>] [--max-yes-pct <n>] [--min-volume-24h <n>] [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>] [--polymarket-tag-id <id>] [--polymarket-tag-ids <csv>] [--sport-tag-id <id>] [--sport-tag-ids <csv>]',
|
|
26
26
|
),
|
|
27
27
|
);
|
|
28
28
|
} else {
|
|
29
29
|
console.log(
|
|
30
|
-
'Usage: pandora [--output table|json] mirror browse [--min-yes-pct <n>] [--max-yes-pct <n>] [--min-volume-24h <n>] [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>]',
|
|
30
|
+
'Usage: pandora [--output table|json] mirror browse [--min-yes-pct <n>] [--max-yes-pct <n>] [--min-volume-24h <n>] [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>] [--polymarket-tag-id <id>] [--polymarket-tag-ids <csv>] [--sport-tag-id <id>] [--sport-tag-ids <csv>]',
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
return;
|
|
@@ -16,6 +16,9 @@ module.exports = async function handleMirrorGo({ shared, context, deps, mirrorGo
|
|
|
16
16
|
buildMirrorPlan,
|
|
17
17
|
deployMirror,
|
|
18
18
|
resolveTrustedDeployPair,
|
|
19
|
+
findMirrorPair,
|
|
20
|
+
defaultMirrorManifestFile,
|
|
21
|
+
hasContractCodeAtAddress,
|
|
19
22
|
verifyMirror,
|
|
20
23
|
runLivePolymarketPreflightForMirror,
|
|
21
24
|
runMirrorSync,
|
|
@@ -46,32 +49,19 @@ module.exports = async function handleMirrorGo({ shared, context, deps, mirrorGo
|
|
|
46
49
|
console.error(`Warning: ${deprecatedForceGateWarning}`);
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
const diagnostics = [];
|
|
53
|
+
if (deprecatedForceGateWarning) diagnostics.push(deprecatedForceGateWarning);
|
|
49
54
|
let planPayload;
|
|
50
|
-
let deployPayload;
|
|
51
55
|
try {
|
|
52
56
|
planPayload = await buildMirrorPlan({
|
|
53
57
|
...options,
|
|
54
58
|
indexerUrl,
|
|
55
59
|
timeoutMs: shared.timeoutMs,
|
|
56
60
|
});
|
|
57
|
-
if (options.executeLive && typeof assertLiveWriteAllowed === 'function') {
|
|
58
|
-
await assertLiveWriteAllowed('mirror.go.deploy.execute', {
|
|
59
|
-
notionalUsdc: options.liquidityUsdc,
|
|
60
|
-
runtimeMode: 'live',
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
deployPayload = await deployMirror({
|
|
64
|
-
...options,
|
|
65
|
-
planData: planPayload,
|
|
66
|
-
execute: options.executeLive,
|
|
67
|
-
indexerUrl,
|
|
68
|
-
timeoutMs: shared.timeoutMs,
|
|
69
|
-
});
|
|
70
61
|
} catch (err) {
|
|
71
62
|
throw coerceMirrorServiceError(err, 'MIRROR_GO_FAILED');
|
|
72
63
|
}
|
|
73
64
|
|
|
74
|
-
const pandoraMarketAddress = deployPayload && deployPayload.pandora ? deployPayload.pandora.marketAddress : null;
|
|
75
65
|
const sourceSelector = {
|
|
76
66
|
polymarketMarketId:
|
|
77
67
|
(planPayload && planPayload.sourceMarket && planPayload.sourceMarket.marketId) || options.polymarketMarketId || null,
|
|
@@ -79,17 +69,115 @@ module.exports = async function handleMirrorGo({ shared, context, deps, mirrorGo
|
|
|
79
69
|
(planPayload && planPayload.sourceMarket && planPayload.sourceMarket.slug) || options.polymarketSlug || null,
|
|
80
70
|
};
|
|
81
71
|
|
|
72
|
+
let reusedManifestLookup = null;
|
|
73
|
+
if (
|
|
74
|
+
options.executeLive &&
|
|
75
|
+
typeof findMirrorPair === 'function' &&
|
|
76
|
+
(sourceSelector.polymarketMarketId || sourceSelector.polymarketSlug)
|
|
77
|
+
) {
|
|
78
|
+
const manifestFilePath =
|
|
79
|
+
options.manifestFile || (typeof defaultMirrorManifestFile === 'function' ? defaultMirrorManifestFile() : null);
|
|
80
|
+
if (manifestFilePath) {
|
|
81
|
+
try {
|
|
82
|
+
const lookup = findMirrorPair(manifestFilePath, sourceSelector);
|
|
83
|
+
if (
|
|
84
|
+
lookup &&
|
|
85
|
+
lookup.pair &&
|
|
86
|
+
lookup.pair.trusted !== false &&
|
|
87
|
+
lookup.pair.pandoraMarketAddress
|
|
88
|
+
) {
|
|
89
|
+
let allowReuse = true;
|
|
90
|
+
if (typeof hasContractCodeAtAddress === 'function') {
|
|
91
|
+
try {
|
|
92
|
+
const hasCode = await hasContractCodeAtAddress({
|
|
93
|
+
marketAddress: lookup.pair.pandoraMarketAddress,
|
|
94
|
+
chainId: options.chainId,
|
|
95
|
+
rpcUrl: options.rpcUrl,
|
|
96
|
+
});
|
|
97
|
+
if (!hasCode) {
|
|
98
|
+
allowReuse = false;
|
|
99
|
+
diagnostics.push(
|
|
100
|
+
`Trusted mirror pair exists but has no bytecode at ${lookup.pair.pandoraMarketAddress}; continuing with fresh deploy.`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
diagnostics.push(
|
|
105
|
+
`On-chain deploy dedupe probe failed (${lookup.pair.pandoraMarketAddress}): ${err && err.message ? err.message : String(err)}. Conservatively reusing trusted pair to avoid duplicate spend.`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (allowReuse) {
|
|
110
|
+
reusedManifestLookup = lookup;
|
|
111
|
+
diagnostics.push(
|
|
112
|
+
`Trusted mirror pair already exists (${lookup.pair.pandoraMarketAddress}); deploy step skipped to prevent duplicate market creation.`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} catch (err) {
|
|
117
|
+
diagnostics.push(`Mirror manifest lookup failed: ${err && err.message ? err.message : String(err)}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let deployPayload;
|
|
123
|
+
try {
|
|
124
|
+
if (reusedManifestLookup) {
|
|
125
|
+
deployPayload = {
|
|
126
|
+
schemaVersion: '1.0.0',
|
|
127
|
+
generatedAt: new Date().toISOString(),
|
|
128
|
+
mode: 'execute',
|
|
129
|
+
dryRun: false,
|
|
130
|
+
sourceMarket: planPayload && planPayload.sourceMarket ? planPayload.sourceMarket : null,
|
|
131
|
+
planDigest: planPayload && planPayload.planDigest ? planPayload.planDigest : null,
|
|
132
|
+
pandora: {
|
|
133
|
+
marketAddress: reusedManifestLookup.pair.pandoraMarketAddress,
|
|
134
|
+
pollAddress: reusedManifestLookup.pair.pandoraPollAddress || null,
|
|
135
|
+
},
|
|
136
|
+
trustManifest: {
|
|
137
|
+
filePath: reusedManifestLookup.filePath,
|
|
138
|
+
pair: reusedManifestLookup.pair,
|
|
139
|
+
},
|
|
140
|
+
diagnostics: [
|
|
141
|
+
'Deploy skipped because trusted manifest already maps this source to an on-chain market.',
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
} else {
|
|
145
|
+
if (options.executeLive && typeof assertLiveWriteAllowed === 'function') {
|
|
146
|
+
await assertLiveWriteAllowed('mirror.go.deploy.execute', {
|
|
147
|
+
notionalUsdc: options.liquidityUsdc,
|
|
148
|
+
runtimeMode: 'live',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
deployPayload = await deployMirror({
|
|
152
|
+
...options,
|
|
153
|
+
planData: planPayload,
|
|
154
|
+
execute: options.executeLive,
|
|
155
|
+
indexerUrl,
|
|
156
|
+
timeoutMs: shared.timeoutMs,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
} catch (err) {
|
|
160
|
+
throw coerceMirrorServiceError(err, 'MIRROR_GO_FAILED');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const pandoraMarketAddress = deployPayload && deployPayload.pandora ? deployPayload.pandora.marketAddress : null;
|
|
164
|
+
|
|
82
165
|
let verifyPayload = null;
|
|
83
166
|
let syncPayload = null;
|
|
84
167
|
let polymarketPreflight = null;
|
|
85
168
|
let suggestedSyncCommand = null;
|
|
86
|
-
let trustManifest =
|
|
169
|
+
let trustManifest =
|
|
170
|
+
(deployPayload && deployPayload.trustManifest ? deployPayload.trustManifest : null)
|
|
171
|
+
|| (reusedManifestLookup
|
|
172
|
+
? {
|
|
173
|
+
filePath: reusedManifestLookup.filePath,
|
|
174
|
+
pair: reusedManifestLookup.pair,
|
|
175
|
+
}
|
|
176
|
+
: null);
|
|
87
177
|
let trustDeploy = Boolean(options.trustDeploy);
|
|
88
178
|
if (!trustDeploy && trustManifest && trustManifest.pair) {
|
|
89
179
|
trustDeploy = true;
|
|
90
180
|
}
|
|
91
|
-
const diagnostics = [];
|
|
92
|
-
if (deprecatedForceGateWarning) diagnostics.push(deprecatedForceGateWarning);
|
|
93
181
|
|
|
94
182
|
if (pandoraMarketAddress) {
|
|
95
183
|
if (!trustManifest && (options.executeLive || options.trustDeploy)) {
|
|
@@ -127,6 +215,23 @@ module.exports = async function handleMirrorGo({ shared, context, deps, mirrorGo
|
|
|
127
215
|
allowRuleMismatch: false,
|
|
128
216
|
});
|
|
129
217
|
} catch (err) {
|
|
218
|
+
if (options.executeLive) {
|
|
219
|
+
throw new CliError(
|
|
220
|
+
'MIRROR_GO_VERIFY_PENDING',
|
|
221
|
+
`Mirror market ${pandoraMarketAddress} is deployed, but verification failed (likely indexer/indexing lag). Do not rerun mirror go --execute-live; run mirror verify against this market address.`,
|
|
222
|
+
{
|
|
223
|
+
pandoraMarketAddress,
|
|
224
|
+
polymarketMarketId: sourceSelector.polymarketMarketId || null,
|
|
225
|
+
polymarketSlug: sourceSelector.polymarketSlug || null,
|
|
226
|
+
manifestFile:
|
|
227
|
+
(trustManifest && trustManifest.filePath)
|
|
228
|
+
|| options.manifestFile
|
|
229
|
+
|| (typeof defaultMirrorManifestFile === 'function' ? defaultMirrorManifestFile() : null),
|
|
230
|
+
reusedManifestPair: Boolean(reusedManifestLookup),
|
|
231
|
+
cause: err && err.message ? err.message : String(err),
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
}
|
|
130
235
|
throw coerceMirrorServiceError(err, 'MIRROR_GO_VERIFY_FAILED');
|
|
131
236
|
}
|
|
132
237
|
|
|
@@ -139,7 +244,8 @@ module.exports = async function handleMirrorGo({ shared, context, deps, mirrorGo
|
|
|
139
244
|
if (options.executeLive) {
|
|
140
245
|
try {
|
|
141
246
|
polymarketPreflight = await runLivePolymarketPreflightForMirror({
|
|
142
|
-
rpcUrl: options.rpcUrl,
|
|
247
|
+
rpcUrl: options.polymarketRpcUrl || options.rpcUrl,
|
|
248
|
+
polymarketRpcUrl: options.polymarketRpcUrl,
|
|
143
249
|
privateKey: options.privateKey,
|
|
144
250
|
funder: options.funder,
|
|
145
251
|
});
|
|
@@ -173,6 +279,7 @@ module.exports = async function handleMirrorGo({ shared, context, deps, mirrorGo
|
|
|
173
279
|
killSwitchFile: null,
|
|
174
280
|
chainId: options.chainId,
|
|
175
281
|
rpcUrl: options.rpcUrl,
|
|
282
|
+
polymarketRpcUrl: options.polymarketRpcUrl,
|
|
176
283
|
privateKey: options.privateKey,
|
|
177
284
|
funder: options.funder,
|
|
178
285
|
usdc: options.usdc,
|
|
@@ -248,6 +355,7 @@ module.exports = async function handleMirrorGo({ shared, context, deps, mirrorGo
|
|
|
248
355
|
? `--polymarket-slug ${sourceSelector.polymarketSlug}`
|
|
249
356
|
: null,
|
|
250
357
|
'--paper',
|
|
358
|
+
options.polymarketRpcUrl ? `--polymarket-rpc-url ${options.polymarketRpcUrl}` : null,
|
|
251
359
|
`--drift-trigger-bps ${options.driftTriggerBps}`,
|
|
252
360
|
`--hedge-trigger-usdc ${options.hedgeTriggerUsdc}`,
|
|
253
361
|
options.forceGate
|
|
@@ -53,6 +53,7 @@ module.exports = async function handleMirrorSync({ shared, context, deps, mirror
|
|
|
53
53
|
liveHedgeEnv: [
|
|
54
54
|
'POLYMARKET_PRIVATE_KEY',
|
|
55
55
|
'POLYMARKET_FUNDER',
|
|
56
|
+
'POLYMARKET_RPC_URL',
|
|
56
57
|
'POLYMARKET_API_KEY',
|
|
57
58
|
'POLYMARKET_API_SECRET',
|
|
58
59
|
'POLYMARKET_API_PASSPHRASE',
|
|
@@ -75,7 +76,7 @@ module.exports = async function handleMirrorSync({ shared, context, deps, mirror
|
|
|
75
76
|
console.log('Daemon stop: pandora mirror sync stop --pid-file <path>|--strategy-hash <hash>');
|
|
76
77
|
console.log('Daemon status: pandora mirror sync status --pid-file <path>|--strategy-hash <hash>');
|
|
77
78
|
console.log(
|
|
78
|
-
'Live hedge env: POLYMARKET_PRIVATE_KEY, POLYMARKET_FUNDER, POLYMARKET_API_KEY, POLYMARKET_API_SECRET, POLYMARKET_API_PASSPHRASE, POLYMARKET_HOST.',
|
|
79
|
+
'Live hedge env: POLYMARKET_PRIVATE_KEY, POLYMARKET_FUNDER, POLYMARKET_RPC_URL, POLYMARKET_API_KEY, POLYMARKET_API_SECRET, POLYMARKET_API_PASSPHRASE, POLYMARKET_HOST.',
|
|
79
80
|
);
|
|
80
81
|
console.log('POLYMARKET_FUNDER must be your Polymarket proxy wallet (Gnosis Safe), not your EOA wallet address.');
|
|
81
82
|
console.log('Polymarket CLOB collateral is Polygon USDC.e; ensure proxy wallet USDC.e balance and approvals are configured.');
|
|
@@ -216,7 +217,8 @@ module.exports = async function handleMirrorSync({ shared, context, deps, mirror
|
|
|
216
217
|
if (options.executeLive) {
|
|
217
218
|
try {
|
|
218
219
|
polymarketPreflight = await runLivePolymarketPreflightForMirror({
|
|
219
|
-
rpcUrl: options.rpcUrl,
|
|
220
|
+
rpcUrl: options.polymarketRpcUrl || options.rpcUrl,
|
|
221
|
+
polymarketRpcUrl: options.polymarketRpcUrl,
|
|
220
222
|
privateKey: options.privateKey,
|
|
221
223
|
funder: options.funder,
|
|
222
224
|
});
|
|
@@ -270,7 +270,17 @@ async function deployMirror(options = {}) {
|
|
|
270
270
|
: Number(planData.distributionHint && planData.distributionHint.distributionNo);
|
|
271
271
|
|
|
272
272
|
const sources = normalizeSources(options.sources);
|
|
273
|
-
if (sources.length < 2) {
|
|
273
|
+
if (options.sourcesProvided && sources.length < 2) {
|
|
274
|
+
throw createServiceError(
|
|
275
|
+
'INVALID_FLAG_VALUE',
|
|
276
|
+
'--sources requires at least two non-empty URLs when explicitly provided.',
|
|
277
|
+
{
|
|
278
|
+
providedCount: Array.isArray(options.sources) ? options.sources.length : 0,
|
|
279
|
+
normalizedCount: sources.length,
|
|
280
|
+
},
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (!options.sourcesProvided && sources.length < 2) {
|
|
274
284
|
diagnostics.push('Using fallback source URLs because explicit --sources were not provided.');
|
|
275
285
|
}
|
|
276
286
|
|
|
@@ -395,6 +405,7 @@ async function browseMirrorMarkets(options = {}) {
|
|
|
395
405
|
gammaUrl: options.polymarketGammaUrl,
|
|
396
406
|
gammaMockUrl: options.polymarketGammaMockUrl,
|
|
397
407
|
mockUrl: options.polymarketMockUrl,
|
|
408
|
+
polymarketTagIds: Array.isArray(options.polymarketTagIds) ? options.polymarketTagIds : [],
|
|
398
409
|
timeoutMs: options.timeoutMs,
|
|
399
410
|
minYesPct: options.minYesPct,
|
|
400
411
|
maxYesPct: options.maxYesPct,
|
|
@@ -42,6 +42,7 @@ function createParseLpFlags(deps) {
|
|
|
42
42
|
wallet: null,
|
|
43
43
|
amountUsdc: null,
|
|
44
44
|
lpTokens: null,
|
|
45
|
+
lpAll: false,
|
|
45
46
|
chainId: null,
|
|
46
47
|
dryRun: false,
|
|
47
48
|
execute: false,
|
|
@@ -78,6 +79,10 @@ function createParseLpFlags(deps) {
|
|
|
78
79
|
i += 1;
|
|
79
80
|
continue;
|
|
80
81
|
}
|
|
82
|
+
if (token === '--all') {
|
|
83
|
+
options.lpAll = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
81
86
|
if (token === '--chain-id') {
|
|
82
87
|
options.chainId = parseInteger(requireFlagValue(rest, i, '--chain-id'), '--chain-id');
|
|
83
88
|
i += 1;
|
|
@@ -172,8 +177,11 @@ function createParseLpFlags(deps) {
|
|
|
172
177
|
if (action === 'add' && options.amountUsdc === null) {
|
|
173
178
|
throw new CliError('MISSING_REQUIRED_FLAG', 'Missing liquidity amount. Use --amount-usdc <amount>.');
|
|
174
179
|
}
|
|
175
|
-
if (action === 'remove' && options.lpTokens === null) {
|
|
176
|
-
throw new CliError('MISSING_REQUIRED_FLAG', 'Missing LP token amount. Use --lp-tokens <amount
|
|
180
|
+
if (action === 'remove' && options.lpTokens === null && !options.lpAll) {
|
|
181
|
+
throw new CliError('MISSING_REQUIRED_FLAG', 'Missing LP token amount. Use --lp-tokens <amount> or --all.');
|
|
182
|
+
}
|
|
183
|
+
if (action === 'remove' && options.lpTokens !== null && options.lpAll) {
|
|
184
|
+
throw new CliError('INVALID_ARGS', 'Use only one remove mode: --lp-tokens <amount> or --all.');
|
|
177
185
|
}
|
|
178
186
|
if (options.dryRun === options.execute) {
|
|
179
187
|
throw new CliError('INVALID_ARGS', 'Use exactly one mode: --dry-run or --execute.');
|
|
@@ -36,6 +36,7 @@ function createParseMirrorDeployFlags(deps) {
|
|
|
36
36
|
arbiter: null,
|
|
37
37
|
category: 3,
|
|
38
38
|
sources: [],
|
|
39
|
+
sourcesProvided: false,
|
|
39
40
|
chainId: null,
|
|
40
41
|
rpcUrl: null,
|
|
41
42
|
privateKey: null,
|
|
@@ -123,6 +124,7 @@ function createParseMirrorDeployFlags(deps) {
|
|
|
123
124
|
if (!entries.length) {
|
|
124
125
|
throw new CliError('MISSING_FLAG_VALUE', 'Missing value for --sources');
|
|
125
126
|
}
|
|
127
|
+
options.sourcesProvided = true;
|
|
126
128
|
options.sources.push(...entries);
|
|
127
129
|
i = j - 1;
|
|
128
130
|
continue;
|
|
@@ -175,7 +177,14 @@ function createParseMirrorDeployFlags(deps) {
|
|
|
175
177
|
continue;
|
|
176
178
|
}
|
|
177
179
|
if (token === '--polymarket-host') {
|
|
178
|
-
|
|
180
|
+
const polymarketHost = requireFlagValue(args, i, '--polymarket-host');
|
|
181
|
+
if (!isSecureHttpUrlOrLocal(polymarketHost)) {
|
|
182
|
+
throw new CliError(
|
|
183
|
+
'INVALID_FLAG_VALUE',
|
|
184
|
+
'--polymarket-host must use https:// (or http://localhost/127.0.0.1 for local testing).',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
options.polymarketHost = polymarketHost;
|
|
179
188
|
i += 1;
|
|
180
189
|
continue;
|
|
181
190
|
}
|
|
@@ -49,12 +49,14 @@ function createParseMirrorGoFlags(deps) {
|
|
|
49
49
|
cooldownMs: 60_000,
|
|
50
50
|
chainId: null,
|
|
51
51
|
rpcUrl: null,
|
|
52
|
+
polymarketRpcUrl: null,
|
|
52
53
|
privateKey: null,
|
|
53
54
|
funder: null,
|
|
54
55
|
usdc: null,
|
|
55
56
|
oracle: null,
|
|
56
57
|
factory: null,
|
|
57
58
|
sources: [],
|
|
59
|
+
sourcesProvided: false,
|
|
58
60
|
manifestFile: null,
|
|
59
61
|
trustDeploy: false,
|
|
60
62
|
forceGate: false,
|
|
@@ -200,6 +202,18 @@ function createParseMirrorGoFlags(deps) {
|
|
|
200
202
|
i += 1;
|
|
201
203
|
continue;
|
|
202
204
|
}
|
|
205
|
+
if (token === '--polymarket-rpc-url') {
|
|
206
|
+
const polymarketRpcUrl = requireFlagValue(args, i, '--polymarket-rpc-url');
|
|
207
|
+
if (!isSecureHttpUrlOrLocal(polymarketRpcUrl)) {
|
|
208
|
+
throw new CliError(
|
|
209
|
+
'INVALID_FLAG_VALUE',
|
|
210
|
+
'--polymarket-rpc-url must use https:// (or http://localhost/127.0.0.1 for local testing).',
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
options.polymarketRpcUrl = polymarketRpcUrl;
|
|
214
|
+
i += 1;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
203
217
|
if (token === '--private-key') {
|
|
204
218
|
options.privateKey = parsePrivateKeyFlag(requireFlagValue(args, i, '--private-key'), '--private-key');
|
|
205
219
|
i += 1;
|
|
@@ -235,6 +249,7 @@ function createParseMirrorGoFlags(deps) {
|
|
|
235
249
|
if (!entries.length) {
|
|
236
250
|
throw new CliError('MISSING_FLAG_VALUE', 'Missing value for --sources');
|
|
237
251
|
}
|
|
252
|
+
options.sourcesProvided = true;
|
|
238
253
|
options.sources.push(...entries);
|
|
239
254
|
i = j - 1;
|
|
240
255
|
continue;
|
|
@@ -329,11 +344,14 @@ function createParseMirrorGoFlags(deps) {
|
|
|
329
344
|
throw new CliError('INVALID_FLAG_VALUE', '--hedge-ratio must be <= 2.');
|
|
330
345
|
}
|
|
331
346
|
if (options.executeLive) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (
|
|
336
|
-
throw new CliError(
|
|
347
|
+
const missing = [];
|
|
348
|
+
if (options.maxOpenExposureUsdc === null) missing.push('--max-open-exposure-usdc');
|
|
349
|
+
if (options.maxTradesPerDay === null) missing.push('--max-trades-per-day');
|
|
350
|
+
if (missing.length) {
|
|
351
|
+
throw new CliError(
|
|
352
|
+
'MISSING_REQUIRED_FLAG',
|
|
353
|
+
`Live mode requires companion risk flags: ${missing.join(', ')}.`,
|
|
354
|
+
);
|
|
337
355
|
}
|
|
338
356
|
}
|
|
339
357
|
|