@0xobelisk/sui-cli 1.2.0-pre.12 ā 1.2.0-pre.121
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 +7 -7
- package/dist/dubhe.js +152 -51
- package/dist/dubhe.js.map +1 -1
- package/package.json +31 -19
- package/src/commands/build.ts +61 -18
- package/src/commands/call.ts +83 -83
- package/src/commands/checkBalance.ts +27 -12
- package/src/commands/convertJson.ts +85 -0
- package/src/commands/doctor.ts +1515 -0
- package/src/commands/faucet.ts +20 -10
- package/src/commands/generate.ts +61 -0
- package/src/commands/generateKey.ts +3 -2
- package/src/commands/index.ts +20 -11
- package/src/commands/info.ts +61 -0
- package/src/commands/loadMetadata.ts +68 -0
- package/src/commands/localnode.ts +22 -6
- package/src/commands/publish.ts +55 -7
- package/src/commands/query.ts +101 -101
- package/src/commands/shell.ts +208 -0
- package/src/commands/{configStore.ts ā storeConfig.ts} +13 -5
- package/src/commands/switchEnv.ts +33 -0
- package/src/commands/test.ts +143 -31
- package/src/commands/upgrade.ts +46 -6
- package/src/commands/wait.ts +333 -22
- package/src/commands/watch.ts +9 -8
- package/src/dubhe.ts +12 -4
- package/src/utils/axios-downloader.ts +116 -0
- package/src/utils/callHandler.ts +118 -118
- package/src/utils/checkBalance.ts +6 -2
- package/src/utils/constants.ts +9 -0
- package/src/utils/generateAccount.ts +1 -1
- package/src/utils/index.ts +4 -3
- package/src/utils/metadataHandler.ts +17 -0
- package/src/utils/publishHandler.ts +408 -289
- package/src/utils/queryStorage.ts +141 -141
- package/src/utils/startNode.ts +115 -16
- package/src/utils/storeConfig.ts +50 -10
- package/src/utils/upgradeHandler.ts +218 -85
- package/src/utils/utils.ts +1041 -63
- package/src/commands/schemagen.ts +0 -40
|
@@ -7,49 +7,33 @@ import {
|
|
|
7
7
|
getVersion,
|
|
8
8
|
getUpgradeCap,
|
|
9
9
|
saveContractData,
|
|
10
|
-
getOnchainSchemas,
|
|
11
10
|
switchEnv,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
initializeDubhe,
|
|
12
|
+
getOnchainResources,
|
|
13
|
+
getStartCheckpoint,
|
|
14
|
+
appendMigrateFunction,
|
|
15
|
+
getDubheDappHubId,
|
|
16
|
+
getDappStorageId,
|
|
17
|
+
getOriginalDubhePackageId,
|
|
18
|
+
updatePublishedToml,
|
|
19
|
+
syncDubheFrameworkAddress,
|
|
20
|
+
clearPublishedTomlEntry,
|
|
21
|
+
restorePublishedTomlEntry,
|
|
22
|
+
readPublishedToml,
|
|
23
|
+
updateEphemeralPubFile,
|
|
24
|
+
getEphemeralPubFilePath,
|
|
25
|
+
updateMoveTomlAddress,
|
|
26
|
+
appendPackageIdToConfig
|
|
15
27
|
} from './utils';
|
|
16
28
|
import * as fs from 'fs';
|
|
17
29
|
import * as path from 'path';
|
|
18
30
|
import { DubheConfig } from '@0xobelisk/sui-common';
|
|
19
31
|
|
|
20
32
|
type Migration = {
|
|
21
|
-
|
|
22
|
-
fields:
|
|
33
|
+
name: string;
|
|
34
|
+
fields: any;
|
|
23
35
|
};
|
|
24
36
|
|
|
25
|
-
function updateMigrateMethod(projectPath: string, migrations: Migration[]): void {
|
|
26
|
-
let filePath = `${projectPath}/sources/codegen/core/schema.move`;
|
|
27
|
-
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
28
|
-
const migrateMethodRegex = new RegExp(
|
|
29
|
-
`public fun migrate\\(_schema: &mut Schema, _ctx: &mut TxContext\\) {[^}]*}`
|
|
30
|
-
);
|
|
31
|
-
const newMigrateMethod = `
|
|
32
|
-
public fun migrate(_schema: &mut Schema, _ctx: &mut TxContext) {
|
|
33
|
-
${migrations
|
|
34
|
-
.map((migration) => {
|
|
35
|
-
let storage_type = '';
|
|
36
|
-
if (migration.fields.includes('StorageValue')) {
|
|
37
|
-
storage_type = `storage_value::new(b"${migration.schemaName}", _ctx)`;
|
|
38
|
-
} else if (migration.fields.includes('StorageMap')) {
|
|
39
|
-
storage_type = `storage_map::new(b"${migration.schemaName}", _ctx)`;
|
|
40
|
-
} else if (migration.fields.includes('StorageDoubleMap')) {
|
|
41
|
-
storage_type = `storage_double_map::new(b"${migration.schemaName}", _ctx)`;
|
|
42
|
-
}
|
|
43
|
-
return `storage::add_field<${migration.fields}>(&mut _schema.id, b"${migration.schemaName}", ${storage_type});`;
|
|
44
|
-
})
|
|
45
|
-
.join('')}
|
|
46
|
-
}
|
|
47
|
-
`;
|
|
48
|
-
|
|
49
|
-
const updatedContent = fileContent.replace(migrateMethodRegex, newMigrateMethod);
|
|
50
|
-
fs.writeFileSync(filePath, updatedContent, 'utf-8');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
37
|
function replaceEnvField(
|
|
54
38
|
filePath: string,
|
|
55
39
|
networkType: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
|
|
@@ -89,24 +73,51 @@ function replaceEnvField(
|
|
|
89
73
|
|
|
90
74
|
return previousValue;
|
|
91
75
|
}
|
|
76
|
+
|
|
92
77
|
export async function upgradeHandler(
|
|
93
78
|
config: DubheConfig,
|
|
94
79
|
name: string,
|
|
95
|
-
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
80
|
+
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
|
|
81
|
+
bumpVersion: boolean = false,
|
|
82
|
+
fullnodeUrls?: string[]
|
|
96
83
|
) {
|
|
97
|
-
await switchEnv(network);
|
|
84
|
+
await switchEnv(network, fullnodeUrls?.[0]);
|
|
98
85
|
|
|
99
86
|
const path = process.cwd();
|
|
100
87
|
const projectPath = `${path}/src/${name}`;
|
|
101
88
|
|
|
102
|
-
const dubhe = initializeDubhe({
|
|
103
|
-
network
|
|
104
|
-
});
|
|
89
|
+
const dubhe = initializeDubhe({ network, fullnodeUrls });
|
|
105
90
|
|
|
106
91
|
let oldVersion = Number(await getVersion(projectPath, network));
|
|
107
92
|
let oldPackageId = await getOldPackageId(projectPath, network);
|
|
108
93
|
let upgradeCap = await getUpgradeCap(projectPath, network);
|
|
109
|
-
let
|
|
94
|
+
let startCheckpoint = await getStartCheckpoint(projectPath, network);
|
|
95
|
+
let dappHubId = await getDubheDappHubId(network);
|
|
96
|
+
let dappStorageId = await getDappStorageId(projectPath, network);
|
|
97
|
+
// For localnet the framework is deployed ephemerally; preserve its package ID in .history.
|
|
98
|
+
const frameworkPackageId =
|
|
99
|
+
network === 'localnet' ? await getOriginalDubhePackageId(network) : undefined;
|
|
100
|
+
let onchainResources = await getOnchainResources(projectPath, network);
|
|
101
|
+
|
|
102
|
+
let pendingMigration: Migration[] = [];
|
|
103
|
+
Object.entries(config.resources ?? {}).forEach(([key, value]) => {
|
|
104
|
+
if (!onchainResources.hasOwnProperty(key)) {
|
|
105
|
+
pendingMigration.push({ name: key, fields: value });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const tables = pendingMigration.map((migration) => migration.name);
|
|
110
|
+
// Trigger migration when new resources were detected (schema change) OR when the
|
|
111
|
+
// caller explicitly requested a version bump (--bump-version flag). The latter
|
|
112
|
+
// covers breaking logic changes and security fixes that must invalidate old clients
|
|
113
|
+
// even though no new resources were added.
|
|
114
|
+
const needsMigration = tables.length > 0 || bumpVersion;
|
|
115
|
+
if (needsMigration) {
|
|
116
|
+
if (bumpVersion && tables.length === 0) {
|
|
117
|
+
console.log(chalk.yellow('--bump-version: forcing version bump with no schema changes.'));
|
|
118
|
+
}
|
|
119
|
+
appendMigrateFunction(projectPath, config.name, oldVersion + 1);
|
|
120
|
+
}
|
|
110
121
|
|
|
111
122
|
const original_published_id = replaceEnvField(
|
|
112
123
|
`${projectPath}/Move.lock`,
|
|
@@ -115,33 +126,96 @@ export async function upgradeHandler(
|
|
|
115
126
|
'0x0000000000000000000000000000000000000000000000000000000000000000'
|
|
116
127
|
);
|
|
117
128
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
// For persistent networks (testnet/mainnet): zero out Published.toml so the
|
|
130
|
+
// package compiles with address 0x0 for upgrade.
|
|
131
|
+
// For localnet: we use --build-env testnet + Pub.localnet.toml, so Published.toml
|
|
132
|
+
// is not consulted during the build and does not need to be cleared.
|
|
133
|
+
const savedPublishedEntry =
|
|
134
|
+
network !== 'localnet' ? clearPublishedTomlEntry(projectPath, network) : undefined;
|
|
135
|
+
|
|
136
|
+
// For testnet/mainnet: auto-sync src/dubhe/Published.toml to the canonical
|
|
137
|
+
// framework address from the SDK before building. Prevents
|
|
138
|
+
// VMVerificationOrDeserializationError when the framework was redeployed
|
|
139
|
+
// since the Published.toml was last written. Skip when upgrading dubhe itself.
|
|
140
|
+
if (name !== 'dubhe' && (network === 'testnet' || network === 'mainnet')) {
|
|
141
|
+
const chainId = await dubhe.suiInteractor.currentClient.getChainIdentifier();
|
|
142
|
+
syncDubheFrameworkAddress(path, network, chainId);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// For localnet upgrades: refresh Pub.localnet.toml with dubhe's current address
|
|
146
|
+
// so that the build can resolve the dubhe dependency.
|
|
147
|
+
// Skip this step when upgrading dubhe itself ā dubhe has no local dubhe dependency,
|
|
148
|
+
// and adding its own address to the pubfile causes PublishErrorNonZeroAddress because
|
|
149
|
+
// the Sui CLI treats the root package's pubfile entry as a non-zero self-address.
|
|
150
|
+
const cwd = process.cwd();
|
|
151
|
+
if (network === 'localnet' && name !== 'dubhe') {
|
|
152
|
+
const dubheProjectPath = `${cwd}/src/dubhe`;
|
|
153
|
+
const dubheEntries = readPublishedToml(dubheProjectPath);
|
|
154
|
+
const dubheEntry = dubheEntries['localnet'];
|
|
155
|
+
if (dubheEntry) {
|
|
156
|
+
const pubfilePath = getEphemeralPubFilePath(cwd, network);
|
|
157
|
+
const dubheUpgradeCap = await getUpgradeCap(dubheProjectPath, network).catch(() => '');
|
|
158
|
+
updateEphemeralPubFile(pubfilePath, dubheEntry.chainId, 'testnet', {
|
|
159
|
+
source: dubheProjectPath,
|
|
160
|
+
publishedAt: dubheEntry.publishedAt,
|
|
161
|
+
originalId: dubheEntry.originalId,
|
|
162
|
+
upgradeCap: dubheUpgradeCap
|
|
163
|
+
});
|
|
123
164
|
}
|
|
124
|
-
}
|
|
125
|
-
updateMigrateMethod(projectPath, pendingMigration);
|
|
165
|
+
}
|
|
126
166
|
|
|
127
167
|
try {
|
|
128
168
|
let modules: any, dependencies: any, digest: any;
|
|
169
|
+
// When upgrading dubhe itself on localnet, Move.toml has a non-zero 'dubhe' address
|
|
170
|
+
// (the mainnet/testnet deploy address). This gets baked into the upgrade bytecode as the
|
|
171
|
+
// self-address, causing PublishErrorNonZeroAddress. Zero it out before the build and
|
|
172
|
+
// restore it afterwards ā mirroring what publishDubheFramework does.
|
|
173
|
+
let savedDubheMoveTomlContent: string | null = null;
|
|
174
|
+
if (network === 'localnet' && name === 'dubhe') {
|
|
175
|
+
const moveTomlPath = `${projectPath}/Move.toml`;
|
|
176
|
+
savedDubheMoveTomlContent = fs.readFileSync(moveTomlPath, 'utf-8');
|
|
177
|
+
updateMoveTomlAddress(projectPath, '0x0');
|
|
178
|
+
}
|
|
129
179
|
try {
|
|
180
|
+
// For localnet: use --build-env testnet --pubfile-path Pub.localnet.toml
|
|
181
|
+
// so the package compiles with address 0x0 (not in pubfile) and links
|
|
182
|
+
// against the already-published dubhe dependency (from pubfile).
|
|
183
|
+
// Exception: when upgrading dubhe itself, skip the pubfile ā dubhe has no
|
|
184
|
+
// local dubhe dependency and including itself in the pubfile causes
|
|
185
|
+
// PublishErrorNonZeroAddress.
|
|
186
|
+
// For testnet/mainnet: use -e <network> as usual.
|
|
187
|
+
let buildCmd: string;
|
|
188
|
+
if (network === 'localnet') {
|
|
189
|
+
if (name !== 'dubhe') {
|
|
190
|
+
const pubfilePath = getEphemeralPubFilePath(cwd, network);
|
|
191
|
+
buildCmd = `sui move build --dump-bytecode-as-base64 --no-tree-shaking --build-env testnet --pubfile-path ${pubfilePath} --path ${path}/src/${name}`;
|
|
192
|
+
} else {
|
|
193
|
+
buildCmd = `sui move build --dump-bytecode-as-base64 --no-tree-shaking --build-env testnet --path ${path}/src/${name}`;
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
buildCmd = `sui move build --dump-bytecode-as-base64 --no-tree-shaking -e ${network} --path ${path}/src/${name}`;
|
|
197
|
+
}
|
|
198
|
+
|
|
130
199
|
const {
|
|
131
200
|
modules: extractedModules,
|
|
132
201
|
dependencies: extractedDependencies,
|
|
133
202
|
digest: extractedDigest
|
|
134
|
-
} = JSON.parse(
|
|
135
|
-
execSync(`sui move build --dump-bytecode-as-base64 --path ${path}/src/${name}`, {
|
|
136
|
-
encoding: 'utf-8'
|
|
137
|
-
})
|
|
138
|
-
);
|
|
203
|
+
} = JSON.parse(execSync(buildCmd, { encoding: 'utf-8', stdio: 'pipe' }));
|
|
139
204
|
|
|
140
205
|
modules = extractedModules;
|
|
141
206
|
dependencies = extractedDependencies;
|
|
142
207
|
digest = extractedDigest;
|
|
143
208
|
} catch (error: any) {
|
|
144
|
-
|
|
209
|
+
// Restore Published.toml before throwing (only for persistent networks)
|
|
210
|
+
if (savedPublishedEntry) {
|
|
211
|
+
restorePublishedTomlEntry(projectPath, network, savedPublishedEntry);
|
|
212
|
+
}
|
|
213
|
+
throw new UpgradeError(error.stdout || error.stderr || error.message);
|
|
214
|
+
} finally {
|
|
215
|
+
// Always restore dubhe Move.toml after build (success or error)
|
|
216
|
+
if (savedDubheMoveTomlContent !== null) {
|
|
217
|
+
fs.writeFileSync(`${projectPath}/Move.toml`, savedDubheMoveTomlContent, 'utf-8');
|
|
218
|
+
}
|
|
145
219
|
}
|
|
146
220
|
|
|
147
221
|
console.log('\nš Starting Upgrade Process...');
|
|
@@ -200,51 +274,110 @@ export async function upgradeHandler(
|
|
|
200
274
|
replaceEnvField(`${projectPath}/Move.lock`, network, 'latest-published-id', newPackageId);
|
|
201
275
|
replaceEnvField(`${projectPath}/Move.lock`, network, 'published-version', oldVersion + 1 + '');
|
|
202
276
|
|
|
277
|
+
// Update Published.toml with the new package ID after upgrade.
|
|
278
|
+
// For localnet: savedPublishedEntry is undefined (we skip clearPublishedTomlEntry),
|
|
279
|
+
// so fall back to reading the current Published.toml entry for chainId/originalId.
|
|
280
|
+
const existingEntry = readPublishedToml(projectPath)[network];
|
|
281
|
+
const chainId = savedPublishedEntry?.chainId ?? existingEntry?.chainId ?? '';
|
|
282
|
+
updatePublishedToml(
|
|
283
|
+
projectPath,
|
|
284
|
+
network,
|
|
285
|
+
chainId,
|
|
286
|
+
newPackageId,
|
|
287
|
+
savedPublishedEntry?.originalId ?? existingEntry?.originalId ?? original_published_id,
|
|
288
|
+
oldVersion + 1
|
|
289
|
+
);
|
|
290
|
+
|
|
203
291
|
saveContractData(
|
|
204
292
|
name,
|
|
205
293
|
network,
|
|
294
|
+
startCheckpoint,
|
|
206
295
|
newPackageId,
|
|
207
|
-
|
|
296
|
+
original_published_id, // originalPackageId: stable across all upgrades
|
|
297
|
+
dappHubId,
|
|
208
298
|
upgradeCap,
|
|
209
299
|
oldVersion + 1,
|
|
210
|
-
config.
|
|
300
|
+
config.resources ?? {},
|
|
301
|
+
config.enums,
|
|
302
|
+
frameworkPackageId,
|
|
303
|
+
dappStorageId || undefined
|
|
211
304
|
);
|
|
212
305
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const migrateTx = new Transaction();
|
|
220
|
-
const newVersion = oldVersion + 1;
|
|
221
|
-
let args = [];
|
|
222
|
-
if (name !== 'dubhe') {
|
|
223
|
-
let dubheSchemaId = await getDubheSchemaId(network);
|
|
224
|
-
args.push(migrateTx.object(dubheSchemaId));
|
|
306
|
+
// Append the new package ID to dubhe.config.json so the indexer can verify
|
|
307
|
+
// event.type_.address against all known package versions on next startup.
|
|
308
|
+
const configJsonPath = `${process.cwd()}/dubhe.config.json`;
|
|
309
|
+
appendPackageIdToConfig(configJsonPath, newPackageId);
|
|
310
|
+
if (fs.existsSync(configJsonPath)) {
|
|
311
|
+
console.log(chalk.green(`ā
Appended ${newPackageId} to dubhe.config.json package_ids`));
|
|
225
312
|
}
|
|
226
|
-
args.push(migrateTx.object(schemaId));
|
|
227
|
-
args.push(migrateTx.pure.address(newPackageId));
|
|
228
|
-
args.push(migrateTx.pure.u32(newVersion));
|
|
229
|
-
migrateTx.moveCall({
|
|
230
|
-
target: `${newPackageId}::${name}_migrate::migrate_to_v${newVersion}`,
|
|
231
|
-
arguments: args
|
|
232
|
-
});
|
|
233
313
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
console.log(
|
|
241
|
-
|
|
314
|
+
// Only run the migration transaction if there are pending schema changes or a
|
|
315
|
+
// forced version bump was requested via --bump-version.
|
|
316
|
+
// A pure "bug-fix" upgrade with no new fields and no --bump-version flag does
|
|
317
|
+
// not need a migration call ā old and new package can coexist safely.
|
|
318
|
+
if (needsMigration) {
|
|
319
|
+
if (pendingMigration.length > 0) {
|
|
320
|
+
console.log(`\nš Starting Migration Process...`);
|
|
321
|
+
pendingMigration.forEach((migration) => {
|
|
322
|
+
console.log('š Added Fields:', JSON.stringify(migration, null, 2));
|
|
323
|
+
});
|
|
324
|
+
} else {
|
|
325
|
+
console.log(`\nš Starting Migration Process (forced version bump)...`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// On localnet the indexer may lag behind the chain; give it time to register
|
|
329
|
+
// the newly upgraded package before the migration transaction references it.
|
|
330
|
+
// On testnet/mainnet the validator processes state immediately ā no delay needed.
|
|
331
|
+
if (network === 'localnet') {
|
|
332
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (!dappStorageId) {
|
|
336
|
+
console.warn(
|
|
337
|
+
chalk.yellow(
|
|
338
|
+
'Warning: dappStorageId not found in latest.json. ' +
|
|
339
|
+
'Re-publish the contract to capture it, or pass DappStorage manually.'
|
|
340
|
+
)
|
|
242
341
|
);
|
|
243
|
-
console.error(error);
|
|
244
342
|
}
|
|
245
|
-
|
|
343
|
+
|
|
344
|
+
const migrateTx = new Transaction();
|
|
345
|
+
const newVersion = oldVersion + 1;
|
|
346
|
+
migrateTx.moveCall({
|
|
347
|
+
target: `${newPackageId}::migrate::migrate_to_v${newVersion}`,
|
|
348
|
+
arguments: [
|
|
349
|
+
migrateTx.object(dappHubId),
|
|
350
|
+
migrateTx.object(dappStorageId),
|
|
351
|
+
migrateTx.pure.address(newPackageId)
|
|
352
|
+
]
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
await dubhe.signAndSendTxn({
|
|
356
|
+
tx: migrateTx,
|
|
357
|
+
onSuccess: (result) => {
|
|
358
|
+
console.log(chalk.green(`Migration Transaction Digest: ${result.digest}`));
|
|
359
|
+
},
|
|
360
|
+
onError: (error) => {
|
|
361
|
+
console.log(
|
|
362
|
+
chalk.red('Migration Transaction failed!, Please execute the migration manually.')
|
|
363
|
+
);
|
|
364
|
+
console.error(error);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
} else {
|
|
368
|
+
console.log(`\nā
No schema changes ā migration step skipped.`);
|
|
369
|
+
// Brief delay to allow localnet to index the upgraded package before
|
|
370
|
+
// subsequent on-chain queries.
|
|
371
|
+
if (network === 'localnet') {
|
|
372
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
246
375
|
} catch (error: any) {
|
|
376
|
+
// Restore Published.toml to original state on failure (persistent networks only)
|
|
377
|
+
if (savedPublishedEntry) {
|
|
378
|
+
restorePublishedTomlEntry(projectPath, network, savedPublishedEntry);
|
|
379
|
+
}
|
|
247
380
|
console.log(chalk.red('upgrade handler execution failed!'));
|
|
248
|
-
|
|
381
|
+
throw error;
|
|
249
382
|
}
|
|
250
383
|
}
|