@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.
Files changed (37) hide show
  1. package/README.md +55 -23
  2. package/build/commands/AcceptCommand.js +62 -15
  3. package/build/commands/ConfigCommand.js +2 -27
  4. package/build/commands/DeprecateCommand.js +32 -11
  5. package/build/commands/InitCommand.js +0 -6
  6. package/build/commands/PublishCommand.js +82 -27
  7. package/build/commands/ScopeRegisterCommand.d.ts +7 -0
  8. package/build/commands/ScopeRegisterCommand.js +115 -0
  9. package/build/commands/TransferCommand.js +118 -30
  10. package/build/commands/UndeprecateCommand.js +31 -10
  11. package/build/index.js +2 -0
  12. package/build/lib/config.d.ts +1 -0
  13. package/build/lib/config.js +3 -2
  14. package/build/lib/ipfs.js +85 -76
  15. package/build/lib/registry.d.ts +1 -1
  16. package/build/lib/registry.js +3 -3
  17. package/build/lib/transaction.d.ts +27 -0
  18. package/build/lib/transaction.js +91 -0
  19. package/build/lib/wallet.d.ts +7 -7
  20. package/build/lib/wallet.js +3 -6
  21. package/build/types/index.d.ts +1 -0
  22. package/package.json +2 -1
  23. package/src/commands/AcceptCommand.ts +89 -16
  24. package/src/commands/ConfigCommand.ts +2 -29
  25. package/src/commands/DeprecateCommand.ts +48 -11
  26. package/src/commands/InitCommand.ts +0 -6
  27. package/src/commands/PublishCommand.ts +138 -28
  28. package/src/commands/ScopeRegisterCommand.ts +164 -0
  29. package/src/commands/TransferCommand.ts +159 -31
  30. package/src/commands/UndeprecateCommand.ts +43 -10
  31. package/src/index.ts +2 -0
  32. package/src/lib/config.ts +3 -2
  33. package/src/lib/ipfs.ts +113 -99
  34. package/src/lib/registry.ts +5 -2
  35. package/src/lib/transaction.ts +205 -0
  36. package/src/lib/wallet.ts +10 -19
  37. package/src/types/index.ts +3 -1
package/README.md CHANGED
@@ -10,7 +10,8 @@
10
10
 
11
11
  [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
12
12
 
13
- Official command-line interface for the OPNet plugin ecosystem. Build, sign, verify, and publish plugins with quantum-resistant MLDSA signatures.
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": "http://localhost:9001"
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 | Description |
418
- |----------|-------------|
419
- | `OPNET_MNEMONIC` | BIP-39 mnemonic phrase |
420
- | `OPNET_PRIVATE_KEY` | Bitcoin WIF private key |
421
- | `OPNET_MLDSA_KEY` | MLDSA private key (hex) |
422
- | `OPNET_MLDSA_LEVEL` | MLDSA security level (44, 65, 87) |
423
- | `OPNET_NETWORK` | Network (mainnet, testnet, regtest) |
424
- | `OPNET_RPC_URL` | RPC endpoint URL |
425
- | `OPNET_IPFS_GATEWAY` | IPFS gateway URL |
426
- | `OPNET_IPFS_PINNING_ENDPOINT` | IPFS pinning service endpoint |
427
- | `OPNET_IPFS_PINNING_KEY` | IPFS pinning API key |
428
- | `OPNET_REGISTRY_ADDRESS` | Registry contract address |
429
- | `OPNET_INDEXER_URL` | Indexer API 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 | Public Key | Signature | Security |
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": { "enabled": false, "collections": [] },
474
- "blocks": { "preProcess": false, "postProcess": false, "onChange": false },
475
- "epochs": { "onChange": false, "onFinalized": false },
476
- "mempool": { "txFeed": false },
477
- "api": { "addEndpoints": false, "addWebsocket": false },
478
- "filesystem": { "configDir": false, "tempDir": false }
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
- this.logger.warn('Acceptance transaction required.');
59
- this.logger.log('Transaction would call: acceptScopeTransfer(');
60
- this.logger.log(` scopeName: "${scopeName}"`);
61
- this.logger.log(')');
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
- this.logger.warn('Acceptance transaction required.');
90
- this.logger.log('Transaction would call: acceptTransfer(');
91
- this.logger.log(` packageName: "${name}"`);
92
- this.logger.log(')');
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
- const defaultConfig = {
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
- this.logger.warn('Deprecation transaction required.');
91
- this.logger.log('Transaction would call: deprecateVersion(');
92
- this.logger.log(` packageName: "${packageName}",`);
93
- this.logger.log(` version: "${targetVersion}",`);
94
- this.logger.log(` reason: "${message}"`);
95
- this.logger.log(')');
96
- this.logger.info('Deprecation (transaction pending)');
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.success('Deprecation submitted!');
99
- this.logger.warn('Note: Registry transaction support is coming soon.');
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) {
@@ -150,12 +150,6 @@ export class InitCommand extends BaseCommand {
150
150
  configDir: false,
151
151
  tempDir: false,
152
152
  },
153
- blockchain: {
154
- blocks: false,
155
- transactions: false,
156
- contracts: false,
157
- utxos: false,
158
- },
159
153
  },
160
154
  resources: {
161
155
  memory: {
@@ -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, name } = parsePackageName(meta.name);
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
- this.logger.warn('Package registration required.');
125
- this.logger.log(`Transaction would call: registerPackage("${meta.name}")`);
126
- this.logger.info('Package registration (transaction pending)');
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
- this.logger.warn('Version publishing required.');
130
- this.logger.log('Transaction would call: publishVersion(');
131
- this.logger.log(` packageName: "${meta.name}",`);
132
- this.logger.log(` version: "${meta.version}",`);
133
- this.logger.log(` ipfsCid: "${pinResult.cid}",`);
134
- this.logger.log(` checksum: <32 bytes>,`);
135
- this.logger.log(` signature: <${parsed.signature.length} bytes>,`);
136
- this.logger.log(` mldsaLevel: ${mldsaLevelToRegistry(mldsaLevel)},`);
137
- this.logger.log(` opnetVersionRange: "${meta.opnetVersion}",`);
138
- this.logger.log(` pluginType: ${pluginTypeToRegistry(meta.pluginType)},`);
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.log(`IPFS CID: ${pinResult.cid}`);
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.warn('Note: Registry transaction support is coming soon.');
150
- this.logger.warn('The binary has been uploaded to IPFS and is ready for registry submission.');
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('Publishing failed');
209
+ this.logger.fail(`Publishing failed`);
155
210
  if (this.isUserCancelled(error)) {
156
211
  this.logger.warn('Publishing cancelled.');
157
212
  process.exit(0);
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class ScopeRegisterCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ }
7
+ export declare const scopeRegisterCommand: import("commander").Command;