@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.
- package/README.md +272 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +200 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.js +18 -0
- package/dist/commands/accept.d.ts +11 -0
- package/dist/commands/accept.js +112 -0
- package/dist/commands/assets.d.ts +9 -0
- package/dist/commands/assets.js +80 -0
- package/dist/commands/balance.d.ts +9 -0
- package/dist/commands/balance.js +74 -0
- package/dist/commands/deposit.d.ts +7 -0
- package/dist/commands/deposit.js +67 -0
- package/dist/commands/exercise.d.ts +14 -0
- package/dist/commands/exercise.js +88 -0
- package/dist/commands/key.d.ts +15 -0
- package/dist/commands/key.js +98 -0
- package/dist/commands/mm-webhook.d.ts +36 -0
- package/dist/commands/mm-webhook.js +152 -0
- package/dist/commands/mm.d.ts +37 -0
- package/dist/commands/mm.js +345 -0
- package/dist/commands/offer.d.ts +29 -0
- package/dist/commands/offer.js +149 -0
- package/dist/commands/option.d.ts +28 -0
- package/dist/commands/option.js +138 -0
- package/dist/commands/quote-webhook.d.ts +36 -0
- package/dist/commands/quote-webhook.js +152 -0
- package/dist/commands/quote.d.ts +37 -0
- package/dist/commands/quote.js +345 -0
- package/dist/commands/redeem.d.ts +14 -0
- package/dist/commands/redeem.js +88 -0
- package/dist/commands/rfq-view.d.ts +25 -0
- package/dist/commands/rfq-view.js +172 -0
- package/dist/commands/rfq.d.ts +4 -0
- package/dist/commands/rfq.js +280 -0
- package/dist/commands/shared.d.ts +81 -0
- package/dist/commands/shared.js +296 -0
- package/dist/commands/withdraw.d.ts +7 -0
- package/dist/commands/withdraw.js +67 -0
- package/dist/config.d.ts +53 -0
- package/dist/config.js +58 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +50 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# `@overcast-xyz/cli`
|
|
2
|
+
|
|
3
|
+
A command-line interface for the **Overcast** Solana options protocol. It covers
|
|
4
|
+
the full lifecycle: funding your protocol vault, browsing and inspecting market
|
|
5
|
+
options, running the request-for-quote (RFQ) engine as a taker, exercising and
|
|
6
|
+
redeeming options, and running an automated RFQ quoter.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ⚠️ Status: pre-release, heavy development
|
|
11
|
+
|
|
12
|
+
Please read this before you build anything on top of the CLI or SDK.
|
|
13
|
+
|
|
14
|
+
- **Not published yet.** Neither the CLI nor the underlying SDK is available on
|
|
15
|
+
npm. **A public release will follow** — for now you build and run it from this
|
|
16
|
+
repository (see [Installation](#installation)).
|
|
17
|
+
- **Under heavy development — expect breaking changes.** The entire CLI and its
|
|
18
|
+
API surface are actively changing. Command names, flags, output shapes, and
|
|
19
|
+
the exported SDK functions **can and will change without notice or a
|
|
20
|
+
deprecation window**. Pin to a specific commit if you need stability.
|
|
21
|
+
- **How the signer is provided will change.** Today a keypair is supplied
|
|
22
|
+
inline via `--keypair` / `OVERCAST_KEYPAIR` (see [Wallet & signing](#wallet--signing)).
|
|
23
|
+
The signing model is being reworked, so the way you provide a signer — and how
|
|
24
|
+
signing happens for RFQ quotes and accepts — **is expected to change**.
|
|
25
|
+
- **Signatures are stubbed today.** RFQ quote submission and quote acceptance
|
|
26
|
+
currently send a placeholder signature (`"0x0"`) because backend signature
|
|
27
|
+
verification is a no-op while the on-chain byte layout is being settled. For
|
|
28
|
+
the **RFQ-quoter webhook**, your endpoint returns only a premium — **no
|
|
29
|
+
signature is required**, and any signature field may be left empty or set to a
|
|
30
|
+
placeholder like `"0x0"` for now. **This part is still under construction** and
|
|
31
|
+
real signing will be wired in later.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Because nothing is published yet, run it from source within the monorepo.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# from the repo root, install workspace dependencies
|
|
41
|
+
pnpm install
|
|
42
|
+
|
|
43
|
+
# then, in this package:
|
|
44
|
+
cd packages/overcast-cli
|
|
45
|
+
|
|
46
|
+
# option A — compile with tsc to dist/ (also builds the SDK types)
|
|
47
|
+
pnpm build
|
|
48
|
+
node dist/bin.js --help
|
|
49
|
+
|
|
50
|
+
# option B — produce a single self-contained executable in dist-bundle/
|
|
51
|
+
pnpm bundle
|
|
52
|
+
./dist-bundle/overcast.js --help
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The examples below use `overcast` as shorthand for whichever of the two you run
|
|
56
|
+
(`node dist/bin.js …` or `./dist-bundle/overcast.js …`).
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Getting started: run an RFQ quoter
|
|
61
|
+
|
|
62
|
+
The quoter takes options and **pays the premium in the cash (stablecoin)
|
|
63
|
+
leg**, so before it can quote anything you need funded, deposited cash inside the
|
|
64
|
+
protocol. Work through these steps in order.
|
|
65
|
+
|
|
66
|
+
**1. Point at your network and wallet.** Set the connection + wallet once so you
|
|
67
|
+
don't repeat the flags on every command (or pass them per-command instead):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
export SOLANA_RPC_URL=http://127.0.0.1:8899 # your RPC (defaults to localhost)
|
|
71
|
+
export OVERCAST_BACKEND_URL=http://127.0.0.1:3000 # the Overcast backend (defaults to localhost)
|
|
72
|
+
export OVERCAST_KEYPAIR=~/.config/solana/id.json # a path, base58 secret key, or JSON byte array
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**2. Check which assets the protocol supports.** Only allowlisted mints can be
|
|
76
|
+
used; note the mint address of the cash asset you want to quote in:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
overcast assets
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**3. Hold the mints in your wallet.** Your wallet must already own the SPL token
|
|
83
|
+
you intend to quote with — i.e. the **cash mint** you'll pay premiums in
|
|
84
|
+
(and any underlying you plan to post). The CLI does not mint or airdrop tokens;
|
|
85
|
+
acquire them the usual way for your network (faucet/mint authority on a test
|
|
86
|
+
validator, a swap/transfer on mainnet).
|
|
87
|
+
|
|
88
|
+
**4. Deposit that cash into the protocol.** Premiums are paid from your on-chain
|
|
89
|
+
**escrow vault**, not straight from your wallet, so move funds in first. Amounts
|
|
90
|
+
are in **native (smallest) units** — e.g. `10000000` = 10 USDC at 6 decimals:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
overcast deposit --mint <cash-mint> --amount <native-units>
|
|
94
|
+
|
|
95
|
+
# confirm it landed
|
|
96
|
+
overcast balance --mint <cash-mint>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**5. Run the quoter.** With cash deposited, start quoting. The built-in
|
|
100
|
+
Black-Scholes pricer needs no extra setup:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
overcast quote
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
It connects to the backend, registers on the TAKER side, and auto-quotes every
|
|
107
|
+
incoming RFQ until you stop it with `Ctrl-C`. To price with your own strategy
|
|
108
|
+
instead, use the [webhook mode](#rfq-quoting).
|
|
109
|
+
|
|
110
|
+
> **In short:** allowlisted mint in your wallet → `deposit` cash into the vault →
|
|
111
|
+
> `quote`. Skipping the deposit is the most common reason a quoter connects
|
|
112
|
+
> but can't quote.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Configuration
|
|
117
|
+
|
|
118
|
+
Every command resolves its connection and wallet settings from **flags first,
|
|
119
|
+
then environment variables, then localhost defaults**.
|
|
120
|
+
|
|
121
|
+
| Setting | Flag | Environment variable | Default |
|
|
122
|
+
| -------------------- | ------------------------------------ | ---------------------- | --------------------------------- |
|
|
123
|
+
| Wallet keypair | `-k, --keypair <path\|base58\|json>` | `OVERCAST_KEYPAIR` | _(required for signing commands)_ |
|
|
124
|
+
| Solana RPC URL | `--rpc <url>` | `SOLANA_RPC_URL` | `http://127.0.0.1:8899` |
|
|
125
|
+
| Overcast backend URL | `--backend <url>` | `OVERCAST_BACKEND_URL` | `http://127.0.0.1:3000` |
|
|
126
|
+
| RFQ transport | `--transport <polling\|websocket>` | — | `websocket` |
|
|
127
|
+
|
|
128
|
+
These four are **global flags**: they can be passed either on the root command
|
|
129
|
+
or on any subcommand, and they show up in every command's `--help` under a
|
|
130
|
+
dedicated "Global Options" section.
|
|
131
|
+
|
|
132
|
+
### Wallet & signing
|
|
133
|
+
|
|
134
|
+
The keypair may be given three ways (all via `--keypair` or `OVERCAST_KEYPAIR`):
|
|
135
|
+
|
|
136
|
+
- a **path** to a Solana CLI `id.json` file,
|
|
137
|
+
- a raw **JSON byte array** string, or
|
|
138
|
+
- a **base58**-encoded secret key string.
|
|
139
|
+
|
|
140
|
+
A file path is read by the CLI itself, so only inlined key material is handed to
|
|
141
|
+
the client. Read-only commands (e.g. `balance`, `option show`, `assets`) work
|
|
142
|
+
without a keypair; anything that signs a transaction requires one.
|
|
143
|
+
|
|
144
|
+
> **Reminder:** this inline mechanism is temporary — see the status note above.
|
|
145
|
+
> How the signer is provided is expected to change.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Commands
|
|
150
|
+
|
|
151
|
+
Run `overcast --help`, or `overcast <command> --help`, for the authoritative,
|
|
152
|
+
always-current list.
|
|
153
|
+
|
|
154
|
+
### Vault management
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Deposit into your protocol escrow vault (amounts are in native/smallest units)
|
|
158
|
+
overcast deposit --mint <mint> --amount <n>
|
|
159
|
+
|
|
160
|
+
# Withdraw from your vault
|
|
161
|
+
overcast withdraw --mint <mint> --amount <n>
|
|
162
|
+
|
|
163
|
+
# Show a vault balance for an asset (defaults to your own wallet)
|
|
164
|
+
overcast balance --mint <mint> [--user <address>]
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Omitted `--mint` / `--amount` values are prompted for interactively.
|
|
168
|
+
|
|
169
|
+
### Discovering options & offers
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# List the backend's supported asset allowlist
|
|
173
|
+
overcast assets
|
|
174
|
+
|
|
175
|
+
# Browse curated market options
|
|
176
|
+
overcast option list [--maker <addr>] [--taker <addr>] [--asset <addr>] [--limit <n>]
|
|
177
|
+
|
|
178
|
+
# Show one option's terms and live claim state
|
|
179
|
+
overcast option show --collateral-offer <id> --settlement-offer <id>
|
|
180
|
+
|
|
181
|
+
# Browse open collateral / settlement offers
|
|
182
|
+
overcast offer list [--kind collateral|settlement|both] [--party <addr>] [--asset <addr>] [--limit <n>]
|
|
183
|
+
|
|
184
|
+
# Look up a single open offer by id
|
|
185
|
+
overcast offer show --id <id>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
A market option is identified by the **pair** of ids
|
|
189
|
+
`(collateral-offer, settlement-offer)`.
|
|
190
|
+
|
|
191
|
+
### Trading an option
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Match a collateral + settlement offer to mint an option
|
|
195
|
+
overcast accept --collateral-offer <id> --settlement-offer <id>
|
|
196
|
+
|
|
197
|
+
# Exercise an option you hold (swap settlement for collateral)
|
|
198
|
+
overcast exercise --collateral-offer <id> --settlement-offer <id> [--amount <n>]
|
|
199
|
+
|
|
200
|
+
# Redeem an option's collateral back to the maker
|
|
201
|
+
overcast redeem --collateral-offer <id> --settlement-offer <id> [--amount <n>]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### RFQ (request-for-quote)
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Create an RFQ, collect quotes live, and accept one — fully interactive
|
|
208
|
+
overcast rfq create
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`rfq create` walks you through the option in trader terms (strategy, underlying,
|
|
212
|
+
quantity, strike, expiry), broadcasts the RFQ on the **MAKER** side, streams in
|
|
213
|
+
quotes as quoters respond, and settles the one you pick on-chain.
|
|
214
|
+
|
|
215
|
+
> The quote-accept step currently sends a stubbed signature (`"0x0"`); real
|
|
216
|
+
> signing lands once the byte layout is settled.
|
|
217
|
+
|
|
218
|
+
### RFQ quoting
|
|
219
|
+
|
|
220
|
+
Run an automated quoter that auto-quotes every incoming RFQ. It acts on the
|
|
221
|
+
**TAKER** side (it takes the option and pays the premium). Two pricing modes:
|
|
222
|
+
|
|
223
|
+
**1. Built-in Black-Scholes pricer (default)**
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
overcast quote \
|
|
227
|
+
--spread 0.02 \ # edge over fair value, as a fraction (default 0.02 = 2%)
|
|
228
|
+
--vol 0.8 \ # annualized implied volatility (default 0.8 = 80%)
|
|
229
|
+
--rate 0.0 # annualized risk-free rate (default 0)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
It identifies the cash (stablecoin) leg and the volatile underlying leg, pulls a
|
|
233
|
+
spot price (CoinGecko, falling back to the backend's last-known price), and
|
|
234
|
+
quotes the fair premium plus your edge.
|
|
235
|
+
|
|
236
|
+
**2. Webhook pricer — bring your own strategy**
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
overcast quote \
|
|
240
|
+
--webhook-url http://127.0.0.1:9000/quote \
|
|
241
|
+
--webhook-timeout 5000 # per-event timeout in ms (default 5000)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The URL can also come from `OVERCAST_QUOTE_WEBHOOK_URL`. In this mode the CLI
|
|
245
|
+
handles all the RFQ plumbing and forwards every lifecycle event to your local
|
|
246
|
+
endpoint; your reply to the `newRfq` event drives the quote. See
|
|
247
|
+
[`webhook-contract/`](./webhook-contract/) for the full wire contract, a JSON
|
|
248
|
+
schema, TypeScript types, and a minimal Express handler.
|
|
249
|
+
|
|
250
|
+
> **Webhook signing is not wired yet.** Your endpoint only supplies a
|
|
251
|
+
> **premium** — the CLI stays authoritative on the rest of the option terms, and
|
|
252
|
+
> **no signature is required from your endpoint**. Where a signature field
|
|
253
|
+
> appears, it can currently be left empty or set to a placeholder like `"0x0"`.
|
|
254
|
+
> This is still under construction.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Programmatic use (SDK)
|
|
259
|
+
|
|
260
|
+
The command implementations are also exported from the package entry point so
|
|
261
|
+
they can be reused or tested outside the CLI:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import {
|
|
265
|
+
deposit,
|
|
266
|
+
rfqCreate,
|
|
267
|
+
runQuoter,
|
|
268
|
+
resolveConfig,
|
|
269
|
+
} from "@overcast-xyz/cli";
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The same pre-release caveats apply — these exports are unstable and will change.
|
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
9
|
+
const accept_1 = require("./commands/accept");
|
|
10
|
+
const assets_1 = require("./commands/assets");
|
|
11
|
+
const balance_1 = require("./commands/balance");
|
|
12
|
+
const deposit_1 = require("./commands/deposit");
|
|
13
|
+
const exercise_1 = require("./commands/exercise");
|
|
14
|
+
const key_1 = require("./commands/key");
|
|
15
|
+
const quote_1 = require("./commands/quote");
|
|
16
|
+
const offer_1 = require("./commands/offer");
|
|
17
|
+
const option_1 = require("./commands/option");
|
|
18
|
+
const redeem_1 = require("./commands/redeem");
|
|
19
|
+
const rfq_1 = require("./commands/rfq");
|
|
20
|
+
const withdraw_1 = require("./commands/withdraw");
|
|
21
|
+
/**
|
|
22
|
+
* Merge a subcommand's own options with the root program's global options
|
|
23
|
+
* (`--keypair`, `--rpc`, `--backend`, `--transport`) so either level can supply them.
|
|
24
|
+
*/
|
|
25
|
+
function withGlobals(cmd, opts) {
|
|
26
|
+
return { ...cmd.optsWithGlobals(), ...opts };
|
|
27
|
+
}
|
|
28
|
+
/** The root program's connection / wallet flags, shared by every command. */
|
|
29
|
+
const GLOBAL_FLAGS = new Set([
|
|
30
|
+
"--keypair",
|
|
31
|
+
"--rpc",
|
|
32
|
+
"--backend",
|
|
33
|
+
"--transport",
|
|
34
|
+
]);
|
|
35
|
+
/**
|
|
36
|
+
* Surface the root program's global options in every (sub)command's `--help`
|
|
37
|
+
* and tint them so they read as inherited, not command-specific.
|
|
38
|
+
*
|
|
39
|
+
* `showGlobalOptions` adds the dedicated "Global Options:" section to subcommand
|
|
40
|
+
* help (commander hides inherited options there by default). We highlight via
|
|
41
|
+
* the option *description* rather than its term: commander@12 measures column
|
|
42
|
+
* widths with raw `String.length`, so colouring a term's flags would let its
|
|
43
|
+
* ANSI codes throw the alignment off — the trailing description column is safe.
|
|
44
|
+
*/
|
|
45
|
+
function highlightGlobalOptions(root) {
|
|
46
|
+
const isGlobal = (option) => GLOBAL_FLAGS.has(option.long ?? "");
|
|
47
|
+
const apply = (cmd) => {
|
|
48
|
+
cmd.configureHelp({
|
|
49
|
+
showGlobalOptions: true,
|
|
50
|
+
optionDescription(option) {
|
|
51
|
+
const description = commander_1.Help.prototype.optionDescription.call(this, option);
|
|
52
|
+
return isGlobal(option) ? picocolors_1.default.cyan(description) : description;
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
cmd.commands.forEach(apply);
|
|
56
|
+
};
|
|
57
|
+
apply(root);
|
|
58
|
+
}
|
|
59
|
+
const program = new commander_1.Command();
|
|
60
|
+
program
|
|
61
|
+
.name("overcast")
|
|
62
|
+
.description("CLI for the Overcast Solana protocol (deposits, RFQ, RFQ quoting)")
|
|
63
|
+
.version("0.1.0")
|
|
64
|
+
.option("-k, --keypair <path|base58|json>", "wallet keypair (or OVERCAST_KEYPAIR)")
|
|
65
|
+
.option("--rpc <url>", "Solana RPC url (or SOLANA_RPC_URL)")
|
|
66
|
+
.option("--backend <url>", "Overcast backend url (or OVERCAST_BACKEND_URL)")
|
|
67
|
+
.addOption(new commander_1.Option("--transport <transport>", "RFQ client transport: polling or websocket (default websocket)").choices(["polling", "websocket"]));
|
|
68
|
+
program
|
|
69
|
+
.command("deposit")
|
|
70
|
+
.description("Deposit funds into your protocol escrow vault")
|
|
71
|
+
.option("-m, --mint <id>", "asset mint to deposit")
|
|
72
|
+
.option("-a, --amount <n>", "amount in native units")
|
|
73
|
+
.action(async function (opts) {
|
|
74
|
+
await (0, deposit_1.deposit)(withGlobals(this, opts));
|
|
75
|
+
});
|
|
76
|
+
program
|
|
77
|
+
.command("withdraw")
|
|
78
|
+
.description("Withdraw funds from your protocol escrow vault")
|
|
79
|
+
.option("-m, --mint <id>", "asset mint to withdraw")
|
|
80
|
+
.option("-a, --amount <n>", "amount in native units")
|
|
81
|
+
.action(async function (opts) {
|
|
82
|
+
await (0, withdraw_1.withdraw)(withGlobals(this, opts));
|
|
83
|
+
});
|
|
84
|
+
program
|
|
85
|
+
.command("balance")
|
|
86
|
+
.description("Show a protocol escrow vault balance for an asset")
|
|
87
|
+
.option("-m, --mint <id>", "asset mint to check")
|
|
88
|
+
.option("-u, --user <id>", "vault owner to inspect (defaults to your wallet)")
|
|
89
|
+
.action(async function (opts) {
|
|
90
|
+
await (0, balance_1.balance)(withGlobals(this, opts));
|
|
91
|
+
});
|
|
92
|
+
const key = program
|
|
93
|
+
.command("key")
|
|
94
|
+
.description("Manage signing keys authorized to act for your wallet");
|
|
95
|
+
key
|
|
96
|
+
.command("add")
|
|
97
|
+
.description("Authorize a key to sign operations on your wallet's behalf")
|
|
98
|
+
.argument("[pubkey]", "public key to authorize")
|
|
99
|
+
.action(async function (pubkey, opts) {
|
|
100
|
+
await (0, key_1.keyAdd)(withGlobals(this, { ...opts, key: pubkey }));
|
|
101
|
+
});
|
|
102
|
+
key
|
|
103
|
+
.command("remove")
|
|
104
|
+
.description("Revoke a previously authorized signing key")
|
|
105
|
+
.argument("[pubkey]", "public key to revoke")
|
|
106
|
+
.action(async function (pubkey, opts) {
|
|
107
|
+
await (0, key_1.keyRemove)(withGlobals(this, { ...opts, key: pubkey }));
|
|
108
|
+
});
|
|
109
|
+
const option = program.command("option").description("Inspect market options");
|
|
110
|
+
option
|
|
111
|
+
.command("show")
|
|
112
|
+
.description("Show an option's terms and live claim state")
|
|
113
|
+
.option("-c, --collateral-offer <id>", "collateral offer id")
|
|
114
|
+
.option("-s, --settlement-offer <id>", "settlement offer id")
|
|
115
|
+
.action(async function (opts) {
|
|
116
|
+
await (0, option_1.optionShow)(withGlobals(this, opts));
|
|
117
|
+
});
|
|
118
|
+
option
|
|
119
|
+
.command("list")
|
|
120
|
+
.description("Browse curated market options from the backend")
|
|
121
|
+
.option("-m, --maker <id>", "filter by maker address")
|
|
122
|
+
.option("-t, --taker <id>", "filter by taker address")
|
|
123
|
+
.option("-a, --asset <id>", "filter by any asset leg")
|
|
124
|
+
.option("-n, --limit <n>", "max rows to return")
|
|
125
|
+
.action(async function (opts) {
|
|
126
|
+
await (0, option_1.optionList)(withGlobals(this, opts));
|
|
127
|
+
});
|
|
128
|
+
const offer = program.command("offer").description("Inspect open offers");
|
|
129
|
+
offer
|
|
130
|
+
.command("show")
|
|
131
|
+
.description("Look up an open collateral or settlement offer by id")
|
|
132
|
+
.option("-i, --id <id>", "offer id")
|
|
133
|
+
.action(async function (opts) {
|
|
134
|
+
await (0, offer_1.offerShow)(withGlobals(this, opts));
|
|
135
|
+
});
|
|
136
|
+
offer
|
|
137
|
+
.command("list")
|
|
138
|
+
.description("Browse open collateral / settlement offers from the backend")
|
|
139
|
+
.option("-k, --kind <kind>", "collateral, settlement, or both (default)")
|
|
140
|
+
.option("-p, --party <id>", "filter by counterparty (maker/taker)")
|
|
141
|
+
.option("-a, --asset <id>", "filter by any asset leg")
|
|
142
|
+
.option("-n, --limit <n>", "max rows")
|
|
143
|
+
.action(async function (opts) {
|
|
144
|
+
await (0, offer_1.offerList)(withGlobals(this, opts));
|
|
145
|
+
});
|
|
146
|
+
program
|
|
147
|
+
.command("accept")
|
|
148
|
+
.description("Match a collateral and settlement offer to mint an option")
|
|
149
|
+
.option("-c, --collateral-offer <id>", "collateral offer id")
|
|
150
|
+
.option("-s, --settlement-offer <id>", "settlement offer id")
|
|
151
|
+
.action(async function (opts) {
|
|
152
|
+
await (0, accept_1.accept)(withGlobals(this, opts));
|
|
153
|
+
});
|
|
154
|
+
program
|
|
155
|
+
.command("exercise")
|
|
156
|
+
.description("Exercise an option you hold (swap settlement for collateral)")
|
|
157
|
+
.option("-c, --collateral-offer <id>", "collateral offer id")
|
|
158
|
+
.option("-s, --settlement-offer <id>", "settlement offer id")
|
|
159
|
+
.option("-a, --amount <n>", "exercise-claims to burn (native units)")
|
|
160
|
+
.action(async function (opts) {
|
|
161
|
+
await (0, exercise_1.exercise)(withGlobals(this, opts));
|
|
162
|
+
});
|
|
163
|
+
program
|
|
164
|
+
.command("redeem")
|
|
165
|
+
.description("Redeem an option's collateral back to the maker")
|
|
166
|
+
.option("-c, --collateral-offer <id>", "collateral offer id")
|
|
167
|
+
.option("-s, --settlement-offer <id>", "settlement offer id")
|
|
168
|
+
.option("-a, --amount <n>", "collateral-return claims to burn (native units)")
|
|
169
|
+
.action(async function (opts) {
|
|
170
|
+
await (0, redeem_1.redeem)(withGlobals(this, opts));
|
|
171
|
+
});
|
|
172
|
+
const rfq = program.command("rfq").description("Request-for-quote operations");
|
|
173
|
+
rfq
|
|
174
|
+
.command("create")
|
|
175
|
+
.description("Create an RFQ, collect quotes, and accept one")
|
|
176
|
+
.action(async function (opts) {
|
|
177
|
+
await (0, rfq_1.rfqCreate)(withGlobals(this, opts));
|
|
178
|
+
});
|
|
179
|
+
program
|
|
180
|
+
.command("assets")
|
|
181
|
+
.description("List the backend's supported asset allowlist")
|
|
182
|
+
.action(async function (opts) {
|
|
183
|
+
await (0, assets_1.assetsList)(withGlobals(this, opts));
|
|
184
|
+
});
|
|
185
|
+
program
|
|
186
|
+
.command("quote")
|
|
187
|
+
.description("Run an RFQ quoter that auto-quotes incoming RFQs (Black-Scholes, or a webhook)")
|
|
188
|
+
.option("-s, --spread <pct>", "quoter edge over fair value, as a fraction (default 0.02)")
|
|
189
|
+
.option("--vol <sigma>", "annualized implied volatility, e.g. 0.8 (default 0.8)")
|
|
190
|
+
.option("--rate <r>", "annualized risk-free rate, e.g. 0.05 (default 0)")
|
|
191
|
+
.option("--webhook-url <url>", "POST each RFQ event to this local URL; its reply drives the quote (or OVERCAST_QUOTE_WEBHOOK_URL)")
|
|
192
|
+
.option("--webhook-timeout <ms>", "per-event webhook timeout in ms (default 5000)")
|
|
193
|
+
.action(async function (opts) {
|
|
194
|
+
await (0, quote_1.runQuoter)(withGlobals(this, opts));
|
|
195
|
+
});
|
|
196
|
+
highlightGlobalOptions(program);
|
|
197
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
198
|
+
console.error(picocolors_1.default.red(err.message));
|
|
199
|
+
process.exit(1);
|
|
200
|
+
});
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { OvercastApp } from "@overcast-xyz/core";
|
|
2
|
+
import { SolanaLayer } from "@overcast-xyz/solana-client";
|
|
3
|
+
import { CliConfig } from "./config";
|
|
4
|
+
/**
|
|
5
|
+
* Build the CLI's {@link OvercastClient} from {@link CliConfig}.
|
|
6
|
+
*
|
|
7
|
+
* The config is mapped onto a {@link SolanaConfig} and handed to the
|
|
8
|
+
* {@link SolanaFactory}; everything the app exposes — signer, transaction
|
|
9
|
+
* writer, on-chain reader and RFQ channel — is then derived from that single
|
|
10
|
+
* config by {@link OvercastApp.create}.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createClient(config: CliConfig): Promise<OvercastApp<SolanaLayer>>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createClient = createClient;
|
|
4
|
+
const core_1 = require("@overcast-xyz/core");
|
|
5
|
+
const solana_client_1 = require("@overcast-xyz/solana-client");
|
|
6
|
+
const config_1 = require("./config");
|
|
7
|
+
/**
|
|
8
|
+
* Build the CLI's {@link OvercastClient} from {@link CliConfig}.
|
|
9
|
+
*
|
|
10
|
+
* The config is mapped onto a {@link SolanaConfig} and handed to the
|
|
11
|
+
* {@link SolanaFactory}; everything the app exposes — signer, transaction
|
|
12
|
+
* writer, on-chain reader and RFQ channel — is then derived from that single
|
|
13
|
+
* config by {@link OvercastApp.create}.
|
|
14
|
+
*/
|
|
15
|
+
function createClient(config) {
|
|
16
|
+
const factory = new solana_client_1.SolanaFactory((0, config_1.toSolanaConfig)(config));
|
|
17
|
+
return core_1.OvercastApp.create({ factory });
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GlobalFlags } from "../config";
|
|
2
|
+
export interface AcceptOptions extends GlobalFlags {
|
|
3
|
+
collateralOffer?: string;
|
|
4
|
+
settlementOffer?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* `overcast accept` — match an existing collateral offer with a settlement
|
|
8
|
+
* offer to mint the option on-chain. Both offers are looked up by their
|
|
9
|
+
* content-address ids and shown for review before submitting.
|
|
10
|
+
*/
|
|
11
|
+
export declare function accept(opts: AcceptOptions): Promise<void>;
|
|
@@ -0,0 +1,112 @@
|
|
|
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.accept = accept;
|
|
40
|
+
const p = __importStar(require("@clack/prompts"));
|
|
41
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
42
|
+
const client_1 = require("../client");
|
|
43
|
+
const config_1 = require("../config");
|
|
44
|
+
const shared_1 = require("./shared");
|
|
45
|
+
/**
|
|
46
|
+
* `overcast accept` — match an existing collateral offer with a settlement
|
|
47
|
+
* offer to mint the option on-chain. Both offers are looked up by their
|
|
48
|
+
* content-address ids and shown for review before submitting.
|
|
49
|
+
*/
|
|
50
|
+
async function accept(opts) {
|
|
51
|
+
const config = (0, config_1.resolveConfig)(opts);
|
|
52
|
+
const app = await (0, client_1.createClient)(config);
|
|
53
|
+
const registry = await (0, shared_1.loadRegistry)(app.api);
|
|
54
|
+
for (const [flag, value] of [
|
|
55
|
+
["--collateral-offer", opts.collateralOffer],
|
|
56
|
+
["--settlement-offer", opts.settlementOffer],
|
|
57
|
+
]) {
|
|
58
|
+
if (value && (0, shared_1.validateAddress)(value)) {
|
|
59
|
+
throw new Error(`invalid ${flag} id: ${value}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
p.intro(picocolors_1.default.cyan("Overcast accept"));
|
|
63
|
+
const collateralId = opts.collateralOffer ?? (await (0, shared_1.promptId)("Collateral offer id"));
|
|
64
|
+
const settlementId = opts.settlementOffer ?? (await (0, shared_1.promptId)("Settlement offer id"));
|
|
65
|
+
const spinner = p.spinner();
|
|
66
|
+
spinner.start("Looking up offers");
|
|
67
|
+
// Offers now share one namespace, so an id could resolve to either side; the
|
|
68
|
+
// match is only valid with a maker-side collateral offer and a taker-side
|
|
69
|
+
// settlement offer, so require each to be the expected side.
|
|
70
|
+
const [collateralOffer, settlementOffer] = await Promise.all([
|
|
71
|
+
app.chain.getOffer(collateralId),
|
|
72
|
+
app.chain.getOffer(settlementId),
|
|
73
|
+
]);
|
|
74
|
+
if (!collateralOffer || collateralOffer.side !== "MAKER") {
|
|
75
|
+
spinner.stop(picocolors_1.default.red(`No maker (collateral) offer found for ${(0, shared_1.short)(collateralId)}.`));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!settlementOffer || settlementOffer.side !== "TAKER") {
|
|
79
|
+
spinner.stop(picocolors_1.default.red(`No taker (settlement) offer found for ${(0, shared_1.short)(settlementId)}.`));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
spinner.stop("Offers found.");
|
|
83
|
+
// Both offers describe the same option terms; show the collateral leg's view
|
|
84
|
+
// plus the two counterparties so the match is reviewable.
|
|
85
|
+
p.note([
|
|
86
|
+
`Maker ${(0, shared_1.short)(collateralOffer.creator)}`,
|
|
87
|
+
`Taker ${(0, shared_1.short)(settlementOffer.creator)}`,
|
|
88
|
+
"",
|
|
89
|
+
(0, shared_1.describeOptionDetails)(collateralOffer.details, registry),
|
|
90
|
+
].join("\n"), "Match");
|
|
91
|
+
if (!(await (0, shared_1.confirm)("Accept this match and mint the option?"))) {
|
|
92
|
+
p.outro(picocolors_1.default.yellow("Cancelled."));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const submit = p.spinner();
|
|
96
|
+
submit.start("Accepting offer and minting option on-chain");
|
|
97
|
+
try {
|
|
98
|
+
const { hash } = await app.chain.acceptOffer(settlementOffer, collateralOffer);
|
|
99
|
+
submit.stop(picocolors_1.default.green(`Option minted. tx ${picocolors_1.default.bold(hash)}`));
|
|
100
|
+
// Surface the id pair so the new option can be inspected / exercised.
|
|
101
|
+
p.note([
|
|
102
|
+
`overcast option show \\`,
|
|
103
|
+
` --collateral-offer ${collateralId} \\`,
|
|
104
|
+
` --settlement-offer ${settlementId}`,
|
|
105
|
+
].join("\n"), "Inspect the option");
|
|
106
|
+
p.outro(picocolors_1.default.green("Done."));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
submit.stop(picocolors_1.default.red(`Accept failed: ${err.message}`));
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { GlobalFlags } from "../config";
|
|
2
|
+
export type AssetsOptions = GlobalFlags;
|
|
3
|
+
/**
|
|
4
|
+
* `overcast assets` — list the backend's asset allowlist via the off-chain
|
|
5
|
+
* {@link OvercastView}. This is the set of assets the protocol will accept on an
|
|
6
|
+
* RFQ / quote / offer, and the metadata every amount elsewhere is formatted
|
|
7
|
+
* against. Read-only: no wallet required.
|
|
8
|
+
*/
|
|
9
|
+
export declare function assetsList(opts: AssetsOptions): Promise<void>;
|