@btc-vision/cli 1.0.2 → 1.0.3
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 +55 -23
- package/build/commands/AcceptCommand.js +62 -15
- package/build/commands/ConfigCommand.js +2 -27
- package/build/commands/DeprecateCommand.js +32 -11
- package/build/commands/InitCommand.js +0 -6
- package/build/commands/PublishCommand.js +82 -27
- package/build/commands/ScopeRegisterCommand.d.ts +7 -0
- package/build/commands/ScopeRegisterCommand.js +115 -0
- package/build/commands/TransferCommand.js +118 -30
- package/build/commands/UndeprecateCommand.js +31 -10
- package/build/index.js +2 -0
- package/build/lib/config.d.ts +1 -0
- package/build/lib/config.js +3 -2
- package/build/lib/ipfs.js +85 -76
- package/build/lib/registry.d.ts +1 -1
- package/build/lib/registry.js +3 -3
- package/build/lib/transaction.d.ts +27 -0
- package/build/lib/transaction.js +91 -0
- package/build/lib/wallet.d.ts +7 -7
- package/build/lib/wallet.js +3 -6
- package/build/types/index.d.ts +1 -0
- package/package.json +2 -1
- package/src/commands/AcceptCommand.ts +89 -16
- package/src/commands/ConfigCommand.ts +2 -29
- package/src/commands/DeprecateCommand.ts +48 -11
- package/src/commands/InitCommand.ts +0 -6
- package/src/commands/PublishCommand.ts +138 -28
- package/src/commands/ScopeRegisterCommand.ts +164 -0
- package/src/commands/TransferCommand.ts +159 -31
- package/src/commands/UndeprecateCommand.ts +43 -10
- package/src/index.ts +2 -0
- package/src/lib/config.ts +3 -2
- package/src/lib/ipfs.ts +113 -99
- package/src/lib/registry.ts +5 -2
- package/src/lib/transaction.ts +205 -0
- package/src/lib/wallet.ts +10 -19
- package/src/types/index.ts +3 -1
package/README.md
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
[](https://github.com/prettier/prettier)
|
|
12
12
|
|
|
13
|
-
Official command-line interface for the OPNet plugin ecosystem. Build, sign, verify, and publish plugins with
|
|
13
|
+
Official command-line interface for the OPNet plugin ecosystem. Build, sign, verify, and publish plugins with
|
|
14
|
+
quantum-resistant MLDSA signatures.
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -19,6 +20,7 @@ npm install -g @btc-vision/cli
|
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
Or use npx:
|
|
23
|
+
|
|
22
24
|
```bash
|
|
23
25
|
npx @btc-vision/cli <command>
|
|
24
26
|
```
|
|
@@ -93,6 +95,7 @@ opnet login --wif "KwDiBf..." --mldsa "hex-key..."
|
|
|
93
95
|
```
|
|
94
96
|
|
|
95
97
|
**Options:**
|
|
98
|
+
|
|
96
99
|
- `-m, --mnemonic <phrase>` - BIP-39 mnemonic phrase (24 words)
|
|
97
100
|
- `--wif <key>` - Bitcoin WIF private key (advanced)
|
|
98
101
|
- `--mldsa <key>` - MLDSA private key hex (advanced, requires --wif)
|
|
@@ -160,6 +163,7 @@ opnet init --force
|
|
|
160
163
|
```
|
|
161
164
|
|
|
162
165
|
**Options:**
|
|
166
|
+
|
|
163
167
|
- `-t, --template <type>` - Template type (standalone, library) [default: standalone]
|
|
164
168
|
- `-y, --yes` - Skip prompts and use defaults
|
|
165
169
|
- `--force` - Overwrite existing files
|
|
@@ -183,6 +187,7 @@ opnet compile --no-sign
|
|
|
183
187
|
```
|
|
184
188
|
|
|
185
189
|
**Options:**
|
|
190
|
+
|
|
186
191
|
- `-o, --output <path>` - Output file path
|
|
187
192
|
- `-d, --dir <path>` - Plugin directory [default: current]
|
|
188
193
|
- `--no-sign` - Skip signing (produce unsigned binary)
|
|
@@ -200,6 +205,7 @@ opnet verify plugin.opnet --json
|
|
|
200
205
|
```
|
|
201
206
|
|
|
202
207
|
**Options:**
|
|
208
|
+
|
|
203
209
|
- `-v, --verbose` - Show detailed information
|
|
204
210
|
- `--json` - Output as JSON
|
|
205
211
|
|
|
@@ -229,6 +235,7 @@ opnet sign plugin.opnet --force # Re-sign with different key
|
|
|
229
235
|
```
|
|
230
236
|
|
|
231
237
|
**Options:**
|
|
238
|
+
|
|
232
239
|
- `-o, --output <path>` - Output file path [default: overwrites input]
|
|
233
240
|
- `--force` - Force re-signing even if already signed by different key
|
|
234
241
|
|
|
@@ -253,6 +260,7 @@ opnet publish --network testnet
|
|
|
253
260
|
```
|
|
254
261
|
|
|
255
262
|
**Options:**
|
|
263
|
+
|
|
256
264
|
- `-n, --network <network>` - Network to publish to [default: mainnet]
|
|
257
265
|
- `--dry-run` - Show what would be published without publishing
|
|
258
266
|
- `-y, --yes` - Skip confirmation prompts
|
|
@@ -268,6 +276,7 @@ opnet deprecate @scope/plugin 1.0.0 --message "Security vulnerability"
|
|
|
268
276
|
```
|
|
269
277
|
|
|
270
278
|
**Options:**
|
|
279
|
+
|
|
271
280
|
- `-m, --message <message>` - Deprecation reason/message
|
|
272
281
|
- `-n, --network <network>` - Network [default: mainnet]
|
|
273
282
|
- `-y, --yes` - Skip confirmation
|
|
@@ -296,6 +305,7 @@ opnet transfer my-plugin --cancel
|
|
|
296
305
|
```
|
|
297
306
|
|
|
298
307
|
**Options:**
|
|
308
|
+
|
|
299
309
|
- `-n, --network <network>` - Network [default: mainnet]
|
|
300
310
|
- `-y, --yes` - Skip confirmation
|
|
301
311
|
- `--cancel` - Cancel pending transfer
|
|
@@ -328,6 +338,7 @@ opnet install @scope/plugin --output ./my-plugins
|
|
|
328
338
|
```
|
|
329
339
|
|
|
330
340
|
**Options:**
|
|
341
|
+
|
|
331
342
|
- `-o, --output <path>` - Output directory [default: ./plugins/]
|
|
332
343
|
- `-n, --network <network>` - Network [default: mainnet]
|
|
333
344
|
- `--skip-verify` - Skip signature verification
|
|
@@ -348,6 +359,7 @@ opnet update --dir ./my-plugins
|
|
|
348
359
|
```
|
|
349
360
|
|
|
350
361
|
**Options:**
|
|
362
|
+
|
|
351
363
|
- `-d, --dir <path>` - Plugins directory [default: ./plugins/]
|
|
352
364
|
- `-n, --network <network>` - Network [default: mainnet]
|
|
353
365
|
- `--skip-verify` - Skip signature verification
|
|
@@ -365,6 +377,7 @@ opnet list --dir ./my-plugins
|
|
|
365
377
|
```
|
|
366
378
|
|
|
367
379
|
**Options:**
|
|
380
|
+
|
|
368
381
|
- `-d, --dir <path>` - Plugins directory [default: ./plugins/]
|
|
369
382
|
- `--json` - Output as JSON
|
|
370
383
|
- `-v, --verbose` - Show detailed information
|
|
@@ -380,6 +393,7 @@ opnet search plugin-name --json
|
|
|
380
393
|
```
|
|
381
394
|
|
|
382
395
|
**Options:**
|
|
396
|
+
|
|
383
397
|
- `-n, --network <network>` - Network [default: mainnet]
|
|
384
398
|
- `--json` - Output as JSON
|
|
385
399
|
|
|
@@ -393,7 +407,7 @@ Configuration is stored in `~/.opnet/config.json`:
|
|
|
393
407
|
"rpcUrls": {
|
|
394
408
|
"mainnet": "https://api.opnet.org",
|
|
395
409
|
"testnet": "https://testnet.opnet.org",
|
|
396
|
-
"regtest": "
|
|
410
|
+
"regtest": "https://regtest.opnet.org"
|
|
397
411
|
},
|
|
398
412
|
"ipfsGateway": "https://ipfs.opnet.org/ipfs/",
|
|
399
413
|
"ipfsGateways": [
|
|
@@ -414,24 +428,24 @@ Configuration is stored in `~/.opnet/config.json`:
|
|
|
414
428
|
|
|
415
429
|
## Environment Variables
|
|
416
430
|
|
|
417
|
-
| Variable
|
|
418
|
-
|
|
419
|
-
| `OPNET_MNEMONIC`
|
|
420
|
-
| `OPNET_PRIVATE_KEY`
|
|
421
|
-
| `OPNET_MLDSA_KEY`
|
|
422
|
-
| `OPNET_MLDSA_LEVEL`
|
|
423
|
-
| `OPNET_NETWORK`
|
|
424
|
-
| `OPNET_RPC_URL`
|
|
425
|
-
| `OPNET_IPFS_GATEWAY`
|
|
426
|
-
| `OPNET_IPFS_PINNING_ENDPOINT` | IPFS pinning service endpoint
|
|
427
|
-
| `OPNET_IPFS_PINNING_KEY`
|
|
428
|
-
| `OPNET_REGISTRY_ADDRESS`
|
|
429
|
-
| `OPNET_INDEXER_URL`
|
|
431
|
+
| Variable | Description |
|
|
432
|
+
|-------------------------------|-------------------------------------|
|
|
433
|
+
| `OPNET_MNEMONIC` | BIP-39 mnemonic phrase |
|
|
434
|
+
| `OPNET_PRIVATE_KEY` | Bitcoin WIF private key |
|
|
435
|
+
| `OPNET_MLDSA_KEY` | MLDSA private key (hex) |
|
|
436
|
+
| `OPNET_MLDSA_LEVEL` | MLDSA security level (44, 65, 87) |
|
|
437
|
+
| `OPNET_NETWORK` | Network (mainnet, testnet, regtest) |
|
|
438
|
+
| `OPNET_RPC_URL` | RPC endpoint URL |
|
|
439
|
+
| `OPNET_IPFS_GATEWAY` | IPFS gateway URL |
|
|
440
|
+
| `OPNET_IPFS_PINNING_ENDPOINT` | IPFS pinning service endpoint |
|
|
441
|
+
| `OPNET_IPFS_PINNING_KEY` | IPFS pinning API key |
|
|
442
|
+
| `OPNET_REGISTRY_ADDRESS` | Registry contract address |
|
|
443
|
+
| `OPNET_INDEXER_URL` | Indexer API URL |
|
|
430
444
|
|
|
431
445
|
## MLDSA Security Levels
|
|
432
446
|
|
|
433
|
-
| Level
|
|
434
|
-
|
|
447
|
+
| Level | Public Key | Signature | Security |
|
|
448
|
+
|----------|-------------|-------------|----------|
|
|
435
449
|
| MLDSA-44 | 1,312 bytes | 2,420 bytes | ~128-bit |
|
|
436
450
|
| MLDSA-65 | 1,952 bytes | 3,309 bytes | ~192-bit |
|
|
437
451
|
| MLDSA-87 | 2,592 bytes | 4,627 bytes | ~256-bit |
|
|
@@ -470,12 +484,30 @@ The `.opnet` binary format consists of:
|
|
|
470
484
|
"description": "My OPNet plugin",
|
|
471
485
|
"pluginType": "standalone",
|
|
472
486
|
"permissions": {
|
|
473
|
-
"database": {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
"
|
|
478
|
-
|
|
487
|
+
"database": {
|
|
488
|
+
"enabled": false,
|
|
489
|
+
"collections": []
|
|
490
|
+
},
|
|
491
|
+
"blocks": {
|
|
492
|
+
"preProcess": false,
|
|
493
|
+
"postProcess": false,
|
|
494
|
+
"onChange": false
|
|
495
|
+
},
|
|
496
|
+
"epochs": {
|
|
497
|
+
"onChange": false,
|
|
498
|
+
"onFinalized": false
|
|
499
|
+
},
|
|
500
|
+
"mempool": {
|
|
501
|
+
"txFeed": false
|
|
502
|
+
},
|
|
503
|
+
"api": {
|
|
504
|
+
"addEndpoints": false,
|
|
505
|
+
"addWebsocket": false
|
|
506
|
+
},
|
|
507
|
+
"filesystem": {
|
|
508
|
+
"configDir": false,
|
|
509
|
+
"tempDir": false
|
|
510
|
+
}
|
|
479
511
|
},
|
|
480
512
|
"resources": {
|
|
481
513
|
"maxMemoryMB": 256,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { confirm } from '@inquirer/prompts';
|
|
2
2
|
import { BaseCommand } from './BaseCommand.js';
|
|
3
|
-
import { getPendingScopeTransfer, getPendingTransfer } from '../lib/registry.js';
|
|
3
|
+
import { getPendingScopeTransfer, getPendingTransfer, getRegistryContract, } from '../lib/registry.js';
|
|
4
4
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
5
5
|
import { CLIWallet } from '../lib/wallet.js';
|
|
6
|
+
import { buildTransactionParams, checkBalance, formatSats, getWalletAddress, } from '../lib/transaction.js';
|
|
6
7
|
export class AcceptCommand extends BaseCommand {
|
|
7
8
|
constructor() {
|
|
8
9
|
super('accept', 'Accept pending ownership transfer');
|
|
@@ -23,7 +24,7 @@ export class AcceptCommand extends BaseCommand {
|
|
|
23
24
|
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
24
25
|
process.exit(1);
|
|
25
26
|
}
|
|
26
|
-
CLIWallet.fromCredentials(credentials);
|
|
27
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
27
28
|
this.logger.success('Wallet loaded');
|
|
28
29
|
const network = (options?.network || 'mainnet');
|
|
29
30
|
const isScope = name.startsWith('@') && !name.includes('/');
|
|
@@ -54,11 +55,37 @@ export class AcceptCommand extends BaseCommand {
|
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
57
|
}
|
|
58
|
+
this.logger.info('Checking wallet balance...');
|
|
59
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
60
|
+
if (!sufficient) {
|
|
61
|
+
this.logger.fail('Insufficient balance');
|
|
62
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
63
|
+
this.logger.error('Please fund your wallet before accepting transfer.');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
57
67
|
this.logger.info('Accepting transfer...');
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
const sender = getWalletAddress(wallet);
|
|
69
|
+
const contract = getRegistryContract(network, sender);
|
|
70
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
71
|
+
const acceptResult = await contract.acceptScopeTransfer(scopeName);
|
|
72
|
+
if (acceptResult.revert) {
|
|
73
|
+
this.logger.fail('Acceptance would fail');
|
|
74
|
+
this.logger.error(`Reason: ${acceptResult.revert}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
if (acceptResult.estimatedGas) {
|
|
78
|
+
this.logger.info(`Estimated gas: ${acceptResult.estimatedGas} sats`);
|
|
79
|
+
}
|
|
80
|
+
const receipt = await acceptResult.sendTransaction(txParams);
|
|
81
|
+
this.logger.log('');
|
|
82
|
+
this.logger.success('Scope transfer accepted successfully!');
|
|
83
|
+
this.logger.log('');
|
|
84
|
+
this.logger.log(`Scope: ${name}`);
|
|
85
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
86
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
87
|
+
this.logger.log('');
|
|
88
|
+
return;
|
|
62
89
|
}
|
|
63
90
|
else {
|
|
64
91
|
const pending = await getPendingTransfer(name, network);
|
|
@@ -85,17 +112,37 @@ export class AcceptCommand extends BaseCommand {
|
|
|
85
112
|
return;
|
|
86
113
|
}
|
|
87
114
|
}
|
|
115
|
+
this.logger.info('Checking wallet balance...');
|
|
116
|
+
const { sufficient: sufficientPkg, balance: balancePkg } = await checkBalance(wallet, network);
|
|
117
|
+
if (!sufficientPkg) {
|
|
118
|
+
this.logger.fail('Insufficient balance');
|
|
119
|
+
this.logger.error(`Wallet balance: ${formatSats(balancePkg)}`);
|
|
120
|
+
this.logger.error('Please fund your wallet before accepting transfer.');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
this.logger.success(`Wallet balance: ${formatSats(balancePkg)}`);
|
|
88
124
|
this.logger.info('Accepting transfer...');
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
125
|
+
const sender = getWalletAddress(wallet);
|
|
126
|
+
const contract = getRegistryContract(network, sender);
|
|
127
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
128
|
+
const acceptResult = await contract.acceptTransfer(name);
|
|
129
|
+
if (acceptResult.revert) {
|
|
130
|
+
this.logger.fail('Acceptance would fail');
|
|
131
|
+
this.logger.error(`Reason: ${acceptResult.revert}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
if (acceptResult.estimatedGas) {
|
|
135
|
+
this.logger.info(`Estimated gas: ${acceptResult.estimatedGas} sats`);
|
|
136
|
+
}
|
|
137
|
+
const receipt = await acceptResult.sendTransaction(txParams);
|
|
138
|
+
this.logger.log('');
|
|
139
|
+
this.logger.success('Package transfer accepted successfully!');
|
|
140
|
+
this.logger.log('');
|
|
141
|
+
this.logger.log(`Package: ${name}`);
|
|
142
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
143
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
144
|
+
this.logger.log('');
|
|
93
145
|
}
|
|
94
|
-
this.logger.info('Acceptance (transaction pending)');
|
|
95
|
-
this.logger.log('');
|
|
96
|
-
this.logger.success('Transfer acceptance submitted!');
|
|
97
|
-
this.logger.warn('Note: Registry transaction support is coming soon.');
|
|
98
|
-
this.logger.log('');
|
|
99
146
|
}
|
|
100
147
|
catch (error) {
|
|
101
148
|
this.logger.fail('Acceptance failed');
|
|
@@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import * as os from 'os';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { BaseCommand } from './BaseCommand.js';
|
|
5
|
-
import { displayConfig, getConfigValue, saveConfig, setConfigValue } from '../lib/config.js';
|
|
5
|
+
import { DEFAULT_CONFIG, displayConfig, getConfigValue, saveConfig, setConfigValue, } from '../lib/config.js';
|
|
6
6
|
export class ConfigCommand extends BaseCommand {
|
|
7
7
|
constructor() {
|
|
8
8
|
super('config', 'Manage CLI configuration');
|
|
@@ -85,32 +85,7 @@ export class ConfigCommand extends BaseCommand {
|
|
|
85
85
|
this.logger.info('Use --yes to confirm.');
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
|
-
|
|
89
|
-
defaultNetwork: 'mainnet',
|
|
90
|
-
rpcUrls: {
|
|
91
|
-
mainnet: 'https://api.opnet.org',
|
|
92
|
-
testnet: 'https://testnet.opnet.org',
|
|
93
|
-
regtest: 'http://localhost:9001',
|
|
94
|
-
},
|
|
95
|
-
ipfsGateway: 'https://ipfs.opnet.org/ipfs/',
|
|
96
|
-
ipfsGateways: [
|
|
97
|
-
'https://ipfs.opnet.org/ipfs/',
|
|
98
|
-
'https://ipfs.io/ipfs/',
|
|
99
|
-
'https://cloudflare-ipfs.com/ipfs/',
|
|
100
|
-
'https://dweb.link/ipfs/',
|
|
101
|
-
],
|
|
102
|
-
ipfsPinningEndpoint: 'https://ipfs.opnet.org/api/v0/add',
|
|
103
|
-
ipfsPinningApiKey: '',
|
|
104
|
-
ipfsPinningAuthHeader: 'Authorization',
|
|
105
|
-
registryAddresses: {
|
|
106
|
-
mainnet: '',
|
|
107
|
-
testnet: '',
|
|
108
|
-
regtest: '',
|
|
109
|
-
},
|
|
110
|
-
defaultMldsaLevel: 44,
|
|
111
|
-
indexerUrl: 'https://indexer.opnet.org',
|
|
112
|
-
};
|
|
113
|
-
saveConfig(defaultConfig);
|
|
88
|
+
saveConfig(DEFAULT_CONFIG);
|
|
114
89
|
this.logger.success('Configuration reset to defaults.');
|
|
115
90
|
}
|
|
116
91
|
handlePath() {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { confirm, input } from '@inquirer/prompts';
|
|
2
2
|
import { BaseCommand } from './BaseCommand.js';
|
|
3
|
-
import { getPackage, getVersion, isVersionImmutable } from '../lib/registry.js';
|
|
3
|
+
import { getPackage, getRegistryContract, getVersion, isVersionImmutable } from '../lib/registry.js';
|
|
4
4
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
5
5
|
import { CLIWallet } from '../lib/wallet.js';
|
|
6
|
+
import { buildTransactionParams, checkBalance, formatSats, getWalletAddress, } from '../lib/transaction.js';
|
|
6
7
|
export class DeprecateCommand extends BaseCommand {
|
|
7
8
|
constructor() {
|
|
8
9
|
super('deprecate', 'Mark a package version as deprecated');
|
|
@@ -25,7 +26,7 @@ export class DeprecateCommand extends BaseCommand {
|
|
|
25
26
|
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
26
27
|
process.exit(1);
|
|
27
28
|
}
|
|
28
|
-
CLIWallet.fromCredentials(credentials);
|
|
29
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
29
30
|
this.logger.success('Wallet loaded');
|
|
30
31
|
this.logger.info('Fetching package info...');
|
|
31
32
|
const network = (options?.network || 'mainnet');
|
|
@@ -86,17 +87,37 @@ export class DeprecateCommand extends BaseCommand {
|
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
88
89
|
}
|
|
90
|
+
this.logger.info('Checking wallet balance...');
|
|
91
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
92
|
+
if (!sufficient) {
|
|
93
|
+
this.logger.fail('Insufficient balance');
|
|
94
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
95
|
+
this.logger.error('Please fund your wallet before deprecating.');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
89
99
|
this.logger.info('Deprecating version...');
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
const sender = getWalletAddress(wallet);
|
|
101
|
+
const contract = getRegistryContract(network, sender);
|
|
102
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
103
|
+
const deprecateResult = await contract.deprecateVersion(packageName, targetVersion, message);
|
|
104
|
+
if (deprecateResult.revert) {
|
|
105
|
+
this.logger.fail('Deprecation would fail');
|
|
106
|
+
this.logger.error(`Reason: ${deprecateResult.revert}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
if (deprecateResult.estimatedGas) {
|
|
110
|
+
this.logger.info(`Estimated gas: ${deprecateResult.estimatedGas} sats`);
|
|
111
|
+
}
|
|
112
|
+
const receipt = await deprecateResult.sendTransaction(txParams);
|
|
113
|
+
this.logger.log('');
|
|
114
|
+
this.logger.success('Version deprecated successfully!');
|
|
97
115
|
this.logger.log('');
|
|
98
|
-
this.logger.
|
|
99
|
-
this.logger.
|
|
116
|
+
this.logger.log(`Package: ${packageName}`);
|
|
117
|
+
this.logger.log(`Version: ${targetVersion}`);
|
|
118
|
+
this.logger.log(`Reason: ${message}`);
|
|
119
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
120
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
100
121
|
this.logger.log('');
|
|
101
122
|
}
|
|
102
123
|
catch (error) {
|
|
@@ -7,7 +7,9 @@ import { formatFileSize, parseOpnetBinary, verifyChecksum } from '../lib/binary.
|
|
|
7
7
|
import { CLIWallet } from '../lib/wallet.js';
|
|
8
8
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
9
9
|
import { uploadPlugin } from '../lib/ipfs.js';
|
|
10
|
-
import { computePermissionsHash, encodeDependencies, getPackage, getScope, mldsaLevelToRegistry, parsePackageName, pluginTypeToRegistry, } from '../lib/registry.js';
|
|
10
|
+
import { computePermissionsHash, encodeDependencies, getPackage, getRegistryContract, getScope, mldsaLevelToRegistry, parsePackageName, pluginTypeToRegistry, } from '../lib/registry.js';
|
|
11
|
+
import { buildTransactionParams, checkBalance, DEFAULT_FEE_RATE, DEFAULT_MAX_SAT_TO_SPEND, formatSats, getWalletAddress, waitForTransactionConfirmation, } from '../lib/transaction.js';
|
|
12
|
+
import { TransactionOutputFlags } from 'opnet';
|
|
11
13
|
export class PublishCommand extends BaseCommand {
|
|
12
14
|
constructor() {
|
|
13
15
|
super('publish', 'Publish a plugin to the OPNet registry');
|
|
@@ -73,7 +75,7 @@ export class PublishCommand extends BaseCommand {
|
|
|
73
75
|
}
|
|
74
76
|
this.logger.success('Wallet verified');
|
|
75
77
|
this.logger.info('Checking registry status...');
|
|
76
|
-
const { scope
|
|
78
|
+
const { scope } = parsePackageName(meta.name);
|
|
77
79
|
const network = (options?.network || 'mainnet');
|
|
78
80
|
if (scope) {
|
|
79
81
|
const scopeInfo = await getScope(scope, network);
|
|
@@ -119,39 +121,92 @@ export class PublishCommand extends BaseCommand {
|
|
|
119
121
|
this.logger.success(`Uploaded to IPFS: ${pinResult.cid}`);
|
|
120
122
|
const permissionsHash = computePermissionsHash(meta.permissions);
|
|
121
123
|
const dependencies = encodeDependencies(meta.dependencies || {});
|
|
124
|
+
this.logger.info('Checking wallet balance...');
|
|
125
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
126
|
+
if (!sufficient) {
|
|
127
|
+
this.logger.fail('Insufficient balance');
|
|
128
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
129
|
+
this.logger.error('Please fund your wallet before publishing.');
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
133
|
+
const sender = getWalletAddress(wallet);
|
|
134
|
+
const contract = getRegistryContract(network, sender);
|
|
135
|
+
const treasuryAddress = await contract.getTreasuryAddress();
|
|
136
|
+
const extraUtxo = {
|
|
137
|
+
address: treasuryAddress.properties.treasuryAddress,
|
|
138
|
+
value: 10_000,
|
|
139
|
+
};
|
|
140
|
+
let txParams = buildTransactionParams(wallet, network, DEFAULT_MAX_SAT_TO_SPEND, DEFAULT_FEE_RATE, extraUtxo);
|
|
122
141
|
if (isNewPackage) {
|
|
123
|
-
this.logger.info('Registering package...');
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
this.logger.info('Registering new package...');
|
|
143
|
+
const outSimulation = [
|
|
144
|
+
{
|
|
145
|
+
index: 1,
|
|
146
|
+
to: treasuryAddress.properties.treasuryAddress,
|
|
147
|
+
value: 10000n,
|
|
148
|
+
flags: TransactionOutputFlags.hasTo,
|
|
149
|
+
scriptPubKey: undefined,
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
contract.setTransactionDetails({
|
|
153
|
+
inputs: [],
|
|
154
|
+
outputs: outSimulation,
|
|
155
|
+
});
|
|
156
|
+
const registerResult = await contract.registerPackage(meta.name);
|
|
157
|
+
if (registerResult.revert) {
|
|
158
|
+
this.logger.fail('Package registration would fail');
|
|
159
|
+
this.logger.error(`Reason: ${registerResult.revert}`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
if (registerResult.estimatedGas) {
|
|
163
|
+
this.logger.info(`Estimated gas: ${registerResult.estimatedGas} sats`);
|
|
164
|
+
}
|
|
165
|
+
const registerReceipt = await registerResult.sendTransaction(txParams);
|
|
166
|
+
this.logger.success('Package registration transaction sent');
|
|
167
|
+
this.logger.log(`Transaction ID: ${registerReceipt.transactionId}`);
|
|
168
|
+
this.logger.log('');
|
|
169
|
+
const confirmationResult = await waitForTransactionConfirmation(registerReceipt.transactionId, network, {
|
|
170
|
+
message: 'Waiting for package registration to confirm',
|
|
171
|
+
});
|
|
172
|
+
if (!confirmationResult.confirmed) {
|
|
173
|
+
if (confirmationResult.revert) {
|
|
174
|
+
this.logger.fail('Package registration failed');
|
|
175
|
+
this.logger.error(`Reason: ${confirmationResult.revert}`);
|
|
176
|
+
}
|
|
177
|
+
else if (confirmationResult.error) {
|
|
178
|
+
this.logger.fail('Package registration not confirmed');
|
|
179
|
+
this.logger.error(confirmationResult.error);
|
|
180
|
+
}
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
this.logger.log('');
|
|
127
184
|
}
|
|
185
|
+
txParams = buildTransactionParams(wallet, network, DEFAULT_MAX_SAT_TO_SPEND, DEFAULT_FEE_RATE);
|
|
128
186
|
this.logger.info('Publishing version...');
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
this.logger.log(` permissionsHash: <32 bytes>,`);
|
|
140
|
-
this.logger.log(` dependencies: <${dependencies.length} bytes>`);
|
|
141
|
-
this.logger.log(')');
|
|
142
|
-
this.logger.info('Version publishing (transaction pending)');
|
|
143
|
-
this.logger.log('');
|
|
144
|
-
this.logger.success('Plugin uploaded successfully!');
|
|
187
|
+
const publishResult = await contract.publishVersion(meta.name, meta.version, pinResult.cid, new Uint8Array(parsed.checksum), new Uint8Array(parsed.signature), mldsaLevelToRegistry(mldsaLevel), meta.opnetVersion, pluginTypeToRegistry(meta.pluginType), permissionsHash, dependencies);
|
|
188
|
+
if (publishResult.revert) {
|
|
189
|
+
this.logger.fail('Version publishing would fail');
|
|
190
|
+
this.logger.error(`Reason: ${publishResult.revert}`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
if (publishResult.estimatedGas) {
|
|
194
|
+
this.logger.info(`Estimated gas: ${publishResult.estimatedGas} sats`);
|
|
195
|
+
}
|
|
196
|
+
const publishReceipt = await publishResult.sendTransaction(txParams);
|
|
145
197
|
this.logger.log('');
|
|
146
|
-
this.logger.
|
|
147
|
-
this.logger.log(`Gateway: https://ipfs.opnet.org/ipfs/${pinResult.cid}`);
|
|
198
|
+
this.logger.success('Plugin published successfully!');
|
|
148
199
|
this.logger.log('');
|
|
149
|
-
this.logger.
|
|
150
|
-
this.logger.
|
|
200
|
+
this.logger.log(`Package: ${meta.name}`);
|
|
201
|
+
this.logger.log(`Version: ${meta.version}`);
|
|
202
|
+
this.logger.log(`IPFS CID: ${pinResult.cid}`);
|
|
203
|
+
this.logger.log(`Transaction ID: ${publishReceipt.transactionId}`);
|
|
204
|
+
this.logger.log(`Fees paid: ${formatSats(publishReceipt.estimatedFees)}`);
|
|
205
|
+
this.logger.log(`Gateway: https://ipfs.opnet.org/ipfs/${pinResult.cid}`);
|
|
151
206
|
this.logger.log('');
|
|
152
207
|
}
|
|
153
208
|
catch (error) {
|
|
154
|
-
this.logger.fail(
|
|
209
|
+
this.logger.fail(`Publishing failed`);
|
|
155
210
|
if (this.isUserCancelled(error)) {
|
|
156
211
|
this.logger.warn('Publishing cancelled.');
|
|
157
212
|
process.exit(0);
|