@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
|
@@ -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();
|
|
@@ -122,8 +122,7 @@ export class SearchCommand extends BaseCommand {
|
|
|
122
122
|
this.logger.info(` IPFS CID: ${latestVersionInfo.ipfsCid}`);
|
|
123
123
|
this.logger.info(` Deprecated: ${latestVersionInfo.deprecated ? 'Yes' : 'No'}`);
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
this.logger.info(` Published: ${publishDate.toISOString().split('T')[0]}`);
|
|
125
|
+
this.logger.info(` Published at: Block ${latestVersionInfo.publishedAt}`);
|
|
127
126
|
}
|
|
128
127
|
|
|
129
128
|
this.logger.log('');
|
|
@@ -5,15 +5,23 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { confirm, input } from '@inquirer/prompts';
|
|
8
|
+
import { Address } from '@btc-vision/transaction';
|
|
8
9
|
import { BaseCommand } from './BaseCommand.js';
|
|
9
10
|
import {
|
|
10
11
|
getPackage,
|
|
11
12
|
getPendingScopeTransfer,
|
|
12
13
|
getPendingTransfer,
|
|
14
|
+
getRegistryContract,
|
|
13
15
|
getScope,
|
|
14
16
|
} from '../lib/registry.js';
|
|
15
17
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
16
18
|
import { CLIWallet } from '../lib/wallet.js';
|
|
19
|
+
import {
|
|
20
|
+
buildTransactionParams,
|
|
21
|
+
checkBalance,
|
|
22
|
+
formatSats,
|
|
23
|
+
getWalletAddress,
|
|
24
|
+
} from '../lib/transaction.js';
|
|
17
25
|
import { NetworkName } from '../types/index.js';
|
|
18
26
|
|
|
19
27
|
interface TransferOptions {
|
|
@@ -53,7 +61,7 @@ export class TransferCommand extends BaseCommand {
|
|
|
53
61
|
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
54
62
|
process.exit(1);
|
|
55
63
|
}
|
|
56
|
-
CLIWallet.fromCredentials(credentials);
|
|
64
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
57
65
|
this.logger.success('Wallet loaded');
|
|
58
66
|
|
|
59
67
|
const network = (options?.network || 'mainnet') as NetworkName;
|
|
@@ -129,28 +137,82 @@ export class TransferCommand extends BaseCommand {
|
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
|
|
140
|
+
// Check wallet balance
|
|
141
|
+
this.logger.info('Checking wallet balance...');
|
|
142
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
143
|
+
if (!sufficient) {
|
|
144
|
+
this.logger.fail('Insufficient balance');
|
|
145
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
146
|
+
this.logger.error('Please fund your wallet before initiating transfer.');
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
150
|
+
|
|
132
151
|
// Execute transfer
|
|
133
152
|
this.logger.info('Initiating transfer...');
|
|
134
153
|
|
|
154
|
+
const sender = getWalletAddress(wallet);
|
|
155
|
+
const contract = getRegistryContract(network, sender);
|
|
156
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
157
|
+
const newOwnerAddress = Address.fromString(targetOwner);
|
|
158
|
+
|
|
135
159
|
if (isScope) {
|
|
136
160
|
const scopeName = name.substring(1);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
161
|
+
const transferResult = await contract.initiateScopeTransfer(
|
|
162
|
+
scopeName,
|
|
163
|
+
newOwnerAddress,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
if (transferResult.revert) {
|
|
167
|
+
this.logger.fail('Transfer initiation would fail');
|
|
168
|
+
this.logger.error(`Reason: ${transferResult.revert}`);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (transferResult.estimatedGas) {
|
|
173
|
+
this.logger.info(`Estimated gas: ${transferResult.estimatedGas} sats`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const receipt = await transferResult.sendTransaction(txParams);
|
|
177
|
+
|
|
178
|
+
this.logger.log('');
|
|
179
|
+
this.logger.success('Scope transfer initiated successfully!');
|
|
180
|
+
this.logger.log('');
|
|
181
|
+
this.logger.log(`Scope: ${name}`);
|
|
182
|
+
this.logger.log(`New Owner: ${targetOwner}`);
|
|
183
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
184
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
185
|
+
this.logger.log('');
|
|
186
|
+
this.logger.warn(
|
|
187
|
+
'Note: The new owner must call `opnet accept` to complete the transfer.',
|
|
188
|
+
);
|
|
142
189
|
} else {
|
|
143
|
-
|
|
144
|
-
this.logger.log('Transaction would call: initiateTransfer(');
|
|
145
|
-
this.logger.log(` packageName: "${name}",`);
|
|
146
|
-
this.logger.log(` newOwner: "${targetOwner}"`);
|
|
147
|
-
this.logger.log(')');
|
|
148
|
-
}
|
|
149
|
-
this.logger.info('Transfer (transaction pending)');
|
|
190
|
+
const transferResult = await contract.initiateTransfer(name, newOwnerAddress);
|
|
150
191
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
192
|
+
if (transferResult.revert) {
|
|
193
|
+
this.logger.fail('Transfer initiation would fail');
|
|
194
|
+
this.logger.error(`Reason: ${transferResult.revert}`);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (transferResult.estimatedGas) {
|
|
199
|
+
this.logger.info(`Estimated gas: ${transferResult.estimatedGas} sats`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const receipt = await transferResult.sendTransaction(txParams);
|
|
203
|
+
|
|
204
|
+
this.logger.log('');
|
|
205
|
+
this.logger.success('Package transfer initiated successfully!');
|
|
206
|
+
this.logger.log('');
|
|
207
|
+
this.logger.log(`Package: ${name}`);
|
|
208
|
+
this.logger.log(`New Owner: ${targetOwner}`);
|
|
209
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
210
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
211
|
+
this.logger.log('');
|
|
212
|
+
this.logger.warn(
|
|
213
|
+
'Note: The new owner must call `opnet accept` to complete the transfer.',
|
|
214
|
+
);
|
|
215
|
+
}
|
|
154
216
|
this.logger.log('');
|
|
155
217
|
} catch (error) {
|
|
156
218
|
this.logger.fail('Transfer failed');
|
|
@@ -170,6 +232,15 @@ export class TransferCommand extends BaseCommand {
|
|
|
170
232
|
): Promise<void> {
|
|
171
233
|
this.logger.info('Checking pending transfer...');
|
|
172
234
|
|
|
235
|
+
// Load wallet for cancellation
|
|
236
|
+
const credentials = loadCredentials();
|
|
237
|
+
if (!credentials || !canSign(credentials)) {
|
|
238
|
+
this.logger.fail('No credentials configured');
|
|
239
|
+
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
243
|
+
|
|
173
244
|
if (isScope) {
|
|
174
245
|
const scopeName = name.substring(1);
|
|
175
246
|
const pending = await getPendingScopeTransfer(scopeName, network);
|
|
@@ -191,12 +262,43 @@ export class TransferCommand extends BaseCommand {
|
|
|
191
262
|
}
|
|
192
263
|
}
|
|
193
264
|
|
|
265
|
+
// Check wallet balance
|
|
266
|
+
this.logger.info('Checking wallet balance...');
|
|
267
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
268
|
+
if (!sufficient) {
|
|
269
|
+
this.logger.fail('Insufficient balance');
|
|
270
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
274
|
+
|
|
194
275
|
this.logger.info('Cancelling transfer...');
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
276
|
+
|
|
277
|
+
const sender = getWalletAddress(wallet);
|
|
278
|
+
const contract = getRegistryContract(network, sender);
|
|
279
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
280
|
+
|
|
281
|
+
const cancelResult = await contract.cancelScopeTransfer(scopeName);
|
|
282
|
+
|
|
283
|
+
if (cancelResult.revert) {
|
|
284
|
+
this.logger.fail('Cancellation would fail');
|
|
285
|
+
this.logger.error(`Reason: ${cancelResult.revert}`);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (cancelResult.estimatedGas) {
|
|
290
|
+
this.logger.info(`Estimated gas: ${cancelResult.estimatedGas} sats`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const receipt = await cancelResult.sendTransaction(txParams);
|
|
294
|
+
|
|
295
|
+
this.logger.log('');
|
|
296
|
+
this.logger.success('Scope transfer cancelled successfully!');
|
|
297
|
+
this.logger.log('');
|
|
298
|
+
this.logger.log(`Scope: ${name}`);
|
|
299
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
300
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
301
|
+
this.logger.log('');
|
|
200
302
|
} else {
|
|
201
303
|
const pending = await getPendingTransfer(name, network);
|
|
202
304
|
if (!pending) {
|
|
@@ -217,18 +319,44 @@ export class TransferCommand extends BaseCommand {
|
|
|
217
319
|
}
|
|
218
320
|
}
|
|
219
321
|
|
|
322
|
+
// Check wallet balance
|
|
323
|
+
this.logger.info('Checking wallet balance...');
|
|
324
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
325
|
+
if (!sufficient) {
|
|
326
|
+
this.logger.fail('Insufficient balance');
|
|
327
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
331
|
+
|
|
220
332
|
this.logger.info('Cancelling transfer...');
|
|
221
|
-
this.logger.warn('Cancellation transaction required.');
|
|
222
|
-
this.logger.log('Transaction would call: cancelTransfer(');
|
|
223
|
-
this.logger.log(` packageName: "${name}"`);
|
|
224
|
-
this.logger.log(')');
|
|
225
|
-
this.logger.info('Cancellation (transaction pending)');
|
|
226
|
-
}
|
|
227
333
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
334
|
+
const sender = getWalletAddress(wallet);
|
|
335
|
+
const contract = getRegistryContract(network, sender);
|
|
336
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
337
|
+
|
|
338
|
+
const cancelResult = await contract.cancelTransfer(name);
|
|
339
|
+
|
|
340
|
+
if (cancelResult.revert) {
|
|
341
|
+
this.logger.fail('Cancellation would fail');
|
|
342
|
+
this.logger.error(`Reason: ${cancelResult.revert}`);
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (cancelResult.estimatedGas) {
|
|
347
|
+
this.logger.info(`Estimated gas: ${cancelResult.estimatedGas} sats`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const receipt = await cancelResult.sendTransaction(txParams);
|
|
351
|
+
|
|
352
|
+
this.logger.log('');
|
|
353
|
+
this.logger.success('Package transfer cancelled successfully!');
|
|
354
|
+
this.logger.log('');
|
|
355
|
+
this.logger.log(`Package: ${name}`);
|
|
356
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
357
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
358
|
+
this.logger.log('');
|
|
359
|
+
}
|
|
232
360
|
}
|
|
233
361
|
}
|
|
234
362
|
|