@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.
Files changed (39) 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/SearchCommand.js +1 -2
  10. package/build/commands/TransferCommand.js +118 -30
  11. package/build/commands/UndeprecateCommand.js +31 -10
  12. package/build/index.js +2 -0
  13. package/build/lib/config.d.ts +1 -0
  14. package/build/lib/config.js +3 -2
  15. package/build/lib/ipfs.js +85 -76
  16. package/build/lib/registry.d.ts +1 -1
  17. package/build/lib/registry.js +3 -3
  18. package/build/lib/transaction.d.ts +27 -0
  19. package/build/lib/transaction.js +91 -0
  20. package/build/lib/wallet.d.ts +7 -7
  21. package/build/lib/wallet.js +3 -6
  22. package/build/types/index.d.ts +1 -0
  23. package/package.json +5 -2
  24. package/src/commands/AcceptCommand.ts +89 -16
  25. package/src/commands/ConfigCommand.ts +2 -29
  26. package/src/commands/DeprecateCommand.ts +48 -11
  27. package/src/commands/InitCommand.ts +0 -6
  28. package/src/commands/PublishCommand.ts +138 -28
  29. package/src/commands/ScopeRegisterCommand.ts +164 -0
  30. package/src/commands/SearchCommand.ts +1 -2
  31. package/src/commands/TransferCommand.ts +159 -31
  32. package/src/commands/UndeprecateCommand.ts +43 -10
  33. package/src/index.ts +2 -0
  34. package/src/lib/config.ts +3 -2
  35. package/src/lib/ipfs.ts +113 -99
  36. package/src/lib/registry.ts +5 -2
  37. package/src/lib/transaction.ts +205 -0
  38. package/src/lib/wallet.ts +10 -19
  39. 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
- const publishDate = new Date(Number(latestVersionInfo.publishedAt) * 1000);
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
- this.logger.warn('Transfer transaction required.');
93
- this.logger.log('Transaction would call: initiateScopeTransfer(');
94
- this.logger.log(` scopeName: "${scopeName}",`);
95
- this.logger.log(` newOwner: "${targetOwner}"`);
96
- this.logger.log(')');
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
- this.logger.warn('Transfer transaction required.');
100
- this.logger.log('Transaction would call: initiateTransfer(');
101
- this.logger.log(` packageName: "${name}",`);
102
- this.logger.log(` newOwner: "${targetOwner}"`);
103
- this.logger.log(')');
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
- this.logger.warn('Cancellation transaction required.');
143
- this.logger.log('Transaction would call: cancelScopeTransfer(');
144
- this.logger.log(` scopeName: "${scopeName}"`);
145
- this.logger.log(')');
146
- this.logger.info('Cancellation (transaction pending)');
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
- this.logger.warn('Cancellation transaction required.');
168
- this.logger.log('Transaction would call: cancelTransfer(');
169
- this.logger.log(` packageName: "${name}"`);
170
- this.logger.log(')');
171
- this.logger.info('Cancellation (transaction pending)');
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
- this.logger.warn('Undeprecation transaction required.');
75
- this.logger.log('Transaction would call: undeprecateVersion(');
76
- this.logger.log(` packageName: "${packageName}",`);
77
- this.logger.log(` version: "${version}"`);
78
- this.logger.log(')');
79
- this.logger.info('Undeprecation (transaction pending)');
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.success('Undeprecation submitted!');
82
- this.logger.warn('Note: Registry transaction support is coming soon.');
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);
@@ -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;
@@ -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
- const config = loadConfig();
46
- const endpoint = config.ipfsPinningEndpoint;
47
- if (!endpoint) {
48
- throw new Error('IPFS pinning endpoint not configured. Run `opnet config set ipfsPinningEndpoint <url>`');
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
- else if (config.ipfsPinningApiKey) {
73
- headers['Authorization'] = `Bearer ${config.ipfsPinningApiKey}`;
74
- }
75
- const url = new URL(endpoint);
76
- let requestUrl;
77
- if (url.hostname.includes('ipfs.opnet.org')) {
78
- requestUrl = endpoint;
79
- }
80
- else if (url.hostname.includes('pinata')) {
81
- requestUrl = 'https://api.pinata.cloud/pinning/pinFileToIPFS';
82
- if (config.ipfsPinningApiKey) {
83
- headers['pinata_api_key'] = config.ipfsPinningApiKey;
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
- else if (url.hostname.includes('web3.storage') || url.hostname.includes('w3s.link')) {
87
- requestUrl = endpoint.endsWith('/') ? endpoint + 'upload' : endpoint + '/upload';
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();
@@ -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>;
@@ -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
  }