@btc-vision/cli 1.0.1 → 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/CompileCommand.js +9 -10
- package/build/commands/ConfigCommand.js +2 -27
- package/build/commands/DeprecateCommand.js +32 -11
- package/build/commands/InitCommand.js +1 -7
- 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/SignCommand.js +6 -9
- package/build/commands/TransferCommand.js +118 -30
- package/build/commands/UndeprecateCommand.js +31 -10
- package/build/index.js +2 -0
- package/build/lib/binary.d.ts +5 -2
- package/build/lib/binary.js +11 -6
- 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/manifest.js +1 -1
- 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/CompileCommand.ts +13 -14
- package/src/commands/ConfigCommand.ts +2 -29
- package/src/commands/DeprecateCommand.ts +48 -11
- package/src/commands/InitCommand.ts +1 -7
- package/src/commands/PublishCommand.ts +138 -28
- package/src/commands/ScopeRegisterCommand.ts +164 -0
- package/src/commands/SignCommand.ts +9 -21
- package/src/commands/TransferCommand.ts +159 -31
- package/src/commands/UndeprecateCommand.ts +43 -10
- package/src/index.ts +2 -0
- package/src/lib/binary.ts +24 -22
- package/src/lib/config.ts +3 -2
- package/src/lib/ipfs.ts +113 -99
- package/src/lib/manifest.ts +1 -1
- 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
|
@@ -8,8 +8,7 @@ import { Command } from 'commander';
|
|
|
8
8
|
import * as os from 'os';
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import { BaseCommand } from './BaseCommand.js';
|
|
11
|
-
import { displayConfig, getConfigValue, saveConfig, setConfigValue } from '../lib/config.js';
|
|
12
|
-
import { CLIConfig } from '../types/index.js';
|
|
11
|
+
import { DEFAULT_CONFIG, displayConfig, getConfigValue, saveConfig, setConfigValue, } from '../lib/config.js';
|
|
13
12
|
|
|
14
13
|
export class ConfigCommand extends BaseCommand {
|
|
15
14
|
constructor() {
|
|
@@ -106,33 +105,7 @@ export class ConfigCommand extends BaseCommand {
|
|
|
106
105
|
return;
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
|
|
110
|
-
defaultNetwork: 'mainnet',
|
|
111
|
-
rpcUrls: {
|
|
112
|
-
mainnet: 'https://api.opnet.org',
|
|
113
|
-
testnet: 'https://testnet.opnet.org',
|
|
114
|
-
regtest: 'http://localhost:9001',
|
|
115
|
-
},
|
|
116
|
-
ipfsGateway: 'https://ipfs.opnet.org/ipfs/',
|
|
117
|
-
ipfsGateways: [
|
|
118
|
-
'https://ipfs.opnet.org/ipfs/',
|
|
119
|
-
'https://ipfs.io/ipfs/',
|
|
120
|
-
'https://cloudflare-ipfs.com/ipfs/',
|
|
121
|
-
'https://dweb.link/ipfs/',
|
|
122
|
-
],
|
|
123
|
-
ipfsPinningEndpoint: 'https://ipfs.opnet.org/api/v0/add',
|
|
124
|
-
ipfsPinningApiKey: '',
|
|
125
|
-
ipfsPinningAuthHeader: 'Authorization',
|
|
126
|
-
registryAddresses: {
|
|
127
|
-
mainnet: '',
|
|
128
|
-
testnet: '',
|
|
129
|
-
regtest: '',
|
|
130
|
-
},
|
|
131
|
-
defaultMldsaLevel: 44,
|
|
132
|
-
indexerUrl: 'https://indexer.opnet.org',
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
saveConfig(defaultConfig);
|
|
108
|
+
saveConfig(DEFAULT_CONFIG);
|
|
136
109
|
this.logger.success('Configuration reset to defaults.');
|
|
137
110
|
}
|
|
138
111
|
|
|
@@ -6,9 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
import { confirm, input } from '@inquirer/prompts';
|
|
8
8
|
import { BaseCommand } from './BaseCommand.js';
|
|
9
|
-
import { getPackage, getVersion, isVersionImmutable } from '../lib/registry.js';
|
|
9
|
+
import { getPackage, getRegistryContract, getVersion, isVersionImmutable } from '../lib/registry.js';
|
|
10
10
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
11
11
|
import { CLIWallet } from '../lib/wallet.js';
|
|
12
|
+
import {
|
|
13
|
+
buildTransactionParams,
|
|
14
|
+
checkBalance,
|
|
15
|
+
formatSats,
|
|
16
|
+
getWalletAddress,
|
|
17
|
+
} from '../lib/transaction.js';
|
|
12
18
|
import { NetworkName } from '../types/index.js';
|
|
13
19
|
|
|
14
20
|
interface DeprecateOptions {
|
|
@@ -48,7 +54,7 @@ export class DeprecateCommand extends BaseCommand {
|
|
|
48
54
|
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
49
55
|
process.exit(1);
|
|
50
56
|
}
|
|
51
|
-
CLIWallet.fromCredentials(credentials);
|
|
57
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
52
58
|
this.logger.success('Wallet loaded');
|
|
53
59
|
|
|
54
60
|
// Get package info
|
|
@@ -128,19 +134,50 @@ export class DeprecateCommand extends BaseCommand {
|
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
136
|
|
|
137
|
+
// Check wallet balance
|
|
138
|
+
this.logger.info('Checking wallet balance...');
|
|
139
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
140
|
+
if (!sufficient) {
|
|
141
|
+
this.logger.fail('Insufficient balance');
|
|
142
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
143
|
+
this.logger.error('Please fund your wallet before deprecating.');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
147
|
+
|
|
131
148
|
// Execute deprecation
|
|
132
149
|
this.logger.info('Deprecating version...');
|
|
133
|
-
this.logger.warn('Deprecation transaction required.');
|
|
134
|
-
this.logger.log('Transaction would call: deprecateVersion(');
|
|
135
|
-
this.logger.log(` packageName: "${packageName}",`);
|
|
136
|
-
this.logger.log(` version: "${targetVersion}",`);
|
|
137
|
-
this.logger.log(` reason: "${message}"`);
|
|
138
|
-
this.logger.log(')');
|
|
139
|
-
this.logger.info('Deprecation (transaction pending)');
|
|
140
150
|
|
|
151
|
+
const sender = getWalletAddress(wallet);
|
|
152
|
+
const contract = getRegistryContract(network, sender);
|
|
153
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
154
|
+
|
|
155
|
+
const deprecateResult = await contract.deprecateVersion(
|
|
156
|
+
packageName,
|
|
157
|
+
targetVersion,
|
|
158
|
+
message,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (deprecateResult.revert) {
|
|
162
|
+
this.logger.fail('Deprecation would fail');
|
|
163
|
+
this.logger.error(`Reason: ${deprecateResult.revert}`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (deprecateResult.estimatedGas) {
|
|
168
|
+
this.logger.info(`Estimated gas: ${deprecateResult.estimatedGas} sats`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const receipt = await deprecateResult.sendTransaction(txParams);
|
|
172
|
+
|
|
173
|
+
this.logger.log('');
|
|
174
|
+
this.logger.success('Version deprecated successfully!');
|
|
141
175
|
this.logger.log('');
|
|
142
|
-
this.logger.
|
|
143
|
-
this.logger.
|
|
176
|
+
this.logger.log(`Package: ${packageName}`);
|
|
177
|
+
this.logger.log(`Version: ${targetVersion}`);
|
|
178
|
+
this.logger.log(`Reason: ${message}`);
|
|
179
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
180
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
144
181
|
this.logger.log('');
|
|
145
182
|
} catch (error) {
|
|
146
183
|
this.logger.fail('Deprecation failed');
|
|
@@ -185,7 +185,7 @@ export class InitCommand extends BaseCommand {
|
|
|
185
185
|
const manifest: Record<string, unknown> = {
|
|
186
186
|
name: config.pluginName,
|
|
187
187
|
version: '1.0.0',
|
|
188
|
-
opnetVersion: '
|
|
188
|
+
opnetVersion: '>=0.0.1',
|
|
189
189
|
main: 'dist/index.jsc',
|
|
190
190
|
target: 'bytenode',
|
|
191
191
|
type: 'plugin',
|
|
@@ -223,12 +223,6 @@ export class InitCommand extends BaseCommand {
|
|
|
223
223
|
configDir: false,
|
|
224
224
|
tempDir: false,
|
|
225
225
|
},
|
|
226
|
-
blockchain: {
|
|
227
|
-
blocks: false,
|
|
228
|
-
transactions: false,
|
|
229
|
-
contracts: false,
|
|
230
|
-
utxos: false,
|
|
231
|
-
},
|
|
232
226
|
},
|
|
233
227
|
resources: {
|
|
234
228
|
memory: {
|
|
@@ -17,12 +17,24 @@ import {
|
|
|
17
17
|
computePermissionsHash,
|
|
18
18
|
encodeDependencies,
|
|
19
19
|
getPackage,
|
|
20
|
+
getRegistryContract,
|
|
20
21
|
getScope,
|
|
21
22
|
mldsaLevelToRegistry,
|
|
22
23
|
parsePackageName,
|
|
23
24
|
pluginTypeToRegistry,
|
|
24
25
|
} from '../lib/registry.js';
|
|
26
|
+
import {
|
|
27
|
+
buildTransactionParams,
|
|
28
|
+
checkBalance,
|
|
29
|
+
DEFAULT_FEE_RATE,
|
|
30
|
+
DEFAULT_MAX_SAT_TO_SPEND,
|
|
31
|
+
formatSats,
|
|
32
|
+
getWalletAddress,
|
|
33
|
+
waitForTransactionConfirmation,
|
|
34
|
+
} from '../lib/transaction.js';
|
|
25
35
|
import { CLIMldsaLevel, NetworkName } from '../types/index.js';
|
|
36
|
+
import { PsbtOutputExtended } from '@btc-vision/bitcoin';
|
|
37
|
+
import { StrippedTransactionOutput, TransactionOutputFlags } from 'opnet';
|
|
26
38
|
|
|
27
39
|
interface PublishOptions {
|
|
28
40
|
network: string;
|
|
@@ -116,12 +128,13 @@ export class PublishCommand extends BaseCommand {
|
|
|
116
128
|
|
|
117
129
|
// Check registry status
|
|
118
130
|
this.logger.info('Checking registry status...');
|
|
119
|
-
const { scope
|
|
131
|
+
const { scope } = parsePackageName(meta.name);
|
|
120
132
|
const network = (options?.network || 'mainnet') as NetworkName;
|
|
121
133
|
|
|
122
134
|
// Check if scoped package
|
|
123
135
|
if (scope) {
|
|
124
136
|
const scopeInfo = await getScope(scope, network);
|
|
137
|
+
|
|
125
138
|
if (!scopeInfo) {
|
|
126
139
|
this.logger.fail(`Scope @${scope} is not registered`);
|
|
127
140
|
this.logger.warn(
|
|
@@ -180,44 +193,141 @@ export class PublishCommand extends BaseCommand {
|
|
|
180
193
|
const permissionsHash = computePermissionsHash(meta.permissions);
|
|
181
194
|
const dependencies = encodeDependencies(meta.dependencies || {});
|
|
182
195
|
|
|
196
|
+
// Check wallet balance
|
|
197
|
+
this.logger.info('Checking wallet balance...');
|
|
198
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
199
|
+
if (!sufficient) {
|
|
200
|
+
this.logger.fail('Insufficient balance');
|
|
201
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
202
|
+
this.logger.error('Please fund your wallet before publishing.');
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
206
|
+
|
|
207
|
+
// Get contract with sender for write operations
|
|
208
|
+
const sender = getWalletAddress(wallet);
|
|
209
|
+
const contract = getRegistryContract(network, sender);
|
|
210
|
+
|
|
211
|
+
const treasuryAddress = await contract.getTreasuryAddress();
|
|
212
|
+
|
|
213
|
+
const extraUtxo: PsbtOutputExtended = {
|
|
214
|
+
address: treasuryAddress.properties.treasuryAddress,
|
|
215
|
+
value: 10_000,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
let txParams = buildTransactionParams(
|
|
219
|
+
wallet,
|
|
220
|
+
network,
|
|
221
|
+
DEFAULT_MAX_SAT_TO_SPEND,
|
|
222
|
+
DEFAULT_FEE_RATE,
|
|
223
|
+
extraUtxo,
|
|
224
|
+
);
|
|
225
|
+
|
|
183
226
|
// Register package if new
|
|
184
227
|
if (isNewPackage) {
|
|
185
|
-
this.logger.info('Registering package...');
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
228
|
+
this.logger.info('Registering new package...');
|
|
229
|
+
|
|
230
|
+
const outSimulation: StrippedTransactionOutput[] = [
|
|
231
|
+
{
|
|
232
|
+
index: 1,
|
|
233
|
+
to: treasuryAddress.properties.treasuryAddress,
|
|
234
|
+
value: 10_000n,
|
|
235
|
+
flags: TransactionOutputFlags.hasTo,
|
|
236
|
+
scriptPubKey: undefined,
|
|
237
|
+
},
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
contract.setTransactionDetails({
|
|
241
|
+
inputs: [],
|
|
242
|
+
outputs: outSimulation,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const registerResult = await contract.registerPackage(meta.name);
|
|
246
|
+
if (registerResult.revert) {
|
|
247
|
+
this.logger.fail('Package registration would fail');
|
|
248
|
+
this.logger.error(`Reason: ${registerResult.revert}`);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (registerResult.estimatedGas) {
|
|
253
|
+
this.logger.info(`Estimated gas: ${registerResult.estimatedGas} sats`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const registerReceipt = await registerResult.sendTransaction(txParams);
|
|
257
|
+
this.logger.success('Package registration transaction sent');
|
|
258
|
+
this.logger.log(`Transaction ID: ${registerReceipt.transactionId}`);
|
|
259
|
+
this.logger.log('');
|
|
260
|
+
|
|
261
|
+
// Wait for registration transaction to be confirmed
|
|
262
|
+
const confirmationResult = await waitForTransactionConfirmation(
|
|
263
|
+
registerReceipt.transactionId,
|
|
264
|
+
network,
|
|
265
|
+
{
|
|
266
|
+
message: 'Waiting for package registration to confirm',
|
|
267
|
+
},
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
if (!confirmationResult.confirmed) {
|
|
271
|
+
if (confirmationResult.revert) {
|
|
272
|
+
this.logger.fail('Package registration failed');
|
|
273
|
+
this.logger.error(`Reason: ${confirmationResult.revert}`);
|
|
274
|
+
} else if (confirmationResult.error) {
|
|
275
|
+
this.logger.fail('Package registration not confirmed');
|
|
276
|
+
this.logger.error(confirmationResult.error);
|
|
277
|
+
}
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
this.logger.log('');
|
|
189
282
|
}
|
|
190
283
|
|
|
284
|
+
txParams = buildTransactionParams(
|
|
285
|
+
wallet,
|
|
286
|
+
network,
|
|
287
|
+
DEFAULT_MAX_SAT_TO_SPEND,
|
|
288
|
+
DEFAULT_FEE_RATE,
|
|
289
|
+
);
|
|
290
|
+
|
|
191
291
|
// Publish version
|
|
192
292
|
this.logger.info('Publishing version...');
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
293
|
+
|
|
294
|
+
const publishResult = await contract.publishVersion(
|
|
295
|
+
meta.name,
|
|
296
|
+
meta.version,
|
|
297
|
+
pinResult.cid,
|
|
298
|
+
new Uint8Array(parsed.checksum),
|
|
299
|
+
new Uint8Array(parsed.signature),
|
|
300
|
+
mldsaLevelToRegistry(mldsaLevel),
|
|
301
|
+
meta.opnetVersion,
|
|
302
|
+
pluginTypeToRegistry(meta.pluginType),
|
|
303
|
+
permissionsHash,
|
|
304
|
+
dependencies,
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
if (publishResult.revert) {
|
|
308
|
+
this.logger.fail('Version publishing would fail');
|
|
309
|
+
this.logger.error(`Reason: ${publishResult.revert}`);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (publishResult.estimatedGas) {
|
|
314
|
+
this.logger.info(`Estimated gas: ${publishResult.estimatedGas} sats`);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const publishReceipt = await publishResult.sendTransaction(txParams);
|
|
207
318
|
|
|
208
319
|
this.logger.log('');
|
|
209
|
-
this.logger.success('Plugin
|
|
210
|
-
this.logger.log('');
|
|
211
|
-
this.logger.log(`IPFS CID: ${pinResult.cid}`);
|
|
212
|
-
this.logger.log(`Gateway: https://ipfs.opnet.org/ipfs/${pinResult.cid}`);
|
|
320
|
+
this.logger.success('Plugin published successfully!');
|
|
213
321
|
this.logger.log('');
|
|
214
|
-
this.logger.
|
|
215
|
-
this.logger.
|
|
216
|
-
|
|
217
|
-
);
|
|
322
|
+
this.logger.log(`Package: ${meta.name}`);
|
|
323
|
+
this.logger.log(`Version: ${meta.version}`);
|
|
324
|
+
this.logger.log(`IPFS CID: ${pinResult.cid}`);
|
|
325
|
+
this.logger.log(`Transaction ID: ${publishReceipt.transactionId}`);
|
|
326
|
+
this.logger.log(`Fees paid: ${formatSats(publishReceipt.estimatedFees)}`);
|
|
327
|
+
this.logger.log(`Gateway: https://ipfs.opnet.org/ipfs/${pinResult.cid}`);
|
|
218
328
|
this.logger.log('');
|
|
219
329
|
} catch (error) {
|
|
220
|
-
this.logger.fail(
|
|
330
|
+
this.logger.fail(`Publishing failed`);
|
|
221
331
|
if (this.isUserCancelled(error)) {
|
|
222
332
|
this.logger.warn('Publishing cancelled.');
|
|
223
333
|
process.exit(0);
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope Register command - Register a new scope in the registry
|
|
3
|
+
*
|
|
4
|
+
* @module commands/ScopeRegisterCommand
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { confirm } from '@inquirer/prompts';
|
|
8
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
9
|
+
import { getRegistryContract, getScope, getScopePrice } from '../lib/registry.js';
|
|
10
|
+
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
11
|
+
import { CLIWallet } from '../lib/wallet.js';
|
|
12
|
+
import {
|
|
13
|
+
buildTransactionParams,
|
|
14
|
+
checkBalance,
|
|
15
|
+
formatSats,
|
|
16
|
+
getWalletAddress,
|
|
17
|
+
} from '../lib/transaction.js';
|
|
18
|
+
import { NetworkName } from '../types/index.js';
|
|
19
|
+
|
|
20
|
+
interface ScopeRegisterOptions {
|
|
21
|
+
network: string;
|
|
22
|
+
yes?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class ScopeRegisterCommand extends BaseCommand {
|
|
26
|
+
constructor() {
|
|
27
|
+
super('scope:register', 'Register a new scope in the registry');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected configure(): void {
|
|
31
|
+
this.command
|
|
32
|
+
.argument('<name>', 'Scope name (without @)')
|
|
33
|
+
.option('-n, --network <network>', 'Network', 'mainnet')
|
|
34
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
35
|
+
.action((name: string, options?: ScopeRegisterOptions) =>
|
|
36
|
+
this.execute(name, options || { network: 'mainnet' }),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private async execute(name: string, options?: ScopeRegisterOptions): Promise<void> {
|
|
41
|
+
try {
|
|
42
|
+
// Remove @ prefix if provided
|
|
43
|
+
const scopeName = name.startsWith('@') ? name.substring(1) : name;
|
|
44
|
+
|
|
45
|
+
// Validate scope name
|
|
46
|
+
if (!/^[a-z][a-z0-9-]*[a-z0-9]$/.test(scopeName) && !/^[a-z]$/.test(scopeName)) {
|
|
47
|
+
this.logger.fail('Invalid scope name');
|
|
48
|
+
this.logger.error('Scope name must:');
|
|
49
|
+
this.logger.error(' - Start with a lowercase letter');
|
|
50
|
+
this.logger.error(' - Contain only lowercase letters, numbers, and hyphens');
|
|
51
|
+
this.logger.error(' - End with a letter or number');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Load credentials
|
|
56
|
+
this.logger.info('Loading wallet...');
|
|
57
|
+
const credentials = loadCredentials();
|
|
58
|
+
if (!credentials || !canSign(credentials)) {
|
|
59
|
+
this.logger.fail('No credentials configured');
|
|
60
|
+
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
64
|
+
this.logger.success('Wallet loaded');
|
|
65
|
+
|
|
66
|
+
const network = (options?.network || 'mainnet') as NetworkName;
|
|
67
|
+
|
|
68
|
+
// Check if scope already exists
|
|
69
|
+
this.logger.info(`Checking if @${scopeName} is available...`);
|
|
70
|
+
const existingScope = await getScope(scopeName, network);
|
|
71
|
+
if (existingScope) {
|
|
72
|
+
this.logger.fail('Scope already registered');
|
|
73
|
+
this.logger.error(`Scope @${scopeName} is already registered.`);
|
|
74
|
+
this.logger.log(`Owner: ${existingScope.owner}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
this.logger.success(`Scope @${scopeName} is available`);
|
|
78
|
+
|
|
79
|
+
// Get scope registration price
|
|
80
|
+
this.logger.info('Fetching registration price...');
|
|
81
|
+
const scopePrice = await getScopePrice(network);
|
|
82
|
+
this.logger.success(`Registration price: ${formatSats(scopePrice)}`);
|
|
83
|
+
|
|
84
|
+
// Check wallet balance
|
|
85
|
+
this.logger.info('Checking wallet balance...');
|
|
86
|
+
const minRequired = scopePrice + 50_000n; // Price + estimated fees
|
|
87
|
+
const { sufficient, balance } = await checkBalance(wallet, network, minRequired);
|
|
88
|
+
if (!sufficient) {
|
|
89
|
+
this.logger.fail('Insufficient balance');
|
|
90
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
91
|
+
this.logger.error(`Required (approx): ${formatSats(minRequired)}`);
|
|
92
|
+
this.logger.error('Please fund your wallet before registering a scope.');
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
96
|
+
|
|
97
|
+
// Display summary
|
|
98
|
+
this.logger.log('');
|
|
99
|
+
this.logger.info('Scope Registration Summary');
|
|
100
|
+
this.logger.log('─'.repeat(50));
|
|
101
|
+
this.logger.log(`Scope: @${scopeName}`);
|
|
102
|
+
this.logger.log(`Price: ${formatSats(scopePrice)}`);
|
|
103
|
+
this.logger.log(`Network: ${options?.network}`);
|
|
104
|
+
this.logger.log(`Address: ${wallet.p2trAddress}`);
|
|
105
|
+
this.logger.log('');
|
|
106
|
+
|
|
107
|
+
// Confirmation
|
|
108
|
+
if (!options?.yes) {
|
|
109
|
+
const confirmed = await confirm({
|
|
110
|
+
message: `Register scope @${scopeName}?`,
|
|
111
|
+
default: true,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (!confirmed) {
|
|
115
|
+
this.logger.warn('Registration cancelled.');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Execute registration
|
|
121
|
+
this.logger.info('Registering scope...');
|
|
122
|
+
|
|
123
|
+
const sender = getWalletAddress(wallet);
|
|
124
|
+
const contract = getRegistryContract(network, sender);
|
|
125
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
126
|
+
|
|
127
|
+
const registerResult = await contract.registerScope(scopeName);
|
|
128
|
+
|
|
129
|
+
if (registerResult.revert) {
|
|
130
|
+
this.logger.fail('Registration would fail');
|
|
131
|
+
this.logger.error(`Reason: ${registerResult.revert}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (registerResult.estimatedGas) {
|
|
136
|
+
this.logger.info(`Estimated gas: ${registerResult.estimatedGas} sats`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const receipt = await registerResult.sendTransaction(txParams);
|
|
140
|
+
|
|
141
|
+
this.logger.log('');
|
|
142
|
+
this.logger.success('Scope registered successfully!');
|
|
143
|
+
this.logger.log('');
|
|
144
|
+
this.logger.log(`Scope: @${scopeName}`);
|
|
145
|
+
this.logger.log(`Owner: ${wallet.p2trAddress}`);
|
|
146
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
147
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
148
|
+
this.logger.log('');
|
|
149
|
+
this.logger.info(
|
|
150
|
+
'You can now publish packages under this scope using: opnet publish',
|
|
151
|
+
);
|
|
152
|
+
this.logger.log('');
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.logger.fail('Scope registration failed');
|
|
155
|
+
if (this.isUserCancelled(error)) {
|
|
156
|
+
this.logger.warn('Registration cancelled.');
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
this.exitWithError(this.formatError(error));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const scopeRegisterCommand = new ScopeRegisterCommand().getCommand();
|
|
@@ -7,12 +7,7 @@
|
|
|
7
7
|
import * as fs from 'fs';
|
|
8
8
|
import * as crypto from 'crypto';
|
|
9
9
|
import { BaseCommand } from './BaseCommand.js';
|
|
10
|
-
import {
|
|
11
|
-
buildOpnetBinary,
|
|
12
|
-
computeChecksum,
|
|
13
|
-
formatFileSize,
|
|
14
|
-
parseOpnetBinary,
|
|
15
|
-
} from '../lib/binary.js';
|
|
10
|
+
import { buildOpnetBinary, formatFileSize, parseOpnetBinary } from '../lib/binary.js';
|
|
16
11
|
import { CLIWallet } from '../lib/wallet.js';
|
|
17
12
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
18
13
|
|
|
@@ -77,27 +72,20 @@ export class SignCommand extends BaseCommand {
|
|
|
77
72
|
process.exit(1);
|
|
78
73
|
}
|
|
79
74
|
|
|
80
|
-
//
|
|
81
|
-
this.logger.info('Signing...');
|
|
82
|
-
const
|
|
83
|
-
const checksum =
|
|
84
|
-
metadataBytes,
|
|
85
|
-
parsed.bytecode,
|
|
86
|
-
parsed.proto ?? Buffer.alloc(0),
|
|
87
|
-
);
|
|
88
|
-
const signature = wallet.signMLDSA(checksum);
|
|
89
|
-
this.logger.success(`Signed (${formatFileSize(signature.length)} signature)`);
|
|
90
|
-
|
|
91
|
-
// Rebuild binary
|
|
92
|
-
this.logger.info('Rebuilding binary...');
|
|
93
|
-
const newBinary = buildOpnetBinary({
|
|
75
|
+
// Rebuild binary with signing
|
|
76
|
+
this.logger.info('Signing and rebuilding binary...');
|
|
77
|
+
const signFn = (checksum: Buffer) => wallet.signMLDSA(checksum);
|
|
78
|
+
const { binary: newBinary, checksum } = buildOpnetBinary({
|
|
94
79
|
mldsaLevel: wallet.securityLevel,
|
|
95
80
|
publicKey: wallet.mldsaPublicKey,
|
|
96
|
-
signature,
|
|
97
81
|
metadata: parsed.metadata,
|
|
98
82
|
bytecode: parsed.bytecode,
|
|
99
83
|
proto: parsed.proto ?? Buffer.alloc(0),
|
|
84
|
+
signFn,
|
|
100
85
|
});
|
|
86
|
+
this.logger.success(
|
|
87
|
+
`Signed (checksum: sha256:${checksum.toString('hex').substring(0, 16)}...)`,
|
|
88
|
+
);
|
|
101
89
|
this.logger.success(`Binary rebuilt (${formatFileSize(newBinary.length)})`);
|
|
102
90
|
|
|
103
91
|
// Write output
|