nebulon-escrow-cli 0.8.4 → 0.8.5
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 +95 -4
- package/package.json +1 -1
- package/src/commands/contract.js +68 -12
- package/src/commands/init.js +9 -15
- package/src/constants.js +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,105 @@
|
|
|
1
1
|
# Nebulon CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Nebulon CLI is the command-line interface for managing Nebulon escrow contracts,
|
|
4
|
+
wallets, capsules, and hosted profile actions. It supports MagicBlock ephemeral
|
|
5
|
+
rollups for contract operations and provides end-to-end contract workflows,
|
|
6
|
+
including terms, milestones, funding, and claims.
|
|
4
7
|
|
|
8
|
+
## Requirements
|
|
9
|
+
|
|
10
|
+
- Node.js 18 or newer
|
|
11
|
+
- npm 9 or newer
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Global install from npm:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g nebulon-escrow-cli
|
|
5
19
|
```
|
|
20
|
+
|
|
21
|
+
From source:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
6
24
|
cd CLI
|
|
7
25
|
npm install
|
|
8
26
|
npm link
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
9
32
|
nebulon init
|
|
33
|
+
nebulon login
|
|
34
|
+
nebulon status
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The `init` flow sets up a capsule, wallet, and network defaults. `login` connects
|
|
38
|
+
to hosted services. Use `status` to verify connectivity and balances.
|
|
39
|
+
|
|
40
|
+
## Command overview
|
|
41
|
+
|
|
42
|
+
Run `nebulon help` for the full list. Common commands:
|
|
43
|
+
|
|
44
|
+
- `nebulon init` - interactive setup (banner shows the CLI version)
|
|
45
|
+
- `nebulon status` - account summary
|
|
46
|
+
- `nebulon login` / `nebulon logout`
|
|
47
|
+
- `nebulon capsule list` / `nebulon capsule use <name>`
|
|
48
|
+
- `nebulon contract ...` - create and manage escrow contracts
|
|
49
|
+
- `nebulon invite list` / `nebulon invite <id>`
|
|
50
|
+
- `nebulon balance` / `nebulon address`
|
|
51
|
+
- `nebulon wallet export`
|
|
52
|
+
- `nebulon config` / `nebulon config <key> <value>`
|
|
53
|
+
|
|
54
|
+
### Contract commands (examples)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
nebulon contract list
|
|
58
|
+
nebulon contract <id> details
|
|
59
|
+
nebulon contract <id> add term payment 20
|
|
60
|
+
nebulon contract <id> add term deadline 8d
|
|
61
|
+
nebulon contract <id> add milestone "Draft spec"
|
|
62
|
+
nebulon contract <id> sign
|
|
63
|
+
nebulon contract <id> fund
|
|
64
|
+
nebulon contract <id> milestone 1 ready
|
|
65
|
+
nebulon contract <id> milestone 1 confirm
|
|
66
|
+
nebulon contract <id> claim_funds
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Verbose diagnostics
|
|
70
|
+
|
|
71
|
+
Use `--verbose` on contract commands to show additional progress and routing
|
|
72
|
+
details:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
nebulon contract <id> fund --verbose
|
|
10
76
|
```
|
|
11
77
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
78
|
+
## Configuration and storage
|
|
79
|
+
|
|
80
|
+
By default, configuration and wallets are stored under:
|
|
81
|
+
|
|
82
|
+
- Windows: `%USERPROFILE%\\.nebulon\\`
|
|
83
|
+
- macOS/Linux: `~/.nebulon/`
|
|
84
|
+
|
|
85
|
+
Capsules live under `~/.nebulon/capsules/`. You can override the base directory
|
|
86
|
+
with the `NEBULON_HOME` environment variable.
|
|
87
|
+
|
|
88
|
+
Wallet import and export:
|
|
89
|
+
|
|
90
|
+
- Import expects keypair files in `C:\\Nebulon\\Wallets` (Windows).
|
|
91
|
+
- Export writes the active wallet to `C:\\Nebulon\\Wallets`.
|
|
92
|
+
|
|
93
|
+
## Version
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
nebulon --version
|
|
97
|
+
nebulon -v
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Troubleshooting
|
|
101
|
+
|
|
102
|
+
- If `login` or hosted actions fail, re-run `nebulon config` and confirm backend
|
|
103
|
+
URLs and network settings.
|
|
104
|
+
- If RPC calls fail, verify the configured `rpcUrl` and `wsUrl`.
|
|
105
|
+
- Use `nebulon contract ... --verbose` for detailed progress output.
|
package/package.json
CHANGED
package/src/commands/contract.js
CHANGED
|
@@ -165,6 +165,9 @@ const getEphemeralProgram = async (config, signerKeypair, options = {}) => {
|
|
|
165
165
|
const getDelegationValidator = async (config) => {
|
|
166
166
|
const endpoint = (config.ephemeralProviderUrl || "").toLowerCase();
|
|
167
167
|
const wsEndpoint = (config.ephemeralWsUrl || "").toLowerCase();
|
|
168
|
+
if (config && config.__verbose) {
|
|
169
|
+
console.log("DEBUG: delegation resolver endpoints", { endpoint, wsEndpoint });
|
|
170
|
+
}
|
|
168
171
|
if (endpoint.includes("localhost") || endpoint.includes("127.0.0.1")) {
|
|
169
172
|
return LOCAL_VALIDATOR_IDENTITY;
|
|
170
173
|
}
|
|
@@ -185,6 +188,9 @@ const getDelegationValidator = async (config) => {
|
|
|
185
188
|
if (shouldUseRouter && config.ephemeralProviderUrl) {
|
|
186
189
|
const cacheKey = `${endpoint}|${wsEndpoint}`;
|
|
187
190
|
if (cachedValidator && cachedValidatorEndpoint === cacheKey) {
|
|
191
|
+
if (config && config.__verbose) {
|
|
192
|
+
console.log("DEBUG: using cached validator", cachedValidator.toBase58());
|
|
193
|
+
}
|
|
188
194
|
return cachedValidator;
|
|
189
195
|
}
|
|
190
196
|
try {
|
|
@@ -193,7 +199,7 @@ const getDelegationValidator = async (config) => {
|
|
|
193
199
|
});
|
|
194
200
|
const closest = await router.getClosestValidator();
|
|
195
201
|
if (config && config.__verbose) {
|
|
196
|
-
console.log("
|
|
202
|
+
console.log("DEBUG: router closest validator:", closest);
|
|
197
203
|
}
|
|
198
204
|
const identity =
|
|
199
205
|
closest?.validatorIdentity || closest?.identity || closest?.validator;
|
|
@@ -209,8 +215,14 @@ const getDelegationValidator = async (config) => {
|
|
|
209
215
|
}
|
|
210
216
|
const fallback = safePublicKey(config.ephemeralValidatorIdentity);
|
|
211
217
|
if (fallback) {
|
|
218
|
+
if (config && config.__verbose) {
|
|
219
|
+
console.log("DEBUG: using configured validator identity", fallback.toBase58());
|
|
220
|
+
}
|
|
212
221
|
return fallback;
|
|
213
222
|
}
|
|
223
|
+
if (config && config.__verbose) {
|
|
224
|
+
console.warn("DEBUG: no validator identity resolved.");
|
|
225
|
+
}
|
|
214
226
|
return null;
|
|
215
227
|
};
|
|
216
228
|
|
|
@@ -1067,6 +1079,13 @@ const ensureDelegatedAccount = async (
|
|
|
1067
1079
|
options
|
|
1068
1080
|
) => {
|
|
1069
1081
|
const info = await program.provider.connection.getAccountInfo(pda, "confirmed");
|
|
1082
|
+
if (isVerbose(options)) {
|
|
1083
|
+
console.log("DEBUG: delegation check", {
|
|
1084
|
+
pda: pda.toBase58(),
|
|
1085
|
+
owner: info?.owner?.toBase58?.(),
|
|
1086
|
+
delegated: Boolean(info && info.owner.equals(DELEGATION_PROGRAM_ID)),
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1070
1089
|
if (info && info.owner.equals(DELEGATION_PROGRAM_ID)) {
|
|
1071
1090
|
if (isVerbose(options)) {
|
|
1072
1091
|
console.log(`Delegation already active for ${pda.toBase58()}; skipping.`);
|
|
@@ -1124,6 +1143,13 @@ const ensureDelegatedEscrow = async (
|
|
|
1124
1143
|
escrowPda,
|
|
1125
1144
|
"confirmed"
|
|
1126
1145
|
);
|
|
1146
|
+
if (isVerbose(options)) {
|
|
1147
|
+
console.log("DEBUG: escrow delegation check", {
|
|
1148
|
+
escrow: escrowPda.toBase58(),
|
|
1149
|
+
owner: info?.owner?.toBase58?.(),
|
|
1150
|
+
delegated: Boolean(info && info.owner.equals(DELEGATION_PROGRAM_ID)),
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1127
1153
|
if (info && info.owner.equals(DELEGATION_PROGRAM_ID)) {
|
|
1128
1154
|
if (isVerbose(options)) {
|
|
1129
1155
|
console.log(`Delegation already active for ${escrowPda.toBase58()}; skipping.`);
|
|
@@ -2210,6 +2236,27 @@ const runSignContract = async (config, contract, options) => {
|
|
|
2210
2236
|
}
|
|
2211
2237
|
|
|
2212
2238
|
console.log("Processing.");
|
|
2239
|
+
if (isVerbose(options)) {
|
|
2240
|
+
console.log("DEBUG: PER config", {
|
|
2241
|
+
ephemeralProviderUrl: config.ephemeralProviderUrl,
|
|
2242
|
+
ephemeralWsUrl: config.ephemeralWsUrl,
|
|
2243
|
+
ephemeralValidatorIdentity: config.ephemeralValidatorIdentity,
|
|
2244
|
+
rpcUrl: config.rpcUrl,
|
|
2245
|
+
wsUrl: config.wsUrl,
|
|
2246
|
+
network: config.network,
|
|
2247
|
+
});
|
|
2248
|
+
try {
|
|
2249
|
+
if (config.ephemeralProviderUrl) {
|
|
2250
|
+
const router = new ConnectionMagicRouter(config.ephemeralProviderUrl, {
|
|
2251
|
+
wsEndpoint: config.ephemeralWsUrl || undefined,
|
|
2252
|
+
});
|
|
2253
|
+
const closest = await router.getClosestValidator();
|
|
2254
|
+
console.log("DEBUG: router closest validator (sign flow):", closest);
|
|
2255
|
+
}
|
|
2256
|
+
} catch (error) {
|
|
2257
|
+
console.log("DEBUG: router closest validator lookup failed:", error?.message || error);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2213
2260
|
logProgress("Submitting signature on ER");
|
|
2214
2261
|
let signSig = null;
|
|
2215
2262
|
let commitSig = null;
|
|
@@ -2228,17 +2275,26 @@ const runSignContract = async (config, contract, options) => {
|
|
|
2228
2275
|
programId,
|
|
2229
2276
|
program.provider
|
|
2230
2277
|
);
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2278
|
+
try {
|
|
2279
|
+
signSig = await erProgram.methods
|
|
2280
|
+
.signPrivateTerms(new anchor.BN(escrowId.toString()))
|
|
2281
|
+
.accounts({
|
|
2282
|
+
user: keypair.publicKey,
|
|
2283
|
+
payer: sessionSigner.publicKey,
|
|
2284
|
+
sessionToken: sessionPda,
|
|
2285
|
+
escrow: escrowPda,
|
|
2286
|
+
terms: termsPda,
|
|
2287
|
+
})
|
|
2288
|
+
.signers([sessionSigner])
|
|
2289
|
+
.rpc({ skipPreflight: true });
|
|
2290
|
+
} catch (error) {
|
|
2291
|
+
console.error("ERROR: signPrivateTerms failed:", error?.message || error);
|
|
2292
|
+
const msg = (error?.message || "").toLowerCase();
|
|
2293
|
+
if (msg.includes("invalidwritableaccount")) {
|
|
2294
|
+
console.error("HINT: InvalidWritableAccount usually means validator mismatch. Run with --verbose and compare router validator vs delegation.");
|
|
2295
|
+
}
|
|
2296
|
+
throw error;
|
|
2297
|
+
}
|
|
2242
2298
|
logTx("Signature submitted.", signSig, true);
|
|
2243
2299
|
try {
|
|
2244
2300
|
logProgress("Committing terms on ER");
|
package/src/commands/init.js
CHANGED
|
@@ -140,21 +140,15 @@ const runInit = async (options = {}, capsuleName) => {
|
|
|
140
140
|
let backendAnswer = { backendUrl: config.backendUrl };
|
|
141
141
|
let backendSource = config.backendSource || "custom";
|
|
142
142
|
let backendNetwork = null;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
name: "server",
|
|
149
|
-
message: "
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
{ name: "custom", message: "Custom server" },
|
|
153
|
-
],
|
|
154
|
-
});
|
|
155
|
-
} catch (error) {
|
|
156
|
-
serverChoice = { server: "custom" };
|
|
157
|
-
}
|
|
143
|
+
const serverChoice = await prompt({
|
|
144
|
+
type: "select",
|
|
145
|
+
name: "server",
|
|
146
|
+
message: "Use official server or custom?",
|
|
147
|
+
choices: [
|
|
148
|
+
{ name: "official", message: "Official server" },
|
|
149
|
+
{ name: "custom", message: "Custom server" },
|
|
150
|
+
],
|
|
151
|
+
});
|
|
158
152
|
|
|
159
153
|
if (serverChoice.server === "custom") {
|
|
160
154
|
backendAnswer = await prompt({
|
package/src/constants.js
CHANGED
|
@@ -26,7 +26,7 @@ const DEFAULT_CONFIG = {
|
|
|
26
26
|
rpcUrl: NETWORK_PRESETS.localnet.rpcUrl,
|
|
27
27
|
wsUrl: NETWORK_PRESETS.localnet.wsUrl,
|
|
28
28
|
programId: PROGRAM_ID_DEFAULT,
|
|
29
|
-
backendUrl: "http://
|
|
29
|
+
backendUrl: "http://174.138.42.117:3333",
|
|
30
30
|
usdcMint: NETWORK_PRESETS.localnet.usdcMint,
|
|
31
31
|
programIdSource: "custom",
|
|
32
32
|
usdcMintSource: "custom",
|