@0xobelisk/sui-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/dist/dubhe.js +56 -54
- package/dist/dubhe.js.map +1 -1
- package/package.json +2 -2
- package/src/commands/index.ts +3 -1
- package/src/commands/query.ts +115 -0
- package/src/commands/schemagen.ts +2 -11
- package/src/utils/index.ts +1 -0
- package/src/utils/queryStorage.ts +166 -0
- package/src/utils/utils.ts +38 -8
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { CommandModule } from 'yargs';
|
|
2
|
+
import { logError } from '../utils/errors';
|
|
3
|
+
import { queryStorage } from '../utils';
|
|
4
|
+
import { loadConfig, DubheConfig } from '@0xobelisk/sui-common';
|
|
5
|
+
|
|
6
|
+
type Options = {
|
|
7
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet';
|
|
8
|
+
'config-path': string;
|
|
9
|
+
schema: string;
|
|
10
|
+
struct: string;
|
|
11
|
+
'object-id'?: string;
|
|
12
|
+
'package-id'?: string;
|
|
13
|
+
'metadata-path'?: string;
|
|
14
|
+
params?: any[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* CLI command module for querying schema struct state
|
|
19
|
+
*
|
|
20
|
+
* Examples:
|
|
21
|
+
*
|
|
22
|
+
* 1. Query StorageValue (no params required):
|
|
23
|
+
* ```bash
|
|
24
|
+
* dubhe query --config-path dubhe.config.ts --network devnet --schema counter --struct value
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* 2. Query StorageMap (one param required):
|
|
28
|
+
* ```bash
|
|
29
|
+
* dubhe query --config-path dubhe.config.ts --network devnet --schema token --struct balances \
|
|
30
|
+
* --params "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* 3. Query StorageDoubleMap (two params required):
|
|
34
|
+
* ```bash
|
|
35
|
+
* dubhe query --config-path dubhe.config.ts --network devnet --schema game --struct player_relations \
|
|
36
|
+
* --params "0x123...456" "0x789...abc"
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
const commandModule: CommandModule<Options, Options> = {
|
|
40
|
+
command: 'query',
|
|
41
|
+
|
|
42
|
+
describe: 'Query dubhe schema struct state',
|
|
43
|
+
|
|
44
|
+
builder: {
|
|
45
|
+
network: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
choices: ['mainnet', 'testnet', 'devnet', 'localnet'],
|
|
48
|
+
desc: 'Node network (mainnet/testnet/devnet/localnet)',
|
|
49
|
+
demandOption: true,
|
|
50
|
+
},
|
|
51
|
+
'config-path': {
|
|
52
|
+
type: 'string',
|
|
53
|
+
default: 'dubhe.config.ts',
|
|
54
|
+
desc: 'Configuration file path',
|
|
55
|
+
},
|
|
56
|
+
schema: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
desc: 'Schema name',
|
|
59
|
+
demandOption: true,
|
|
60
|
+
},
|
|
61
|
+
struct: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
desc: 'Struct name',
|
|
64
|
+
demandOption: true,
|
|
65
|
+
},
|
|
66
|
+
'object-id': {
|
|
67
|
+
type: 'string',
|
|
68
|
+
desc: 'Object ID (optional)',
|
|
69
|
+
},
|
|
70
|
+
'package-id': {
|
|
71
|
+
type: 'string',
|
|
72
|
+
desc: 'Package ID (optional)',
|
|
73
|
+
},
|
|
74
|
+
'metadata-path': {
|
|
75
|
+
type: 'string',
|
|
76
|
+
desc: 'Path to metadata JSON file (optional)',
|
|
77
|
+
},
|
|
78
|
+
params: {
|
|
79
|
+
type: 'array',
|
|
80
|
+
desc: 'Params for storage type: StorageValue(no params), StorageMap(1 param), StorageDoubleMap(2 params)',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
async handler({
|
|
85
|
+
network,
|
|
86
|
+
'config-path': configPath,
|
|
87
|
+
schema,
|
|
88
|
+
struct,
|
|
89
|
+
'object-id': objectId,
|
|
90
|
+
'package-id': packageId,
|
|
91
|
+
'metadata-path': metadataPath,
|
|
92
|
+
params,
|
|
93
|
+
}) {
|
|
94
|
+
try {
|
|
95
|
+
const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
|
|
96
|
+
|
|
97
|
+
await queryStorage({
|
|
98
|
+
dubheConfig,
|
|
99
|
+
schema,
|
|
100
|
+
struct,
|
|
101
|
+
objectId,
|
|
102
|
+
network,
|
|
103
|
+
packageId,
|
|
104
|
+
metadataFilePath: metadataPath,
|
|
105
|
+
params,
|
|
106
|
+
});
|
|
107
|
+
} catch (error: any) {
|
|
108
|
+
logError(error);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
process.exit(0);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default commandModule;
|
|
@@ -5,7 +5,6 @@ import chalk from 'chalk';
|
|
|
5
5
|
type Options = {
|
|
6
6
|
'config-path'?: string;
|
|
7
7
|
network?: 'mainnet' | 'testnet' | 'devnet' | 'localnet';
|
|
8
|
-
'framework-id'?: string;
|
|
9
8
|
};
|
|
10
9
|
|
|
11
10
|
const commandModule: CommandModule<Options, Options> = {
|
|
@@ -24,20 +23,12 @@ const commandModule: CommandModule<Options, Options> = {
|
|
|
24
23
|
choices: ['mainnet', 'testnet', 'devnet', 'localnet'] as const,
|
|
25
24
|
desc: 'Node network (mainnet/testnet/devnet/localnet)',
|
|
26
25
|
},
|
|
27
|
-
'framework-id': {
|
|
28
|
-
type: 'string',
|
|
29
|
-
desc: 'Framework Package ID',
|
|
30
|
-
},
|
|
31
26
|
},
|
|
32
27
|
|
|
33
|
-
async handler({
|
|
34
|
-
'config-path': configPath,
|
|
35
|
-
network,
|
|
36
|
-
'framework-id': frameworkId,
|
|
37
|
-
}) {
|
|
28
|
+
async handler({ 'config-path': configPath, network }) {
|
|
38
29
|
try {
|
|
39
30
|
const dubheConfig = (await loadConfig(configPath)) as DubheConfig;
|
|
40
|
-
await schemaGen(dubheConfig, undefined, network
|
|
31
|
+
await schemaGen(dubheConfig, undefined, network);
|
|
41
32
|
process.exit(0);
|
|
42
33
|
} catch (error: any) {
|
|
43
34
|
console.log(chalk.red('Schemagen failed!'));
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Dubhe, loadMetadata } from '@0xobelisk/sui-client';
|
|
2
|
+
import { DubheCliError } from './errors';
|
|
3
|
+
import { validatePrivateKey, getOldPackageId, getObjectId } from './utils';
|
|
4
|
+
import { DubheConfig } from '@0xobelisk/sui-common';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
|
|
8
|
+
function validateParams(storageType: string, params: any[]): boolean {
|
|
9
|
+
const formatStorageType = storageType.split('<')[0].trim();
|
|
10
|
+
switch (formatStorageType) {
|
|
11
|
+
case 'StorageValue':
|
|
12
|
+
return params.length === 0;
|
|
13
|
+
case 'StorageMap':
|
|
14
|
+
return params.length === 1;
|
|
15
|
+
case 'StorageDoubleMap':
|
|
16
|
+
return params.length === 2;
|
|
17
|
+
default:
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getExpectedParamsCount(storageType: string): number {
|
|
23
|
+
const formatStorageType = storageType.split('<')[0].trim();
|
|
24
|
+
switch (formatStorageType) {
|
|
25
|
+
case 'StorageValue':
|
|
26
|
+
return 0;
|
|
27
|
+
case 'StorageMap':
|
|
28
|
+
return 1;
|
|
29
|
+
case 'StorageDoubleMap':
|
|
30
|
+
return 2;
|
|
31
|
+
default:
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function queryStorage({
|
|
37
|
+
dubheConfig,
|
|
38
|
+
schema,
|
|
39
|
+
struct,
|
|
40
|
+
params,
|
|
41
|
+
network,
|
|
42
|
+
objectId,
|
|
43
|
+
packageId,
|
|
44
|
+
metadataFilePath,
|
|
45
|
+
}: {
|
|
46
|
+
dubheConfig: DubheConfig;
|
|
47
|
+
schema: string;
|
|
48
|
+
struct: string;
|
|
49
|
+
params?: any[];
|
|
50
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet';
|
|
51
|
+
objectId?: string;
|
|
52
|
+
packageId?: string;
|
|
53
|
+
metadataFilePath?: string;
|
|
54
|
+
}) {
|
|
55
|
+
const privateKey = process.env.PRIVATE_KEY;
|
|
56
|
+
if (!privateKey) {
|
|
57
|
+
throw new DubheCliError(
|
|
58
|
+
`Missing PRIVATE_KEY environment variable.
|
|
59
|
+
Run 'echo "PRIVATE_KEY=YOUR_PRIVATE_KEY" > .env'
|
|
60
|
+
in your contracts directory to use the default sui private key.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const privateKeyFormat = validatePrivateKey(privateKey);
|
|
64
|
+
if (privateKeyFormat === false) {
|
|
65
|
+
throw new DubheCliError(`Please check your privateKey.`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const path = process.cwd();
|
|
69
|
+
const projectPath = `${path}/contracts/${dubheConfig.name}`;
|
|
70
|
+
|
|
71
|
+
packageId = packageId || (await getOldPackageId(projectPath, network));
|
|
72
|
+
|
|
73
|
+
objectId = objectId || (await getObjectId(projectPath, network, schema));
|
|
74
|
+
|
|
75
|
+
let metadata;
|
|
76
|
+
if (metadataFilePath) {
|
|
77
|
+
metadata = await loadMetadataFromFile(metadataFilePath);
|
|
78
|
+
} else {
|
|
79
|
+
metadata = await loadMetadata(network, packageId);
|
|
80
|
+
}
|
|
81
|
+
if (!metadata) {
|
|
82
|
+
throw new DubheCliError(
|
|
83
|
+
`Metadata file not found. Please provide a metadata file path or set the packageId.`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!dubheConfig.schemas[schema]) {
|
|
88
|
+
throw new DubheCliError(
|
|
89
|
+
`Schema "${schema}" not found in dubhe config. Available schemas: ${Object.keys(
|
|
90
|
+
dubheConfig.schemas
|
|
91
|
+
).join(', ')}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!dubheConfig.schemas[schema].structure[struct]) {
|
|
96
|
+
throw new DubheCliError(
|
|
97
|
+
`Struct "${struct}" not found in schema "${schema}". Available structs: ${Object.keys(
|
|
98
|
+
dubheConfig.schemas[schema].structure
|
|
99
|
+
).join(', ')}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const storageType = dubheConfig.schemas[schema].structure[struct];
|
|
104
|
+
|
|
105
|
+
const processedParams = params || [];
|
|
106
|
+
if (!validateParams(storageType, processedParams)) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Invalid params count for ${storageType}. ` +
|
|
109
|
+
`Expected: ${getExpectedParamsCount(storageType)}, ` +
|
|
110
|
+
`Got: ${processedParams.length}`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const dubhe = new Dubhe({
|
|
115
|
+
secretKey: privateKeyFormat,
|
|
116
|
+
networkType: network,
|
|
117
|
+
packageId,
|
|
118
|
+
metadata,
|
|
119
|
+
});
|
|
120
|
+
const result = await dubhe.state({
|
|
121
|
+
schema,
|
|
122
|
+
struct,
|
|
123
|
+
objectId,
|
|
124
|
+
storageType,
|
|
125
|
+
params: processedParams,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
console.log(result);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Load metadata from a JSON file and construct the metadata structure
|
|
133
|
+
* @param metadataFilePath Path to the metadata JSON file
|
|
134
|
+
* @param network Network type
|
|
135
|
+
* @param packageId Package ID
|
|
136
|
+
* @returns Constructed metadata object
|
|
137
|
+
*/
|
|
138
|
+
export async function loadMetadataFromFile(metadataFilePath: string) {
|
|
139
|
+
// Verify file extension is .json
|
|
140
|
+
if (path.extname(metadataFilePath) !== '.json') {
|
|
141
|
+
throw new Error('Metadata file must be in JSON format');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// Read JSON file content
|
|
146
|
+
const rawData = fs.readFileSync(metadataFilePath, 'utf8');
|
|
147
|
+
const jsonData = JSON.parse(rawData);
|
|
148
|
+
|
|
149
|
+
// Validate JSON structure
|
|
150
|
+
if (!jsonData || typeof jsonData !== 'object') {
|
|
151
|
+
throw new Error('Invalid JSON format');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Construct metadata structure
|
|
155
|
+
const metadata = {
|
|
156
|
+
...jsonData,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return metadata;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (error instanceof Error) {
|
|
162
|
+
throw new Error(`Failed to read metadata file: ${error.message}`);
|
|
163
|
+
}
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
package/src/utils/utils.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { spawn } from 'child_process';
|
|
|
10
10
|
export type schema = {
|
|
11
11
|
name: string;
|
|
12
12
|
objectId: string;
|
|
13
|
-
structure: Record<string, string
|
|
13
|
+
structure: Record<string, string>;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export type DeploymentJsonType = {
|
|
@@ -110,6 +110,27 @@ export async function getOldPackageId(
|
|
|
110
110
|
return deployment.packageId;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
export async function getObjectId(
|
|
114
|
+
projectPath: string,
|
|
115
|
+
network: string,
|
|
116
|
+
schemaName: string
|
|
117
|
+
): Promise<string> {
|
|
118
|
+
const deployment = await getDeploymentJson(projectPath, network);
|
|
119
|
+
const schema = deployment.schemas.find(schema =>
|
|
120
|
+
schema.name
|
|
121
|
+
.toLowerCase()
|
|
122
|
+
.endsWith(`::${schemaName.toLowerCase()}_schema::${schemaName}`)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (!schema?.objectId) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Schema '${schemaName}' not found in deployment history`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return schema.objectId;
|
|
132
|
+
}
|
|
133
|
+
|
|
113
134
|
export async function getUpgradeCap(
|
|
114
135
|
projectPath: string,
|
|
115
136
|
network: string
|
|
@@ -134,7 +155,7 @@ export function saveContractData(
|
|
|
134
155
|
packageId: string,
|
|
135
156
|
upgradeCap: string,
|
|
136
157
|
version: number,
|
|
137
|
-
|
|
158
|
+
schemas: schema[]
|
|
138
159
|
) {
|
|
139
160
|
const DeploymentData: DeploymentJsonType = {
|
|
140
161
|
projectName,
|
|
@@ -167,7 +188,9 @@ export async function writeOutput(
|
|
|
167
188
|
}
|
|
168
189
|
}
|
|
169
190
|
|
|
170
|
-
function getDubheDependency(
|
|
191
|
+
function getDubheDependency(
|
|
192
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
193
|
+
): string {
|
|
171
194
|
switch (network) {
|
|
172
195
|
case 'localnet':
|
|
173
196
|
return 'Dubhe = { local = "../dubhe-framework" }';
|
|
@@ -180,14 +203,19 @@ function getDubheDependency(network: 'mainnet' | 'testnet' | 'devnet' | 'localn
|
|
|
180
203
|
}
|
|
181
204
|
}
|
|
182
205
|
|
|
183
|
-
export function updateDubheDependency(
|
|
206
|
+
export function updateDubheDependency(
|
|
207
|
+
filePath: string,
|
|
208
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
209
|
+
) {
|
|
184
210
|
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
185
211
|
const newDependency = getDubheDependency(network);
|
|
186
212
|
const updatedContent = fileContent.replace(/Dubhe = \{.*\}/, newDependency);
|
|
187
213
|
fs.writeFileSync(filePath, updatedContent, 'utf-8');
|
|
188
214
|
console.log(`Updated Dubhe dependency in ${filePath} for ${network}.`);
|
|
189
215
|
}
|
|
190
|
-
export async function switchEnv(
|
|
216
|
+
export async function switchEnv(
|
|
217
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
218
|
+
) {
|
|
191
219
|
try {
|
|
192
220
|
return new Promise<void>((resolve, reject) => {
|
|
193
221
|
const suiProcess = spawn(
|
|
@@ -195,7 +223,7 @@ export async function switchEnv(network: 'mainnet' | 'testnet' | 'devnet' | 'loc
|
|
|
195
223
|
['client', 'switch', '--env', network],
|
|
196
224
|
{
|
|
197
225
|
env: { ...process.env },
|
|
198
|
-
stdio: 'pipe'
|
|
226
|
+
stdio: 'pipe',
|
|
199
227
|
}
|
|
200
228
|
);
|
|
201
229
|
|
|
@@ -209,9 +237,11 @@ export async function switchEnv(network: 'mainnet' | 'testnet' | 'devnet' | 'loc
|
|
|
209
237
|
reject(error); // Reject promise on error
|
|
210
238
|
});
|
|
211
239
|
|
|
212
|
-
suiProcess.on('exit',
|
|
240
|
+
suiProcess.on('exit', code => {
|
|
213
241
|
if (code !== 0) {
|
|
214
|
-
console.error(
|
|
242
|
+
console.error(
|
|
243
|
+
chalk.red(`\n❌ Process exited with code: ${code}`)
|
|
244
|
+
);
|
|
215
245
|
reject(new Error(`Process exited with code: ${code}`));
|
|
216
246
|
} else {
|
|
217
247
|
resolve(); // Resolve promise on successful exit
|