@0xobelisk/sui-cli 1.1.3 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xobelisk/sui-cli",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Tookit for interacting with move eps framework",
5
5
  "keywords": [
6
6
  "sui",
@@ -49,8 +49,8 @@
49
49
  "yargs": "^17.7.1",
50
50
  "zod": "^3.22.3",
51
51
  "zod-validation-error": "^1.3.0",
52
- "@0xobelisk/sui-client": "1.1.3",
53
- "@0xobelisk/sui-common": "1.1.3"
52
+ "@0xobelisk/sui-client": "1.1.4",
53
+ "@0xobelisk/sui-common": "1.1.5"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/ejs": "^3.1.1",
@@ -2,7 +2,9 @@ import type { CommandModule } from 'yargs';
2
2
  import { execSync } from 'child_process';
3
3
  import chalk from 'chalk';
4
4
  import { DubheConfig, loadConfig } from '@0xobelisk/sui-common';
5
- import { switchEnv, updateDubheDependency } from '../utils';
5
+ import { switchEnv } from '../utils';
6
+ import {generateCargoToml} from "./install";
7
+ import {writeFileSync} from "fs";
6
8
 
7
9
  type Options = {
8
10
  'config-path': string;
@@ -22,6 +24,7 @@ const commandModule: CommandModule<Options, Options> = {
22
24
  },
23
25
  network: {
24
26
  type: 'string',
27
+ default: 'localnet',
25
28
  choices: ['mainnet', 'testnet', 'devnet', 'localnet'],
26
29
  desc: 'Node network (mainnet/testnet/devnet/localnet)',
27
30
  },
@@ -45,7 +48,8 @@ const commandModule: CommandModule<Options, Options> = {
45
48
  const path = process.cwd();
46
49
  const projectPath = `${path}/contracts/${dubheConfig.name}`;
47
50
  await switchEnv(network);
48
- await updateDubheDependency(projectPath + '/Move.toml', network);
51
+ const cargoTomlContent = generateCargoToml(dubheConfig.dependencies, dubheConfig.name, network);
52
+ writeFileSync(`${projectPath}/Move.toml`, cargoTomlContent, { encoding: 'utf-8' });
49
53
  const command = `sui move build --path ${projectPath} ${
50
54
  dumpBytecodeAsBase64 ? ` --dump-bytecode-as-base64` : ''
51
55
  }`;
@@ -15,6 +15,7 @@ import query from './query';
15
15
  import call from './call';
16
16
  import indexer from './indexer';
17
17
  import watch from './watch';
18
+ import install from "./install";
18
19
 
19
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Each command has different options
20
21
  export const commands: CommandModule<any, any>[] = [
@@ -33,4 +34,5 @@ export const commands: CommandModule<any, any>[] = [
33
34
  configStore,
34
35
  indexer,
35
36
  watch,
37
+ install
36
38
  ];
@@ -0,0 +1,126 @@
1
+ import type { CommandModule } from 'yargs';
2
+ import { execSync } from 'child_process';
3
+ import { DubheConfig, loadConfig } from '@0xobelisk/sui-common';
4
+ import {existsSync, writeFileSync} from 'fs';
5
+ import {homedir} from "node:os";
6
+ import {join} from "path";
7
+ import {readFileSync} from "node:fs";
8
+
9
+ type Options = {
10
+ 'config-path': string;
11
+ network: any;
12
+ };
13
+
14
+ export function getRepoNameFromUrl(url: string): string {
15
+ // Split the URL by '/' and filter out empty strings
16
+ const parts = url.split('/').filter(part => part.length > 0);
17
+
18
+ // Get the last part and remove any ${} syntax if present
19
+ const lastPart = parts[parts.length - 1];
20
+ return lastPart.replace(/^\${(.*)}$/, '$1');
21
+ }
22
+
23
+ export function generateCargoToml(
24
+ dependencies: DubheConfig['dependencies'],
25
+ projectName: string,
26
+ network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
27
+ ): string {
28
+ let cargoToml = `[package]\nname = "${projectName}"\nversion = "1.0.0"\nedition = "2024"\n\n[dependencies]\n`;
29
+ cargoToml += `Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "mainnet-v1.38.3" }\n`;
30
+
31
+ if (network === 'localnet') {
32
+ const dubhePath = `${homedir()}/.dubhe/dependencies/dubhe-framework`;
33
+ cargoToml += `Dubhe = { local = "${dubhePath}" }\n`;
34
+
35
+ dependencies.forEach(dep => {
36
+ const repoName = getRepoNameFromUrl(dep.git);
37
+ if (dep.name && dep.name.toLowerCase() === 'dubhe') {
38
+ return;
39
+ }
40
+
41
+ let localPath = `${homedir()}/.dubhe/dependencies/${repoName}`;
42
+ if (dep.subdir) {
43
+ localPath += `/${dep.subdir}`;
44
+ }
45
+ const depName = dep.name || repoName;
46
+ cargoToml += `${depName} = { local = "${localPath}" }\n`;
47
+
48
+ const moveTomlPath = join(localPath, 'Move.toml');
49
+ if (existsSync(moveTomlPath)) {
50
+ try {
51
+ let moveTomlContent = readFileSync(moveTomlPath, 'utf-8');
52
+ const dubheRegex = /Dubhe\s*=\s*{[^}]*}/g;
53
+ if (moveTomlContent.match(dubheRegex)) {
54
+ moveTomlContent = moveTomlContent.replace(
55
+ dubheRegex,
56
+ `Dubhe = { local = "${dubhePath}" }`
57
+ );
58
+ writeFileSync(moveTomlPath, moveTomlContent, 'utf-8');
59
+ }
60
+ } catch (error) {
61
+ console.error(`Failed to update Move.toml at ${moveTomlPath}: ${error}`);
62
+ }
63
+ }
64
+ });
65
+ } else {
66
+ dependencies.forEach(dep => {
67
+ const repoName = getRepoNameFromUrl(dep.git);
68
+ const depName = dep.name || repoName;
69
+ cargoToml += `${depName} = { git = "${dep.git}", rev = "${dep.rev}"`;
70
+ if (dep.subdir) {
71
+ cargoToml += `, subdir = "${dep.subdir}"`;
72
+ }
73
+ cargoToml += ` }\n`;
74
+ });
75
+ }
76
+ cargoToml += `\n[addresses]\nsui = "0x2"\n${projectName} = "0x0"\n`;
77
+ return cargoToml;
78
+ }
79
+
80
+ const commandModule: CommandModule<Options, Options> = {
81
+ command: 'install',
82
+ describe: 'Install a repository in Dubhe contracts',
83
+ builder(yargs) {
84
+ return yargs
85
+ .options({
86
+ 'config-path': {
87
+ type: 'string',
88
+ default: 'dubhe.config.ts',
89
+ description: 'Path to the configuration file',
90
+ },
91
+ }).options({
92
+ 'network': {
93
+ type: 'string',
94
+ default: 'localnet',
95
+ description: 'Path to the configuration file',
96
+ },
97
+ });
98
+
99
+ },
100
+ async handler({ 'config-path': configPath, network }) {
101
+ try {
102
+ const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
103
+ await dubheInstall(dubheConfig, network);
104
+ } catch (error: any) {
105
+ console.error(`Error installing repository: ${error.message}`);
106
+ process.exit(1);
107
+ }
108
+ },
109
+ };
110
+
111
+ export async function dubheInstall(dubheConfig: DubheConfig, network: 'mainnet' | 'testnet' | 'devnet' | 'localnet') {
112
+ dubheConfig.dependencies.forEach(dependency => {
113
+ const projectName = getRepoNameFromUrl(dependency.git);
114
+ const dependencyPath = join(homedir(), '.dubhe', 'dependencies', projectName);
115
+ if (!existsSync(dependencyPath)) {
116
+ console.log(`🚀 Installing repository: ${dependency.git}`);
117
+ const command = `git clone --depth 1 --branch ${dependency.rev} ${dependency.git} ${dependencyPath}`;
118
+ execSync(command, { stdio: 'inherit', encoding: 'utf-8' });
119
+ }
120
+ });
121
+ const cargoTomlContent = generateCargoToml(dubheConfig.dependencies, dubheConfig.name, network);
122
+ const projectPath = `${process.cwd()}/contracts/${dubheConfig.name}/Move.toml`;
123
+ writeFileSync(projectPath, cargoTomlContent, { encoding: 'utf-8' });
124
+ }
125
+
126
+ export default commandModule;
@@ -1,18 +1,33 @@
1
1
  import type { CommandModule } from 'yargs';
2
2
  import { startLocalNode } from '../utils/startNode';
3
+ import {DubheConfig, loadConfig} from "@0xobelisk/sui-common";
4
+ import {dubheInstall} from "./install";
3
5
 
4
- const commandModule: CommandModule = {
6
+ type Options = {
7
+ 'config-path': string;
8
+ };
9
+
10
+ const commandModule: CommandModule<Options, Options> = {
5
11
  command: 'node',
6
12
 
7
13
  describe: 'Manage local Sui node',
8
14
 
9
15
  builder(yargs) {
10
16
  return yargs
17
+ .options({
18
+ 'config-path': {
19
+ type: 'string',
20
+ default: 'dubhe.config.ts',
21
+ description: 'Path to the configuration file',
22
+ },
23
+ })
11
24
  },
12
25
 
13
- async handler() {
26
+ async handler({ 'config-path': configPath }) {
14
27
  try {
15
- await startLocalNode();
28
+ const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
29
+ await dubheInstall(dubheConfig, 'localnet');
30
+ await startLocalNode(dubheConfig);
16
31
  } catch (error) {
17
32
  console.error('Error executing command:', error);
18
33
  process.exit(1);
@@ -5,13 +5,15 @@ import { DubheCliError } from './errors';
5
5
  import {
6
6
  saveContractData,
7
7
  validatePrivateKey,
8
- updateDubheDependency,
9
8
  switchEnv,
10
9
  delay,
11
10
  } from './utils';
12
11
  import { DubheConfig } from '@0xobelisk/sui-common';
13
12
  import * as fs from 'fs';
14
13
  import * as path from 'path';
14
+ import {homedir} from "node:os";
15
+ import {generateCargoToml, getRepoNameFromUrl} from "../commands/install";
16
+ import {writeFileSync} from "fs";
15
17
 
16
18
  async function removeEnvContent(
17
19
  filePath: string,
@@ -145,7 +147,6 @@ function buildContract(projectPath: string): string[][] {
145
147
  );
146
148
  modules = buildResult.modules;
147
149
  dependencies = buildResult.dependencies;
148
- console.log(' └─ Build successful');
149
150
  } catch (error: any) {
150
151
  console.error(chalk.red(' └─ Build failed'));
151
152
  console.error(error.stdout);
@@ -233,7 +234,7 @@ async function publishContract(
233
234
 
234
235
  const deployHookTx = new Transaction();
235
236
  deployHookTx.moveCall({
236
- target: `${packageId}::genesis::run`,
237
+ target: `${packageId}::${dubheConfig.name}_genesis::run`,
237
238
  arguments: [deployHookTx.object('0x6')],
238
239
  });
239
240
 
@@ -288,116 +289,88 @@ async function publishContract(
288
289
  }
289
290
  }
290
291
 
291
- async function checkDubheFramework(projectPath: string): Promise<boolean> {
292
- if (!fs.existsSync(projectPath)) {
293
- console.log(chalk.yellow('\nℹ️ Dubhe Framework Files Not Found'));
294
- console.log(chalk.yellow(' ├─ Expected Path:'), projectPath);
295
- console.log(chalk.yellow(' ├─ To set up Dubhe Framework:'));
296
- console.log(
297
- chalk.yellow(
298
- ' │ 1. Create directory: mkdir -p contracts/dubhe-framework'
299
- )
300
- );
301
- console.log(
302
- chalk.yellow(
303
- ' │ 2. Clone repository: git clone https://github.com/0xobelisk/dubhe-framework contracts/dubhe-framework'
304
- )
305
- );
306
- console.log(
307
- chalk.yellow(
308
- ' │ 3. Or download from: https://github.com/0xobelisk/dubhe-framework'
309
- )
310
- );
311
- console.log(chalk.yellow(' └─ After setup, restart the local node'));
312
- return false;
313
- }
314
- return true;
315
- }
316
-
317
292
  export async function publishDubheFramework(
293
+ dubheConfig: DubheConfig,
318
294
  dubhe: Dubhe,
319
295
  network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
320
296
  ) {
321
- const path = process.cwd();
322
- const projectPath = `${path}/contracts/dubhe-framework`;
323
-
324
- if (!(await checkDubheFramework(projectPath))) {
325
- console.log(chalk.yellow('\n❗ Framework Deployment Skipped'));
326
- return;
327
- }
328
-
329
- // const chainId = await client.getChainIdentifier();
330
297
  const chainId =
331
298
  await dubhe.suiInteractor.currentClient.getChainIdentifier();
332
- await removeEnvContent(`${projectPath}/Move.lock`, network);
333
- console.log('\n🚀 Starting Contract Publication...');
334
- console.log(` ├─ Project: ${projectPath}`);
335
- console.log(` ├─ Network: ${network}`);
336
-
337
- console.log(` └─ Account: ${dubhe.getAddress()}`);
338
-
339
- console.log('\n📦 Building Contract...');
340
- const [modules, dependencies] = buildContract(projectPath);
341
-
342
- console.log('\n🔄 Publishing Contract...');
343
- const tx = new Transaction();
344
- const [upgradeCap] = tx.publish({ modules, dependencies });
345
- tx.transferObjects([upgradeCap], dubhe.getAddress());
346
-
347
- let result;
348
- try {
349
- result = await dubhe.signAndSendTxn({ tx });
350
- } catch (error: any) {
351
- console.error(chalk.red(' └─ Publication failed'));
352
- console.error(error.message);
353
- process.exit(1);
354
- }
299
+ for (const dependency of dubheConfig.dependencies) {
300
+ const projectName = getRepoNameFromUrl(dependency.git);
301
+ let projectPath = `${homedir()}/.dubhe/dependencies/${projectName}`
302
+ if (dependency.subdir) {
303
+ projectPath += `/${dependency.subdir}`;
304
+ }
305
+ console.log(`\n🚀 Initialize dependencies...`);
306
+ console.log(` ├─ Project: ${projectPath}`);
307
+ if (!fs.existsSync(projectPath)) {
308
+ console.log(chalk.yellow('\nℹ️ Please install Dubhe Framework'));
309
+ console.log(chalk.yellow('\nℹ️ Execute the following command:'));
310
+ console.log(chalk.yellow(`pnpm dubhe install`));
311
+ continue;
312
+ }
355
313
 
356
- if (result.effects?.status.status === 'failure') {
357
- console.log(chalk.red(' └─ Publication failed'));
358
- process.exit(1);
359
- }
314
+ await removeEnvContent(`${projectPath}/Move.lock`, network);
315
+ const [modules, dependencies] = buildContract(projectPath);
360
316
 
361
- let version = 1;
362
- let packageId = '';
363
- let schemas: Record<string, string> = {};
364
- let upgradeCapId = '';
317
+ const tx = new Transaction();
318
+ const [upgradeCap] = tx.publish({ modules, dependencies });
319
+ tx.transferObjects([upgradeCap], dubhe.getAddress());
365
320
 
366
- result.objectChanges!.map(object => {
367
- if (object.type === 'published') {
368
- console.log(` ├─ Package ID: ${object.packageId}`);
369
- packageId = object.packageId;
370
- }
371
- if (
372
- object.type === 'created' &&
373
- object.objectType === '0x2::package::UpgradeCap'
374
- ) {
375
- console.log(` ├─ Upgrade Cap: ${object.objectId}`);
376
- upgradeCapId = object.objectId;
321
+ let result;
322
+ try {
323
+ result = await dubhe.signAndSendTxn({ tx });
324
+ } catch (error: any) {
325
+ console.error(chalk.red(' └─ Publication failed'));
326
+ console.error(error.message);
327
+ process.exit(1);
377
328
  }
378
- });
379
-
380
- console.log(` └─ Transaction: ${result.digest}`);
381
329
 
382
- updateEnvFile(
383
- `${projectPath}/Move.lock`,
384
- network,
385
- 'publish',
386
- chainId,
387
- packageId
388
- );
330
+ if (result.effects?.status.status === 'failure') {
331
+ console.log(chalk.red(' └─ Publication failed'));
332
+ process.exit(1);
333
+ }
389
334
 
390
- saveContractData(
391
- 'dubhe-framework',
392
- network,
393
- packageId,
394
- '',
395
- upgradeCapId,
396
- version,
397
- schemas
398
- );
399
- await delay(1000);
400
- console.log(chalk.green('\n✅ Dubhe Framework deployed successfully'));
335
+ result.objectChanges!.map(async object => {
336
+ if (object.type === 'published') {
337
+ console.log(` ├─ Package ID: ${object.packageId}`);
338
+ updateEnvFile(
339
+ `${projectPath}/Move.lock`,
340
+ network,
341
+ 'publish',
342
+ chainId,
343
+ object.packageId
344
+ );
345
+
346
+ if (dependency.name === 'merak') {
347
+ await delay(2000);
348
+ const deployHookTx = new Transaction();
349
+ deployHookTx.moveCall({
350
+ target: `${object.packageId}::genesis::run`,
351
+ arguments: [deployHookTx.object('0x6')],
352
+ });
353
+
354
+ let deployHookResult;
355
+ try {
356
+ deployHookResult = await dubhe.signAndSendTxn({tx: deployHookTx});
357
+ } catch (error: any) {
358
+ console.error(chalk.red(' └─ Deploy hook execution failed'));
359
+ console.error(error.message);
360
+ process.exit(1);
361
+ }
362
+
363
+ if (deployHookResult.effects?.status.status === 'success') {
364
+ deployHookResult.objectChanges?.map(object => {
365
+ if (object.type === 'created' && object.objectType.includes('schema::Schema')) {
366
+ console.log(` ├─ Schema ID: ${object.objectId}`);
367
+ }
368
+ });
369
+ }
370
+ }
371
+ }
372
+ });
373
+ }
401
374
  }
402
375
 
403
376
  export async function publishHandler(
@@ -425,12 +398,10 @@ in your contracts directory to use the default sui private key.`
425
398
  networkType: network,
426
399
  });
427
400
 
428
- if (network === 'localnet') {
429
- await publishDubheFramework(dubhe, network);
430
- }
431
-
432
401
  const path = process.cwd();
433
402
  const projectPath = `${path}/contracts/${dubheConfig.name}`;
434
- await updateDubheDependency(`${projectPath}/Move.toml`, network);
403
+ const cargoTomlContent = generateCargoToml(dubheConfig.dependencies, dubheConfig.name, network);
404
+ writeFileSync(`${projectPath}/Move.toml`, cargoTomlContent, { encoding: 'utf-8' });
405
+
435
406
  await publishContract(dubhe, dubheConfig, network, projectPath, gasBudget);
436
407
  }
@@ -1,8 +1,9 @@
1
1
  import { execSync, spawn } from 'child_process';
2
2
  import chalk from 'chalk';
3
3
  import { printDubhe } from './printDubhe';
4
- import { delay, DubheCliError, validatePrivateKey } from '../utils';
4
+ import {delay, DubheCliError, publishDubheFramework, validatePrivateKey} from '../utils';
5
5
  import { Dubhe } from '@0xobelisk/sui-client';
6
+ import {DubheConfig} from "@0xobelisk/sui-common";
6
7
 
7
8
  function isSuiStartRunning(): boolean {
8
9
  try {
@@ -70,7 +71,7 @@ async function printAccounts() {
70
71
  )
71
72
  );
72
73
  }
73
- export async function startLocalNode() {
74
+ export async function startLocalNode(dubheConfig: DubheConfig) {
74
75
  if (isSuiStartRunning()) {
75
76
  console.log(chalk.yellow('\n⚠️ Warning: Local Node Already Running'));
76
77
  console.log(chalk.yellow(' ├─ Cannot start a new instance'));
@@ -102,7 +103,6 @@ export async function startLocalNode() {
102
103
  console.log(' └─ Force Regenesis: Yes');
103
104
  console.log(' └─ HTTP server: http://127.0.0.1:9000/');
104
105
  console.log(' └─ Faucet server: http://127.0.0.1:9123/');
105
-
106
106
  await printAccounts();
107
107
 
108
108
  await delay(2000);
@@ -114,7 +114,13 @@ export async function startLocalNode() {
114
114
  throw new DubheCliError(`Please check your privateKey.`);
115
115
  }
116
116
 
117
- console.log(chalk.green('🎉 Local environment is ready!'));
117
+ console.log(chalk.green('🎉 Local environment is ready!'))
118
+ const dubhe = new Dubhe({
119
+ secretKey: privateKeyFormat,
120
+ networkType: "localnet",
121
+ });
122
+
123
+ await publishDubheFramework(dubheConfig, dubhe, 'localnet');
118
124
 
119
125
  process.on('SIGINT', () => {
120
126
  console.log(chalk.yellow('\n🔔 Stopping Local Node...'));
@@ -129,4 +135,4 @@ export async function startLocalNode() {
129
135
  console.error(chalk.red(` └─ Error: ${error.message}`));
130
136
  process.exit(1);
131
137
  }
132
- }
138
+ }
@@ -201,10 +201,6 @@ export async function switchEnv(
201
201
  }
202
202
  );
203
203
 
204
- suiProcess.stdout.on('data', data => {
205
- console.log(chalk.green(`${data.toString()}`));
206
- });
207
-
208
204
  suiProcess.on('error', error => {
209
205
  console.error(chalk.red('\n❌ Failed to Switch Env'));
210
206
  console.error(chalk.red(` Error: ${error.message}`));