@0xobelisk/sui-cli 1.2.0-pre.3 → 1.2.0-pre.34
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/dist/dubhe.js +49 -66
- package/dist/dubhe.js.map +1 -1
- package/package.json +4 -3
- package/src/commands/build.ts +3 -2
- package/src/commands/call.ts +83 -83
- package/src/commands/convertJson.ts +49 -0
- package/src/commands/doctor.ts +348 -0
- package/src/commands/index.ts +14 -7
- package/src/commands/info.ts +49 -0
- package/src/commands/loadMetadata.ts +50 -0
- package/src/commands/localnode.ts +19 -11
- package/src/commands/publish.ts +3 -1
- package/src/commands/query.ts +101 -101
- package/src/commands/schemagen.ts +4 -1
- package/src/commands/switchEnv.ts +24 -0
- package/src/commands/test.ts +1 -1
- package/src/commands/upgrade.ts +38 -38
- package/src/dubhe.ts +11 -3
- package/src/utils/callHandler.ts +118 -118
- package/src/utils/index.ts +2 -2
- package/src/utils/metadataHandler.ts +16 -0
- package/src/utils/publishHandler.ts +94 -269
- package/src/utils/queryStorage.ts +141 -141
- package/src/utils/startNode.ts +161 -106
- package/src/utils/storeConfig.ts +6 -12
- package/src/utils/upgradeHandler.ts +250 -249
- package/src/utils/utils.ts +291 -35
package/src/utils/utils.ts
CHANGED
|
@@ -6,17 +6,19 @@ import { FsIibError } from './errors';
|
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
|
-
import { Dubhe, NetworkType, SuiMoveNormalizedModules } from '@0xobelisk/sui-client';
|
|
9
|
+
import { Dubhe, NetworkType, SuiMoveNormalizedModules, loadMetadata } from '@0xobelisk/sui-client';
|
|
10
10
|
import { DubheCliError } from './errors';
|
|
11
|
+
import packageJson from '../../package.json';
|
|
12
|
+
import { Component, MoveType, EmptyComponent, DubheConfig } from '@0xobelisk/sui-common';
|
|
11
13
|
|
|
12
14
|
export type DeploymentJsonType = {
|
|
13
15
|
projectName: string;
|
|
14
16
|
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet';
|
|
15
17
|
packageId: string;
|
|
16
|
-
|
|
18
|
+
dappHub: string;
|
|
17
19
|
upgradeCap: string;
|
|
18
20
|
version: number;
|
|
19
|
-
|
|
21
|
+
components: Record<string, Component | MoveType | EmptyComponent>;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
export function validatePrivateKey(privateKey: string): false | string {
|
|
@@ -75,43 +77,43 @@ export async function getDeploymentJson(
|
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
export async function
|
|
80
|
+
export async function getDeploymentDappHub(projectPath: string, network: string): Promise<string> {
|
|
79
81
|
try {
|
|
80
82
|
const data = await fsAsync.readFile(
|
|
81
83
|
`${projectPath}/.history/sui_${network}/latest.json`,
|
|
82
84
|
'utf8'
|
|
83
85
|
);
|
|
84
86
|
const deployment = JSON.parse(data) as DeploymentJsonType;
|
|
85
|
-
return deployment.
|
|
87
|
+
return deployment.dappHub;
|
|
86
88
|
} catch (error) {
|
|
87
89
|
return '';
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
export async function
|
|
93
|
+
export async function getDubheDappHub(network: string) {
|
|
92
94
|
const path = process.cwd();
|
|
93
|
-
const contractPath = `${path}/
|
|
95
|
+
const contractPath = `${path}/src/dubhe`;
|
|
94
96
|
|
|
95
97
|
switch (network) {
|
|
96
98
|
case 'mainnet':
|
|
97
|
-
return await
|
|
99
|
+
return await getDeploymentDappHub(contractPath, 'mainnet');
|
|
98
100
|
case 'testnet':
|
|
99
|
-
return '
|
|
101
|
+
return await getDeploymentDappHub(contractPath, 'testnet');
|
|
100
102
|
case 'devnet':
|
|
101
|
-
return await
|
|
103
|
+
return await getDeploymentDappHub(contractPath, 'devnet');
|
|
102
104
|
case 'localnet':
|
|
103
|
-
return await
|
|
105
|
+
return await getDeploymentDappHub(contractPath, 'localnet');
|
|
104
106
|
default:
|
|
105
107
|
throw new Error(`Invalid network: ${network}`);
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
export async function
|
|
111
|
+
export async function getOnchainComponents(
|
|
110
112
|
projectPath: string,
|
|
111
113
|
network: string
|
|
112
|
-
): Promise<Record<string,
|
|
114
|
+
): Promise<Record<string, Component | MoveType | EmptyComponent>> {
|
|
113
115
|
const deployment = await getDeploymentJson(projectPath, network);
|
|
114
|
-
return deployment.
|
|
116
|
+
return deployment.components;
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
export async function getVersion(projectPath: string, network: string): Promise<number> {
|
|
@@ -132,9 +134,9 @@ export async function getOldPackageId(projectPath: string, network: string): Pro
|
|
|
132
134
|
return deployment.packageId;
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
export async function
|
|
137
|
+
export async function getDappHub(projectPath: string, network: string): Promise<string> {
|
|
136
138
|
const deployment = await getDeploymentJson(projectPath, network);
|
|
137
|
-
return deployment.
|
|
139
|
+
return deployment.dappHub;
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
export async function getUpgradeCap(projectPath: string, network: string): Promise<string> {
|
|
@@ -142,34 +144,62 @@ export async function getUpgradeCap(projectPath: string, network: string): Promi
|
|
|
142
144
|
return deployment.upgradeCap;
|
|
143
145
|
}
|
|
144
146
|
|
|
145
|
-
export function saveContractData(
|
|
147
|
+
export async function saveContractData(
|
|
146
148
|
projectName: string,
|
|
147
149
|
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
|
|
148
150
|
packageId: string,
|
|
149
|
-
|
|
151
|
+
dappHub: string,
|
|
150
152
|
upgradeCap: string,
|
|
151
153
|
version: number,
|
|
152
|
-
|
|
154
|
+
components: Record<string, Component | MoveType | EmptyComponent>
|
|
153
155
|
) {
|
|
154
156
|
const DeploymentData: DeploymentJsonType = {
|
|
155
157
|
projectName,
|
|
156
158
|
network,
|
|
157
159
|
packageId,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
+
dappHub,
|
|
161
|
+
components,
|
|
160
162
|
upgradeCap,
|
|
161
163
|
version
|
|
162
164
|
};
|
|
163
165
|
|
|
164
166
|
const path = process.cwd();
|
|
165
167
|
const storeDeploymentData = JSON.stringify(DeploymentData, null, 2);
|
|
166
|
-
writeOutput(
|
|
168
|
+
await writeOutput(
|
|
167
169
|
storeDeploymentData,
|
|
168
|
-
`${path}/
|
|
170
|
+
`${path}/src/${projectName}/.history/sui_${network}/latest.json`,
|
|
169
171
|
'Update deploy log'
|
|
170
172
|
);
|
|
171
173
|
}
|
|
172
174
|
|
|
175
|
+
export async function saveMetadata(
|
|
176
|
+
projectName: string,
|
|
177
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
|
|
178
|
+
packageId: string
|
|
179
|
+
) {
|
|
180
|
+
const path = process.cwd();
|
|
181
|
+
|
|
182
|
+
// Save metadata files
|
|
183
|
+
try {
|
|
184
|
+
const metadata = await loadMetadata(network, packageId);
|
|
185
|
+
if (metadata) {
|
|
186
|
+
const metadataJson = JSON.stringify(metadata, null, 2);
|
|
187
|
+
|
|
188
|
+
// Save packageId-specific metadata file
|
|
189
|
+
await writeOutput(
|
|
190
|
+
metadataJson,
|
|
191
|
+
`${path}/src/${projectName}/.history/sui_${network}/${packageId}.json`,
|
|
192
|
+
'Save package metadata'
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// Save latest metadata.json
|
|
196
|
+
await writeOutput(metadataJson, `${path}/metadata.json`, 'Save latest metadata');
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.warn(chalk.yellow(`Warning: Failed to save metadata: ${error}`));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
173
203
|
export async function writeOutput(
|
|
174
204
|
output: string,
|
|
175
205
|
fullOutputPath: string,
|
|
@@ -186,11 +216,11 @@ export async function writeOutput(
|
|
|
186
216
|
function getDubheDependency(network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'): string {
|
|
187
217
|
switch (network) {
|
|
188
218
|
case 'localnet':
|
|
189
|
-
return 'Dubhe = { local = "../dubhe
|
|
219
|
+
return 'Dubhe = { local = "../dubhe" }';
|
|
190
220
|
case 'testnet':
|
|
191
|
-
return
|
|
221
|
+
return `Dubhe = { git = "https://github.com/0xobelisk/dubhe-wip.git", subdir = "packages/sui-framework/src/dubhe", rev = "v${packageJson.version}" }`;
|
|
192
222
|
case 'mainnet':
|
|
193
|
-
return
|
|
223
|
+
return `Dubhe = { git = "https://github.com/0xobelisk/dubhe-wip.git", subdir = "packages/sui-framework/src/dubhe", rev = "v${packageJson.version}" }`;
|
|
194
224
|
default:
|
|
195
225
|
throw new Error(`Unsupported network: ${network}`);
|
|
196
226
|
}
|
|
@@ -206,32 +236,147 @@ export async function updateDubheDependency(
|
|
|
206
236
|
fs.writeFileSync(filePath, updatedContent, 'utf-8');
|
|
207
237
|
console.log(`Updated Dubhe dependency in ${filePath} for ${network}.`);
|
|
208
238
|
}
|
|
239
|
+
|
|
240
|
+
async function checkRpcAvailability(rpcUrl: string): Promise<boolean> {
|
|
241
|
+
try {
|
|
242
|
+
const response = await fetch(rpcUrl, {
|
|
243
|
+
method: 'POST',
|
|
244
|
+
headers: {
|
|
245
|
+
'Content-Type': 'application/json'
|
|
246
|
+
},
|
|
247
|
+
body: JSON.stringify({
|
|
248
|
+
jsonrpc: '2.0',
|
|
249
|
+
id: 1,
|
|
250
|
+
method: 'sui_getLatestCheckpointSequenceNumber',
|
|
251
|
+
params: []
|
|
252
|
+
})
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const data = await response.json();
|
|
260
|
+
return !data.error;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function addEnv(
|
|
267
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
268
|
+
): Promise<void> {
|
|
269
|
+
const rpcMap = {
|
|
270
|
+
localnet: 'http://127.0.0.1:9000',
|
|
271
|
+
devnet: 'https://fullnode.devnet.sui.io:443/',
|
|
272
|
+
testnet: 'https://fullnode.testnet.sui.io:443/',
|
|
273
|
+
mainnet: 'https://fullnode.mainnet.sui.io:443/'
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const rpcUrl = rpcMap[network];
|
|
277
|
+
|
|
278
|
+
// Check RPC availability first
|
|
279
|
+
const isRpcAvailable = await checkRpcAvailability(rpcUrl);
|
|
280
|
+
if (!isRpcAvailable) {
|
|
281
|
+
throw new Error(
|
|
282
|
+
`RPC endpoint ${rpcUrl} is not available. Please check your network connection or try again later.`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return new Promise<void>((resolve, reject) => {
|
|
287
|
+
let errorOutput = '';
|
|
288
|
+
let stdoutOutput = '';
|
|
289
|
+
|
|
290
|
+
const suiProcess = spawn(
|
|
291
|
+
'sui',
|
|
292
|
+
['client', 'new-env', '--alias', network, '--rpc', rpcMap[network]],
|
|
293
|
+
{
|
|
294
|
+
env: { ...process.env },
|
|
295
|
+
stdio: 'pipe'
|
|
296
|
+
}
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
// Capture standard output
|
|
300
|
+
suiProcess.stdout.on('data', (data) => {
|
|
301
|
+
stdoutOutput += data.toString();
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Capture error output
|
|
305
|
+
suiProcess.stderr.on('data', (data) => {
|
|
306
|
+
errorOutput += data.toString();
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Handle process errors (e.g., command not found)
|
|
310
|
+
suiProcess.on('error', (error) => {
|
|
311
|
+
console.error(chalk.red(`\n❌ Failed to execute sui command: ${error.message}`));
|
|
312
|
+
reject(new Error(`Failed to execute sui command: ${error.message}`));
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Handle process exit
|
|
316
|
+
suiProcess.on('exit', (code) => {
|
|
317
|
+
// Check if "already exists" message is present
|
|
318
|
+
if (errorOutput.includes('already exists') || stdoutOutput.includes('already exists')) {
|
|
319
|
+
console.log(chalk.yellow(`Environment ${network} already exists, proceeding...`));
|
|
320
|
+
resolve();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (code === 0) {
|
|
325
|
+
console.log(chalk.green(`Successfully added environment ${network}`));
|
|
326
|
+
resolve();
|
|
327
|
+
} else {
|
|
328
|
+
const finalError = errorOutput || stdoutOutput || `Process exited with code ${code}`;
|
|
329
|
+
console.error(chalk.red(`\n❌ Failed to add environment ${network}`));
|
|
330
|
+
console.error(chalk.red(` └─ ${finalError.trim()}`));
|
|
331
|
+
reject(new Error(finalError));
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
209
337
|
export async function switchEnv(network: 'mainnet' | 'testnet' | 'devnet' | 'localnet') {
|
|
210
338
|
try {
|
|
339
|
+
// First, try to add the environment
|
|
340
|
+
await addEnv(network);
|
|
341
|
+
|
|
342
|
+
// Then switch to the specified environment
|
|
211
343
|
return new Promise<void>((resolve, reject) => {
|
|
344
|
+
let errorOutput = '';
|
|
345
|
+
let stdoutOutput = '';
|
|
346
|
+
|
|
212
347
|
const suiProcess = spawn('sui', ['client', 'switch', '--env', network], {
|
|
213
348
|
env: { ...process.env },
|
|
214
349
|
stdio: 'pipe'
|
|
215
350
|
});
|
|
216
351
|
|
|
352
|
+
suiProcess.stdout.on('data', (data) => {
|
|
353
|
+
stdoutOutput += data.toString();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
suiProcess.stderr.on('data', (data) => {
|
|
357
|
+
errorOutput += data.toString();
|
|
358
|
+
});
|
|
359
|
+
|
|
217
360
|
suiProcess.on('error', (error) => {
|
|
218
|
-
console.error(chalk.red(
|
|
219
|
-
|
|
220
|
-
reject(error); // Reject promise on error
|
|
361
|
+
console.error(chalk.red(`\n❌ Failed to execute sui command: ${error.message}`));
|
|
362
|
+
reject(new Error(`Failed to execute sui command: ${error.message}`));
|
|
221
363
|
});
|
|
222
364
|
|
|
223
365
|
suiProcess.on('exit', (code) => {
|
|
224
|
-
if (code
|
|
225
|
-
console.
|
|
226
|
-
|
|
366
|
+
if (code === 0) {
|
|
367
|
+
console.log(chalk.green(`Successfully switched to environment ${network}`));
|
|
368
|
+
resolve();
|
|
227
369
|
} else {
|
|
228
|
-
|
|
370
|
+
const finalError = errorOutput || stdoutOutput || `Process exited with code ${code}`;
|
|
371
|
+
console.error(chalk.red(`\n❌ Failed to switch to environment ${network}`));
|
|
372
|
+
console.error(chalk.red(` └─ ${finalError.trim()}`));
|
|
373
|
+
reject(new Error(finalError));
|
|
229
374
|
}
|
|
230
375
|
});
|
|
231
376
|
});
|
|
232
377
|
} catch (error) {
|
|
233
|
-
|
|
234
|
-
|
|
378
|
+
// Re-throw the error for the caller to handle
|
|
379
|
+
throw error;
|
|
235
380
|
}
|
|
236
381
|
}
|
|
237
382
|
|
|
@@ -271,3 +416,114 @@ export function initializeDubhe({
|
|
|
271
416
|
metadata
|
|
272
417
|
});
|
|
273
418
|
}
|
|
419
|
+
|
|
420
|
+
export function generateConfigJson(config: DubheConfig): string {
|
|
421
|
+
const components = Object.entries(config.components).map(([name, component]) => {
|
|
422
|
+
if (typeof component === 'string') {
|
|
423
|
+
return {
|
|
424
|
+
[name]: {
|
|
425
|
+
fields: [
|
|
426
|
+
{ entity_id: 'address' },
|
|
427
|
+
{ value: component }
|
|
428
|
+
],
|
|
429
|
+
keys: ['entity_id']
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (Object.keys(component as object).length === 0) {
|
|
435
|
+
return {
|
|
436
|
+
[name]: {
|
|
437
|
+
fields: [
|
|
438
|
+
{ entity_id: 'address' }
|
|
439
|
+
],
|
|
440
|
+
keys: ['entity_id']
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const fields = (component as any).fields || {};
|
|
446
|
+
const keys = (component as any).keys || ['entity_id'];
|
|
447
|
+
|
|
448
|
+
// ensure entity_id field exists
|
|
449
|
+
if (!fields.entity_id && keys.includes('entity_id')) {
|
|
450
|
+
fields.entity_id = 'address';
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
[name]: {
|
|
455
|
+
fields: Object.entries(fields).map(([fieldName, fieldType]) => ({
|
|
456
|
+
[fieldName]: fieldType
|
|
457
|
+
})),
|
|
458
|
+
keys: keys
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
const resources = Object.entries(config.resources).map(([name, resource]) => {
|
|
464
|
+
if (typeof resource === 'string') {
|
|
465
|
+
return {
|
|
466
|
+
[name]: {
|
|
467
|
+
fields: [
|
|
468
|
+
{ value: resource }
|
|
469
|
+
],
|
|
470
|
+
keys: []
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (Object.keys(resource as object).length === 0) {
|
|
476
|
+
return {
|
|
477
|
+
[name]: {
|
|
478
|
+
fields: [],
|
|
479
|
+
keys: []
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const fields = (resource as any).fields || {};
|
|
485
|
+
const keys = (resource as any).keys || [];
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
[name]: {
|
|
489
|
+
fields: Object.entries(fields).map(([fieldName, fieldType]) => ({
|
|
490
|
+
[fieldName]: fieldType
|
|
491
|
+
})),
|
|
492
|
+
keys: keys
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// handle enums
|
|
498
|
+
const enums = Object.entries(config.enums || {}).map(([name, enumFields]) => {
|
|
499
|
+
// Sort enum values by first letter
|
|
500
|
+
let sortedFields = enumFields.sort((a, b) => a.localeCompare(b)).map((value, index) => ({
|
|
501
|
+
[index]: value
|
|
502
|
+
}));
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
[name]: {
|
|
506
|
+
fields: sortedFields
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
return JSON.stringify({
|
|
512
|
+
components,
|
|
513
|
+
resources,
|
|
514
|
+
enums
|
|
515
|
+
}, null, 2);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Updates the dubhe address in Move.toml file
|
|
520
|
+
* @param path - Directory path containing Move.toml file
|
|
521
|
+
* @param packageAddress - New dubhe package address to set
|
|
522
|
+
*/
|
|
523
|
+
export function updateMoveTomlAddress(path: string, packageAddress: string) {
|
|
524
|
+
const moveTomlPath = `${path}/Move.toml`;
|
|
525
|
+
const moveTomlContent = fs.readFileSync(moveTomlPath, 'utf-8');
|
|
526
|
+
// Use regex to match any dubhe address, not just "0x0"
|
|
527
|
+
const updatedContent = moveTomlContent.replace(/dubhe\s*=\s*"[^"]*"/, `dubhe = "${packageAddress}"`);
|
|
528
|
+
fs.writeFileSync(moveTomlPath, updatedContent, 'utf-8');
|
|
529
|
+
}
|