@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
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/manifest.js
CHANGED
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
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Address } from '@btc-vision/transaction';
|
|
2
|
+
import { TransactionParameters } from 'opnet';
|
|
3
|
+
import { CLIWallet } from './wallet.js';
|
|
4
|
+
import { NetworkName } from '../types/index.js';
|
|
5
|
+
import { PsbtOutputExtended } from '@btc-vision/bitcoin';
|
|
6
|
+
export declare const DEFAULT_MAX_SAT_TO_SPEND = 100000n;
|
|
7
|
+
export declare const DEFAULT_FEE_RATE = 6;
|
|
8
|
+
export declare function buildTransactionParams(wallet: CLIWallet, network: NetworkName, maxSatToSpend?: bigint, feeRate?: number, extra?: PsbtOutputExtended): TransactionParameters;
|
|
9
|
+
export declare function getWalletAddress(wallet: CLIWallet): Address;
|
|
10
|
+
export declare function formatSats(sats: bigint): string;
|
|
11
|
+
export declare function checkBalance(wallet: CLIWallet, network: NetworkName, minBalance?: bigint): Promise<{
|
|
12
|
+
sufficient: boolean;
|
|
13
|
+
balance: bigint;
|
|
14
|
+
}>;
|
|
15
|
+
export declare const DEFAULT_POLL_INTERVAL = 10000;
|
|
16
|
+
export declare const DEFAULT_MAX_WAIT_TIME = 600000;
|
|
17
|
+
export interface TransactionConfirmationResult {
|
|
18
|
+
confirmed: boolean;
|
|
19
|
+
blockNumber?: bigint;
|
|
20
|
+
revert?: string;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function waitForTransactionConfirmation(txHash: string, network: NetworkName, options?: {
|
|
24
|
+
pollInterval?: number;
|
|
25
|
+
maxWaitTime?: number;
|
|
26
|
+
message?: string;
|
|
27
|
+
}): Promise<TransactionConfirmationResult>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import ora from 'ora';
|
|
2
|
+
import { getNetwork } from './wallet.js';
|
|
3
|
+
import { getProvider } from './provider.js';
|
|
4
|
+
export const DEFAULT_MAX_SAT_TO_SPEND = 100000n;
|
|
5
|
+
export const DEFAULT_FEE_RATE = 6;
|
|
6
|
+
export function buildTransactionParams(wallet, network, maxSatToSpend = DEFAULT_MAX_SAT_TO_SPEND, feeRate = DEFAULT_FEE_RATE, extra) {
|
|
7
|
+
const bitcoinNetwork = getNetwork(network);
|
|
8
|
+
return {
|
|
9
|
+
signer: wallet.keypair,
|
|
10
|
+
mldsaSigner: wallet.mldsaKeypair,
|
|
11
|
+
refundTo: wallet.p2trAddress,
|
|
12
|
+
maximumAllowedSatToSpend: maxSatToSpend,
|
|
13
|
+
network: bitcoinNetwork,
|
|
14
|
+
feeRate,
|
|
15
|
+
extraOutputs: extra ? [extra] : undefined,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function getWalletAddress(wallet) {
|
|
19
|
+
return wallet.address;
|
|
20
|
+
}
|
|
21
|
+
export function formatSats(sats) {
|
|
22
|
+
const btc = Number(sats) / 100_000_000;
|
|
23
|
+
if (btc >= 0.001) {
|
|
24
|
+
return `${sats} sats (${btc.toFixed(8)} BTC)`;
|
|
25
|
+
}
|
|
26
|
+
return `${sats} sats`;
|
|
27
|
+
}
|
|
28
|
+
export async function checkBalance(wallet, network, minBalance = 10000n) {
|
|
29
|
+
const provider = getProvider(network);
|
|
30
|
+
const balance = await provider.getBalance(wallet.p2trAddress);
|
|
31
|
+
return {
|
|
32
|
+
sufficient: balance >= minBalance,
|
|
33
|
+
balance,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export const DEFAULT_POLL_INTERVAL = 10_000;
|
|
37
|
+
export const DEFAULT_MAX_WAIT_TIME = 600_000;
|
|
38
|
+
export async function waitForTransactionConfirmation(txHash, network, options) {
|
|
39
|
+
const pollInterval = options?.pollInterval ?? DEFAULT_POLL_INTERVAL;
|
|
40
|
+
const maxWaitTime = options?.maxWaitTime ?? DEFAULT_MAX_WAIT_TIME;
|
|
41
|
+
const message = options?.message ?? 'Waiting for transaction confirmation';
|
|
42
|
+
const provider = getProvider(network);
|
|
43
|
+
const startTime = Date.now();
|
|
44
|
+
const spinner = ora({
|
|
45
|
+
text: `${message} (0s elapsed)`,
|
|
46
|
+
spinner: 'dots',
|
|
47
|
+
}).start();
|
|
48
|
+
const updateSpinnerText = () => {
|
|
49
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
50
|
+
spinner.text = `${message} (${elapsed}s elapsed)`;
|
|
51
|
+
};
|
|
52
|
+
const textUpdateInterval = setInterval(updateSpinnerText, 1000);
|
|
53
|
+
try {
|
|
54
|
+
while (Date.now() - startTime < maxWaitTime) {
|
|
55
|
+
try {
|
|
56
|
+
const tx = await provider.getTransaction(txHash);
|
|
57
|
+
if (tx.blockNumber !== undefined && tx.blockNumber !== null) {
|
|
58
|
+
const blockNum = typeof tx.blockNumber === 'bigint'
|
|
59
|
+
? tx.blockNumber
|
|
60
|
+
: BigInt(tx.blockNumber);
|
|
61
|
+
spinner.succeed(`Transaction confirmed in block ${blockNum}`);
|
|
62
|
+
return {
|
|
63
|
+
confirmed: true,
|
|
64
|
+
blockNumber: blockNum,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (tx.revert) {
|
|
68
|
+
spinner.fail(`Transaction reverted: ${tx.revert}`);
|
|
69
|
+
return {
|
|
70
|
+
confirmed: false,
|
|
71
|
+
revert: tx.revert,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
}
|
|
77
|
+
await sleep(pollInterval);
|
|
78
|
+
}
|
|
79
|
+
spinner.fail(`Timeout waiting for transaction confirmation`);
|
|
80
|
+
return {
|
|
81
|
+
confirmed: false,
|
|
82
|
+
error: `Transaction not confirmed within ${maxWaitTime / 1000} seconds`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
clearInterval(textUpdateInterval);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function sleep(ms) {
|
|
90
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
91
|
+
}
|
package/build/lib/wallet.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Network } from '@btc-vision/bitcoin';
|
|
2
|
-
import { MLDSASecurityLevel } from '@btc-vision/bip32';
|
|
3
|
-
import {
|
|
1
|
+
import { Network, Signer } from '@btc-vision/bitcoin';
|
|
2
|
+
import { MLDSASecurityLevel, QuantumBIP32Interface } from '@btc-vision/bip32';
|
|
3
|
+
import { Address } from '@btc-vision/transaction';
|
|
4
4
|
import { CLICredentials, CLIMldsaLevel, NetworkName } from '../types/index.js';
|
|
5
|
+
import { ECPairInterface } from 'ecpair';
|
|
5
6
|
export declare function getNetwork(networkName: NetworkName): Network;
|
|
6
7
|
export declare function getMLDSASecurityLevel(level: CLIMldsaLevel): MLDSASecurityLevel;
|
|
7
8
|
export declare class CLIWallet {
|
|
@@ -9,14 +10,13 @@ export declare class CLIWallet {
|
|
|
9
10
|
private readonly network;
|
|
10
11
|
private readonly mldsaLevel;
|
|
11
12
|
private constructor();
|
|
13
|
+
get address(): Address;
|
|
12
14
|
get p2trAddress(): string;
|
|
13
|
-
get
|
|
14
|
-
get
|
|
15
|
-
get mldsaKeypair(): EcKeyPair;
|
|
15
|
+
get keypair(): Signer | ECPairInterface | null;
|
|
16
|
+
get mldsaKeypair(): QuantumBIP32Interface;
|
|
16
17
|
get mldsaPublicKey(): Buffer;
|
|
17
18
|
get mldsaPublicKeyHash(): string;
|
|
18
19
|
get securityLevel(): CLIMldsaLevel;
|
|
19
|
-
get walletNetwork(): Network;
|
|
20
20
|
static fromCredentials(credentials: CLICredentials): CLIWallet;
|
|
21
21
|
static load(): CLIWallet;
|
|
22
22
|
static verifyMLDSA(data: Buffer, signature: Buffer, publicKey: Buffer, level: CLIMldsaLevel): boolean;
|
package/build/lib/wallet.js
CHANGED
|
@@ -32,12 +32,12 @@ export class CLIWallet {
|
|
|
32
32
|
this.network = network;
|
|
33
33
|
this.mldsaLevel = mldsaLevel;
|
|
34
34
|
}
|
|
35
|
+
get address() {
|
|
36
|
+
return this.wallet.address;
|
|
37
|
+
}
|
|
35
38
|
get p2trAddress() {
|
|
36
39
|
return this.wallet.p2tr;
|
|
37
40
|
}
|
|
38
|
-
get underlyingWallet() {
|
|
39
|
-
return this.wallet;
|
|
40
|
-
}
|
|
41
41
|
get keypair() {
|
|
42
42
|
return this.wallet.keypair;
|
|
43
43
|
}
|
|
@@ -54,9 +54,6 @@ export class CLIWallet {
|
|
|
54
54
|
get securityLevel() {
|
|
55
55
|
return this.mldsaLevel;
|
|
56
56
|
}
|
|
57
|
-
get walletNetwork() {
|
|
58
|
-
return this.network;
|
|
59
|
-
}
|
|
60
57
|
static fromCredentials(credentials) {
|
|
61
58
|
const network = getNetwork(credentials.network);
|
|
62
59
|
const securityLevel = getMLDSASecurityLevel(credentials.mldsaLevel);
|
package/build/types/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface CLIConfig {
|
|
|
8
8
|
ipfsGateways: string[];
|
|
9
9
|
ipfsPinningEndpoint: string;
|
|
10
10
|
ipfsPinningApiKey: string;
|
|
11
|
+
ipfsPinningSecret: string;
|
|
11
12
|
ipfsPinningAuthHeader: string;
|
|
12
13
|
registryAddresses: Record<NetworkName, string>;
|
|
13
14
|
defaultMldsaLevel: CLIMldsaLevel;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for the OPNet plugin ecosystem - scaffolding, compilation, signing, and registry interaction",
|
|
6
6
|
"author": "OP_NET",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"@inquirer/prompts": "^7.5.3",
|
|
53
53
|
"bytenode": "^1.5.7",
|
|
54
54
|
"commander": "^14.0.0",
|
|
55
|
+
"ecpair": "^2.1.0",
|
|
55
56
|
"esbuild": "^0.25.5",
|
|
56
57
|
"opnet": "^1.7.18",
|
|
57
58
|
"ora": "^8.2.0"
|
|
@@ -6,9 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
import { confirm } from '@inquirer/prompts';
|
|
8
8
|
import { BaseCommand } from './BaseCommand.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
getPendingScopeTransfer,
|
|
11
|
+
getPendingTransfer,
|
|
12
|
+
getRegistryContract,
|
|
13
|
+
} from '../lib/registry.js';
|
|
10
14
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
11
15
|
import { CLIWallet } from '../lib/wallet.js';
|
|
16
|
+
import {
|
|
17
|
+
buildTransactionParams,
|
|
18
|
+
checkBalance,
|
|
19
|
+
formatSats,
|
|
20
|
+
getWalletAddress,
|
|
21
|
+
} from '../lib/transaction.js';
|
|
12
22
|
import { NetworkName } from '../types/index.js';
|
|
13
23
|
|
|
14
24
|
interface AcceptOptions {
|
|
@@ -41,7 +51,7 @@ export class AcceptCommand extends BaseCommand {
|
|
|
41
51
|
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
42
52
|
process.exit(1);
|
|
43
53
|
}
|
|
44
|
-
CLIWallet.fromCredentials(credentials);
|
|
54
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
45
55
|
this.logger.success('Wallet loaded');
|
|
46
56
|
|
|
47
57
|
const network = (options?.network || 'mainnet') as NetworkName;
|
|
@@ -84,12 +94,46 @@ export class AcceptCommand extends BaseCommand {
|
|
|
84
94
|
}
|
|
85
95
|
}
|
|
86
96
|
|
|
97
|
+
// Check wallet balance
|
|
98
|
+
this.logger.info('Checking wallet balance...');
|
|
99
|
+
const { sufficient, balance } = await checkBalance(wallet, network);
|
|
100
|
+
if (!sufficient) {
|
|
101
|
+
this.logger.fail('Insufficient balance');
|
|
102
|
+
this.logger.error(`Wallet balance: ${formatSats(balance)}`);
|
|
103
|
+
this.logger.error('Please fund your wallet before accepting transfer.');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
this.logger.success(`Wallet balance: ${formatSats(balance)}`);
|
|
107
|
+
|
|
87
108
|
// Execute acceptance
|
|
88
109
|
this.logger.info('Accepting transfer...');
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
110
|
+
|
|
111
|
+
const sender = getWalletAddress(wallet);
|
|
112
|
+
const contract = getRegistryContract(network, sender);
|
|
113
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
114
|
+
|
|
115
|
+
const acceptResult = await contract.acceptScopeTransfer(scopeName);
|
|
116
|
+
|
|
117
|
+
if (acceptResult.revert) {
|
|
118
|
+
this.logger.fail('Acceptance would fail');
|
|
119
|
+
this.logger.error(`Reason: ${acceptResult.revert}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (acceptResult.estimatedGas) {
|
|
124
|
+
this.logger.info(`Estimated gas: ${acceptResult.estimatedGas} sats`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const receipt = await acceptResult.sendTransaction(txParams);
|
|
128
|
+
|
|
129
|
+
this.logger.log('');
|
|
130
|
+
this.logger.success('Scope transfer accepted successfully!');
|
|
131
|
+
this.logger.log('');
|
|
132
|
+
this.logger.log(`Scope: ${name}`);
|
|
133
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
134
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
135
|
+
this.logger.log('');
|
|
136
|
+
return;
|
|
93
137
|
} else {
|
|
94
138
|
const pending = await getPendingTransfer(name, network);
|
|
95
139
|
|
|
@@ -123,20 +167,49 @@ export class AcceptCommand extends BaseCommand {
|
|
|
123
167
|
}
|
|
124
168
|
}
|
|
125
169
|
|
|
170
|
+
// Check wallet balance
|
|
171
|
+
this.logger.info('Checking wallet balance...');
|
|
172
|
+
const { sufficient: sufficientPkg, balance: balancePkg } = await checkBalance(
|
|
173
|
+
wallet,
|
|
174
|
+
network,
|
|
175
|
+
);
|
|
176
|
+
if (!sufficientPkg) {
|
|
177
|
+
this.logger.fail('Insufficient balance');
|
|
178
|
+
this.logger.error(`Wallet balance: ${formatSats(balancePkg)}`);
|
|
179
|
+
this.logger.error('Please fund your wallet before accepting transfer.');
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
this.logger.success(`Wallet balance: ${formatSats(balancePkg)}`);
|
|
183
|
+
|
|
126
184
|
// Execute acceptance
|
|
127
185
|
this.logger.info('Accepting transfer...');
|
|
128
|
-
this.logger.warn('Acceptance transaction required.');
|
|
129
|
-
this.logger.log('Transaction would call: acceptTransfer(');
|
|
130
|
-
this.logger.log(` packageName: "${name}"`);
|
|
131
|
-
this.logger.log(')');
|
|
132
|
-
}
|
|
133
186
|
|
|
134
|
-
|
|
187
|
+
const sender = getWalletAddress(wallet);
|
|
188
|
+
const contract = getRegistryContract(network, sender);
|
|
189
|
+
const txParams = buildTransactionParams(wallet, network);
|
|
190
|
+
|
|
191
|
+
const acceptResult = await contract.acceptTransfer(name);
|
|
135
192
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
193
|
+
if (acceptResult.revert) {
|
|
194
|
+
this.logger.fail('Acceptance would fail');
|
|
195
|
+
this.logger.error(`Reason: ${acceptResult.revert}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (acceptResult.estimatedGas) {
|
|
200
|
+
this.logger.info(`Estimated gas: ${acceptResult.estimatedGas} sats`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const receipt = await acceptResult.sendTransaction(txParams);
|
|
204
|
+
|
|
205
|
+
this.logger.log('');
|
|
206
|
+
this.logger.success('Package transfer accepted successfully!');
|
|
207
|
+
this.logger.log('');
|
|
208
|
+
this.logger.log(`Package: ${name}`);
|
|
209
|
+
this.logger.log(`Transaction ID: ${receipt.transactionId}`);
|
|
210
|
+
this.logger.log(`Fees paid: ${formatSats(receipt.estimatedFees)}`);
|
|
211
|
+
this.logger.log('');
|
|
212
|
+
}
|
|
140
213
|
} catch (error) {
|
|
141
214
|
this.logger.fail('Acceptance failed');
|
|
142
215
|
if (this.isUserCancelled(error)) {
|
|
@@ -10,7 +10,7 @@ import * as esbuild from 'esbuild';
|
|
|
10
10
|
import bytenode from 'bytenode';
|
|
11
11
|
import { BaseCommand } from './BaseCommand.js';
|
|
12
12
|
import { getManifestPath, loadManifest } from '../lib/manifest.js';
|
|
13
|
-
import { buildOpnetBinary,
|
|
13
|
+
import { buildOpnetBinary, formatFileSize } from '../lib/binary.js';
|
|
14
14
|
import { CLIWallet } from '../lib/wallet.js';
|
|
15
15
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
16
16
|
import { CLIMldsaLevel } from '../types/index.js';
|
|
@@ -105,8 +105,8 @@ export class CompileCommand extends BaseCommand {
|
|
|
105
105
|
|
|
106
106
|
// Prepare signing
|
|
107
107
|
let publicKey: Buffer;
|
|
108
|
-
let signature: Buffer;
|
|
109
108
|
let mldsaLevel: CLIMldsaLevel;
|
|
109
|
+
let signFn: ((checksum: Buffer) => Buffer) | undefined;
|
|
110
110
|
|
|
111
111
|
if (options.sign) {
|
|
112
112
|
this.logger.info('Loading wallet for signing...');
|
|
@@ -125,33 +125,31 @@ export class CompileCommand extends BaseCommand {
|
|
|
125
125
|
|
|
126
126
|
this.logger.success(`Wallet loaded (MLDSA-${mldsaLevel})`);
|
|
127
127
|
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
const metadataBytes = Buffer.from(JSON.stringify(manifest), 'utf-8');
|
|
131
|
-
const checksum = computeChecksum(metadataBytes, bytecode, proto);
|
|
132
|
-
|
|
133
|
-
signature = wallet.signMLDSA(checksum);
|
|
134
|
-
this.logger.success(
|
|
135
|
-
`Plugin signed (${formatFileSize(signature.length)} signature)`,
|
|
136
|
-
);
|
|
128
|
+
// Create signing function that will be called with the final checksum
|
|
129
|
+
signFn = (checksum: Buffer) => wallet.signMLDSA(checksum);
|
|
137
130
|
} else {
|
|
138
131
|
this.logger.warn('Skipping signing (--no-sign)');
|
|
139
132
|
// Use dummy values for unsigned binary
|
|
140
133
|
mldsaLevel = 44;
|
|
141
134
|
publicKey = Buffer.alloc(1312); // MLDSA-44 public key size
|
|
142
|
-
signature = Buffer.alloc(2420); // MLDSA-44 signature size
|
|
143
135
|
}
|
|
144
136
|
|
|
145
137
|
// Build .opnet binary
|
|
146
138
|
this.logger.info('Assembling .opnet binary...');
|
|
147
|
-
const binary = buildOpnetBinary({
|
|
139
|
+
const { binary, checksum } = buildOpnetBinary({
|
|
148
140
|
mldsaLevel,
|
|
149
141
|
publicKey,
|
|
150
|
-
signature,
|
|
151
142
|
metadata: manifest,
|
|
152
143
|
bytecode,
|
|
153
144
|
proto,
|
|
145
|
+
signFn,
|
|
154
146
|
});
|
|
147
|
+
|
|
148
|
+
if (options.sign) {
|
|
149
|
+
this.logger.success(
|
|
150
|
+
`Plugin signed (checksum: sha256:${checksum.toString('hex').substring(0, 16)}...)`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
155
153
|
this.logger.success(`Binary assembled (${formatFileSize(binary.length)})`);
|
|
156
154
|
|
|
157
155
|
// Write output
|
|
@@ -178,6 +176,7 @@ export class CompileCommand extends BaseCommand {
|
|
|
178
176
|
this.logger.log(`Plugin: ${manifest.name}@${manifest.version}`);
|
|
179
177
|
this.logger.log(`Type: ${manifest.pluginType}`);
|
|
180
178
|
this.logger.log(`MLDSA Level: ${mldsaLevel}`);
|
|
179
|
+
this.logger.log(`Checksum: sha256:${checksum.toString('hex')}`);
|
|
181
180
|
this.logger.log(`Signed: ${options.sign ? 'Yes' : 'No'}`);
|
|
182
181
|
this.logger.log('');
|
|
183
182
|
|