@kashdao/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/CONTRIBUTING.md +159 -0
  3. package/LICENSE +21 -0
  4. package/README.md +868 -0
  5. package/SECURITY.md +110 -0
  6. package/dist/account-AVFLEM5D.js +90 -0
  7. package/dist/account-AVFLEM5D.js.map +1 -0
  8. package/dist/auth-GUJCVKTD.js +239 -0
  9. package/dist/auth-GUJCVKTD.js.map +1 -0
  10. package/dist/chunk-BN2CUM42.js +9 -0
  11. package/dist/chunk-BN2CUM42.js.map +1 -0
  12. package/dist/chunk-BRK7KJ4O.js +154 -0
  13. package/dist/chunk-BRK7KJ4O.js.map +1 -0
  14. package/dist/chunk-KMBMQIZ7.js +393 -0
  15. package/dist/chunk-KMBMQIZ7.js.map +1 -0
  16. package/dist/chunk-LBIRQHX5.js +133 -0
  17. package/dist/chunk-LBIRQHX5.js.map +1 -0
  18. package/dist/chunk-MIXOZU2S.js +366 -0
  19. package/dist/chunk-MIXOZU2S.js.map +1 -0
  20. package/dist/chunk-QJMF73M5.js +129 -0
  21. package/dist/chunk-QJMF73M5.js.map +1 -0
  22. package/dist/chunk-UZNSYATZ.js +680 -0
  23. package/dist/chunk-UZNSYATZ.js.map +1 -0
  24. package/dist/chunk-VIADBYFY.js +94 -0
  25. package/dist/chunk-VIADBYFY.js.map +1 -0
  26. package/dist/chunk-YHCG2SUC.js +159 -0
  27. package/dist/chunk-YHCG2SUC.js.map +1 -0
  28. package/dist/chunk-YJX3JJ4M.js +174 -0
  29. package/dist/chunk-YJX3JJ4M.js.map +1 -0
  30. package/dist/client-IOM55ZCS.js +14 -0
  31. package/dist/client-IOM55ZCS.js.map +1 -0
  32. package/dist/completion-ZZGX5BQC.js +100 -0
  33. package/dist/completion-ZZGX5BQC.js.map +1 -0
  34. package/dist/config-XJL5TSYK.js +690 -0
  35. package/dist/config-XJL5TSYK.js.map +1 -0
  36. package/dist/config-store-3ZSYGXMQ.js +44 -0
  37. package/dist/config-store-3ZSYGXMQ.js.map +1 -0
  38. package/dist/docs-MDKKSJDC.js +102 -0
  39. package/dist/docs-MDKKSJDC.js.map +1 -0
  40. package/dist/eoa-IQ72EIHR.js +551 -0
  41. package/dist/eoa-IQ72EIHR.js.map +1 -0
  42. package/dist/errors-WMZIEGQI.js +18 -0
  43. package/dist/errors-WMZIEGQI.js.map +1 -0
  44. package/dist/explain-H4EH3KH3.js +165 -0
  45. package/dist/explain-H4EH3KH3.js.map +1 -0
  46. package/dist/global-options-XOLJUPTT.js +18 -0
  47. package/dist/global-options-XOLJUPTT.js.map +1 -0
  48. package/dist/health-VZEIII74.js +98 -0
  49. package/dist/health-VZEIII74.js.map +1 -0
  50. package/dist/help-footer-GTANVDNP.js +43 -0
  51. package/dist/help-footer-GTANVDNP.js.map +1 -0
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.js +251 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/intro-PQQTYWR7.js +41 -0
  56. package/dist/intro-PQQTYWR7.js.map +1 -0
  57. package/dist/markets-C37JZDPE.js +295 -0
  58. package/dist/markets-C37JZDPE.js.map +1 -0
  59. package/dist/output-VCBZ3FM7.js +22 -0
  60. package/dist/output-VCBZ3FM7.js.map +1 -0
  61. package/dist/portfolio-2AEPIJIG.js +115 -0
  62. package/dist/portfolio-2AEPIJIG.js.map +1 -0
  63. package/dist/protocol-XEQXF2GW.js +1572 -0
  64. package/dist/protocol-XEQXF2GW.js.map +1 -0
  65. package/dist/quote-I4DVJ7ZB.js +144 -0
  66. package/dist/quote-I4DVJ7ZB.js.map +1 -0
  67. package/dist/schema-PIFQ65TS.js +410 -0
  68. package/dist/schema-PIFQ65TS.js.map +1 -0
  69. package/dist/setup-USZ6IODD.js +260 -0
  70. package/dist/setup-USZ6IODD.js.map +1 -0
  71. package/dist/stdin-YW2CEQXU.js +28 -0
  72. package/dist/stdin-YW2CEQXU.js.map +1 -0
  73. package/dist/trace-IZBYTUFO.js +103 -0
  74. package/dist/trace-IZBYTUFO.js.map +1 -0
  75. package/dist/trade-IIZSEXEI.js +586 -0
  76. package/dist/trade-IIZSEXEI.js.map +1 -0
  77. package/dist/version-JDK3PEP5.js +117 -0
  78. package/dist/version-JDK3PEP5.js.map +1 -0
  79. package/dist/version-check-TDCION37.js +138 -0
  80. package/dist/version-check-TDCION37.js.map +1 -0
  81. package/dist/webhooks-PTVRKICZ.js +680 -0
  82. package/dist/webhooks-PTVRKICZ.js.map +1 -0
  83. package/dist/with-retry-4FIZG3A7.js +223 -0
  84. package/dist/with-retry-4FIZG3A7.js.map +1 -0
  85. package/package.json +99 -0
@@ -0,0 +1,1572 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadRawPrivateKey,
4
+ resolveCliCustomChain
5
+ } from "./chunk-YJX3JJ4M.js";
6
+ import {
7
+ formatAddress,
8
+ formatBigDecimal,
9
+ formatGwei,
10
+ formatUsdcAtomic,
11
+ formatWad
12
+ } from "./chunk-BRK7KJ4O.js";
13
+ import {
14
+ readConfig
15
+ } from "./chunk-KMBMQIZ7.js";
16
+ import {
17
+ DEFAULT_SLIPPAGE_BPS,
18
+ HEX_HASH_REGEX,
19
+ MAX_UINT256,
20
+ decimalToAtomicUsdc,
21
+ decimalToAtomicWad,
22
+ extractPartialHash,
23
+ parseDeadlineSec,
24
+ parseOutcomeIndex,
25
+ parseSlippageBps,
26
+ parseUppercaseSide,
27
+ validateAddress,
28
+ validateAddressOptional
29
+ } from "./chunk-YHCG2SUC.js";
30
+ import {
31
+ parseOptionalPositiveFloat,
32
+ parseOptionalPositiveInt,
33
+ parsePositiveInt,
34
+ readGlobals
35
+ } from "./chunk-LBIRQHX5.js";
36
+ import {
37
+ isQuiet,
38
+ log,
39
+ print,
40
+ printJson,
41
+ style,
42
+ writeNdjson
43
+ } from "./chunk-VIADBYFY.js";
44
+ import "./chunk-MIXOZU2S.js";
45
+ import {
46
+ CliError,
47
+ CliValidationError,
48
+ EXIT_CODES,
49
+ toCliError
50
+ } from "./chunk-UZNSYATZ.js";
51
+
52
+ // src/commands/protocol/index.ts
53
+ import { Command as Command13 } from "commander";
54
+
55
+ // src/commands/protocol/allowance.ts
56
+ import { Command } from "commander";
57
+
58
+ // src/utils/direct-client.ts
59
+ async function buildDirectClient(opts) {
60
+ const globals = opts.globals;
61
+ const config = await readConfig({
62
+ ...globals?.profile === void 0 ? {} : { profile: globals.profile },
63
+ ...globals?.configPath === void 0 ? {} : { configPath: globals.configPath }
64
+ });
65
+ const rpc = globals?.baseUrl ?? config.rpcUrl;
66
+ if (!rpc) {
67
+ throw missingDirectConfig("rpcUrl", "--rpc-url or `kash config set rpcUrl <url>`");
68
+ }
69
+ if (!config.smartAccount) {
70
+ throw missingDirectConfig(
71
+ "smartAccount",
72
+ "`kash config set smartAccount 0x\u2026` (the ERC-4337 account address whose funds you trade with)"
73
+ );
74
+ }
75
+ const { createSmartAccountClient } = await import("@kashdao/protocol-sdk");
76
+ const signer = opts.requireSigner ? await loadSigner(config.signerKeyRef) : noopSigner();
77
+ const bundlerConfig = resolveBundlerConfig(config);
78
+ const customChain = await maybeBuildCustomChain(config, rpc);
79
+ if (customChain !== void 0 && customChain.smartAccount === void 0) {
80
+ throw missingDirectConfig(
81
+ "customChain.smartAccount",
82
+ "all three: `kash config set customChain.smartAccount.factoryAddress 0x\u2026`, `customChain.smartAccount.implementationAddress 0x\u2026`, `customChain.smartAccount.entryPointAddress 0x\u2026` (SA mode requires the local SimpleAccount factory + EntryPoint addresses on a custom chain \u2014 Anvil deploys these to addresses that differ from the canonical permissionless.js defaults)"
83
+ );
84
+ }
85
+ const client = createSmartAccountClient({
86
+ chainId: config.defaultChainId,
87
+ rpc,
88
+ signer,
89
+ ...bundlerConfig === void 0 ? {} : { bundler: bundlerConfig },
90
+ ...customChain === void 0 ? {} : { customChain }
91
+ });
92
+ return {
93
+ client,
94
+ chainId: config.defaultChainId,
95
+ smartAccount: config.smartAccount
96
+ };
97
+ }
98
+ function noopSigner() {
99
+ return {
100
+ ownerAddress: "0x0000000000000000000000000000000000000000",
101
+ async signUserOpHash() {
102
+ throw new CliError("CLI bug: read-only direct-mode command attempted to sign a UserOp.", {
103
+ code: "UNEXPECTED",
104
+ suggestion: "File an issue at https://github.com/KashDAO/cli/issues \u2014 this should not happen."
105
+ });
106
+ }
107
+ };
108
+ }
109
+ async function loadSigner(ref) {
110
+ const rawKey = await loadRawPrivateKey(ref);
111
+ const [{ privateKeyToAccount }, { viemAccountSigner }] = await Promise.all([
112
+ import("viem/accounts"),
113
+ import("@kashdao/protocol-sdk")
114
+ ]);
115
+ return viemAccountSigner(privateKeyToAccount(rawKey));
116
+ }
117
+ function resolveBundlerConfig(config) {
118
+ if (config.bundlerProvider === void 0 && config.bundlerUrl === void 0) {
119
+ return void 0;
120
+ }
121
+ if (config.bundlerProvider === void 0) {
122
+ return config.bundlerUrl;
123
+ }
124
+ if (config.bundlerUrl === void 0) {
125
+ throw new CliError(
126
+ `Direct-mode config: bundlerProvider="${config.bundlerProvider}" requires a bundlerUrl.`,
127
+ {
128
+ code: "DIRECT_CONFIG_MISSING",
129
+ suggestion: "`kash config set bundlerUrl <provider-rpc-url>` (e.g. https://api.pimlico.io/v2/8453/rpc?apikey=\u2026)"
130
+ }
131
+ );
132
+ }
133
+ return {
134
+ provider: config.bundlerProvider,
135
+ url: config.bundlerUrl
136
+ };
137
+ }
138
+ async function maybeBuildCustomChain(config, rpc) {
139
+ if (config.customChain === void 0) return void 0;
140
+ if (config.defaultChainId === void 0) {
141
+ throw missingDirectConfig(
142
+ "defaultChainId",
143
+ "`kash config set defaultChainId <id>` (required when customChain is set so the CLI can construct the viem chain)"
144
+ );
145
+ }
146
+ return resolveCliCustomChain({
147
+ customChain: config.customChain,
148
+ chainId: config.defaultChainId,
149
+ rpc
150
+ });
151
+ }
152
+ function missingDirectConfig(field, hint) {
153
+ return new CliError(`Direct-mode config missing: ${field}.`, {
154
+ code: "DIRECT_CONFIG_MISSING",
155
+ exitCode: EXIT_CODES.GENERIC,
156
+ suggestion: `Set ${field} via ${hint}.`,
157
+ actions: [
158
+ {
159
+ type: "check_input",
160
+ field,
161
+ description: hint
162
+ }
163
+ ]
164
+ });
165
+ }
166
+
167
+ // src/commands/protocol/allowance.ts
168
+ var allowanceCommand = new Command("allowance").description("Read the on-chain USDC allowance from `account` to `spender`.").argument("<spender>", "spender contract address (e.g. a market) \u2014 0x-prefixed").argument("[account]", "owner address (default: profile's smartAccount)").addHelpText(
169
+ "after",
170
+ `
171
+ Examples:
172
+ $ kash protocol allowance 0xMarket...
173
+ $ kash protocol allowance 0xMarket... 0xOwner...
174
+ $ kash protocol allowance 0xMarket... --json --quiet | jq -r '.allowanceAtomic'
175
+
176
+ Notes:
177
+ - The allowance is returned in USDC atomic units (6 decimals). A
178
+ value larger than the trade's atomic-USDC input means \`approve\`
179
+ can be skipped on the next trade.
180
+ `
181
+ ).action(async (spender, account, _opts, cmd) => {
182
+ const globals = readGlobals(cmd);
183
+ const spenderAddress = validateAddress(spender, "spender");
184
+ const accountAddress = validateAddressOptional(account, "account");
185
+ try {
186
+ const resolved = await buildDirectClient({ globals });
187
+ const owner = accountAddress ?? resolved.smartAccount;
188
+ const allowance = await resolved.client.account.usdcAllowance(owner, spenderAddress);
189
+ const payload = {
190
+ owner,
191
+ spender: spenderAddress,
192
+ chainId: resolved.chainId,
193
+ allowanceAtomic: allowance.toString()
194
+ };
195
+ if (globals.json) {
196
+ printJson(payload);
197
+ return;
198
+ }
199
+ print("");
200
+ print(` ${style.dim("Owner ")} ${formatAddress(owner, 10, 8)}`);
201
+ print(` ${style.dim("Spender ")} ${formatAddress(spenderAddress, 10, 8)}`);
202
+ print(
203
+ ` ${style.dim("Allowance")} ${formatUsdcAtomic(allowance.toString())} ${style.dim(`(${allowance.toString()} atomic)`)}`
204
+ );
205
+ } catch (cause) {
206
+ throw toCliError(cause);
207
+ }
208
+ });
209
+
210
+ // src/commands/protocol/balance.ts
211
+ import { Command as Command2 } from "commander";
212
+ var balanceCommand = new Command2("balance").description("Read on-chain USDC + gas balance for a smart account (defaults to the profile's).").argument("[account]", "smart-account address (defaults to the active profile's smartAccount)").addHelpText(
213
+ "after",
214
+ `
215
+ Examples:
216
+ $ kash protocol balance
217
+ $ kash protocol balance 0xabc...
218
+ $ kash protocol balance --json --quiet | jq '.usdcAtomic'
219
+
220
+ Direct-mode requires rpcUrl + smartAccount + signerKeyRef configured
221
+ on the active profile (signer is loaded but never invoked for reads).
222
+ `
223
+ ).action(async (account, _opts, cmd) => {
224
+ const globals = readGlobals(cmd);
225
+ const accountOverride = validateAddressOptional(account, "account");
226
+ let resolved;
227
+ try {
228
+ resolved = await buildDirectClient({ globals });
229
+ } catch (cause) {
230
+ throw toCliError(cause);
231
+ }
232
+ const target = accountOverride ?? resolved.smartAccount;
233
+ let usdcAtomic;
234
+ let gasWei;
235
+ try {
236
+ [usdcAtomic, gasWei] = await Promise.all([
237
+ resolved.client.account.usdcBalance(target),
238
+ resolved.client.account.gasBalance(target)
239
+ ]);
240
+ } catch (cause) {
241
+ throw toCliError(cause);
242
+ }
243
+ const payload = {
244
+ account: target,
245
+ chainId: resolved.chainId,
246
+ usdcAtomic: usdcAtomic.toString(),
247
+ gasWei: gasWei.toString()
248
+ };
249
+ if (globals.json) {
250
+ printJson(payload);
251
+ return;
252
+ }
253
+ print("");
254
+ print(` ${style.dim("Account ")} ${formatAddress(target, 10, 8)}`);
255
+ print(` ${style.dim("Chain ")} ${String(resolved.chainId)}`);
256
+ print(` ${style.dim("USDC ")} ${formatUsdcAtomic(usdcAtomic.toString())}`);
257
+ print(
258
+ ` ${style.dim("ETH (gas) ")} ${formatBigDecimal(gasWei, { baseDecimals: 18, displayDecimals: 6 })} ETH`
259
+ );
260
+ });
261
+
262
+ // src/commands/protocol/decode-revert.ts
263
+ import { Command as Command3 } from "commander";
264
+ var HEX_DATA_RE = /^0x[0-9a-fA-F]+$/;
265
+ var decodeRevertCommand = new Command3("decode-revert").description("Decode raw revert data into (name, args) using the Market + EntryPoint ABIs.").argument("<data>", "raw revert data (0x-prefixed hex)").addHelpText(
266
+ "after",
267
+ `
268
+ Examples:
269
+ $ kash protocol decode-revert 0x4e487b710000000000000000000000000000000000000000000000000000000000000011
270
+ $ kash protocol decode-revert 0x... --json --quiet | jq -r '.name'
271
+
272
+ Notes:
273
+ - Returns \`null\` if the selector doesn't match any known error
274
+ (the data may be raw \`Error(string)\` or vendor-specific).
275
+ - The \`args\` array stringifies bigint values to preserve precision.
276
+ `
277
+ ).action(async (data, _opts, cmd) => {
278
+ const globals = readGlobals(cmd);
279
+ if (!HEX_DATA_RE.test(data) || data.length < 10) {
280
+ throw new CliValidationError(
281
+ "<data> must be 0x-prefixed hex with at least a 4-byte selector.",
282
+ `Got "${data}".`,
283
+ "data"
284
+ );
285
+ }
286
+ try {
287
+ const { decodeMarketRevert } = await import("@kashdao/protocol-sdk");
288
+ const decoded = decodeMarketRevert(data);
289
+ const args = decoded?.args.map((a) => typeof a === "bigint" ? a.toString() : a) ?? null;
290
+ const payload = decoded === null ? null : { name: decoded.name, args };
291
+ if (globals.json) {
292
+ printJson(payload);
293
+ return;
294
+ }
295
+ if (decoded === null) {
296
+ print("");
297
+ print(` ${style.dim("No match \u2014 selector not in Market or EntryPoint ABIs.")}`);
298
+ print(` ${style.dim("Data: ")}${data.slice(0, 10)}${data.length > 10 ? "\u2026" : ""}`);
299
+ return;
300
+ }
301
+ print("");
302
+ print(` ${style.dim("Error ")} ${style.bold(decoded.name)}`);
303
+ if (args && args.length > 0) {
304
+ print(` ${style.dim("Args ")}`);
305
+ for (const [i, arg] of args.entries()) {
306
+ print(` [${String(i)}] ${String(arg)}`);
307
+ }
308
+ } else {
309
+ print(` ${style.dim("Args ")} (none)`);
310
+ }
311
+ } catch (cause) {
312
+ throw toCliError(cause);
313
+ }
314
+ });
315
+
316
+ // src/commands/protocol/fees.ts
317
+ import { Command as Command4 } from "commander";
318
+ var feesCommand = new Command4("fees").description("EIP-1559 fee estimate for the configured chain (uses eth_feeHistory).").option("--blocks <n>", "number of recent blocks to sample (default 4)").option(
319
+ "--percentile <n>",
320
+ "priority-fee percentile to take from each sampled block (1-99, default 50)"
321
+ ).option(
322
+ "--base-multiplier <n>",
323
+ "multiplier applied to predicted next-block base fee (default 2.0; raise for congested chains)"
324
+ ).option("--priority-floor-gwei <n>", "floor for maxPriorityFeePerGas in gwei (default 1)").addHelpText(
325
+ "after",
326
+ `
327
+ Examples:
328
+ $ kash protocol fees
329
+ $ kash protocol fees --base-multiplier 3 --json --quiet | jq -r '.maxFeePerGas'
330
+ $ kash protocol fees --blocks 16 --percentile 75 # smoother, more conservative
331
+ `
332
+ ).action(async (options, cmd) => {
333
+ const globals = readGlobals(cmd);
334
+ const blocks = parseOptionalPositiveInt(options.blocks, "blocks");
335
+ const percentile = parseOptionalPositiveInt(options.percentile, "percentile");
336
+ if (percentile !== void 0 && (percentile < 1 || percentile > 99)) {
337
+ throw new CliValidationError(
338
+ "--percentile must be between 1 and 99.",
339
+ void 0,
340
+ "percentile"
341
+ );
342
+ }
343
+ const baseMultiplier = parseOptionalPositiveFloat(options.baseMultiplier, "base-multiplier");
344
+ const priorityFloorGwei = parseOptionalPositiveInt(
345
+ options.priorityFloorGwei,
346
+ "priority-floor-gwei"
347
+ );
348
+ try {
349
+ const resolved = await buildDirectClient({ globals });
350
+ const estimate = await resolved.client.bundler.estimateFees({
351
+ ...blocks === void 0 ? {} : { blockCount: blocks },
352
+ ...percentile === void 0 ? {} : { rewardPercentile: percentile },
353
+ ...baseMultiplier === void 0 ? {} : { baseMultiplier },
354
+ ...priorityFloorGwei === void 0 ? {} : { priorityFeeFloorWei: BigInt(priorityFloorGwei) * 10n ** 9n }
355
+ });
356
+ const payload = {
357
+ chainId: resolved.chainId,
358
+ maxFeePerGas: estimate.maxFeePerGas.toString(),
359
+ maxPriorityFeePerGas: estimate.maxPriorityFeePerGas.toString()
360
+ };
361
+ if (globals.json) {
362
+ printJson(payload);
363
+ return;
364
+ }
365
+ print("");
366
+ print(` ${style.dim("Chain ")} ${String(resolved.chainId)}`);
367
+ print(
368
+ ` ${style.dim("maxFee ")} ${formatGwei(estimate.maxFeePerGas)} gwei ${style.dim(`(${estimate.maxFeePerGas.toString()} wei)`)}`
369
+ );
370
+ print(
371
+ ` ${style.dim("maxPrio ")} ${formatGwei(estimate.maxPriorityFeePerGas)} gwei ${style.dim(`(${estimate.maxPriorityFeePerGas.toString()} wei)`)}`
372
+ );
373
+ } catch (cause) {
374
+ throw toCliError(cause);
375
+ }
376
+ });
377
+
378
+ // src/commands/protocol/market.ts
379
+ import { Command as Command5 } from "commander";
380
+ var marketCommand = new Command5("market").description("Read on-chain market state (reserves, supplies, weights, derived probabilities).").argument("<address>", "market contract address (0x-prefixed)").addHelpText(
381
+ "after",
382
+ `
383
+ Examples:
384
+ $ kash protocol market 0xabc...
385
+ $ kash protocol market 0xabc... --json --quiet | jq '.outcomes'
386
+ `
387
+ ).action(async (address, _opts, cmd) => {
388
+ const globals = readGlobals(cmd);
389
+ validateAddress(address, "address");
390
+ let resolved;
391
+ let state;
392
+ try {
393
+ resolved = await buildDirectClient({ globals });
394
+ state = await resolved.client.markets.state(address);
395
+ } catch (cause) {
396
+ throw toCliError(cause);
397
+ }
398
+ const payload = {
399
+ marketAddress: state.marketAddress,
400
+ chainId: resolved.chainId,
401
+ status: state.status,
402
+ readAt: state.readAt.toString(),
403
+ reserveWad: state.reserveWad.toString(),
404
+ outcomes: state.outcomes.map((o) => ({
405
+ index: o.index,
406
+ outstandingTokensWad: o.outstandingTokensWad.toString(),
407
+ weightWad: o.weightWad.toString(),
408
+ probability: o.probability
409
+ }))
410
+ };
411
+ if (globals.json) {
412
+ printJson(payload);
413
+ return;
414
+ }
415
+ print("");
416
+ print(` ${style.dim("Market ")} ${formatAddress(state.marketAddress, 10, 8)}`);
417
+ print(` ${style.dim("Status ")} ${state.status}`);
418
+ print(` ${style.dim("Outcomes ")} ${String(state.outcomes.length)}`);
419
+ print("");
420
+ for (const outcome of state.outcomes) {
421
+ const pct = (outcome.probability * 100).toFixed(2);
422
+ print(
423
+ ` [${String(outcome.index)}] p=${pct}% outstanding=${formatWad(outcome.outstandingTokensWad)} weight=${formatWad(outcome.weightWad)}`
424
+ );
425
+ }
426
+ });
427
+
428
+ // src/commands/protocol/position.ts
429
+ import { Command as Command6 } from "commander";
430
+ var positionCommand = new Command6("position").description("Read on-chain outcome-token holdings (ERC-1155) for a market.").argument("<market>", "market contract address (0x-prefixed)").argument(
431
+ "[account]",
432
+ "EOA or smart-account address whose holdings to read (default: profile's smartAccount)"
433
+ ).addHelpText(
434
+ "after",
435
+ `
436
+ Examples:
437
+ $ kash protocol position 0xMarket...
438
+ $ kash protocol position 0xMarket... 0xOther...
439
+ $ kash protocol position 0xMarket... --json --quiet | jq '.holdings[].balanceWad'
440
+ `
441
+ ).action(async (market, account, _opts, cmd) => {
442
+ const globals = readGlobals(cmd);
443
+ const marketAddr = validateAddress(market, "market");
444
+ const accountAddr = validateAddressOptional(account, "account");
445
+ let resolved;
446
+ let position;
447
+ try {
448
+ resolved = await buildDirectClient({ globals });
449
+ const target = accountAddr ?? resolved.smartAccount;
450
+ position = await resolved.client.account.position(target, marketAddr);
451
+ const payload = {
452
+ account: target,
453
+ marketAddress: position.marketAddress,
454
+ chainId: resolved.chainId,
455
+ numOutcomes: position.numOutcomes,
456
+ holdings: position.holdings.map((h) => ({
457
+ outcomeIndex: h.outcomeIndex,
458
+ balanceWad: h.balanceWad.toString()
459
+ }))
460
+ };
461
+ if (globals.json) {
462
+ printJson(payload);
463
+ return;
464
+ }
465
+ print("");
466
+ print(` ${style.dim("Account ")} ${formatAddress(target, 10, 8)}`);
467
+ print(` ${style.dim("Market ")} ${formatAddress(position.marketAddress, 10, 8)}`);
468
+ print(` ${style.dim("Outcomes ")} ${String(position.numOutcomes)}`);
469
+ print("");
470
+ for (const h of position.holdings) {
471
+ const balanceLabel = formatWad(h.balanceWad);
472
+ print(
473
+ ` [${String(h.outcomeIndex)}] balance=${balanceLabel} ${style.dim(`(${h.balanceWad.toString()} WAD)`)}`
474
+ );
475
+ }
476
+ } catch (cause) {
477
+ throw toCliError(cause);
478
+ }
479
+ });
480
+
481
+ // src/commands/protocol/quote.ts
482
+ import { Command as Command7 } from "commander";
483
+ var quoteCommand = new Command7("quote").description("On-chain price quote for buying or selling an outcome.").argument("<address>", "market contract address (0x-prefixed)").requiredOption("-s, --side <buy|sell>", "trade side: buy or sell").requiredOption("-o, --outcome <index>", "outcome index (0-based)").requiredOption(
484
+ "-a, --amount <decimal>",
485
+ 'amount: USDC decimal for buy (e.g. "10"), outcome-token decimal for sell (e.g. "1.5")'
486
+ ).addHelpText(
487
+ "after",
488
+ `
489
+ Examples:
490
+ $ kash protocol quote 0xabc... --side buy --outcome 0 --amount 10
491
+ $ kash protocol quote 0xabc... --side sell --outcome 1 --tokens 1.5 --json
492
+ `
493
+ ).action(async (address, options, cmd) => {
494
+ const globals = readGlobals(cmd);
495
+ const marketAddress = validateAddress(address, "address");
496
+ const side = parseUppercaseSide(options.side, "side");
497
+ const outcome = parseOutcomeIndex(options.outcome);
498
+ const amount = side === "BUY" ? decimalToAtomicUsdc(options.amount, "amount") : decimalToAtomicWad(options.amount, "amount");
499
+ let resolved;
500
+ let quote;
501
+ try {
502
+ resolved = await buildDirectClient({ globals });
503
+ quote = await resolved.client.markets.quote(marketAddress, {
504
+ side,
505
+ outcome,
506
+ amount
507
+ });
508
+ } catch (cause) {
509
+ throw toCliError(cause);
510
+ }
511
+ const payload = {
512
+ side: quote.side,
513
+ outcomeIndex: quote.outcomeIndex,
514
+ amountIn: quote.amountIn.toString(),
515
+ amountOut: quote.amountOut.toString(),
516
+ reserveAfterWad: quote.reserveAfterWad.toString(),
517
+ pricesAfterWad: quote.pricesAfterWad.map((p) => p.toString())
518
+ };
519
+ if (globals.json) {
520
+ printJson(payload);
521
+ return;
522
+ }
523
+ print("");
524
+ print(` ${style.dim("Market ")} ${formatAddress(address, 10, 8)}`);
525
+ print(` ${style.dim("Action ")} ${quote.side} outcome ${String(quote.outcomeIndex)}`);
526
+ if (quote.side === "BUY") {
527
+ print(` ${style.dim("Spend ")} ${formatUsdcAtomic(quote.amountIn)}`);
528
+ print(` ${style.dim("Tokens out")} ${formatWad(quote.amountOut)}`);
529
+ } else {
530
+ print(` ${style.dim("Tokens in ")} ${formatWad(quote.amountIn)}`);
531
+ print(` ${style.dim("USDC out ")} ${formatUsdcAtomic(quote.amountOut)}`);
532
+ }
533
+ print(
534
+ ` ${style.dim("Probs after")} [${quote.pricesAfterWad.map((p) => `${(Number(p) / 1e18 * 100).toFixed(2)}%`).join(", ")}]`
535
+ );
536
+ });
537
+
538
+ // src/commands/protocol/smart-account.ts
539
+ import { Command as Command8 } from "commander";
540
+ var computeCommand = new Command8("compute").description("Derive the deterministic smart-account address for an EOA owner.").requiredOption("-o, --owner <address>", "EOA owner address (0x-prefixed)").option("-s, --salt <n>", "optional salt as a non-negative integer (default 0)", "0").addHelpText(
541
+ "after",
542
+ `
543
+ Examples:
544
+ $ kash protocol smart-account compute --owner 0xEoa...
545
+ $ kash protocol smart-account compute --owner 0xEoa... --salt 1
546
+ $ kash protocol smart-account compute --owner 0xEoa... --json --quiet | jq -r '.address'
547
+ `
548
+ ).action(async (options, cmd) => {
549
+ const globals = readGlobals(cmd);
550
+ const ownerAddress = validateAddress(options.owner, "owner");
551
+ let salt;
552
+ if (options.salt !== void 0) {
553
+ let parsed;
554
+ try {
555
+ parsed = BigInt(options.salt);
556
+ } catch {
557
+ throw new CliValidationError(
558
+ "--salt must be a non-negative integer (decimal or 0x-hex).",
559
+ `Got "${options.salt}".`,
560
+ "salt"
561
+ );
562
+ }
563
+ if (parsed < 0n) {
564
+ throw new CliValidationError(
565
+ "--salt must be non-negative.",
566
+ `Got "${options.salt}".`,
567
+ "salt"
568
+ );
569
+ }
570
+ salt = parsed === 0n ? void 0 : parsed;
571
+ }
572
+ try {
573
+ const resolved = await buildDirectClient({ globals });
574
+ const address = await resolved.client.account.computeAddress(ownerAddress, salt);
575
+ const payload = {
576
+ owner: ownerAddress,
577
+ salt: (salt ?? 0n).toString(),
578
+ chainId: resolved.chainId,
579
+ address
580
+ };
581
+ if (globals.json) {
582
+ printJson(payload);
583
+ return;
584
+ }
585
+ print("");
586
+ print(` ${style.dim("Owner ")} ${formatAddress(ownerAddress, 10, 8)}`);
587
+ print(` ${style.dim("Salt ")} ${(salt ?? 0n).toString()}`);
588
+ print(` ${style.dim("Address")} ${address}`);
589
+ } catch (cause) {
590
+ throw toCliError(cause);
591
+ }
592
+ });
593
+ var isDeployedCommand = new Command8("is-deployed").description("Check whether a smart account has bytecode (i.e. has been deployed on-chain).").argument("[address]", "smart account address (default: profile's smartAccount)").addHelpText(
594
+ "after",
595
+ `
596
+ Examples:
597
+ $ kash protocol smart-account is-deployed
598
+ $ kash protocol smart-account is-deployed 0xSa...
599
+ $ kash protocol smart-account is-deployed --json --quiet | jq -r '.deployed'
600
+ `
601
+ ).action(async (address, _opts, cmd) => {
602
+ const globals = readGlobals(cmd);
603
+ const addressOverride = validateAddressOptional(address, "address");
604
+ try {
605
+ const resolved = await buildDirectClient({ globals });
606
+ const target = addressOverride ?? resolved.smartAccount;
607
+ const deployed = await resolved.client.account.isDeployed(target);
608
+ const payload = {
609
+ address: target,
610
+ chainId: resolved.chainId,
611
+ deployed
612
+ };
613
+ if (globals.json) {
614
+ printJson(payload);
615
+ return;
616
+ }
617
+ print("");
618
+ print(` ${style.dim("Address ")} ${formatAddress(target, 10, 8)}`);
619
+ print(
620
+ ` ${style.dim("Deployed")} ${deployed ? style.success("yes") : style.dim("no \u2014 first UserOp will deploy")}`
621
+ );
622
+ } catch (cause) {
623
+ throw toCliError(cause);
624
+ }
625
+ });
626
+ var smartAccountCommand = new Command8("smart-account").description("Smart-account address derivation and deployment-status checks.").addCommand(computeCommand).addCommand(isDeployedCommand);
627
+
628
+ // src/commands/protocol/token-id.ts
629
+ import { Command as Command9 } from "commander";
630
+ var tokenIdCommand = new Command9("token-id").description(
631
+ "Compute the ERC-1155 token id for a (marketId, outcomeIndex) pair (offline; no RPC)."
632
+ ).requiredOption(
633
+ "-m, --market-id <bigint>",
634
+ "on-chain numeric market id (decimal or 0x-prefixed hex)"
635
+ ).requiredOption("-o, --outcome <index>", "outcome index (0-255)").addHelpText(
636
+ "after",
637
+ `
638
+ Examples:
639
+ $ kash protocol token-id --market-id 42 --outcome 0
640
+ $ kash protocol token-id --market-id 0x2a --outcome 1 --json --quiet | jq -r '.tokenId'
641
+
642
+ Notes:
643
+ - The numeric \`marketId\` is the on-chain id read from
644
+ \`IMarket(<addr>).marketId()\`. It's NOT the same as the market
645
+ contract address.
646
+ - The token id encoding is \`(marketId << 8) | outcomeIndex\`,
647
+ matching the on-chain \`OutcomeTokens1155\` contract.
648
+ `
649
+ ).action(async (options, cmd) => {
650
+ const globals = readGlobals(cmd);
651
+ let marketId;
652
+ try {
653
+ marketId = BigInt(options.marketId);
654
+ } catch {
655
+ throw new CliValidationError(
656
+ "--market-id must be a non-negative integer (decimal or 0x-hex).",
657
+ `Got "${options.marketId}".`,
658
+ "market-id"
659
+ );
660
+ }
661
+ if (marketId < 0n) {
662
+ throw new CliValidationError(
663
+ "--market-id must be non-negative.",
664
+ `Got "${options.marketId}".`,
665
+ "market-id"
666
+ );
667
+ }
668
+ const outcomeIndex = Number.parseInt(options.outcome, 10);
669
+ if (!Number.isFinite(outcomeIndex) || outcomeIndex < 0 || outcomeIndex > 255) {
670
+ throw new CliValidationError(
671
+ "--outcome must be an integer between 0 and 255.",
672
+ `Got "${options.outcome}".`,
673
+ "outcome"
674
+ );
675
+ }
676
+ try {
677
+ const { tokenIdFor } = await import("@kashdao/protocol-sdk");
678
+ const tokenId = tokenIdFor(marketId, outcomeIndex);
679
+ const payload = {
680
+ marketId: marketId.toString(),
681
+ outcomeIndex,
682
+ tokenId: tokenId.toString(),
683
+ tokenIdHex: `0x${tokenId.toString(16)}`
684
+ };
685
+ if (globals.json) {
686
+ printJson(payload);
687
+ return;
688
+ }
689
+ print("");
690
+ print(` ${style.dim("Market id ")} ${marketId.toString()}`);
691
+ print(` ${style.dim("Outcome ")} ${String(outcomeIndex)}`);
692
+ print(` ${style.dim("Token id ")} ${tokenId.toString()}`);
693
+ print(` ${style.dim(" (hex) ")} 0x${tokenId.toString(16)}`);
694
+ } catch (cause) {
695
+ throw toCliError(cause);
696
+ }
697
+ });
698
+
699
+ // src/commands/protocol/trade.ts
700
+ import { Command as Command10 } from "commander";
701
+ import ora from "ora";
702
+
703
+ // src/utils/userop-json.ts
704
+ function serializeUserOp(userOp) {
705
+ const out = {};
706
+ for (const [k, v] of Object.entries(userOp)) {
707
+ out[k] = typeof v === "bigint" ? v.toString() : v;
708
+ }
709
+ return out;
710
+ }
711
+
712
+ // src/commands/protocol/trade.ts
713
+ var buyCommand = new Command10("buy").description("One-shot BUY: prepare \u2192 simulate \u2192 sign \u2192 submit (and wait by default).").argument("<market>", "market contract address (0x-prefixed)").requiredOption("-o, --outcome <index>", "outcome index (0-based)").requiredOption("-a, --amount <usdc>", 'USDC to spend (decimal \u2014 e.g. "10" or "12.50")').option("--slippage-bps <n>", "slippage tolerance in basis points (default 50 = 0.5%)").option("--deadline-sec <n>", "unix-seconds deadline (default now + 5min)").option("--dry-run", "prepare + simulate but DO NOT sign or submit; print the UserOp").option("--no-simulate", "skip the eth_call preflight (faster, riskier)").option("--no-wait", "fire-and-forget: return userOpHash without waiting for inclusion").option("--wait-timeout-ms <n>", "cap on the receipt wait (default 60000)").option("--wait-interval-ms <n>", "receipt poll interval (default 1500)").addHelpText(
714
+ "after",
715
+ `
716
+ Examples:
717
+ $ kash protocol trade buy 0xMarket... -o 0 -a 10
718
+ $ kash protocol trade buy 0xMarket... -o 0 -a 10 --slippage-bps 100 # 1% slippage
719
+ $ kash protocol trade buy 0xMarket... -o 0 -a 10 --dry-run --json
720
+ $ kash protocol trade buy 0xMarket... -o 0 -a 10 --no-wait --json --quiet
721
+ `
722
+ ).action(async (market, options, cmd) => {
723
+ await runTrade("buy", market, options, cmd);
724
+ });
725
+ var sellCommand = new Command10("sell").description("One-shot SELL: prepare \u2192 simulate \u2192 sign \u2192 submit (and wait by default).").argument("<market>", "market contract address (0x-prefixed)").requiredOption("-o, --outcome <index>", "outcome index (0-based)").requiredOption("-t, --tokens <amount>", "outcome tokens to sell (decimal \u2014 WAD precision)").option("--slippage-bps <n>", "slippage tolerance in basis points (default 50 = 0.5%)").option("--deadline-sec <n>", "unix-seconds deadline (default now + 5min)").option("--dry-run", "prepare + simulate but DO NOT sign or submit; print the UserOp").option("--no-simulate", "skip the eth_call preflight (faster, riskier)").option("--no-wait", "fire-and-forget: return userOpHash without waiting for inclusion").option("--wait-timeout-ms <n>", "cap on the receipt wait (default 60000)").option("--wait-interval-ms <n>", "receipt poll interval (default 1500)").addHelpText(
726
+ "after",
727
+ `
728
+ Examples:
729
+ $ kash protocol trade sell 0xMarket... -o 0 -t 1.5
730
+ $ kash protocol trade sell 0xMarket... -o 1 -t 0.5 --json --quiet
731
+ `
732
+ ).action(async (market, options, cmd) => {
733
+ await runTrade("sell", market, options, cmd);
734
+ });
735
+ var closeCommand = new Command10("close").description("Sell the entire SA balance for an outcome (one-shot prepare \u2192 submit).").argument("<market>", "market contract address (0x-prefixed)").requiredOption("-o, --outcome <index>", "outcome index (0-based)").option("--slippage-bps <n>", "slippage tolerance in basis points (default 50 = 0.5%)").option("--deadline-sec <n>", "unix-seconds deadline (default now + 5min)").option("--dry-run", "prepare + simulate but DO NOT sign or submit; print the UserOp").option("--no-simulate", "skip the eth_call preflight (faster, riskier)").option("--no-wait", "fire-and-forget: return userOpHash without waiting for inclusion").option("--wait-timeout-ms <n>", "cap on the receipt wait (default 60000)").option("--wait-interval-ms <n>", "receipt poll interval (default 1500)").addHelpText(
736
+ "after",
737
+ `
738
+ Examples:
739
+ $ kash protocol trade close 0xMarket... -o 0
740
+ $ kash protocol trade close 0xMarket... -o 1 --slippage-bps 200 --json
741
+ `
742
+ ).action(async (market, options, cmd) => {
743
+ await runTrade("close", market, options, cmd);
744
+ });
745
+ var approveCommand = new Command10("approve").description("USDC approval \u2014 required once before the first BUY.").argument("<spender>", "spender contract address (typically a Market) \u2014 0x-prefixed").option(
746
+ "-a, --amount <usdc>",
747
+ "atomic-USDC amount to approve (decimal); default is unlimited (MAX_UINT256)"
748
+ ).option("--dry-run", "prepare + simulate but DO NOT sign or submit; print the UserOp").option("--no-simulate", "skip the eth_call preflight (faster, riskier)").option("--no-wait", "fire-and-forget: return userOpHash without waiting for inclusion").option("--wait-timeout-ms <n>", "cap on the receipt wait (default 60000)").option("--wait-interval-ms <n>", "receipt poll interval (default 1500)").addHelpText(
749
+ "after",
750
+ `
751
+ Examples:
752
+ $ kash protocol trade approve 0xMarket... # unlimited approval
753
+ $ kash protocol trade approve 0xMarket... --amount 100 # cap at 100 USDC
754
+ $ kash protocol trade approve 0xMarket... --dry-run --json # preview only
755
+ `
756
+ ).action(async (spender, options, cmd) => {
757
+ await runApprove(spender, options, cmd);
758
+ });
759
+ var tradeCommand = new Command10("trade").description("Direct-mode trade execution (UserOp signed locally, submitted via bundler).").addCommand(buyCommand).addCommand(sellCommand).addCommand(closeCommand).addCommand(approveCommand);
760
+ async function runTrade(side, market, options, cmd) {
761
+ const globals = readGlobals(cmd);
762
+ const marketAddress = validateAddress(market, "market");
763
+ const outcome = parseOutcomeIndex(options.outcome);
764
+ const slippageBps = options.slippageBps ? parseSlippageBps(options.slippageBps) : DEFAULT_SLIPPAGE_BPS;
765
+ const deadline = options.deadlineSec ? parseDeadlineSec(options.deadlineSec) : void 0;
766
+ const waitTimeoutMs = options.waitTimeoutMs ? parsePositiveInt(options.waitTimeoutMs, "wait-timeout-ms") : void 0;
767
+ const waitIntervalMs = options.waitIntervalMs ? parsePositiveInt(options.waitIntervalMs, "wait-interval-ms") : void 0;
768
+ let amountUsdc;
769
+ let amountTokens;
770
+ if (side === "buy") {
771
+ const buyOpts = options;
772
+ amountUsdc = decimalToAtomicUsdc(buyOpts.amount, "amount");
773
+ } else if (side === "sell") {
774
+ const sellOpts = options;
775
+ amountTokens = decimalToAtomicWad(sellOpts.tokens, "tokens");
776
+ }
777
+ try {
778
+ const resolved = await buildDirectClient({ globals, requireSigner: true });
779
+ if (options.dryRun === true) {
780
+ const built = await prepareForSide(resolved.client, side, {
781
+ market: marketAddress,
782
+ smartAccount: resolved.smartAccount,
783
+ outcome,
784
+ slippageBps,
785
+ ...deadline === void 0 ? {} : { deadline },
786
+ ...amountUsdc === void 0 ? {} : { amountUsdc },
787
+ ...amountTokens === void 0 ? {} : { amountTokens },
788
+ simulate: options.simulate !== false
789
+ });
790
+ emitDryRun(built, side, market, globals.json);
791
+ return;
792
+ }
793
+ const sendOptions = {
794
+ simulate: options.simulate !== false,
795
+ wait: options.wait !== false,
796
+ ...waitTimeoutMs === void 0 ? {} : { waitTimeoutMs },
797
+ ...waitIntervalMs === void 0 ? {} : { waitIntervalMs }
798
+ };
799
+ const spinner = !globals.json && !isQuiet() ? ora({
800
+ text: `Preparing ${side} on ${formatAddress(market, 8, 6)}\u2026`,
801
+ stream: process.stderr
802
+ }).start() : void 0;
803
+ let result;
804
+ try {
805
+ if (side === "buy") {
806
+ result = await resolved.client.trades.send.buy(
807
+ marketAddress,
808
+ {
809
+ account: resolved.smartAccount,
810
+ outcome,
811
+ amountUsdc,
812
+ maxSlippageBps: slippageBps,
813
+ ...deadline === void 0 ? {} : { deadline }
814
+ },
815
+ sendOptions
816
+ );
817
+ } else if (side === "sell") {
818
+ result = await resolved.client.trades.send.sell(
819
+ marketAddress,
820
+ {
821
+ account: resolved.smartAccount,
822
+ outcome,
823
+ amountTokens,
824
+ maxSlippageBps: slippageBps,
825
+ ...deadline === void 0 ? {} : { deadline }
826
+ },
827
+ sendOptions
828
+ );
829
+ } else {
830
+ result = await resolved.client.trades.send.closePosition(
831
+ marketAddress,
832
+ {
833
+ account: resolved.smartAccount,
834
+ outcome,
835
+ maxSlippageBps: slippageBps,
836
+ ...deadline === void 0 ? {} : { deadline }
837
+ },
838
+ sendOptions
839
+ );
840
+ }
841
+ } catch (cause) {
842
+ spinner?.fail(`${side} failed.`);
843
+ const partialHash = extractPartialUserOpHash(cause);
844
+ if (partialHash !== void 0) {
845
+ if (globals.json) {
846
+ printJson({ userOpHash: partialHash, waited: false, partial: true });
847
+ } else {
848
+ log.warn(
849
+ `Submit succeeded but wait timed out. UserOp is on the bundler queue \u2014 resume with: kash protocol userop wait ${partialHash}`
850
+ );
851
+ }
852
+ }
853
+ throw cause;
854
+ }
855
+ spinner?.succeed(`${side} submitted.`);
856
+ emitSendResult(result, side, market, globals.json);
857
+ } catch (cause) {
858
+ throw toCliError(cause);
859
+ }
860
+ }
861
+ async function runApprove(spender, options, cmd) {
862
+ const globals = readGlobals(cmd);
863
+ const spenderAddress = validateAddress(spender, "spender");
864
+ const amount = options.amount ? decimalToAtomicUsdc(options.amount, "amount") : MAX_UINT256;
865
+ const waitTimeoutMs = options.waitTimeoutMs ? parsePositiveInt(options.waitTimeoutMs, "wait-timeout-ms") : void 0;
866
+ const waitIntervalMs = options.waitIntervalMs ? parsePositiveInt(options.waitIntervalMs, "wait-interval-ms") : void 0;
867
+ try {
868
+ const resolved = await buildDirectClient({ globals, requireSigner: true });
869
+ const params = {
870
+ account: resolved.smartAccount,
871
+ spender: spenderAddress,
872
+ amount
873
+ };
874
+ if (options.dryRun === true) {
875
+ const built = await resolved.client.trades.prepareApprove(params, {
876
+ simulate: options.simulate !== false
877
+ });
878
+ emitDryRun(built, "approve", spender, globals.json);
879
+ return;
880
+ }
881
+ const sendOptions = {
882
+ simulate: options.simulate !== false,
883
+ wait: options.wait !== false,
884
+ ...waitTimeoutMs === void 0 ? {} : { waitTimeoutMs },
885
+ ...waitIntervalMs === void 0 ? {} : { waitIntervalMs }
886
+ };
887
+ const spinner = !globals.json && !isQuiet() ? ora({
888
+ text: `Approving ${formatAddress(spender, 8, 6)}\u2026`,
889
+ stream: process.stderr
890
+ }).start() : void 0;
891
+ let result;
892
+ try {
893
+ result = await resolved.client.trades.send.approve(params, sendOptions);
894
+ } catch (cause) {
895
+ spinner?.fail("approve failed.");
896
+ const partialHash = extractPartialUserOpHash(cause);
897
+ if (partialHash !== void 0) {
898
+ if (globals.json) {
899
+ printJson({ userOpHash: partialHash, waited: false, partial: true });
900
+ } else {
901
+ log.warn(
902
+ `Submit succeeded but wait timed out. The approve UserOp is on the bundler queue \u2014 resume with: kash protocol userop wait ${partialHash}`
903
+ );
904
+ }
905
+ }
906
+ throw cause;
907
+ }
908
+ spinner?.succeed("approve submitted.");
909
+ emitSendResult(result, "approve", spender, globals.json);
910
+ } catch (cause) {
911
+ throw toCliError(cause);
912
+ }
913
+ }
914
+ async function prepareForSide(client, side, args) {
915
+ const opts = { simulate: args.simulate };
916
+ const baseParams = {
917
+ account: args.smartAccount,
918
+ outcome: args.outcome,
919
+ maxSlippageBps: args.slippageBps,
920
+ ...args.deadline === void 0 ? {} : { deadline: args.deadline }
921
+ };
922
+ if (side === "buy") {
923
+ return await client.trades.prepareBuy(
924
+ args.market,
925
+ { ...baseParams, amountUsdc: args.amountUsdc },
926
+ opts
927
+ );
928
+ }
929
+ if (side === "sell") {
930
+ return await client.trades.prepareSell(
931
+ args.market,
932
+ { ...baseParams, amountTokens: args.amountTokens },
933
+ opts
934
+ );
935
+ }
936
+ return await client.trades.prepareClosePosition(args.market, baseParams, opts);
937
+ }
938
+ function emitDryRun(built, side, target, json) {
939
+ const userOp = serializeUserOp(built.userOp);
940
+ if (json) {
941
+ printJson({
942
+ side,
943
+ target,
944
+ userOp,
945
+ userOpHash: built.userOpHash,
946
+ typedData: built.typedData,
947
+ dryRun: true
948
+ });
949
+ return;
950
+ }
951
+ print("");
952
+ print(` ${style.bold("Dry run")} \u2014 UserOp prepared but NOT signed/submitted.`);
953
+ print(` ${style.dim("Side ")} ${side}`);
954
+ print(` ${style.dim("Target ")} ${formatAddress(target, 10, 8)}`);
955
+ const senderRaw = userOp["sender"];
956
+ const sender = typeof senderRaw === "string" ? senderRaw : "";
957
+ print(` ${style.dim("Sender ")} ${formatAddress(sender, 10, 8)}`);
958
+ print(` ${style.dim("Nonce ")} ${String(userOp["nonce"])}`);
959
+ print(` ${style.dim("Hash ")} ${built.userOpHash}`);
960
+ print("");
961
+ print(style.dim("Re-run without --dry-run to sign and submit."));
962
+ }
963
+ function emitSendResult(result, side, target, json) {
964
+ const waited = "transactionHash" in result;
965
+ const payload = waited ? {
966
+ side,
967
+ target,
968
+ userOpHash: result.userOpHash,
969
+ transactionHash: result.transactionHash,
970
+ blockNumber: result.blockNumber.toString(),
971
+ success: result.success,
972
+ actualGasUsed: result.gasUsed.toString()
973
+ } : {
974
+ side,
975
+ target,
976
+ userOpHash: result.userOpHash,
977
+ waited: false
978
+ };
979
+ if (json) {
980
+ printJson(payload);
981
+ return;
982
+ }
983
+ print("");
984
+ if (waited) {
985
+ if (result.success) {
986
+ log.success(`${side} included in block ${result.blockNumber.toString()}.`);
987
+ } else {
988
+ log.error(`${side} included but reverted on-chain.`);
989
+ }
990
+ print(` ${style.dim("UserOp ")} ${result.userOpHash}`);
991
+ print(` ${style.dim("Tx ")} ${result.transactionHash}`);
992
+ print(` ${style.dim("Block ")} ${result.blockNumber.toString()}`);
993
+ print(` ${style.dim("Gas used ")} ${result.gasUsed.toString()}`);
994
+ } else {
995
+ log.success(`${side} submitted (fire-and-forget).`);
996
+ print(` ${style.dim("UserOp ")} ${result.userOpHash}`);
997
+ log.info(`Use \`kash protocol userop wait ${result.userOpHash}\` to poll for inclusion.`);
998
+ }
999
+ }
1000
+ function extractPartialUserOpHash(cause) {
1001
+ return extractPartialHash(cause, {
1002
+ code: "BUNDLER_RECEIPT_TIMEOUT",
1003
+ contextKey: "userOpHash"
1004
+ });
1005
+ }
1006
+
1007
+ // src/commands/protocol/userop.ts
1008
+ import { readFile, writeFile } from "fs/promises";
1009
+ import { Command as Command11 } from "commander";
1010
+ var buildBuyCommand = new Command11("buy").description("Build a fully-populated unsigned BUY UserOp ready for offline signing.").argument("<market>", "market contract address (0x-prefixed)").requiredOption("-o, --outcome <index>", "outcome index (0-based)").requiredOption("-a, --amount <usdc>", "USDC to spend (decimal)").option("--slippage-bps <n>", "slippage tolerance in bps (default 50 = 0.5%)").option("--deadline-sec <n>", "unix-seconds deadline (default now + 5min)").option("--out <path>", "write the UserOp envelope to a file (default stdout)").option("--no-simulate", "skip eth_call preflight in `prepare`").action(
1011
+ async (market, options, cmd) => {
1012
+ await runBuild("buy", market, options, cmd);
1013
+ }
1014
+ );
1015
+ var buildSellCommand = new Command11("sell").description("Build a fully-populated unsigned SELL UserOp ready for offline signing.").argument("<market>", "market contract address (0x-prefixed)").requiredOption("-o, --outcome <index>", "outcome index (0-based)").requiredOption("-t, --tokens <amount>", "outcome tokens to sell (WAD decimal)").option("--slippage-bps <n>", "slippage tolerance in bps (default 50 = 0.5%)").option("--deadline-sec <n>", "unix-seconds deadline (default now + 5min)").option("--out <path>", "write the UserOp envelope to a file (default stdout)").option("--no-simulate", "skip eth_call preflight in `prepare`").action(
1016
+ async (market, options, cmd) => {
1017
+ await runBuild("sell", market, options, cmd);
1018
+ }
1019
+ );
1020
+ var buildCloseCommand = new Command11("close").description("Build a fully-populated unsigned UserOp that closes a position (full balance).").argument("<market>", "market contract address (0x-prefixed)").requiredOption("-o, --outcome <index>", "outcome index (0-based)").option("--slippage-bps <n>", "slippage tolerance in bps (default 50 = 0.5%)").option("--deadline-sec <n>", "unix-seconds deadline (default now + 5min)").option("--out <path>", "write the UserOp envelope to a file (default stdout)").option("--no-simulate", "skip eth_call preflight in `prepare`").action(async (market, options, cmd) => {
1021
+ await runBuild("close", market, options, cmd);
1022
+ });
1023
+ var buildApproveCommand = new Command11("approve").description("Build a fully-populated unsigned approve UserOp ready for offline signing.").argument("<spender>", "spender contract address (typically a Market) \u2014 0x-prefixed").option("-a, --amount <usdc>", "atomic-USDC amount (decimal); default unlimited (MAX_UINT256)").option("--out <path>", "write the UserOp envelope to a file (default stdout)").option("--no-simulate", "skip eth_call preflight in `prepare`").action(
1024
+ async (spender, options, cmd) => {
1025
+ await runBuildApprove(spender, options, cmd);
1026
+ }
1027
+ );
1028
+ var buildCommand = new Command11("build").description("Build (prepare) a fully-populated unsigned UserOp ready for offline signing.").addCommand(buildBuyCommand).addCommand(buildSellCommand).addCommand(buildCloseCommand).addCommand(buildApproveCommand);
1029
+ async function runBuild(side, market, options, cmd) {
1030
+ const globals = readGlobals(cmd);
1031
+ const marketAddress = validateAddress(market, "market");
1032
+ const outcome = parseOutcomeIndex(options.outcome);
1033
+ const slippageBps = options.slippageBps ? parseSlippageBps(options.slippageBps) : DEFAULT_SLIPPAGE_BPS;
1034
+ const deadline = options.deadlineSec ? parseDeadlineSec(options.deadlineSec) : void 0;
1035
+ let amountUsdc;
1036
+ let amountTokens;
1037
+ if (side === "buy") {
1038
+ amountUsdc = decimalToAtomicUsdc(options.amount, "amount");
1039
+ } else if (side === "sell") {
1040
+ amountTokens = decimalToAtomicWad(options.tokens, "tokens");
1041
+ }
1042
+ try {
1043
+ const resolved = await buildDirectClient({ globals });
1044
+ const baseParams = {
1045
+ account: resolved.smartAccount,
1046
+ outcome,
1047
+ maxSlippageBps: slippageBps,
1048
+ ...deadline === void 0 ? {} : { deadline }
1049
+ };
1050
+ const opts = { simulate: options.simulate !== false };
1051
+ let built;
1052
+ if (side === "buy") {
1053
+ built = await resolved.client.trades.prepareBuy(
1054
+ marketAddress,
1055
+ { ...baseParams, amountUsdc },
1056
+ opts
1057
+ );
1058
+ } else if (side === "sell") {
1059
+ built = await resolved.client.trades.prepareSell(
1060
+ marketAddress,
1061
+ { ...baseParams, amountTokens },
1062
+ opts
1063
+ );
1064
+ } else {
1065
+ built = await resolved.client.trades.prepareClosePosition(marketAddress, baseParams, opts);
1066
+ }
1067
+ await emitBuildResult(built, side, market, options.out, globals.json);
1068
+ } catch (cause) {
1069
+ throw toCliError(cause);
1070
+ }
1071
+ }
1072
+ async function runBuildApprove(spender, options, cmd) {
1073
+ const globals = readGlobals(cmd);
1074
+ const spenderAddress = validateAddress(spender, "spender");
1075
+ const amount = options.amount ? decimalToAtomicUsdc(options.amount, "amount") : MAX_UINT256;
1076
+ try {
1077
+ const resolved = await buildDirectClient({ globals });
1078
+ const built = await resolved.client.trades.prepareApprove(
1079
+ {
1080
+ account: resolved.smartAccount,
1081
+ spender: spenderAddress,
1082
+ amount
1083
+ },
1084
+ { simulate: options.simulate !== false }
1085
+ );
1086
+ await emitBuildResult(built, "approve", spender, options.out, globals.json);
1087
+ } catch (cause) {
1088
+ throw toCliError(cause);
1089
+ }
1090
+ }
1091
+ var simulateCommand = new Command11("simulate").description("Simulate (eth_call) an UnsignedUserOp from a file or stdin.").argument("[file]", 'path to a UserOp JSON file (omit or pass "-" for stdin)').addHelpText(
1092
+ "after",
1093
+ `
1094
+ Examples:
1095
+ $ kash protocol userop build buy 0xMarket... -o 0 -a 10 --out trade.json
1096
+ $ kash protocol userop simulate trade.json
1097
+ $ cat trade.json | kash protocol userop simulate
1098
+ `
1099
+ ).action(async (file, _opts, cmd) => {
1100
+ const globals = readGlobals(cmd);
1101
+ try {
1102
+ const userOp = await readUnsignedUserOp(file);
1103
+ const resolved = await buildDirectClient({ globals });
1104
+ const result = await resolved.client.trades.simulate(userOp);
1105
+ if (globals.json) {
1106
+ printJson(serializeSimulationResult(result));
1107
+ return;
1108
+ }
1109
+ print("");
1110
+ if (result.willSucceed) {
1111
+ log.success("Simulation: would succeed.");
1112
+ if (result.gasEstimate !== void 0) {
1113
+ print(` ${style.dim("Gas estimate ")} ${result.gasEstimate.toString()}`);
1114
+ }
1115
+ } else {
1116
+ log.error("Simulation: would revert.");
1117
+ print(` ${style.dim("Reason ")} ${result.revertReason}`);
1118
+ if (result.decodedError) {
1119
+ print(` ${style.dim("Custom err")} ${result.decodedError.name}`);
1120
+ }
1121
+ }
1122
+ } catch (cause) {
1123
+ throw toCliError(cause);
1124
+ }
1125
+ });
1126
+ var submitCommand = new Command11("submit").description("Submit a SignedUserOp from a file or stdin to the configured bundler.").argument("[file]", 'path to a SignedUserOp JSON file (omit or pass "-" for stdin)').option("--skip-staleness-check", "bypass the EIP-191 staleness check (for typed-data signers)").option("--wait", "wait for receipt after submitting").option("--wait-timeout-ms <n>", "cap on the receipt wait (default 60000)").option("--wait-interval-ms <n>", "receipt poll interval (default 1500)").addHelpText(
1127
+ "after",
1128
+ `
1129
+ Examples:
1130
+ $ kash protocol userop submit signed.json --json
1131
+ $ cat signed.json | kash protocol userop submit --wait
1132
+ `
1133
+ ).action(async (file, options, cmd) => {
1134
+ const globals = readGlobals(cmd);
1135
+ const waitTimeoutMs = options.waitTimeoutMs ? parsePositiveInt(options.waitTimeoutMs, "wait-timeout-ms") : void 0;
1136
+ const waitIntervalMs = options.waitIntervalMs ? parsePositiveInt(options.waitIntervalMs, "wait-interval-ms") : void 0;
1137
+ try {
1138
+ const signed = await readSignedUserOp(file);
1139
+ const resolved = await buildDirectClient({ globals });
1140
+ const result = await resolved.client.trades.submit(signed, {
1141
+ skipStalenessCheck: options.skipStalenessCheck === true
1142
+ });
1143
+ if (options.wait !== true) {
1144
+ if (globals.json) {
1145
+ printJson({ userOpHash: result.userOpHash, waited: false });
1146
+ return;
1147
+ }
1148
+ log.success("UserOp submitted.");
1149
+ print(` ${style.dim("Hash")} ${result.userOpHash}`);
1150
+ log.info(`Use \`kash protocol userop wait ${result.userOpHash}\` to poll for inclusion.`);
1151
+ return;
1152
+ }
1153
+ const waitOpts = {
1154
+ ...waitTimeoutMs === void 0 ? {} : { timeoutMs: waitTimeoutMs },
1155
+ ...waitIntervalMs === void 0 ? {} : { intervalMs: waitIntervalMs }
1156
+ };
1157
+ let receipt;
1158
+ try {
1159
+ receipt = await resolved.client.bundler.waitForReceipt(result.userOpHash, waitOpts);
1160
+ } catch (waitCause) {
1161
+ if (globals.json) {
1162
+ printJson({ userOpHash: result.userOpHash, waited: false, partial: true });
1163
+ } else {
1164
+ log.warn(
1165
+ `Submit succeeded but wait failed. UserOp is on the bundler queue \u2014 resume with: kash protocol userop wait ${result.userOpHash}`
1166
+ );
1167
+ }
1168
+ throw toCliError(waitCause);
1169
+ }
1170
+ if (globals.json) {
1171
+ printJson(serializeReceipt(receipt));
1172
+ return;
1173
+ }
1174
+ emitReceiptHuman(receipt);
1175
+ } catch (cause) {
1176
+ throw toCliError(cause);
1177
+ }
1178
+ });
1179
+ var hashCommand = new Command11("hash").description("Recompute the canonical EIP-4337 v0.7 hash for a UnsignedUserOp.").argument("[file]", 'path to a UserOp JSON file (omit or pass "-" for stdin)').addHelpText(
1180
+ "after",
1181
+ `
1182
+ Examples:
1183
+ $ kash protocol userop hash trade.json
1184
+ $ cat trade.json | kash protocol userop hash --json --quiet | jq -r '.userOpHash'
1185
+
1186
+ Notes:
1187
+ - The hash baked into a freshly-built UserOp goes stale after gas
1188
+ fields are populated. Always recompute via this command (or
1189
+ \`hashOf\` in the SDK) before signing.
1190
+ `
1191
+ ).action(async (file, _opts, cmd) => {
1192
+ const globals = readGlobals(cmd);
1193
+ try {
1194
+ const userOp = await readUnsignedUserOp(file);
1195
+ const resolved = await buildDirectClient({ globals });
1196
+ const userOpHash = resolved.client.trades.hashOf(userOp);
1197
+ if (globals.json) {
1198
+ printJson({ userOpHash });
1199
+ return;
1200
+ }
1201
+ print(userOpHash);
1202
+ } catch (cause) {
1203
+ throw toCliError(cause);
1204
+ }
1205
+ });
1206
+ var receiptCommand = new Command11("receipt").description("Fetch the bundler receipt for a UserOp hash (null if not yet included).").argument("<hash>", "UserOp hash (0x-prefixed, 32 bytes)").addHelpText(
1207
+ "after",
1208
+ `
1209
+ Examples:
1210
+ $ kash protocol userop receipt 0xabc...
1211
+ $ kash protocol userop receipt 0xabc... --json --quiet | jq -r '.success'
1212
+ `
1213
+ ).action(async (hash, _opts, cmd) => {
1214
+ const globals = readGlobals(cmd);
1215
+ if (!HEX_HASH_REGEX.test(hash)) {
1216
+ throw new CliValidationError(
1217
+ "<hash> must be a 0x-prefixed 32-byte hex hash.",
1218
+ `Got "${hash}".`,
1219
+ "hash"
1220
+ );
1221
+ }
1222
+ try {
1223
+ const resolved = await buildDirectClient({ globals });
1224
+ const receipt = await resolved.client.bundler.getReceipt(hash);
1225
+ if (globals.json) {
1226
+ printJson(receipt === null ? null : serializeReceipt(receipt));
1227
+ return;
1228
+ }
1229
+ if (receipt === null) {
1230
+ log.info("Not yet included (receipt is null).");
1231
+ return;
1232
+ }
1233
+ emitReceiptHuman(receipt);
1234
+ } catch (cause) {
1235
+ throw toCliError(cause);
1236
+ }
1237
+ });
1238
+ var waitCommand = new Command11("wait").description("Wait for a UserOp to be included; polls with exponential backoff.").argument("<hash>", "UserOp hash (0x-prefixed, 32 bytes)").option(
1239
+ "--wait-timeout-ms, --timeout-ms <n>",
1240
+ "total time budget across polls (default 60000) \u2014 distinct from the global --timeout-ms (per-HTTP-request)"
1241
+ ).option("--interval-ms <n>", "poll interval (default 1500)").addHelpText(
1242
+ "after",
1243
+ `
1244
+ Examples:
1245
+ $ kash protocol userop wait 0xabc...
1246
+ $ kash protocol userop wait 0xabc... --wait-timeout-ms 300000 # 5min budget
1247
+ `
1248
+ ).action(
1249
+ async (hash, options, cmd) => {
1250
+ const globals = readGlobals(cmd);
1251
+ if (!HEX_HASH_REGEX.test(hash)) {
1252
+ throw new CliValidationError(
1253
+ "<hash> must be a 0x-prefixed 32-byte hex hash.",
1254
+ `Got "${hash}".`,
1255
+ "hash"
1256
+ );
1257
+ }
1258
+ const timeoutMs = options.timeoutMs ? parsePositiveInt(options.timeoutMs, "timeout-ms") : void 0;
1259
+ const intervalMs = options.intervalMs ? parsePositiveInt(options.intervalMs, "interval-ms") : void 0;
1260
+ try {
1261
+ const resolved = await buildDirectClient({ globals });
1262
+ const waitOpts = {
1263
+ ...timeoutMs === void 0 ? {} : { timeoutMs },
1264
+ ...intervalMs === void 0 ? {} : { intervalMs }
1265
+ };
1266
+ const receipt = await resolved.client.bundler.waitForReceipt(
1267
+ hash,
1268
+ waitOpts
1269
+ );
1270
+ if (globals.json) {
1271
+ printJson(serializeReceipt(receipt));
1272
+ return;
1273
+ }
1274
+ emitReceiptHuman(receipt);
1275
+ } catch (cause) {
1276
+ throw toCliError(cause);
1277
+ }
1278
+ }
1279
+ );
1280
+ var useropCommand = new Command11("userop").description("Granular UserOp lifecycle: build, simulate, submit, hash, receipt, wait.").addCommand(buildCommand).addCommand(simulateCommand).addCommand(submitCommand).addCommand(hashCommand).addCommand(receiptCommand).addCommand(waitCommand);
1281
+ async function emitBuildResult(built, side, target, outPath, json) {
1282
+ const payload = {
1283
+ side,
1284
+ target,
1285
+ userOp: serializeUserOp(built.userOp),
1286
+ userOpHash: built.userOpHash,
1287
+ typedData: built.typedData
1288
+ };
1289
+ const serialized = JSON.stringify(payload, null, 2);
1290
+ if (outPath) {
1291
+ try {
1292
+ await writeFile(outPath, `${serialized}
1293
+ `, { encoding: "utf8", mode: 384 });
1294
+ } catch (cause) {
1295
+ throw new CliError(`Failed to write ${outPath}.`, {
1296
+ code: "CONFIGURATION",
1297
+ recoverable: true,
1298
+ suggestion: "Verify the path is writable and the parent directory exists.",
1299
+ cause
1300
+ });
1301
+ }
1302
+ if (!json) {
1303
+ log.success(`Wrote ${outPath} (mode 0600).`);
1304
+ print(` ${style.dim("Side ")} ${side}`);
1305
+ print(` ${style.dim("Target")} ${formatAddress(target, 10, 8)}`);
1306
+ print(` ${style.dim("Hash ")} ${built.userOpHash}`);
1307
+ }
1308
+ return;
1309
+ }
1310
+ if (json) {
1311
+ printJson(payload);
1312
+ return;
1313
+ }
1314
+ log.success(`Built ${side} UserOp.`);
1315
+ log.detail("Hash", built.userOpHash);
1316
+ print(serialized);
1317
+ }
1318
+ async function readUnsignedUserOp(file) {
1319
+ const raw = await readSource(file);
1320
+ const parsed = parseEnvelope(raw);
1321
+ return deserializeUserOp(parsed.userOp);
1322
+ }
1323
+ var SIGNED_USEROP_SIG_REGEX = /^0x([0-9a-fA-F]{2})+$/;
1324
+ async function readSignedUserOp(file) {
1325
+ const raw = await readSource(file);
1326
+ const parsed = parseEnvelope(raw);
1327
+ const userOp = deserializeUserOp(parsed.userOp);
1328
+ const signature = userOp.signature;
1329
+ if (typeof signature !== "string" || signature === "0x") {
1330
+ throw new CliValidationError(
1331
+ "UserOp envelope is missing a non-empty `signature` field.",
1332
+ "Sign the userOp externally and write the resulting signature into the `userOp.signature` field."
1333
+ );
1334
+ }
1335
+ if (!SIGNED_USEROP_SIG_REGEX.test(signature)) {
1336
+ throw new CliValidationError(
1337
+ "UserOp `signature` must be a 0x-prefixed hex string with even-length payload.",
1338
+ `Got ${String(signature.length)} characters. Expected at least 132 (0x + 130 hex chars for a 65-byte ECDSA signature).`
1339
+ );
1340
+ }
1341
+ if (signature.length < 132) {
1342
+ throw new CliValidationError(
1343
+ "UserOp `signature` is too short to be a valid ECDSA signature.",
1344
+ `Got ${String(signature.length)} hex chars; canonical ECDSA signatures are 132 (0x + 130). Did the signer truncate output?`
1345
+ );
1346
+ }
1347
+ return userOp;
1348
+ }
1349
+ async function readSource(file) {
1350
+ if (file !== void 0 && file !== "-") {
1351
+ try {
1352
+ return await readFile(file, "utf8");
1353
+ } catch (cause) {
1354
+ throw new CliError(`Failed to read ${file}.`, {
1355
+ code: "CONFIGURATION",
1356
+ recoverable: true,
1357
+ suggestion: "Verify the path exists and is readable.",
1358
+ cause
1359
+ });
1360
+ }
1361
+ }
1362
+ const chunks = [];
1363
+ for await (const chunk of process.stdin) {
1364
+ if (typeof chunk === "string") chunks.push(Buffer.from(chunk));
1365
+ else if (chunk instanceof Buffer) chunks.push(chunk);
1366
+ else chunks.push(Buffer.from(chunk));
1367
+ }
1368
+ if (chunks.length === 0) {
1369
+ throw new CliValidationError(
1370
+ "No input received on stdin.",
1371
+ "Pass a file path or pipe a UserOp JSON envelope into stdin."
1372
+ );
1373
+ }
1374
+ const { stripBom } = await import("./stdin-YW2CEQXU.js");
1375
+ return stripBom(Buffer.concat(chunks).toString("utf8"));
1376
+ }
1377
+ function parseEnvelope(raw) {
1378
+ let parsed;
1379
+ try {
1380
+ parsed = JSON.parse(raw);
1381
+ } catch {
1382
+ throw new CliValidationError(
1383
+ "Input is not valid JSON.",
1384
+ "Expected the JSON output of `kash protocol userop build`."
1385
+ );
1386
+ }
1387
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.userOp !== "object" || parsed.userOp === null) {
1388
+ throw new CliValidationError(
1389
+ "Input does not contain a `userOp` object.",
1390
+ 'The envelope must look like { userOp: {...}, userOpHash: "0x\u2026", typedData: {...} }.'
1391
+ );
1392
+ }
1393
+ return parsed;
1394
+ }
1395
+ function deserializeUserOp(input) {
1396
+ const BIGINT_FIELDS = /* @__PURE__ */ new Set([
1397
+ "nonce",
1398
+ "callGasLimit",
1399
+ "verificationGasLimit",
1400
+ "preVerificationGas",
1401
+ "maxFeePerGas",
1402
+ "maxPriorityFeePerGas",
1403
+ "paymasterVerificationGasLimit",
1404
+ "paymasterPostOpGasLimit"
1405
+ ]);
1406
+ const out = {};
1407
+ for (const [k, v] of Object.entries(input)) {
1408
+ if (BIGINT_FIELDS.has(k) && typeof v === "string") {
1409
+ try {
1410
+ out[k] = BigInt(v);
1411
+ } catch {
1412
+ throw new CliValidationError(
1413
+ `UserOp field "${k}" is not a valid bigint string.`,
1414
+ `Got ${typeof v === "string" ? `"${v}"` : String(v)}.`
1415
+ );
1416
+ }
1417
+ } else {
1418
+ out[k] = v;
1419
+ }
1420
+ }
1421
+ return out;
1422
+ }
1423
+ function serializeReceipt(receipt) {
1424
+ return {
1425
+ userOpHash: receipt.userOpHash,
1426
+ sender: receipt.sender,
1427
+ nonce: receipt.nonce,
1428
+ success: receipt.success,
1429
+ actualGasCost: receipt.actualGasCost,
1430
+ actualGasUsed: receipt.actualGasUsed,
1431
+ receipt: {
1432
+ transactionHash: receipt.receipt.transactionHash,
1433
+ blockNumber: receipt.receipt.blockNumber,
1434
+ status: receipt.receipt.status
1435
+ }
1436
+ };
1437
+ }
1438
+ function emitReceiptHuman(receipt) {
1439
+ print("");
1440
+ if (receipt.success) {
1441
+ log.success("UserOp included successfully.");
1442
+ } else {
1443
+ log.error("UserOp included but reverted on-chain.");
1444
+ }
1445
+ print(` ${style.dim("UserOp ")} ${receipt.userOpHash}`);
1446
+ print(` ${style.dim("Tx ")} ${receipt.receipt.transactionHash}`);
1447
+ print(` ${style.dim("Block ")} ${receipt.receipt.blockNumber}`);
1448
+ print(` ${style.dim("Gas ")} ${receipt.actualGasUsed}`);
1449
+ }
1450
+ function serializeSimulationResult(result) {
1451
+ if (result.willSucceed) {
1452
+ return {
1453
+ willSucceed: true,
1454
+ ...result.gasEstimate === void 0 ? {} : { gasEstimate: result.gasEstimate.toString() }
1455
+ };
1456
+ }
1457
+ return {
1458
+ willSucceed: false,
1459
+ revertReason: result.revertReason,
1460
+ ...result.decodedError === void 0 ? {} : {
1461
+ decodedError: {
1462
+ name: result.decodedError.name,
1463
+ args: result.decodedError.args.map((a) => typeof a === "bigint" ? a.toString() : a)
1464
+ }
1465
+ }
1466
+ };
1467
+ }
1468
+
1469
+ // src/commands/protocol/watch.ts
1470
+ import { Command as Command12 } from "commander";
1471
+ var watchCommand = new Command12("watch").description("Subscribe to on-chain trade events for a market (NDJSON stream).").argument("<market>", "market contract address (0x-prefixed)").option("--max-events <n>", "exit cleanly after observing N events").option("--timeout-ms <n>", "exit cleanly after this wall-clock budget (ms)").addHelpText(
1472
+ "after",
1473
+ `
1474
+ Examples:
1475
+ $ kash protocol watch 0xMarket... --json
1476
+ $ kash protocol watch 0xMarket... --json --quiet | jq -r '.transactionHash'
1477
+ $ kash protocol watch 0xMarket... --max-events 100 --json
1478
+
1479
+ Notes:
1480
+ - Best-effort: on RPC reconnect, missed events are NOT replayed. For
1481
+ gap-free history, use \`kash markets predictions <id>\` (indexer-backed).
1482
+ - Press Ctrl-C to terminate cleanly. The subscription unsubscribes
1483
+ before the process exits.
1484
+ `
1485
+ ).action(async (market, options, cmd) => {
1486
+ const globals = readGlobals(cmd);
1487
+ const marketAddress = validateAddress(market, "market");
1488
+ const maxEvents = options.maxEvents ? parsePositiveInt(options.maxEvents, "max-events") : void 0;
1489
+ const timeoutMs = options.timeoutMs ? parsePositiveInt(options.timeoutMs, "timeout-ms") : void 0;
1490
+ try {
1491
+ const resolved = await buildDirectClient({ globals });
1492
+ let resolveDone;
1493
+ let count = 0;
1494
+ const done = new Promise((resolve) => {
1495
+ resolveDone = resolve;
1496
+ });
1497
+ const subscriptionHolder = { current: void 0 };
1498
+ let teardownTriggered = false;
1499
+ const teardown = (reason2) => {
1500
+ if (teardownTriggered) return;
1501
+ teardownTriggered = true;
1502
+ try {
1503
+ subscriptionHolder.current?.unsubscribe();
1504
+ } catch {
1505
+ }
1506
+ resolveDone?.(reason2);
1507
+ };
1508
+ subscriptionHolder.current = resolved.client.markets.watch(marketAddress, {
1509
+ onEvent: (event) => {
1510
+ if (globals.json) {
1511
+ writeNdjson(event);
1512
+ } else {
1513
+ print(formatEventHuman(event));
1514
+ }
1515
+ count += 1;
1516
+ if (maxEvents !== void 0 && count >= maxEvents) {
1517
+ teardown(`reached --max-events ${String(maxEvents)}`);
1518
+ }
1519
+ },
1520
+ onReconnect: () => {
1521
+ if (!globals.json) {
1522
+ log.warn("Watcher reconnecting \u2014 missed events will NOT be replayed.");
1523
+ }
1524
+ },
1525
+ onError: (err) => {
1526
+ if (!globals.json) {
1527
+ log.warn(`Watcher error: ${err.message}`);
1528
+ }
1529
+ }
1530
+ });
1531
+ const onSigint = () => teardown("SIGINT");
1532
+ const onSigterm = () => teardown("SIGTERM");
1533
+ process.on("SIGINT", onSigint);
1534
+ process.on("SIGTERM", onSigterm);
1535
+ const timeoutHandle = timeoutMs !== void 0 ? setTimeout(() => teardown(`reached --timeout-ms ${String(timeoutMs)}`), timeoutMs) : void 0;
1536
+ timeoutHandle?.unref();
1537
+ if (!globals.json) {
1538
+ log.info(`Watching market ${marketAddress}\u2026`);
1539
+ log.detail("Stop", "Ctrl-C");
1540
+ if (maxEvents !== void 0) log.detail("Max events", String(maxEvents));
1541
+ if (timeoutMs !== void 0) log.detail("Timeout (ms)", String(timeoutMs));
1542
+ }
1543
+ const reason = await done;
1544
+ process.off("SIGINT", onSigint);
1545
+ process.off("SIGTERM", onSigterm);
1546
+ if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
1547
+ if (!globals.json) {
1548
+ log.info(`Watcher stopped (${reason}).`);
1549
+ }
1550
+ } catch (cause) {
1551
+ throw toCliError(cause);
1552
+ }
1553
+ });
1554
+ function formatEventHuman(event) {
1555
+ const time = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
1556
+ if (event.type === "TRADE") {
1557
+ return `${style.dim(time)} ${style.bold("TRADE")} side=${event.side} out=${String(event.outcome)} usdc=${event.assetsUsdc.toString()} tokens=${event.tokensWad.toString()} tx=${event.transactionHash.slice(0, 10)}\u2026`;
1558
+ }
1559
+ if (event.type === "RESOLVED") {
1560
+ return `${style.dim(time)} ${style.bold("RESOLVED")} winner=${String(event.winningOutcome)} tx=${event.transactionHash.slice(0, 10)}\u2026`;
1561
+ }
1562
+ return `${style.dim(time)} ${style.bold("FROZEN")} observedAt=${event.observedAt.toString()} tx=${event.transactionHash.slice(0, 10)}\u2026`;
1563
+ }
1564
+
1565
+ // src/commands/protocol/index.ts
1566
+ var protocolCommand = new Command13("protocol").description(
1567
+ "Self-orchestrated direct-to-chain operations (uses @kashdao/protocol-sdk). Like the default Kash-orchestrated mode, this path is non-custodial \u2014 the difference is who builds and submits trades."
1568
+ ).addCommand(balanceCommand).addCommand(marketCommand).addCommand(quoteCommand).addCommand(positionCommand).addCommand(allowanceCommand).addCommand(smartAccountCommand).addCommand(feesCommand).addCommand(tokenIdCommand).addCommand(decodeRevertCommand).addCommand(tradeCommand).addCommand(useropCommand).addCommand(watchCommand);
1569
+ export {
1570
+ protocolCommand
1571
+ };
1572
+ //# sourceMappingURL=protocol-XEQXF2GW.js.map