@overcast-xyz/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 (44) hide show
  1. package/README.md +272 -0
  2. package/dist/bin.d.ts +2 -0
  3. package/dist/bin.js +200 -0
  4. package/dist/client.d.ts +12 -0
  5. package/dist/client.js +18 -0
  6. package/dist/commands/accept.d.ts +11 -0
  7. package/dist/commands/accept.js +112 -0
  8. package/dist/commands/assets.d.ts +9 -0
  9. package/dist/commands/assets.js +80 -0
  10. package/dist/commands/balance.d.ts +9 -0
  11. package/dist/commands/balance.js +74 -0
  12. package/dist/commands/deposit.d.ts +7 -0
  13. package/dist/commands/deposit.js +67 -0
  14. package/dist/commands/exercise.d.ts +14 -0
  15. package/dist/commands/exercise.js +88 -0
  16. package/dist/commands/key.d.ts +15 -0
  17. package/dist/commands/key.js +98 -0
  18. package/dist/commands/mm-webhook.d.ts +36 -0
  19. package/dist/commands/mm-webhook.js +152 -0
  20. package/dist/commands/mm.d.ts +37 -0
  21. package/dist/commands/mm.js +345 -0
  22. package/dist/commands/offer.d.ts +29 -0
  23. package/dist/commands/offer.js +149 -0
  24. package/dist/commands/option.d.ts +28 -0
  25. package/dist/commands/option.js +138 -0
  26. package/dist/commands/quote-webhook.d.ts +36 -0
  27. package/dist/commands/quote-webhook.js +152 -0
  28. package/dist/commands/quote.d.ts +37 -0
  29. package/dist/commands/quote.js +345 -0
  30. package/dist/commands/redeem.d.ts +14 -0
  31. package/dist/commands/redeem.js +88 -0
  32. package/dist/commands/rfq-view.d.ts +25 -0
  33. package/dist/commands/rfq-view.js +172 -0
  34. package/dist/commands/rfq.d.ts +4 -0
  35. package/dist/commands/rfq.js +280 -0
  36. package/dist/commands/shared.d.ts +81 -0
  37. package/dist/commands/shared.js +296 -0
  38. package/dist/commands/withdraw.d.ts +7 -0
  39. package/dist/commands/withdraw.js +67 -0
  40. package/dist/config.d.ts +53 -0
  41. package/dist/config.js +58 -0
  42. package/dist/index.d.ts +18 -0
  43. package/dist/index.js +50 -0
  44. package/package.json +38 -0
@@ -0,0 +1,37 @@
1
+ import { NewRfqPayload } from "@overcast/core";
2
+ import { GlobalFlags } from "../config";
3
+ /** A market maker's priced premium for an RFQ: asset address + native amount. */
4
+ export interface Premium {
5
+ premiumAsset: string;
6
+ premiumAmount: bigint;
7
+ }
8
+ /**
9
+ * Prices an incoming RFQ. Returns the premium to quote, or `null` to skip the
10
+ * RFQ. May be async so pricers can consult external data (e.g. a spot oracle).
11
+ */
12
+ export type QuotePricer = (rfq: NewRfqPayload) => Promise<Premium | null>;
13
+ export interface MmOptions extends GlobalFlags {
14
+ /** MM edge added on top of the Black-Scholes fair value (fraction). */
15
+ spread?: string;
16
+ /** Annualized implied volatility, e.g. `0.8` for 80%. */
17
+ vol?: string;
18
+ /** Annualized risk-free rate, e.g. `0.05` for 5%. */
19
+ rate?: string;
20
+ /** Endpoint each RFQ event is POSTed to; its reply drives the quote. */
21
+ webhookUrl?: string;
22
+ /** Per-event webhook timeout, in milliseconds. */
23
+ webhookTimeout?: string;
24
+ }
25
+ /**
26
+ * `overcast mm` — run a market maker that auto-quotes every incoming RFQ using
27
+ * a Black-Scholes price.
28
+ *
29
+ * A cash-secured put and a covered call are the same instrument — an option to
30
+ * swap `settlementAmount` of one asset for `collateralAmount` of the other —
31
+ * differing only in which leg is collateral. We identify the stablecoin (cash)
32
+ * leg and the volatile (underlying) leg, then price:
33
+ * - settlement is cash → collateral is the underlying → **covered call**
34
+ * - collateral is cash → settlement is the underlying → **cash-secured put**
35
+ * See {@link blackScholesPricer}.
36
+ */
37
+ export declare function runMarketMaker(opts: MmOptions): Promise<void>;
@@ -0,0 +1,345 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.runMarketMaker = runMarketMaker;
40
+ const p = __importStar(require("@clack/prompts"));
41
+ const core_1 = require("@overcast/core");
42
+ const picocolors_1 = __importDefault(require("picocolors"));
43
+ const client_1 = require("../client");
44
+ const config_1 = require("../config");
45
+ const mm_webhook_1 = require("./mm-webhook");
46
+ /** MM edge as a fraction of fair value (2%). */
47
+ const DEFAULT_SPREAD = 0.02;
48
+ /** Annualized implied volatility used for every quote (80%). */
49
+ const DEFAULT_VOL = 0.8;
50
+ /** Annualized risk-free rate (crypto convention: assume 0). */
51
+ const DEFAULT_RATE = 0;
52
+ /** Per-event webhook timeout when `--webhook-timeout` is omitted. */
53
+ const DEFAULT_WEBHOOK_TIMEOUT_MS = 5000;
54
+ const SECONDS_PER_YEAR = 365 * 24 * 60 * 60;
55
+ /**
56
+ * Symbols treated as the cash / numeraire leg. A quote needs exactly one cash
57
+ * leg and one volatile-underlying leg; the cash side is assumed to be ~$1.
58
+ */
59
+ const STABLE_SYMBOLS = new Set([
60
+ "USDC",
61
+ "USDT",
62
+ "USDH",
63
+ "PYUSD",
64
+ "DAI",
65
+ "USDP",
66
+ "TUSD",
67
+ "USD",
68
+ ]);
69
+ /**
70
+ * `overcast mm` — run a market maker that auto-quotes every incoming RFQ using
71
+ * a Black-Scholes price.
72
+ *
73
+ * A cash-secured put and a covered call are the same instrument — an option to
74
+ * swap `settlementAmount` of one asset for `collateralAmount` of the other —
75
+ * differing only in which leg is collateral. We identify the stablecoin (cash)
76
+ * leg and the volatile (underlying) leg, then price:
77
+ * - settlement is cash → collateral is the underlying → **covered call**
78
+ * - collateral is cash → settlement is the underlying → **cash-secured put**
79
+ * See {@link blackScholesPricer}.
80
+ */
81
+ async function runMarketMaker(opts) {
82
+ const config = (0, config_1.resolveConfig)(opts);
83
+ const app = await (0, client_1.createClient)(config);
84
+ const spread = opts.spread ? Number(opts.spread) : DEFAULT_SPREAD;
85
+ const vol = opts.vol ? Number(opts.vol) : DEFAULT_VOL;
86
+ const rate = opts.rate ? Number(opts.rate) : DEFAULT_RATE;
87
+ const rfq = app.rfq;
88
+ // Webhook mode: delegate pricing (and forward every lifecycle event) to a
89
+ // developer-supplied local endpoint. Falls back to the built-in Black-Scholes
90
+ // pricer when no `--webhook-url` (or `OVERCAST_MM_WEBHOOK_URL`) is set.
91
+ const webhookUrl = opts.webhookUrl ?? process.env.OVERCAST_MM_WEBHOOK_URL;
92
+ const webhook = webhookUrl
93
+ ? {
94
+ url: webhookUrl,
95
+ timeoutMs: Number(opts.webhookTimeout) || DEFAULT_WEBHOOK_TIMEOUT_MS,
96
+ }
97
+ : null;
98
+ const pricer = webhook
99
+ ? (0, mm_webhook_1.webhookPricer)(webhook, app.publicKey)
100
+ : blackScholesPricer({ spread, vol, rate });
101
+ p.intro(picocolors_1.default.magenta("Overcast Market Maker"));
102
+ // The market maker takes the option (pays the premium), so it acts on the
103
+ // TAKER side — the opposite of the RFQ creator's MAKER side.
104
+ await rfq.registerRole("TAKER");
105
+ p.log.info(`Maker ${picocolors_1.default.bold(app.publicKey)}`);
106
+ p.log.info(picocolors_1.default.dim(webhook
107
+ ? `Webhook — ${webhook.url} (timeout ${webhook.timeoutMs}ms)`
108
+ : `Black-Scholes — vol ${vol * 100}%, rate ${rate * 100}%, edge ${spread * 100}%`));
109
+ p.log.info(picocolors_1.default.dim("Listening for RFQs. Press Ctrl-C to stop."));
110
+ rfq.onQuoteAccepted = (a) => {
111
+ p.log.success(`Quote ${short(a.quoteId)} accepted — option ${picocolors_1.default.bold(a.optionId)} (RFQ ${short(a.rfqId)})`);
112
+ if (webhook)
113
+ (0, mm_webhook_1.postEvent)(webhook, "quoteAccepted", a, app.publicKey);
114
+ };
115
+ rfq.onError = (e) => {
116
+ p.log.error(`RFQ error: ${e.message}`);
117
+ if (webhook)
118
+ (0, mm_webhook_1.postEvent)(webhook, "error", e, app.publicKey);
119
+ };
120
+ // In webhook mode, forward the remaining lifecycle events too so the endpoint
121
+ // has full visibility (these are fire-and-forget — their replies are ignored).
122
+ if (webhook) {
123
+ rfq.onNewQuote = (q) => (0, mm_webhook_1.postEvent)(webhook, "newQuote", q, app.publicKey);
124
+ rfq.onRfqCancelled = (c) => (0, mm_webhook_1.postEvent)(webhook, "rfqCancelled", c, app.publicKey);
125
+ rfq.onRfqExpired = (e) => (0, mm_webhook_1.postEvent)(webhook, "rfqExpired", e, app.publicKey);
126
+ }
127
+ // Auto-quote every incoming RFQ (Black-Scholes, or the webhook's reply).
128
+ rfq.onNewRfq = (incoming) => {
129
+ void quoteRfq(rfq, app.publicKey, incoming, pricer);
130
+ };
131
+ // Run until interrupted.
132
+ await new Promise((resolve) => {
133
+ process.on("SIGINT", () => {
134
+ p.log.warn("Shutting down…");
135
+ rfq.disconnect();
136
+ resolve();
137
+ });
138
+ });
139
+ p.outro(picocolors_1.default.magenta("Market maker stopped."));
140
+ }
141
+ /**
142
+ * Price an incoming RFQ and submit a quote over the full option terms — the
143
+ * RFQ's legs completed with the maker's premium — on the TAKER side. The client
144
+ * authors the settlement offer and signs it with the protocol signer, so no
145
+ * signature is passed here.
146
+ */
147
+ async function quoteRfq(rfq, creator, incoming, pricer) {
148
+ try {
149
+ const premium = await pricer(incoming);
150
+ if (!premium)
151
+ return; // the pricer already logged why it skipped
152
+ const details = (0, core_1.completeOptionDetails)(incoming.details, premium);
153
+ if (!details) {
154
+ skip(incoming.rfqId, "RFQ is missing option terms");
155
+ return;
156
+ }
157
+ const { quoteId } = await rfq.submitQuote({
158
+ rfqId: incoming.rfqId,
159
+ details,
160
+ creator,
161
+ side: "TAKER",
162
+ });
163
+ p.log.success(`Quoted ${premium.premiumAmount} ${short(premium.premiumAsset)} on RFQ ${short(incoming.rfqId)} (quote ${short(quoteId)})`);
164
+ }
165
+ catch (err) {
166
+ p.log.error(`Failed to quote RFQ ${short(incoming.rfqId)}: ${err.message}`);
167
+ }
168
+ }
169
+ /**
170
+ * Black-Scholes pricer. Maps an RFQ onto European-option terms and quotes the
171
+ * fair premium plus a `spread` edge, denominated in the cash (stablecoin) leg.
172
+ *
173
+ * The contract lets the holder swap the underlying for cash at the implied
174
+ * strike `K = cashAmount / underlyingAmount` (USD per underlying unit), with
175
+ * `underlyingAmount` as the contract size.
176
+ *
177
+ * Spot comes from a live CoinGecko lookup, falling back to the asset's
178
+ * backend-supplied {@link Asset.lastKnownPriceUsd} (the only source for RWAs,
179
+ * which have no `coinGeckoId`). Volatility uses the asset's own
180
+ * {@link Asset.volatility} when present, else the configured default. Returns
181
+ * `null` (skips the RFQ) when the legs can't be priced — no single cash leg, no
182
+ * usable spot price, or the option has already expired.
183
+ */
184
+ function blackScholesPricer(params) {
185
+ const { spread, vol, rate } = params;
186
+ return async (rfq) => {
187
+ const Sleep = (ms) => new Promise((r) => setTimeout(r, ms));
188
+ await Sleep(Math.random() * 1000);
189
+ // `d` is the curated (wire) shape — assets resolved, amounts as decimal
190
+ // strings — but only *partially* filled: the RFQ may omit some terms. We can
191
+ // only price a fully-specified pair of legs, so bail otherwise.
192
+ const d = rfq.details;
193
+ if (d.endTimestamp == null ||
194
+ !d.collateralAsset ||
195
+ d.collateralAmount == null ||
196
+ !d.settlementAsset ||
197
+ d.settlementAmount == null) {
198
+ skip(rfq.rfqId, "RFQ does not specify both option legs");
199
+ return null;
200
+ }
201
+ // Capture the now-narrowed terms before any further `await` widens them back.
202
+ const endTimestamp = d.endTimestamp;
203
+ const collateral = {
204
+ asset: d.collateralAsset,
205
+ amount: (0, core_1.toDisplayAmount)((0, core_1.amountFromWire)(d.collateralAmount), d.collateralAsset),
206
+ };
207
+ const settlement = {
208
+ asset: d.settlementAsset,
209
+ amount: (0, core_1.toDisplayAmount)((0, core_1.amountFromWire)(d.settlementAmount), d.settlementAsset),
210
+ };
211
+ // Need exactly one cash leg and one volatile leg to define an option.
212
+ const collateralIsCash = isStable(collateral.asset);
213
+ const settlementIsCash = isStable(settlement.asset);
214
+ if (collateralIsCash === settlementIsCash) {
215
+ skip(rfq.rfqId, `need exactly one stablecoin leg (got ${symbol(collateral.asset)}/${symbol(settlement.asset)})`);
216
+ return null;
217
+ }
218
+ // settlement = cash → covered call; collateral = cash → cash-secured put.
219
+ const isCall = settlementIsCash;
220
+ const cash = isCall ? settlement : collateral;
221
+ const underlying = isCall ? collateral : settlement;
222
+ // Prefer a live CoinGecko spot, falling back to the backend-supplied last
223
+ // known price. For RWAs (no `coinGeckoId`) the last known price is the only
224
+ // source — they're never auto-refreshed against a live feed.
225
+ const liveSpot = underlying.asset.coinGeckoId
226
+ ? await fetchSpotUsd(underlying.asset.coinGeckoId)
227
+ : null;
228
+ const spot = liveSpot ?? underlying.asset.lastKnownPriceUsd ?? null;
229
+ if (spot == null) {
230
+ skip(rfq.rfqId, `no spot or last known price for ${symbol(underlying.asset)}`);
231
+ return null;
232
+ }
233
+ // Use the asset's own volatility when the backend provides one; otherwise
234
+ // fall back to the configured default.
235
+ const sigma = underlying.asset.volatility ?? vol;
236
+ const nowSec = Math.floor(Date.now() / 1000);
237
+ const years = (endTimestamp - nowSec) / SECONDS_PER_YEAR;
238
+ if (years <= 0) {
239
+ skip(rfq.rfqId, "option has already expired");
240
+ return null;
241
+ }
242
+ const strike = Number(cash.amount) / Number(underlying.amount); // USD per underlying unit
243
+ const perUnit = blackScholes(isCall, spot, strike, years, rate, sigma);
244
+ const fairCash = perUnit * Number(underlying.amount); // total premium in cash units
245
+ const quotedCash = fairCash * (1 + spread) * (Math.random() * 0.05 + 0.95); // add the MM's edge
246
+ // Pricing is done in floats (display units); the quoted premium becomes an
247
+ // exact native `bigint`. The backend requires a positive premium, so clamp
248
+ // to at least one native unit.
249
+ const decimals = cash.asset.decimals ?? 0;
250
+ const native = (0, core_1.parseUnits)(quotedCash.toFixed(decimals), decimals);
251
+ const premiumAmount = native < 1n ? 1n : native;
252
+ return { premiumAsset: cash.asset.address, premiumAmount };
253
+ };
254
+ }
255
+ /**
256
+ * Black-Scholes price of one European option, in the same currency as `S`/`K`.
257
+ *
258
+ * @param isCall true for a call, false for a put
259
+ * @param S spot price of the underlying
260
+ * @param K strike price
261
+ * @param T time to expiry, in years
262
+ * @param r annualized risk-free rate
263
+ * @param sigma annualized volatility
264
+ */
265
+ function blackScholes(isCall, S, K, T, r, sigma) {
266
+ // Degenerate inputs: the option is worth its (undiscounted) intrinsic value.
267
+ if (sigma <= 0 || T <= 0) {
268
+ return Math.max(0, isCall ? S - K : K - S);
269
+ }
270
+ const sqrtT = Math.sqrt(T);
271
+ const d1 = (Math.log(S / K) + (r + (sigma * sigma) / 2) * T) / (sigma * sqrtT);
272
+ const d2 = d1 - sigma * sqrtT;
273
+ const disc = Math.exp(-r * T);
274
+ return isCall
275
+ ? S * normCdf(d1) - K * disc * normCdf(d2)
276
+ : K * disc * normCdf(-d2) - S * normCdf(-d1);
277
+ }
278
+ /**
279
+ * Standard normal CDF via the Zelen & Severo rational approximation
280
+ * (Abramowitz & Stegun 26.2.17), accurate to ~7.5e-8.
281
+ */
282
+ function normCdf(x) {
283
+ if (x < 0)
284
+ return 1 - normCdf(-x);
285
+ const t = 1 / (1 + 0.2316419 * x);
286
+ const poly = t *
287
+ (0.31938153 +
288
+ t *
289
+ (-0.356563782 +
290
+ t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))));
291
+ const pdf = 0.3989422804014327 * Math.exp((-x * x) / 2);
292
+ return 1 - pdf * poly;
293
+ }
294
+ // --- spot prices -------------------------------------------------------------
295
+ /** Cached CoinGecko USD prices, refreshed on a short TTL to avoid rate limits. */
296
+ const SPOT_TTL_MS = 60_000;
297
+ const spotCache = new Map();
298
+ /**
299
+ * Fetch the USD spot price for a CoinGecko id, served from a short-lived cache.
300
+ * Returns `null` if the lookup fails (network error or unknown id) so the
301
+ * pricer can skip the RFQ rather than quote on stale or missing data.
302
+ */
303
+ async function fetchSpotUsd(coinGeckoId) {
304
+ const cached = spotCache.get(coinGeckoId);
305
+ if (cached && Date.now() - cached.at < SPOT_TTL_MS)
306
+ return cached.price;
307
+ try {
308
+ const url = `https://api.coingecko.com/api/v3/simple/price?ids=${encodeURIComponent(coinGeckoId)}&vs_currencies=usd`;
309
+ const res = await fetch(url);
310
+ if (!res.ok)
311
+ return null;
312
+ const body = (await res.json());
313
+ const price = body[coinGeckoId]?.usd;
314
+ if (typeof price !== "number" || !(price > 0))
315
+ return null;
316
+ spotCache.set(coinGeckoId, { price, at: Date.now() });
317
+ return price;
318
+ }
319
+ catch {
320
+ return null;
321
+ }
322
+ }
323
+ // --- helpers -----------------------------------------------------------------
324
+ /**
325
+ * Whether an asset is treated as the cash / numeraire leg. The backend's own
326
+ * {@link Asset.cashLike} classification wins when present; otherwise fall back to
327
+ * the stablecoin-symbol heuristic for assets that predate the flag.
328
+ */
329
+ function isStable(asset) {
330
+ if (asset.cashLike != null)
331
+ return asset.cashLike;
332
+ return asset.symbol ? STABLE_SYMBOLS.has(asset.symbol.toUpperCase()) : false;
333
+ }
334
+ /** A display label for an asset (symbol, falling back to a short address). */
335
+ function symbol(asset) {
336
+ return asset.symbol ?? short(asset.address);
337
+ }
338
+ /** Log why an RFQ was skipped without quoting. */
339
+ function skip(rfqId, reason) {
340
+ p.log.warn(`Skipping RFQ ${short(rfqId)}: ${reason}`);
341
+ }
342
+ /** Shorten a base58 id for log lines. */
343
+ function short(id) {
344
+ return id.length <= 12 ? id : `${id.slice(0, 6)}…${id.slice(-4)}`;
345
+ }
@@ -0,0 +1,29 @@
1
+ import { GlobalFlags } from "../config";
2
+ export interface OfferShowOptions extends GlobalFlags {
3
+ /** Offer id to look up; prompts when omitted. */
4
+ id?: string;
5
+ }
6
+ /**
7
+ * `overcast offer show` — look up an open offer by its content-address id.
8
+ *
9
+ * Collateral (maker) and settlement (taker) offers now share a single
10
+ * content-addressed namespace; the offer's own `side` says which it is.
11
+ */
12
+ export declare function offerShow(opts: OfferShowOptions): Promise<void>;
13
+ export interface OfferListOptions extends GlobalFlags {
14
+ /** Which side to list: `collateral` (maker), `settlement` (taker), or `both` (default). */
15
+ kind?: string;
16
+ /** Filter to the offer's counterparty (its creator). */
17
+ party?: string;
18
+ /** Filter to offers touching this asset. */
19
+ asset?: string;
20
+ /** Max rows. */
21
+ limit?: string;
22
+ }
23
+ /**
24
+ * `overcast offer list` — browse open offers via the off-chain
25
+ * {@link OvercastView}. Read-only. Offers now live in a single namespace;
26
+ * `--kind` narrows the listing to one side (by the offer's `side`), and with
27
+ * none given both sides are shown.
28
+ */
29
+ export declare function offerList(opts: OfferListOptions): Promise<void>;
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.offerShow = offerShow;
40
+ exports.offerList = offerList;
41
+ const p = __importStar(require("@clack/prompts"));
42
+ const picocolors_1 = __importDefault(require("picocolors"));
43
+ const client_1 = require("../client");
44
+ const config_1 = require("../config");
45
+ const shared_1 = require("./shared");
46
+ /**
47
+ * `overcast offer show` — look up an open offer by its content-address id.
48
+ *
49
+ * Collateral (maker) and settlement (taker) offers now share a single
50
+ * content-addressed namespace; the offer's own `side` says which it is.
51
+ */
52
+ async function offerShow(opts) {
53
+ const config = (0, config_1.resolveConfig)(opts, { keypairOptional: true });
54
+ const app = await (0, client_1.createClient)(config);
55
+ const registry = await (0, shared_1.loadRegistry)(app.api);
56
+ if (opts.id && (0, shared_1.validateAddress)(opts.id)) {
57
+ throw new Error(`invalid offer id: ${opts.id}`);
58
+ }
59
+ const id = opts.id ?? (await (0, shared_1.promptId)("Offer id"));
60
+ p.intro(picocolors_1.default.cyan("Overcast offer"));
61
+ const spinner = p.spinner();
62
+ spinner.start("Looking up offer");
63
+ try {
64
+ const offer = await app.chain.getOffer(id);
65
+ if (!offer) {
66
+ spinner.stop(picocolors_1.default.yellow(`No open offer found for ${(0, shared_1.short)(id)}.`));
67
+ return;
68
+ }
69
+ spinner.stop(`${sideLabel(offer.side)} offer ${(0, shared_1.short)(id)}`);
70
+ p.note([
71
+ `Creator ${(0, shared_1.short)(offer.creator)}`,
72
+ "",
73
+ (0, shared_1.describeOptionDetails)(offer.details, registry),
74
+ ].join("\n"), `${sideLabel(offer.side)} offer`);
75
+ p.outro(picocolors_1.default.green("Done."));
76
+ }
77
+ catch (err) {
78
+ spinner.stop(picocolors_1.default.red(`Lookup failed: ${err.message}`));
79
+ throw err;
80
+ }
81
+ }
82
+ /**
83
+ * `overcast offer list` — browse open offers via the off-chain
84
+ * {@link OvercastView}. Read-only. Offers now live in a single namespace;
85
+ * `--kind` narrows the listing to one side (by the offer's `side`), and with
86
+ * none given both sides are shown.
87
+ */
88
+ async function offerList(opts) {
89
+ const kind = parseKind(opts.kind);
90
+ const config = (0, config_1.resolveConfig)(opts, { keypairOptional: true });
91
+ const app = await (0, client_1.createClient)(config);
92
+ const query = {
93
+ filter: {
94
+ party: (0, shared_1.optionalAddress)("--party", opts.party),
95
+ asset: (0, shared_1.optionalAddress)("--asset", opts.asset),
96
+ },
97
+ page: opts.limit ? { limit: (0, shared_1.parseLimit)(opts.limit) } : undefined,
98
+ };
99
+ p.intro(picocolors_1.default.cyan("Overcast offers"));
100
+ const spinner = p.spinner();
101
+ spinner.start("Loading offers");
102
+ try {
103
+ const { items } = await app.api.listOffers(query);
104
+ // A single namespace has no per-side query filter, so narrow by the offer's
105
+ // own `side` here when `--kind` asks for one.
106
+ const offers = kind === "both" ? items : items.filter((o) => o.side === kindSide(kind));
107
+ spinner.stop("Offers loaded.");
108
+ if (!renderOffers(offers)) {
109
+ p.outro(picocolors_1.default.yellow("No offers match."));
110
+ return;
111
+ }
112
+ p.outro(picocolors_1.default.green("Done."));
113
+ }
114
+ catch (err) {
115
+ spinner.stop(picocolors_1.default.red(`List failed: ${err.message}`));
116
+ throw err;
117
+ }
118
+ }
119
+ /** Print the offers, one per line with side + creator; returns whether any were shown. */
120
+ function renderOffers(offers) {
121
+ if (offers.length === 0) {
122
+ p.log.info(`${picocolors_1.default.bold("Offers")} ${picocolors_1.default.dim("— none")}`);
123
+ return false;
124
+ }
125
+ p.log.info(picocolors_1.default.bold(`Offers (${offers.length})`));
126
+ for (const offer of offers) {
127
+ p.log.message([
128
+ `${picocolors_1.default.bold((0, shared_1.short)(offer.id))} ${(0, shared_1.summarizeCuratedDetails)(offer.details)}`,
129
+ picocolors_1.default.dim(`${sideLabel(offer.side).toLowerCase()} ${(0, shared_1.short)(offer.creator)}`),
130
+ ].join("\n"));
131
+ }
132
+ return true;
133
+ }
134
+ /** Human-readable name for an offer's side. */
135
+ function sideLabel(side) {
136
+ return side === "MAKER" ? "Collateral" : "Settlement";
137
+ }
138
+ /** The {@link OptionSide} a non-`both` `--kind` selects. */
139
+ function kindSide(kind) {
140
+ return kind === "collateral" ? "MAKER" : "TAKER";
141
+ }
142
+ /** Validate the `--kind` flag, defaulting to listing both sides. */
143
+ function parseKind(value) {
144
+ const kind = (value ?? "both").toLowerCase();
145
+ if (kind !== "collateral" && kind !== "settlement" && kind !== "both") {
146
+ throw new Error(`invalid --kind: ${value} (expected collateral, settlement, or both)`);
147
+ }
148
+ return kind;
149
+ }
@@ -0,0 +1,28 @@
1
+ import { GlobalFlags } from "../config";
2
+ export interface OptionShowOptions extends GlobalFlags {
3
+ collateralOffer?: string;
4
+ settlementOffer?: string;
5
+ }
6
+ /**
7
+ * `overcast option show` — resolve a market option from its
8
+ * `(collateralOffer, settlementOffer)` id pair and print its terms alongside
9
+ * the live claim state: the outstanding exercise / redeem claim supplies and,
10
+ * when a wallet is supplied, that wallet's own holdings of each.
11
+ */
12
+ export declare function optionShow(opts: OptionShowOptions): Promise<void>;
13
+ export interface OptionListOptions extends GlobalFlags {
14
+ /** Filter to options where this address is the maker. */
15
+ maker?: string;
16
+ /** Filter to options where this address is the taker. */
17
+ taker?: string;
18
+ /** Filter to options touching this asset (premium, collateral, or settlement). */
19
+ asset?: string;
20
+ /** Max rows to return. */
21
+ limit?: string;
22
+ }
23
+ /**
24
+ * `overcast option list` — browse the backend's curated market options via the
25
+ * off-chain {@link OvercastView}. Read-only: no wallet required. Filters map
26
+ * straight onto the view's {@link OptionFilter}; results are newest-first.
27
+ */
28
+ export declare function optionList(opts: OptionListOptions): Promise<void>;