@perpscope/percolator-adapter 0.9.0 → 1.0.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 +4 -1
- package/bin/perpscope.mjs +106 -3
- package/package.json +1 -1
- package/src/lib/percolator-adapter.js +1 -1
package/README.md
CHANGED
|
@@ -72,12 +72,15 @@ The full field-level contract is documented in `../../docs/field-compatibility-m
|
|
|
72
72
|
## CLI
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
+
perpscope init perpscope.capture.json
|
|
75
76
|
perpscope compat report capture.json
|
|
76
77
|
perpscope compat diff previous.json current.json
|
|
77
|
-
perpscope compat doctor capture.json
|
|
78
|
+
perpscope compat doctor capture.json --strict
|
|
78
79
|
perpscope compat badge capture.json --json
|
|
79
80
|
```
|
|
80
81
|
|
|
82
|
+
Doctor exit codes are CI-ready: `0` means required fields pass, `1` means rejected or required fields missing, and `2` means strict mode found useful-field gaps, unknown fields, or alias suggestions.
|
|
83
|
+
|
|
81
84
|
Try it locally with:
|
|
82
85
|
|
|
83
86
|
```bash
|
package/bin/perpscope.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import {
|
|
4
4
|
buildCompatibilityBadge,
|
|
5
5
|
buildCompatibilityDoctor,
|
|
@@ -13,19 +13,104 @@ import {
|
|
|
13
13
|
} from "../index.js";
|
|
14
14
|
|
|
15
15
|
const [, , ...args] = process.argv;
|
|
16
|
+
const CAPTURE_TEMPLATE = {
|
|
17
|
+
label: "My terminal read-only capture",
|
|
18
|
+
cluster: "mainnet-beta",
|
|
19
|
+
market: {
|
|
20
|
+
symbol: "SOL-PERP",
|
|
21
|
+
base: "SOL",
|
|
22
|
+
quote: "USDC",
|
|
23
|
+
slab: "PERCOLAT_SOL_...",
|
|
24
|
+
program: "Perco1ator111111111111111111111111111111111"
|
|
25
|
+
},
|
|
26
|
+
oracle: {
|
|
27
|
+
priceUsd: 181.61,
|
|
28
|
+
ageSecs: 2
|
|
29
|
+
},
|
|
30
|
+
engine: {
|
|
31
|
+
currentSlot: 346892118,
|
|
32
|
+
lastMarketSlot: 346892090,
|
|
33
|
+
fundingRateBpsPerHour: 0.82,
|
|
34
|
+
openInterestUsd: 2430000,
|
|
35
|
+
longOpenInterestUsd: 1320000,
|
|
36
|
+
shortOpenInterestUsd: 1110000,
|
|
37
|
+
insuranceUsd: 148000,
|
|
38
|
+
stressConsumedBps: 118,
|
|
39
|
+
stressLimitBps: 500
|
|
40
|
+
},
|
|
41
|
+
execution: {
|
|
42
|
+
bestBid: 181.52,
|
|
43
|
+
bestAsk: 181.71,
|
|
44
|
+
receipts: [
|
|
45
|
+
{
|
|
46
|
+
label: "latest fill",
|
|
47
|
+
sourceTimestamp: "2026-06-20T13:24:12Z",
|
|
48
|
+
spreadBps: 10.5,
|
|
49
|
+
impactBps: 8.4,
|
|
50
|
+
markout1mBps: 4.2,
|
|
51
|
+
markout5mBps: -1.7,
|
|
52
|
+
routeLatencyMs: 132,
|
|
53
|
+
priorityFeeMicrolamports: 2200
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
account: {
|
|
58
|
+
side: "long",
|
|
59
|
+
positionSize: 420,
|
|
60
|
+
positionNotionalUsd: 76276.2,
|
|
61
|
+
collateralUsd: 8400,
|
|
62
|
+
unrealizedPnlUsd: 3067.2,
|
|
63
|
+
liquidationPrice: 162.94
|
|
64
|
+
},
|
|
65
|
+
history: {
|
|
66
|
+
fundingSkew: [
|
|
67
|
+
{
|
|
68
|
+
sourceTimestamp: "2026-06-20T13:24:00Z",
|
|
69
|
+
slot: 346892086,
|
|
70
|
+
fundingBpsPerHour: 0.82,
|
|
71
|
+
longOpenInterestUsd: 1320000,
|
|
72
|
+
shortOpenInterestUsd: 1110000,
|
|
73
|
+
stressConsumedBps: 118,
|
|
74
|
+
stressLimitBps: 500,
|
|
75
|
+
oracleAgeSec: 2.1
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
};
|
|
16
80
|
|
|
17
81
|
function usage() {
|
|
18
82
|
return [
|
|
19
83
|
"Usage:",
|
|
84
|
+
" perpscope init [output.json] [--force]",
|
|
20
85
|
" perpscope compat report <capture.json>",
|
|
21
86
|
" perpscope compat diff <previous.json> <current.json>",
|
|
22
|
-
" perpscope compat doctor <capture.json>",
|
|
87
|
+
" perpscope compat doctor <capture.json> [--strict|--json]",
|
|
23
88
|
" perpscope compat badge <capture.json> [--json|--markdown]",
|
|
24
89
|
"",
|
|
25
90
|
"Read-only only: the adapter rejects wallet, signer, transaction, instruction, order, private key, seed, mnemonic, and API key fields."
|
|
26
91
|
].join("\n");
|
|
27
92
|
}
|
|
28
93
|
|
|
94
|
+
function initMessage(path) {
|
|
95
|
+
return [
|
|
96
|
+
`Created ${path}`,
|
|
97
|
+
"",
|
|
98
|
+
"Next:",
|
|
99
|
+
` perpscope compat doctor ${path}`,
|
|
100
|
+
` perpscope compat badge ${path}`,
|
|
101
|
+
"",
|
|
102
|
+
"Edit the capture with sanitized read-only decoded state before sharing it."
|
|
103
|
+
].join("\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function initCapture(path = "perpscope.capture.json", options = {}) {
|
|
107
|
+
if (existsSync(path) && !options.force) {
|
|
108
|
+
throw new Error(`${path} already exists. Use --force to overwrite.`);
|
|
109
|
+
}
|
|
110
|
+
writeFileSync(path, `${JSON.stringify(CAPTURE_TEMPLATE, null, 2)}\n`);
|
|
111
|
+
return path;
|
|
112
|
+
}
|
|
113
|
+
|
|
29
114
|
function readCapture(path) {
|
|
30
115
|
if (!path) throw new Error("Missing capture path.");
|
|
31
116
|
return parsePercolatorJson(readFileSync(path, "utf8"));
|
|
@@ -56,12 +141,28 @@ function formatDoctor(doctor) {
|
|
|
56
141
|
return lines.join("\n");
|
|
57
142
|
}
|
|
58
143
|
|
|
144
|
+
function doctorExitCode(doctor, options = {}) {
|
|
145
|
+
if (doctor.status === "rejected" || doctor.required.mapped < doctor.required.total) return 1;
|
|
146
|
+
if (options.strict && (
|
|
147
|
+
doctor.useful.mapped < doctor.useful.total ||
|
|
148
|
+
doctor.unknownFields.length ||
|
|
149
|
+
doctor.aliasSuggestions.length
|
|
150
|
+
)) return 2;
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
59
154
|
function main() {
|
|
60
155
|
const [scope, command, ...rest] = args;
|
|
61
156
|
if (!scope || scope === "--help" || scope === "-h") {
|
|
62
157
|
console.log(usage());
|
|
63
158
|
return;
|
|
64
159
|
}
|
|
160
|
+
if (scope === "init") {
|
|
161
|
+
const output = command && !command.startsWith("--") ? command : "perpscope.capture.json";
|
|
162
|
+
const flags = [command, ...rest].filter(Boolean);
|
|
163
|
+
console.log(initMessage(initCapture(output, { force: flags.includes("--force") })));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
65
166
|
if (scope !== "compat") throw new Error(`Unknown scope: ${scope}`);
|
|
66
167
|
if (command === "report") {
|
|
67
168
|
const input = readCapture(rest[0]);
|
|
@@ -76,7 +177,9 @@ function main() {
|
|
|
76
177
|
}
|
|
77
178
|
if (command === "doctor") {
|
|
78
179
|
const input = readCapture(rest[0]);
|
|
79
|
-
|
|
180
|
+
const doctor = buildCompatibilityDoctor(buildReport(input), { input });
|
|
181
|
+
console.log(rest.includes("--json") ? JSON.stringify(doctor, null, 2) : formatDoctor(doctor));
|
|
182
|
+
process.exitCode = doctorExitCode(doctor, { strict: rest.includes("--strict") });
|
|
80
183
|
return;
|
|
81
184
|
}
|
|
82
185
|
if (command === "badge") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normalizeFundingSkewHistory } from "./funding-history.js";
|
|
2
2
|
|
|
3
|
-
export const PERPSCOPE_ADAPTER_VERSION = "0.
|
|
3
|
+
export const PERPSCOPE_ADAPTER_VERSION = "1.0.0";
|
|
4
4
|
|
|
5
5
|
const KEYPAIR_FIELD_PATTERN = /(^|_)(secret|private|keypair|mnemonic|seed|walletPath|wallet)(_|$)/i;
|
|
6
6
|
const HISTORY_COMMAND_KEYS = new Set([
|