@0xobelisk/sui-cli 1.2.0-pre.98 → 2.0.0
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/LICENSE +92 -0
- package/README.md +5 -5
- package/dist/dubhe.js +121 -102
- package/dist/dubhe.js.map +1 -1
- package/package.json +24 -24
- package/src/commands/build.ts +15 -3
- package/src/commands/checkBalance.ts +17 -9
- package/src/commands/convertJson.ts +39 -16
- package/src/commands/faucet.ts +9 -3
- package/src/commands/{schemagen.ts → generate.ts} +16 -6
- package/src/commands/index.ts +4 -4
- package/src/commands/info.ts +8 -2
- package/src/commands/loadMetadata.ts +13 -2
- package/src/commands/publish.ts +39 -5
- package/src/commands/shell.ts +15 -5
- package/src/commands/{configStore.ts → storeConfig.ts} +2 -1
- package/src/commands/switchEnv.ts +13 -6
- package/src/commands/test.ts +106 -37
- package/src/commands/upgrade.ts +36 -3
- package/src/commands/watch.ts +7 -7
- package/src/dubhe.ts +0 -0
- package/src/utils/checkBalance.ts +6 -2
- package/src/utils/constants.ts +8 -4
- package/src/utils/metadataHandler.ts +4 -3
- package/src/utils/publishHandler.ts +213 -142
- package/src/utils/storeConfig.ts +55 -9
- package/src/utils/upgradeHandler.ts +127 -98
- package/src/utils/utils.ts +405 -123
|
@@ -10,19 +10,23 @@ import {
|
|
|
10
10
|
switchEnv,
|
|
11
11
|
initializeDubhe,
|
|
12
12
|
getOnchainResources,
|
|
13
|
-
getOnchainComponents,
|
|
14
13
|
getStartCheckpoint,
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
appendMigrateFunction,
|
|
15
|
+
getDubheDappHubId,
|
|
16
|
+
getDappStorageId,
|
|
17
|
+
getOriginalDubhePackageId,
|
|
17
18
|
updatePublishedToml,
|
|
19
|
+
syncDubheFrameworkAddress,
|
|
18
20
|
clearPublishedTomlEntry,
|
|
19
21
|
restorePublishedTomlEntry,
|
|
20
22
|
readPublishedToml,
|
|
21
23
|
updateEphemeralPubFile,
|
|
22
|
-
getEphemeralPubFilePath
|
|
24
|
+
getEphemeralPubFilePath,
|
|
25
|
+
updateMoveTomlAddress,
|
|
26
|
+
appendPackageIdToConfig,
|
|
27
|
+
removeEnvFromMoveLock
|
|
23
28
|
} from './utils';
|
|
24
29
|
import * as fs from 'fs';
|
|
25
|
-
import * as path from 'path';
|
|
26
30
|
import { DubheConfig } from '@0xobelisk/sui-common';
|
|
27
31
|
|
|
28
32
|
type Migration = {
|
|
@@ -30,93 +34,56 @@ type Migration = {
|
|
|
30
34
|
fields: any;
|
|
31
35
|
};
|
|
32
36
|
|
|
33
|
-
function replaceEnvField(
|
|
34
|
-
filePath: string,
|
|
35
|
-
networkType: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
|
|
36
|
-
field: 'original-published-id' | 'latest-published-id' | 'published-version',
|
|
37
|
-
newValue: string
|
|
38
|
-
): string {
|
|
39
|
-
const envFilePath = path.resolve(filePath);
|
|
40
|
-
const envContent = fs.readFileSync(envFilePath, 'utf-8');
|
|
41
|
-
const envLines = envContent.split('\n');
|
|
42
|
-
|
|
43
|
-
const networkSectionIndex = envLines.findIndex((line) => line.trim() === `[env.${networkType}]`);
|
|
44
|
-
if (networkSectionIndex === -1) {
|
|
45
|
-
console.log(`Network type [env.${networkType}] not found in the file.`);
|
|
46
|
-
return '';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let fieldIndex = -1;
|
|
50
|
-
let previousValue: string = '';
|
|
51
|
-
for (let i = networkSectionIndex + 1; i < envLines.length; i++) {
|
|
52
|
-
const line = envLines[i].trim();
|
|
53
|
-
if (line.startsWith('[')) break; // End of the current network section
|
|
54
|
-
|
|
55
|
-
if (line.startsWith(field)) {
|
|
56
|
-
fieldIndex = i;
|
|
57
|
-
previousValue = line.split('=')[1].trim().replace(/"/g, '');
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (fieldIndex !== -1) {
|
|
63
|
-
envLines[fieldIndex] = `${field} = "${newValue}"`;
|
|
64
|
-
const newEnvContent = envLines.join('\n');
|
|
65
|
-
fs.writeFileSync(envFilePath, newEnvContent, 'utf-8');
|
|
66
|
-
} else {
|
|
67
|
-
console.log(`${field} not found for [env.${networkType}].`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return previousValue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
37
|
export async function upgradeHandler(
|
|
74
38
|
config: DubheConfig,
|
|
75
39
|
name: string,
|
|
76
|
-
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
40
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
|
|
41
|
+
bumpVersion: boolean = false,
|
|
42
|
+
fullnodeUrls?: string[]
|
|
77
43
|
) {
|
|
78
|
-
await switchEnv(network);
|
|
44
|
+
await switchEnv(network, fullnodeUrls?.[0]);
|
|
79
45
|
|
|
80
46
|
const path = process.cwd();
|
|
81
47
|
const projectPath = `${path}/src/${name}`;
|
|
82
48
|
|
|
83
|
-
const dubhe = initializeDubhe({ network });
|
|
49
|
+
const dubhe = initializeDubhe({ network, fullnodeUrls });
|
|
84
50
|
|
|
85
51
|
let oldVersion = Number(await getVersion(projectPath, network));
|
|
86
52
|
let oldPackageId = await getOldPackageId(projectPath, network);
|
|
87
53
|
let upgradeCap = await getUpgradeCap(projectPath, network);
|
|
88
54
|
let startCheckpoint = await getStartCheckpoint(projectPath, network);
|
|
89
|
-
let
|
|
55
|
+
let dappHubId = await getDubheDappHubId(network);
|
|
56
|
+
let dappStorageId = await getDappStorageId(projectPath, network);
|
|
57
|
+
// For localnet the framework is deployed ephemerally; preserve its package ID in .history.
|
|
58
|
+
const frameworkPackageId =
|
|
59
|
+
network === 'localnet' ? await getOriginalDubhePackageId(network) : undefined;
|
|
90
60
|
let onchainResources = await getOnchainResources(projectPath, network);
|
|
91
|
-
let onchainComponents = await getOnchainComponents(projectPath, network);
|
|
92
61
|
|
|
93
62
|
let pendingMigration: Migration[] = [];
|
|
94
|
-
Object.entries(config.resources).forEach(([key, value]) => {
|
|
63
|
+
Object.entries(config.resources ?? {}).forEach(([key, value]) => {
|
|
95
64
|
if (!onchainResources.hasOwnProperty(key)) {
|
|
96
65
|
pendingMigration.push({ name: key, fields: value });
|
|
97
66
|
}
|
|
98
67
|
});
|
|
99
|
-
Object.entries(config.components).forEach(([key, value]) => {
|
|
100
|
-
if (!onchainComponents.hasOwnProperty(key)) {
|
|
101
|
-
pendingMigration.push({ name: key, fields: value });
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
68
|
|
|
105
69
|
const tables = pendingMigration.map((migration) => migration.name);
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
70
|
+
// Trigger migration when new resources were detected (schema change) OR when the
|
|
71
|
+
// caller explicitly requested a version bump (--bump-version flag). The latter
|
|
72
|
+
// covers breaking logic changes and security fixes that must invalidate old clients
|
|
73
|
+
// even though no new resources were added.
|
|
74
|
+
const needsMigration = tables.length > 0 || bumpVersion;
|
|
75
|
+
if (needsMigration) {
|
|
76
|
+
if (bumpVersion && tables.length === 0) {
|
|
77
|
+
console.log(chalk.yellow('--bump-version: forcing version bump with no schema changes.'));
|
|
78
|
+
}
|
|
79
|
+
appendMigrateFunction(projectPath, config.name, oldVersion + 1);
|
|
112
80
|
}
|
|
113
81
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
);
|
|
82
|
+
// Read original_published_id from Published.toml before clearing it.
|
|
83
|
+
// Published.toml is the canonical source of truth for deployed addresses;
|
|
84
|
+
// Move.lock [env.*] is owned by the Sui CLI and must not be read or written
|
|
85
|
+
// by our toolchain.
|
|
86
|
+
const original_published_id = readPublishedToml(projectPath)[network]?.originalId ?? '';
|
|
120
87
|
|
|
121
88
|
// For persistent networks (testnet/mainnet): zero out Published.toml so the
|
|
122
89
|
// package compiles with address 0x0 for upgrade.
|
|
@@ -125,10 +92,28 @@ export async function upgradeHandler(
|
|
|
125
92
|
const savedPublishedEntry =
|
|
126
93
|
network !== 'localnet' ? clearPublishedTomlEntry(projectPath, network) : undefined;
|
|
127
94
|
|
|
95
|
+
// Clear Move.lock [env.*] so the Sui CLI does not pick up a stale non-zero
|
|
96
|
+
// address and fail with PublishErrorNonZeroAddress during build.
|
|
97
|
+
// We intentionally do NOT write it back afterwards — Published.toml is the
|
|
98
|
+
// source of truth and the Sui CLI will regenerate the lock section if needed.
|
|
99
|
+
removeEnvFromMoveLock(`${projectPath}/Move.lock`, network);
|
|
100
|
+
|
|
101
|
+
// For testnet/mainnet: auto-sync src/dubhe/Published.toml to the canonical
|
|
102
|
+
// framework address from the SDK before building. Prevents
|
|
103
|
+
// VMVerificationOrDeserializationError when the framework was redeployed
|
|
104
|
+
// since the Published.toml was last written. Skip when upgrading dubhe itself.
|
|
105
|
+
if (name !== 'dubhe' && (network === 'testnet' || network === 'mainnet')) {
|
|
106
|
+
const chainId = await dubhe.suiInteractor.currentClient.getChainIdentifier();
|
|
107
|
+
syncDubheFrameworkAddress(path, network, chainId);
|
|
108
|
+
}
|
|
109
|
+
|
|
128
110
|
// For localnet upgrades: refresh Pub.localnet.toml with dubhe's current address
|
|
129
111
|
// so that the build can resolve the dubhe dependency.
|
|
112
|
+
// Skip this step when upgrading dubhe itself — dubhe has no local dubhe dependency,
|
|
113
|
+
// and adding its own address to the pubfile causes PublishErrorNonZeroAddress because
|
|
114
|
+
// the Sui CLI treats the root package's pubfile entry as a non-zero self-address.
|
|
130
115
|
const cwd = process.cwd();
|
|
131
|
-
if (network === 'localnet') {
|
|
116
|
+
if (network === 'localnet' && name !== 'dubhe') {
|
|
132
117
|
const dubheProjectPath = `${cwd}/src/dubhe`;
|
|
133
118
|
const dubheEntries = readPublishedToml(dubheProjectPath);
|
|
134
119
|
const dubheEntry = dubheEntries['localnet'];
|
|
@@ -146,15 +131,32 @@ export async function upgradeHandler(
|
|
|
146
131
|
|
|
147
132
|
try {
|
|
148
133
|
let modules: any, dependencies: any, digest: any;
|
|
134
|
+
// When upgrading dubhe itself on localnet, Move.toml has a non-zero 'dubhe' address
|
|
135
|
+
// (the mainnet/testnet deploy address). This gets baked into the upgrade bytecode as the
|
|
136
|
+
// self-address, causing PublishErrorNonZeroAddress. Zero it out before the build and
|
|
137
|
+
// restore it afterwards — mirroring what publishDubheFramework does.
|
|
138
|
+
let savedDubheMoveTomlContent: string | null = null;
|
|
139
|
+
if (network === 'localnet' && name === 'dubhe') {
|
|
140
|
+
const moveTomlPath = `${projectPath}/Move.toml`;
|
|
141
|
+
savedDubheMoveTomlContent = fs.readFileSync(moveTomlPath, 'utf-8');
|
|
142
|
+
updateMoveTomlAddress(projectPath, '0x0');
|
|
143
|
+
}
|
|
149
144
|
try {
|
|
150
145
|
// For localnet: use --build-env testnet --pubfile-path Pub.localnet.toml
|
|
151
146
|
// so the package compiles with address 0x0 (not in pubfile) and links
|
|
152
147
|
// against the already-published dubhe dependency (from pubfile).
|
|
148
|
+
// Exception: when upgrading dubhe itself, skip the pubfile — dubhe has no
|
|
149
|
+
// local dubhe dependency and including itself in the pubfile causes
|
|
150
|
+
// PublishErrorNonZeroAddress.
|
|
153
151
|
// For testnet/mainnet: use -e <network> as usual.
|
|
154
152
|
let buildCmd: string;
|
|
155
153
|
if (network === 'localnet') {
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
if (name !== 'dubhe') {
|
|
155
|
+
const pubfilePath = getEphemeralPubFilePath(cwd, network);
|
|
156
|
+
buildCmd = `sui move build --dump-bytecode-as-base64 --no-tree-shaking --build-env testnet --pubfile-path ${pubfilePath} --path ${path}/src/${name}`;
|
|
157
|
+
} else {
|
|
158
|
+
buildCmd = `sui move build --dump-bytecode-as-base64 --no-tree-shaking --build-env testnet --path ${path}/src/${name}`;
|
|
159
|
+
}
|
|
158
160
|
} else {
|
|
159
161
|
buildCmd = `sui move build --dump-bytecode-as-base64 --no-tree-shaking -e ${network} --path ${path}/src/${name}`;
|
|
160
162
|
}
|
|
@@ -174,6 +176,11 @@ export async function upgradeHandler(
|
|
|
174
176
|
restorePublishedTomlEntry(projectPath, network, savedPublishedEntry);
|
|
175
177
|
}
|
|
176
178
|
throw new UpgradeError(error.stdout || error.stderr || error.message);
|
|
179
|
+
} finally {
|
|
180
|
+
// Always restore dubhe Move.toml after build (success or error)
|
|
181
|
+
if (savedDubheMoveTomlContent !== null) {
|
|
182
|
+
fs.writeFileSync(`${projectPath}/Move.toml`, savedDubheMoveTomlContent, 'utf-8');
|
|
183
|
+
}
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
console.log('\n🚀 Starting Upgrade Process...');
|
|
@@ -223,15 +230,6 @@ export async function upgradeHandler(
|
|
|
223
230
|
}
|
|
224
231
|
});
|
|
225
232
|
|
|
226
|
-
replaceEnvField(
|
|
227
|
-
`${projectPath}/Move.lock`,
|
|
228
|
-
network,
|
|
229
|
-
'original-published-id',
|
|
230
|
-
original_published_id
|
|
231
|
-
);
|
|
232
|
-
replaceEnvField(`${projectPath}/Move.lock`, network, 'latest-published-id', newPackageId);
|
|
233
|
-
replaceEnvField(`${projectPath}/Move.lock`, network, 'published-version', oldVersion + 1 + '');
|
|
234
|
-
|
|
235
233
|
// Update Published.toml with the new package ID after upgrade.
|
|
236
234
|
// For localnet: savedPublishedEntry is undefined (we skip clearPublishedTomlEntry),
|
|
237
235
|
// so fall back to reading the current Published.toml entry for chainId/originalId.
|
|
@@ -251,33 +249,62 @@ export async function upgradeHandler(
|
|
|
251
249
|
network,
|
|
252
250
|
startCheckpoint,
|
|
253
251
|
newPackageId,
|
|
254
|
-
|
|
252
|
+
original_published_id, // originalPackageId: stable across all upgrades
|
|
253
|
+
dappHubId,
|
|
255
254
|
upgradeCap,
|
|
256
255
|
oldVersion + 1,
|
|
257
|
-
config.
|
|
258
|
-
config.
|
|
259
|
-
|
|
256
|
+
config.resources ?? {},
|
|
257
|
+
config.enums,
|
|
258
|
+
frameworkPackageId,
|
|
259
|
+
dappStorageId || undefined
|
|
260
260
|
);
|
|
261
261
|
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
console.log(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
262
|
+
// Append the new package ID to dubhe.config.json so the indexer can verify
|
|
263
|
+
// event.type_.address against all known package versions on next startup.
|
|
264
|
+
const configJsonPath = `${process.cwd()}/dubhe.config.json`;
|
|
265
|
+
appendPackageIdToConfig(configJsonPath, newPackageId);
|
|
266
|
+
if (fs.existsSync(configJsonPath)) {
|
|
267
|
+
console.log(chalk.green(`✅ Appended ${newPackageId} to dubhe.config.json package_ids`));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Only run the migration transaction if there are pending schema changes or a
|
|
271
|
+
// forced version bump was requested via --bump-version.
|
|
272
|
+
// A pure "bug-fix" upgrade with no new fields and no --bump-version flag does
|
|
273
|
+
// not need a migration call — old and new package can coexist safely.
|
|
274
|
+
if (needsMigration) {
|
|
275
|
+
if (pendingMigration.length > 0) {
|
|
276
|
+
console.log(`\n🚀 Starting Migration Process...`);
|
|
277
|
+
pendingMigration.forEach((migration) => {
|
|
278
|
+
console.log('📋 Added Fields:', JSON.stringify(migration, null, 2));
|
|
279
|
+
});
|
|
280
|
+
} else {
|
|
281
|
+
console.log(`\n🚀 Starting Migration Process (forced version bump)...`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// On localnet the indexer may lag behind the chain; give it time to register
|
|
285
|
+
// the newly upgraded package before the migration transaction references it.
|
|
286
|
+
// On testnet/mainnet the validator processes state immediately — no delay needed.
|
|
287
|
+
if (network === 'localnet') {
|
|
288
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!dappStorageId) {
|
|
292
|
+
console.warn(
|
|
293
|
+
chalk.yellow(
|
|
294
|
+
'Warning: dappStorageId not found in latest.json. ' +
|
|
295
|
+
'Re-publish the contract to capture it, or pass DappStorage manually.'
|
|
296
|
+
)
|
|
297
|
+
);
|
|
298
|
+
}
|
|
272
299
|
|
|
273
300
|
const migrateTx = new Transaction();
|
|
274
301
|
const newVersion = oldVersion + 1;
|
|
275
302
|
migrateTx.moveCall({
|
|
276
303
|
target: `${newPackageId}::migrate::migrate_to_v${newVersion}`,
|
|
277
304
|
arguments: [
|
|
278
|
-
migrateTx.object(
|
|
279
|
-
migrateTx.
|
|
280
|
-
migrateTx.pure.
|
|
305
|
+
migrateTx.object(dappHubId),
|
|
306
|
+
migrateTx.object(dappStorageId),
|
|
307
|
+
migrateTx.pure.address(newPackageId)
|
|
281
308
|
]
|
|
282
309
|
});
|
|
283
310
|
|
|
@@ -297,7 +324,9 @@ export async function upgradeHandler(
|
|
|
297
324
|
console.log(`\n✅ No schema changes — migration step skipped.`);
|
|
298
325
|
// Brief delay to allow localnet to index the upgraded package before
|
|
299
326
|
// subsequent on-chain queries.
|
|
300
|
-
|
|
327
|
+
if (network === 'localnet') {
|
|
328
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
329
|
+
}
|
|
301
330
|
}
|
|
302
331
|
} catch (error: any) {
|
|
303
332
|
// Restore Published.toml to original state on failure (persistent networks only)
|