@btc-vision/cli 1.0.2 → 1.0.4
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/SearchCommand.js +1 -2
- 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 +5 -2
- 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/SearchCommand.ts +1 -2
- 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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
3
|
+
import { getRegistryContract, getScope, getScopePrice } from '../lib/registry.js';
|
|
4
|
+
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
5
|
+
import { CLIWallet } from '../lib/wallet.js';
|
|
6
|
+
import { buildTransactionParams, checkBalance, formatSats, getWalletAddress, } from '../lib/transaction.js';
|
|
7
|
+
export class ScopeRegisterCommand extends BaseCommand {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('scope:register', 'Register a new scope in the registry');
|
|
10
|
+
}
|
|
11
|
+
configure() {
|
|
12
|
+
this.command
|
|
13
|
+
.argument('<name>', 'Scope name (without @)')
|
|
14
|
+
.option('-n, --network <network>', 'Network', 'mainnet')
|
|
15
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
16
|
+
.action((name, options) => this.execute(name, options || { network: 'mainnet' }));
|
|
17
|
+
}
|
|
18
|
+
async execute(name, options) {
|
|
19
|
+
try {
|
|
20
|
+
const scopeName = name.startsWith('@') ? name.substring(1) : name;
|
|
21
|
+
if (!/^[a-z][a-z0-9-]*[a-z0-9]$/.test(scopeName) && !/^[a-z]$/.test(scopeName)) {
|
|
22
|
+
this.logger.fail('Invalid scope name');
|
|
23
|
+
this.logger.error('Scope name must:');
|
|
24
|
+
this.logger.error(' - Start with a lowercase letter');
|
|
25
|
+
this.logger.error(' - Contain only lowercase letters, numbers, and hyphens');
|
|
26
|
+
this.logger.error(' - End with a letter or number');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
this.logger.info('Loading wallet...');
|
|
30
|
+
const credentials = loadCredentials();
|
|
31
|
+
if (!credentials || !canSign(credentials)) {
|
|
32
|
+
this.logger.fail('No credentials configured');
|
|
33
|
+
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
37
|
+
this.logger.success('Wallet loaded');
|
|
38
|
+
const network = (options?.network || 'mainnet');
|
|
39
|
+
this.logger.info(`Checking if @${scopeName} is available...`);
|
|
40
|
+
const existingScope = await getScope(scopeName, network);
|
|
41
|
+
if (existingScope) {
|
|
42
|
+
this.logger.fail('Scope already registered');
|
|
43
|
+
this.logger.error(`Scope @${scopeName} is already registered.`);
|
|
44
|
+
this.logger.log(`Owner: ${existingScope.owner}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
this.logger.success(`Scope @${scopeName} is available`);
|
|
48
|
+
this.logger.info('Fetching registration price...');
|
|
49
|
+
const scopePrice = await getScopePrice(network);
|
|
50
|
+
this.logger.success(`Registration price: ${formatSats(scopePrice)}`);
|
|
51
|
+
this.logger.info('Checking wallet balance...');
|
|
52
|
+
const minRequired = scopePrice + 50000n;
|
|
53
|
+
const { sufficient, balance } = await checkBalance(wallet, network, minRequired);
|
|
54
|
+
if (!sufficient) {
|
|
55
|
+
this.logger.fail('Insufficient balance');
|
|
56
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
57
|
+
this.logger.error(`Required (approx): ${formatSats(minRequired)}`);
|
|
58
|
+
this.logger.error('Please fund your wallet before registering a scope.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
62
|
+
this.logger.log('');
|
|
63
|
+
this.logger.info('Scope Registration Summary');
|
|
64
|
+
this.logger.log('─'.repeat(50));
|
|
65
|
+
this.logger.log(`Scope: @${scopeName}`);
|
|
66
|
+
this.logger.log(`Price: ${formatSats(scopePrice)}`);
|
|
67
|
+
this.logger.log(`Network: ${options?.network}`);
|
|
68
|
+
this.logger.log(`Address: ${wallet.p2trAddress}`);
|
|
69
|
+
this.logger.log('');
|
|
70
|
+
if (!options?.yes) {
|
|
71
|
+
const confirmed = await confirm({
|
|
72
|
+
message: `Register scope @${scopeName}?`,
|
|
73
|
+
default: true,
|
|
74
|
+
});
|
|
75
|
+
if (!confirmed) {
|
|
76
|
+
this.logger.warn('Registration cancelled.');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
this.logger.info('Registering scope...');
|
|
81
|
+
const sender = getWalletAddress(wallet);
|
|
82
|
+
const contract = getRegistryContract(network, sender);
|
|
83
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
84
|
+
const registerResult = await contract.registerScope(scopeName);
|
|
85
|
+
if (registerResult.revert) {
|
|
86
|
+
this.logger.fail('Registration would fail');
|
|
87
|
+
this.logger.error(`Reason: ${registerResult.revert}`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
if (registerResult.estimatedGas) {
|
|
91
|
+
this.logger.info(`Estimated gas: ${registerResult.estimatedGas} sats`);
|
|
92
|
+
}
|
|
93
|
+
const receipt = await registerResult.sendTransaction(txParams);
|
|
94
|
+
this.logger.log('');
|
|
95
|
+
this.logger.success('Scope registered successfully!');
|
|
96
|
+
this.logger.log('');
|
|
97
|
+
this.logger.log(`Scope: @${scopeName}`);
|
|
98
|
+
this.logger.log(`Owner: ${wallet.p2trAddress}`);
|
|
99
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
100
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
101
|
+
this.logger.log('');
|
|
102
|
+
this.logger.info('You can now publish packages under this scope using: opnet publish');
|
|
103
|
+
this.logger.log('');
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.logger.fail('Scope registration failed');
|
|
107
|
+
if (this.isUserCancelled(error)) {
|
|
108
|
+
this.logger.warn('Registration cancelled.');
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
this.exitWithError(this.formatError(error));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export const scopeRegisterCommand = new ScopeRegisterCommand().getCommand();
|
|
@@ -79,8 +79,7 @@ export class SearchCommand extends BaseCommand {
|
|
|
79
79
|
this.logger.info(` OPNet Range: ${latestVersionInfo.opnetVersionRange}`);
|
|
80
80
|
this.logger.info(` IPFS CID: ${latestVersionInfo.ipfsCid}`);
|
|
81
81
|
this.logger.info(` Deprecated: ${latestVersionInfo.deprecated ? 'Yes' : 'No'}`);
|
|
82
|
-
|
|
83
|
-
this.logger.info(` Published: ${publishDate.toISOString().split('T')[0]}`);
|
|
82
|
+
this.logger.info(` Published at: Block ${latestVersionInfo.publishedAt}`);
|
|
84
83
|
}
|
|
85
84
|
this.logger.log('');
|
|
86
85
|
this.logger.info('Install with:');
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { confirm, input } from '@inquirer/prompts';
|
|
2
|
+
import { Address } from '@btc-vision/transaction';
|
|
2
3
|
import { BaseCommand } from './BaseCommand.js';
|
|
3
|
-
import { getPackage, getPendingScopeTransfer, getPendingTransfer, getScope, } from '../lib/registry.js';
|
|
4
|
+
import { getPackage, getPendingScopeTransfer, getPendingTransfer, getRegistryContract, getScope, } from '../lib/registry.js';
|
|
4
5
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
5
6
|
import { CLIWallet } from '../lib/wallet.js';
|
|
7
|
+
import { buildTransactionParams, checkBalance, formatSats, getWalletAddress, } from '../lib/transaction.js';
|
|
6
8
|
export class TransferCommand extends BaseCommand {
|
|
7
9
|
constructor() {
|
|
8
10
|
super('transfer', 'Initiate ownership transfer of a package or scope');
|
|
@@ -25,7 +27,7 @@ export class TransferCommand extends BaseCommand {
|
|
|
25
27
|
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
26
28
|
process.exit(1);
|
|
27
29
|
}
|
|
28
|
-
CLIWallet.fromCredentials(credentials);
|
|
30
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
29
31
|
this.logger.success('Wallet loaded');
|
|
30
32
|
const network = (options?.network || 'mainnet');
|
|
31
33
|
const isScope = name.startsWith('@') && !name.includes('/');
|
|
@@ -86,26 +88,63 @@ export class TransferCommand extends BaseCommand {
|
|
|
86
88
|
return;
|
|
87
89
|
}
|
|
88
90
|
}
|
|
91
|
+
this.logger.info('Checking wallet balance...');
|
|
92
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
93
|
+
if (!sufficient) {
|
|
94
|
+
this.logger.fail('Insufficient balance');
|
|
95
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
96
|
+
this.logger.error('Please fund your wallet before initiating transfer.');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
89
100
|
this.logger.info('Initiating transfer...');
|
|
101
|
+
const sender = getWalletAddress(wallet);
|
|
102
|
+
const contract = getRegistryContract(network, sender);
|
|
103
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
104
|
+
const newOwnerAddress = Address.fromString(targetOwner);
|
|
90
105
|
if (isScope) {
|
|
91
106
|
const scopeName = name.substring(1);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
const transferResult = await contract.initiateScopeTransfer(scopeName, newOwnerAddress);
|
|
108
|
+
if (transferResult.revert) {
|
|
109
|
+
this.logger.fail('Transfer initiation would fail');
|
|
110
|
+
this.logger.error(`Reason: ${transferResult.revert}`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
if (transferResult.estimatedGas) {
|
|
114
|
+
this.logger.info(`Estimated gas: ${transferResult.estimatedGas} sats`);
|
|
115
|
+
}
|
|
116
|
+
const receipt = await transferResult.sendTransaction(txParams);
|
|
117
|
+
this.logger.log('');
|
|
118
|
+
this.logger.success('Scope transfer initiated successfully!');
|
|
119
|
+
this.logger.log('');
|
|
120
|
+
this.logger.log(`Scope: ${name}`);
|
|
121
|
+
this.logger.log(`New Owner: ${targetOwner}`);
|
|
122
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
123
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
124
|
+
this.logger.log('');
|
|
125
|
+
this.logger.warn('Note: The new owner must call `opnet accept` to complete the transfer.');
|
|
97
126
|
}
|
|
98
127
|
else {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
128
|
+
const transferResult = await contract.initiateTransfer(name, newOwnerAddress);
|
|
129
|
+
if (transferResult.revert) {
|
|
130
|
+
this.logger.fail('Transfer initiation would fail');
|
|
131
|
+
this.logger.error(`Reason: ${transferResult.revert}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
if (transferResult.estimatedGas) {
|
|
135
|
+
this.logger.info(`Estimated gas: ${transferResult.estimatedGas} sats`);
|
|
136
|
+
}
|
|
137
|
+
const receipt = await transferResult.sendTransaction(txParams);
|
|
138
|
+
this.logger.log('');
|
|
139
|
+
this.logger.success('Package transfer initiated successfully!');
|
|
140
|
+
this.logger.log('');
|
|
141
|
+
this.logger.log(`Package: ${name}`);
|
|
142
|
+
this.logger.log(`New Owner: ${targetOwner}`);
|
|
143
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
144
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
145
|
+
this.logger.log('');
|
|
146
|
+
this.logger.warn('Note: The new owner must call `opnet accept` to complete the transfer.');
|
|
104
147
|
}
|
|
105
|
-
this.logger.info('Transfer (transaction pending)');
|
|
106
|
-
this.logger.log('');
|
|
107
|
-
this.logger.success('Transfer initiated!');
|
|
108
|
-
this.logger.warn('Note: Registry transaction support is coming soon.');
|
|
109
148
|
this.logger.log('');
|
|
110
149
|
}
|
|
111
150
|
catch (error) {
|
|
@@ -119,6 +158,13 @@ export class TransferCommand extends BaseCommand {
|
|
|
119
158
|
}
|
|
120
159
|
async handleCancel(name, isScope, network, options) {
|
|
121
160
|
this.logger.info('Checking pending transfer...');
|
|
161
|
+
const credentials = loadCredentials();
|
|
162
|
+
if (!credentials || !canSign(credentials)) {
|
|
163
|
+
this.logger.fail('No credentials configured');
|
|
164
|
+
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
122
168
|
if (isScope) {
|
|
123
169
|
const scopeName = name.substring(1);
|
|
124
170
|
const pending = await getPendingScopeTransfer(scopeName, network);
|
|
@@ -138,12 +184,35 @@ export class TransferCommand extends BaseCommand {
|
|
|
138
184
|
return;
|
|
139
185
|
}
|
|
140
186
|
}
|
|
187
|
+
this.logger.info('Checking wallet balance...');
|
|
188
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
189
|
+
if (!sufficient) {
|
|
190
|
+
this.logger.fail('Insufficient balance');
|
|
191
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
141
195
|
this.logger.info('Cancelling transfer...');
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
196
|
+
const sender = getWalletAddress(wallet);
|
|
197
|
+
const contract = getRegistryContract(network, sender);
|
|
198
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
199
|
+
const cancelResult = await contract.cancelScopeTransfer(scopeName);
|
|
200
|
+
if (cancelResult.revert) {
|
|
201
|
+
this.logger.fail('Cancellation would fail');
|
|
202
|
+
this.logger.error(`Reason: ${cancelResult.revert}`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
if (cancelResult.estimatedGas) {
|
|
206
|
+
this.logger.info(`Estimated gas: ${cancelResult.estimatedGas} sats`);
|
|
207
|
+
}
|
|
208
|
+
const receipt = await cancelResult.sendTransaction(txParams);
|
|
209
|
+
this.logger.log('');
|
|
210
|
+
this.logger.success('Scope transfer cancelled successfully!');
|
|
211
|
+
this.logger.log('');
|
|
212
|
+
this.logger.log(`Scope: ${name}`);
|
|
213
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
214
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
215
|
+
this.logger.log('');
|
|
147
216
|
}
|
|
148
217
|
else {
|
|
149
218
|
const pending = await getPendingTransfer(name, network);
|
|
@@ -163,17 +232,36 @@ export class TransferCommand extends BaseCommand {
|
|
|
163
232
|
return;
|
|
164
233
|
}
|
|
165
234
|
}
|
|
235
|
+
this.logger.info('Checking wallet balance...');
|
|
236
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
237
|
+
if (!sufficient) {
|
|
238
|
+
this.logger.fail('Insufficient balance');
|
|
239
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
166
243
|
this.logger.info('Cancelling transfer...');
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
244
|
+
const sender = getWalletAddress(wallet);
|
|
245
|
+
const contract = getRegistryContract(network, sender);
|
|
246
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
247
|
+
const cancelResult = await contract.cancelTransfer(name);
|
|
248
|
+
if (cancelResult.revert) {
|
|
249
|
+
this.logger.fail('Cancellation would fail');
|
|
250
|
+
this.logger.error(`Reason: ${cancelResult.revert}`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
if (cancelResult.estimatedGas) {
|
|
254
|
+
this.logger.info(`Estimated gas: ${cancelResult.estimatedGas} sats`);
|
|
255
|
+
}
|
|
256
|
+
const receipt = await cancelResult.sendTransaction(txParams);
|
|
257
|
+
this.logger.log('');
|
|
258
|
+
this.logger.success('Package transfer cancelled successfully!');
|
|
259
|
+
this.logger.log('');
|
|
260
|
+
this.logger.log(`Package: ${name}`);
|
|
261
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
262
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
263
|
+
this.logger.log('');
|
|
172
264
|
}
|
|
173
|
-
this.logger.log('');
|
|
174
|
-
this.logger.success('Transfer cancellation submitted!');
|
|
175
|
-
this.logger.warn('Note: Registry transaction support is coming soon.');
|
|
176
|
-
this.logger.log('');
|
|
177
265
|
}
|
|
178
266
|
}
|
|
179
267
|
export const transferCommand = new TransferCommand().getCommand();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { confirm } 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 UndeprecateCommand extends BaseCommand {
|
|
7
8
|
constructor() {
|
|
8
9
|
super('undeprecate', 'Remove deprecation from a package version');
|
|
@@ -24,7 +25,7 @@ export class UndeprecateCommand extends BaseCommand {
|
|
|
24
25
|
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
25
26
|
process.exit(1);
|
|
26
27
|
}
|
|
27
|
-
CLIWallet.fromCredentials(credentials);
|
|
28
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
28
29
|
this.logger.success('Wallet loaded');
|
|
29
30
|
this.logger.info('Fetching package info...');
|
|
30
31
|
const network = (options?.network || 'mainnet');
|
|
@@ -70,16 +71,36 @@ export class UndeprecateCommand extends BaseCommand {
|
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
73
|
}
|
|
74
|
+
this.logger.info('Checking wallet balance...');
|
|
75
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
76
|
+
if (!sufficient) {
|
|
77
|
+
this.logger.fail('Insufficient balance');
|
|
78
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
79
|
+
this.logger.error('Please fund your wallet before undeprecating.');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
73
83
|
this.logger.info('Removing deprecation...');
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
const sender = getWalletAddress(wallet);
|
|
85
|
+
const contract = getRegistryContract(network, sender);
|
|
86
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
87
|
+
const undeprecateResult = await contract.undeprecateVersion(packageName, version);
|
|
88
|
+
if (undeprecateResult.revert) {
|
|
89
|
+
this.logger.fail('Undeprecation would fail');
|
|
90
|
+
this.logger.error(`Reason: ${undeprecateResult.revert}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
if (undeprecateResult.estimatedGas) {
|
|
94
|
+
this.logger.info(`Estimated gas: ${undeprecateResult.estimatedGas} sats`);
|
|
95
|
+
}
|
|
96
|
+
const receipt = await undeprecateResult.sendTransaction(txParams);
|
|
97
|
+
this.logger.log('');
|
|
98
|
+
this.logger.success('Deprecation removed successfully!');
|
|
80
99
|
this.logger.log('');
|
|
81
|
-
this.logger.
|
|
82
|
-
this.logger.
|
|
100
|
+
this.logger.log(`Package: ${packageName}`);
|
|
101
|
+
this.logger.log(`Version: ${version}`);
|
|
102
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
103
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
83
104
|
this.logger.log('');
|
|
84
105
|
}
|
|
85
106
|
catch (error) {
|
package/build/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { deprecateCommand } from './commands/DeprecateCommand.js';
|
|
|
16
16
|
import { undeprecateCommand } from './commands/UndeprecateCommand.js';
|
|
17
17
|
import { transferCommand } from './commands/TransferCommand.js';
|
|
18
18
|
import { acceptCommand } from './commands/AcceptCommand.js';
|
|
19
|
+
import { scopeRegisterCommand } from './commands/ScopeRegisterCommand.js';
|
|
19
20
|
import { installCommand } from './commands/InstallCommand.js';
|
|
20
21
|
import { updateCommand } from './commands/UpdateCommand.js';
|
|
21
22
|
import { listCommand } from './commands/ListCommand.js';
|
|
@@ -41,6 +42,7 @@ program.addCommand(deprecateCommand);
|
|
|
41
42
|
program.addCommand(undeprecateCommand);
|
|
42
43
|
program.addCommand(transferCommand);
|
|
43
44
|
program.addCommand(acceptCommand);
|
|
45
|
+
program.addCommand(scopeRegisterCommand);
|
|
44
46
|
program.addCommand(installCommand);
|
|
45
47
|
program.addCommand(updateCommand);
|
|
46
48
|
program.addCommand(listCommand);
|
package/build/lib/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CLIConfig, CLIMldsaLevel, NetworkName } from '../types/index.js';
|
|
2
|
+
export declare const DEFAULT_CONFIG: CLIConfig;
|
|
2
3
|
export declare function ensureConfigDir(): void;
|
|
3
4
|
export declare function loadConfig(): CLIConfig;
|
|
4
5
|
export declare function saveConfig(config: CLIConfig): void;
|
package/build/lib/config.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
const CONFIG_DIR = path.join(os.homedir(), '.opnet');
|
|
5
5
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
6
|
-
const DEFAULT_CONFIG = {
|
|
6
|
+
export const DEFAULT_CONFIG = {
|
|
7
7
|
defaultNetwork: 'regtest',
|
|
8
8
|
rpcUrls: {
|
|
9
9
|
mainnet: 'https://api.opnet.org',
|
|
@@ -19,11 +19,12 @@ const DEFAULT_CONFIG = {
|
|
|
19
19
|
],
|
|
20
20
|
ipfsPinningEndpoint: 'https://ipfs.opnet.org/api/v0/add',
|
|
21
21
|
ipfsPinningApiKey: '',
|
|
22
|
+
ipfsPinningSecret: '',
|
|
22
23
|
ipfsPinningAuthHeader: 'Authorization',
|
|
23
24
|
registryAddresses: {
|
|
24
25
|
mainnet: '',
|
|
25
26
|
testnet: '',
|
|
26
|
-
regtest: '',
|
|
27
|
+
regtest: '0x0737d17d0eff9915208f3c20ed7659587889bc94d25972672b3a6c03ff4ddbcc',
|
|
27
28
|
},
|
|
28
29
|
defaultMldsaLevel: 44,
|
|
29
30
|
indexerUrl: 'https://indexer.opnet.org',
|
package/build/lib/ipfs.js
CHANGED
|
@@ -42,86 +42,95 @@ async function httpRequest(url, options) {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
export async function pinToIPFS(data, name) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const boundary = '----FormBoundary' + Math.random().toString(36).substring(2);
|
|
51
|
-
const fileName = name || 'plugin.opnet';
|
|
52
|
-
const formParts = [];
|
|
53
|
-
formParts.push(Buffer.from(`--${boundary}\r\n` +
|
|
54
|
-
`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n` +
|
|
55
|
-
`Content-Type: application/octet-stream\r\n\r\n`));
|
|
56
|
-
formParts.push(data);
|
|
57
|
-
formParts.push(Buffer.from('\r\n'));
|
|
58
|
-
formParts.push(Buffer.from(`--${boundary}--\r\n`));
|
|
59
|
-
const body = Buffer.concat(formParts);
|
|
60
|
-
const headers = {
|
|
61
|
-
'Content-Type': `multipart/form-data; boundary=${boundary}`,
|
|
62
|
-
'Content-Length': body.length.toString(),
|
|
63
|
-
};
|
|
64
|
-
if (config.ipfsPinningAuthHeader) {
|
|
65
|
-
const [headerName, headerValue] = config.ipfsPinningAuthHeader
|
|
66
|
-
.split(':')
|
|
67
|
-
.map((s) => s.trim());
|
|
68
|
-
if (headerName && headerValue) {
|
|
69
|
-
headers[headerName] = headerValue;
|
|
45
|
+
try {
|
|
46
|
+
const config = loadConfig();
|
|
47
|
+
const endpoint = config.ipfsPinningEndpoint;
|
|
48
|
+
if (!endpoint) {
|
|
49
|
+
throw new Error('IPFS pinning endpoint not configured. Run `opnet config set ipfsPinningEndpoint <url>`');
|
|
70
50
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
51
|
+
const boundary = '----FormBoundary' + Math.random().toString(36).substring(2);
|
|
52
|
+
const fileName = name || 'plugin.opnet';
|
|
53
|
+
const formParts = [];
|
|
54
|
+
formParts.push(Buffer.from(`--${boundary}\r\n` +
|
|
55
|
+
`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n` +
|
|
56
|
+
`Content-Type: application/octet-stream\r\n\r\n`));
|
|
57
|
+
formParts.push(data);
|
|
58
|
+
formParts.push(Buffer.from('\r\n'));
|
|
59
|
+
formParts.push(Buffer.from(`--${boundary}--\r\n`));
|
|
60
|
+
const body = Buffer.concat(formParts);
|
|
61
|
+
const headers = {
|
|
62
|
+
'Content-Type': `multipart/form-data; boundary=${boundary}`,
|
|
63
|
+
'Content-Length': body.length.toString(),
|
|
64
|
+
};
|
|
65
|
+
if (config.ipfsPinningAuthHeader) {
|
|
66
|
+
const [headerName, headerValue] = config.ipfsPinningAuthHeader
|
|
67
|
+
.split(':')
|
|
68
|
+
.map((s) => s.trim());
|
|
69
|
+
if (headerName && headerValue) {
|
|
70
|
+
headers[headerName] = headerValue;
|
|
71
|
+
}
|
|
84
72
|
}
|
|
73
|
+
else if (config.ipfsPinningApiKey) {
|
|
74
|
+
headers['Authorization'] = `Bearer ${config.ipfsPinningApiKey}`;
|
|
75
|
+
}
|
|
76
|
+
const url = new URL(endpoint);
|
|
77
|
+
let requestUrl;
|
|
78
|
+
if (url.hostname.includes('ipfs.opnet.org')) {
|
|
79
|
+
requestUrl = endpoint;
|
|
80
|
+
}
|
|
81
|
+
else if (url.hostname.includes('pinata')) {
|
|
82
|
+
requestUrl = 'https://api.pinata.cloud/pinning/pinFileToIPFS';
|
|
83
|
+
if (config.ipfsPinningApiKey && !config.ipfsPinningApiKey.startsWith('eyJ')) {
|
|
84
|
+
headers['pinata_api_key'] = config.ipfsPinningApiKey;
|
|
85
|
+
if (config.ipfsPinningSecret) {
|
|
86
|
+
headers['pinata_secret_api_key'] = config.ipfsPinningSecret;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (url.hostname.includes('web3.storage') || url.hostname.includes('w3s.link')) {
|
|
91
|
+
requestUrl = endpoint.endsWith('/') ? endpoint + 'upload' : endpoint + '/upload';
|
|
92
|
+
}
|
|
93
|
+
else if (url.hostname.includes('nft.storage')) {
|
|
94
|
+
requestUrl = 'https://api.nft.storage/upload';
|
|
95
|
+
}
|
|
96
|
+
else if (url.pathname.includes('/api/v0/')) {
|
|
97
|
+
requestUrl = endpoint;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
requestUrl = endpoint.endsWith('/') ? endpoint + 'pins' : endpoint + '/pins';
|
|
101
|
+
}
|
|
102
|
+
const response = await httpRequest(requestUrl, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers,
|
|
105
|
+
body,
|
|
106
|
+
timeout: 120000,
|
|
107
|
+
});
|
|
108
|
+
const result = JSON.parse(response.toString());
|
|
109
|
+
let cid;
|
|
110
|
+
if (typeof result.IpfsHash === 'string') {
|
|
111
|
+
cid = result.IpfsHash;
|
|
112
|
+
}
|
|
113
|
+
else if (typeof result.cid === 'string') {
|
|
114
|
+
cid = result.cid;
|
|
115
|
+
}
|
|
116
|
+
else if (typeof result.Hash === 'string') {
|
|
117
|
+
cid = result.Hash;
|
|
118
|
+
}
|
|
119
|
+
else if (result.value &&
|
|
120
|
+
typeof result.value.cid === 'string') {
|
|
121
|
+
cid = result.value.cid;
|
|
122
|
+
}
|
|
123
|
+
if (!cid) {
|
|
124
|
+
throw new Error(`Failed to extract CID from pinning response: ${JSON.stringify(result)}`);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
cid,
|
|
128
|
+
size: data.length,
|
|
129
|
+
};
|
|
85
130
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
else if (url.hostname.includes('nft.storage')) {
|
|
90
|
-
requestUrl = 'https://api.nft.storage/upload';
|
|
91
|
-
}
|
|
92
|
-
else if (url.pathname.includes('/api/v0/')) {
|
|
93
|
-
requestUrl = endpoint;
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
requestUrl = endpoint.endsWith('/') ? endpoint + 'pins' : endpoint + '/pins';
|
|
97
|
-
}
|
|
98
|
-
const response = await httpRequest(requestUrl, {
|
|
99
|
-
method: 'POST',
|
|
100
|
-
headers,
|
|
101
|
-
body,
|
|
102
|
-
timeout: 120000,
|
|
103
|
-
});
|
|
104
|
-
const result = JSON.parse(response.toString());
|
|
105
|
-
let cid;
|
|
106
|
-
if (typeof result.IpfsHash === 'string') {
|
|
107
|
-
cid = result.IpfsHash;
|
|
108
|
-
}
|
|
109
|
-
else if (typeof result.cid === 'string') {
|
|
110
|
-
cid = result.cid;
|
|
111
|
-
}
|
|
112
|
-
else if (typeof result.Hash === 'string') {
|
|
113
|
-
cid = result.Hash;
|
|
114
|
-
}
|
|
115
|
-
else if (result.value && typeof result.value.cid === 'string') {
|
|
116
|
-
cid = result.value.cid;
|
|
117
|
-
}
|
|
118
|
-
if (!cid) {
|
|
119
|
-
throw new Error(`Failed to extract CID from pinning response: ${JSON.stringify(result)}`);
|
|
131
|
+
catch (e) {
|
|
132
|
+
throw new Error(`IPFS pinning failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
120
133
|
}
|
|
121
|
-
return {
|
|
122
|
-
cid,
|
|
123
|
-
size: data.length,
|
|
124
|
-
};
|
|
125
134
|
}
|
|
126
135
|
export async function fetchFromIPFS(cid) {
|
|
127
136
|
const config = loadConfig();
|
package/build/lib/registry.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface PendingTransferInfo {
|
|
|
32
32
|
pendingOwner: Address;
|
|
33
33
|
initiatedAt: bigint;
|
|
34
34
|
}
|
|
35
|
-
export declare function getRegistryContract(network?: NetworkName): IPackageRegistry;
|
|
35
|
+
export declare function getRegistryContract(network?: NetworkName, sender?: Address): IPackageRegistry;
|
|
36
36
|
export declare function clearRegistryCache(): void;
|
|
37
37
|
export declare function getScope(scopeName: string, network?: NetworkName): Promise<ScopeInfo | null>;
|
|
38
38
|
export declare function getScopeOwner(scopeName: string, network?: NetworkName): Promise<Address | null>;
|
package/build/lib/registry.js
CHANGED
|
@@ -5,10 +5,10 @@ import { getNetwork } from './wallet.js';
|
|
|
5
5
|
import { loadConfig } from './config.js';
|
|
6
6
|
import { PACKAGE_REGISTRY_ABI } from './PackageRegistry.abi.js';
|
|
7
7
|
const registryCache = new Map();
|
|
8
|
-
export function getRegistryContract(network) {
|
|
8
|
+
export function getRegistryContract(network, sender) {
|
|
9
9
|
const config = loadConfig();
|
|
10
10
|
const targetNetwork = network || config.defaultNetwork;
|
|
11
|
-
const cacheKey = targetNetwork;
|
|
11
|
+
const cacheKey = sender ? `${targetNetwork}:${sender.toHex()}` : targetNetwork;
|
|
12
12
|
const cached = registryCache.get(cacheKey);
|
|
13
13
|
if (cached) {
|
|
14
14
|
return cached;
|
|
@@ -16,7 +16,7 @@ export function getRegistryContract(network) {
|
|
|
16
16
|
const provider = getProvider(targetNetwork);
|
|
17
17
|
const registryAddress = getRegistryContractAddress(targetNetwork);
|
|
18
18
|
const bitcoinNetwork = getNetwork(targetNetwork);
|
|
19
|
-
const contract = getContract(registryAddress, PACKAGE_REGISTRY_ABI, provider, bitcoinNetwork);
|
|
19
|
+
const contract = getContract(registryAddress, PACKAGE_REGISTRY_ABI, provider, bitcoinNetwork, sender);
|
|
20
20
|
registryCache.set(cacheKey, contract);
|
|
21
21
|
return contract;
|
|
22
22
|
}
|