@0xobelisk/sui-cli 0.4.9
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 +90 -0
- package/dist/obelisk.js +27 -0
- package/dist/obelisk.js.map +1 -0
- package/package.json +68 -0
- package/src/commands/faucet.ts +73 -0
- package/src/commands/hello.ts +18 -0
- package/src/commands/index.ts +20 -0
- package/src/commands/localnode.ts +28 -0
- package/src/commands/publish.ts +43 -0
- package/src/commands/schemagen.ts +32 -0
- package/src/commands/test.ts +38 -0
- package/src/commands/upgrade.ts +53 -0
- package/src/modules.d.ts +11 -0
- package/src/obelisk.ts +36 -0
- package/src/utils/errors.ts +46 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/printObelisk.ts +20 -0
- package/src/utils/publishHandler.ts +184 -0
- package/src/utils/upgradeHandler.ts +237 -0
- package/src/utils/utils.ts +153 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CommandModule } from "yargs";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
|
|
5
|
+
type Options = {
|
|
6
|
+
packagePath: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const commandModule: CommandModule<Options, Options> = {
|
|
10
|
+
command: "test",
|
|
11
|
+
|
|
12
|
+
describe: "Run tests in Obelisk contracts",
|
|
13
|
+
|
|
14
|
+
builder(yargs) {
|
|
15
|
+
return yargs.options({
|
|
16
|
+
packagePath: {
|
|
17
|
+
type: "string",
|
|
18
|
+
default: ".",
|
|
19
|
+
description: "Options to pass to forge test",
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
async handler({ packagePath }) {
|
|
25
|
+
// Start an internal anvil process if no world address is provided
|
|
26
|
+
try {
|
|
27
|
+
execSync(`sui move test --path ${packagePath}`, {
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
});
|
|
30
|
+
} catch (error: any) {
|
|
31
|
+
console.error(chalk.red("Error executing sui move test:"));
|
|
32
|
+
console.log(error.stdout);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default commandModule;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { CommandModule } from "yargs";
|
|
2
|
+
import { logError } from "../utils/errors";
|
|
3
|
+
import { upgradeHandler } from "../utils";
|
|
4
|
+
import { ObeliskConfig, loadConfig, ValueType } from "@0xobelisk/sui-common";
|
|
5
|
+
|
|
6
|
+
type Options = {
|
|
7
|
+
network: any;
|
|
8
|
+
configPath: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const commandModule: CommandModule<Options, Options> = {
|
|
12
|
+
command: "upgrade",
|
|
13
|
+
|
|
14
|
+
describe: "Upgrade your move contracts",
|
|
15
|
+
|
|
16
|
+
builder(yargs) {
|
|
17
|
+
return yargs.options({
|
|
18
|
+
network: {
|
|
19
|
+
type: "string",
|
|
20
|
+
choices: ["mainnet", "testnet", "devnet", "localnet"],
|
|
21
|
+
desc: "Network of the node (mainnet/testnet/devnet/localnet)",
|
|
22
|
+
},
|
|
23
|
+
configPath: {
|
|
24
|
+
type: "string",
|
|
25
|
+
default: "obelisk.config.ts",
|
|
26
|
+
decs: "Path to the config file",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
async handler({ network, configPath }) {
|
|
32
|
+
try {
|
|
33
|
+
const obeliskConfig = (await loadConfig(configPath)) as ObeliskConfig;
|
|
34
|
+
|
|
35
|
+
let schemaNames = Object.keys(obeliskConfig.schemas).filter(
|
|
36
|
+
(key) =>
|
|
37
|
+
!(
|
|
38
|
+
typeof obeliskConfig.schemas === "object" &&
|
|
39
|
+
"ephemeral" in obeliskConfig.schemas &&
|
|
40
|
+
(obeliskConfig.schemas[key] as ValueType).ephemeral
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
await upgradeHandler(obeliskConfig.name, network, schemaNames);
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
logError(error);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
process.exit(0);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default commandModule;
|
package/src/modules.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// adding .js to minimal would break clients down the line because it probably won't get a synthetic default import
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
declare module "protobufjs/minimal" {
|
|
4
|
+
export const configure: any;
|
|
5
|
+
export const util: any;
|
|
6
|
+
export const Reader: any;
|
|
7
|
+
export type Reader = any;
|
|
8
|
+
export const Writer: any;
|
|
9
|
+
export type Writer = any;
|
|
10
|
+
}
|
|
11
|
+
declare module "rollup-plugin-preserve-shebang";
|
package/src/obelisk.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import yargs from "yargs";
|
|
4
|
+
import { hideBin } from "yargs/helpers";
|
|
5
|
+
import { commands } from "./commands";
|
|
6
|
+
import { logError } from "./utils/errors";
|
|
7
|
+
|
|
8
|
+
// Load .env file into process.env
|
|
9
|
+
import * as dotenv from "dotenv";
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
dotenv.config();
|
|
12
|
+
|
|
13
|
+
yargs(hideBin(process.argv))
|
|
14
|
+
// Explicit name to display in help (by default it's the entry file, which may not be "obelisk" for e.g. ts-node)
|
|
15
|
+
.scriptName("obelisk")
|
|
16
|
+
// Use the commands directory to scaffold
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- command array overload isn't typed, see https://github.com/yargs/yargs/blob/main/docs/advanced.md#esm-hierarchy
|
|
18
|
+
.command(commands as any)
|
|
19
|
+
// Enable strict mode.
|
|
20
|
+
.strict()
|
|
21
|
+
// Custom error handler
|
|
22
|
+
.fail((msg, err) => {
|
|
23
|
+
console.error(chalk.red(msg));
|
|
24
|
+
if (msg.includes("Missing required argument")) {
|
|
25
|
+
console.log(
|
|
26
|
+
chalk.yellow(`Run 'pnpm obelisk ${process.argv[2]} --help' for a list of available and required arguments.`)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
console.log("");
|
|
30
|
+
logError(err);
|
|
31
|
+
console.log("");
|
|
32
|
+
|
|
33
|
+
process.exit(1);
|
|
34
|
+
})
|
|
35
|
+
// Useful aliases.
|
|
36
|
+
.alias({ h: "help" }).argv;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { ZodError } from "zod";
|
|
3
|
+
import { fromZodError, ValidationError } from "zod-validation-error";
|
|
4
|
+
|
|
5
|
+
export class NotInsideProjectError extends Error {
|
|
6
|
+
name = "NotInsideProjectError";
|
|
7
|
+
message = "You are not inside a Obelisk project";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class ObeliskCliError extends Error {
|
|
11
|
+
name = "ObeliskCliError";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class UpgradeError extends Error {
|
|
15
|
+
name = "UpgradeError";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class FsIibError extends Error {
|
|
19
|
+
name = "FsIibError";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function logError(error: unknown) {
|
|
23
|
+
if (error instanceof ValidationError) {
|
|
24
|
+
console.log(chalk.redBright(error.message));
|
|
25
|
+
} else if (error instanceof ZodError) {
|
|
26
|
+
// TODO currently this error shouldn't happen, use `fromZodErrorCustom`
|
|
27
|
+
const validationError = fromZodError(error, {
|
|
28
|
+
prefixSeparator: "\n- ",
|
|
29
|
+
issueSeparator: "\n- ",
|
|
30
|
+
});
|
|
31
|
+
console.log(chalk.redBright(validationError.message));
|
|
32
|
+
} else if (error instanceof NotInsideProjectError) {
|
|
33
|
+
console.log(chalk.red(error.message));
|
|
34
|
+
console.log("");
|
|
35
|
+
// TODO add docs to the website and update the link to the specific page
|
|
36
|
+
console.log(
|
|
37
|
+
chalk.blue(
|
|
38
|
+
`To learn more about Obelisk's configuration, please go to https://github.com/0xobelisk`
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
} else if (error instanceof ObeliskCliError) {
|
|
42
|
+
console.log(chalk.red(error));
|
|
43
|
+
} else {
|
|
44
|
+
console.log(error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
|
|
3
|
+
export function printObelisk() {
|
|
4
|
+
console.log(
|
|
5
|
+
chalk.yellow(`
|
|
6
|
+
Welcome to obelisk world
|
|
7
|
+
\t\t\t --from team@obelisk
|
|
8
|
+
________ ________ _______ ___ ___ ________ ___ __
|
|
9
|
+
|\\ __ \\|\\ __ \\|\\ ___ \\ |\\ \\ |\\ \\|\\ ____\\|\\ \\|\\ \\
|
|
10
|
+
\\ \\ \\|\\ \\ \\ \\|\\ /\\ \\ __/|\\ \\ \\ \\ \\ \\ \\ \\___|\\ \\ \\/ /|_
|
|
11
|
+
\\ \\ \\\\\\ \\ \\ __ \\ \\ \\_|/_\\ \\ \\ \\ \\ \\ \\_____ \\ \\ ___ \\
|
|
12
|
+
\\ \\ \\\\\\ \\ \\ \\|\\ \\ \\ \\_|\\ \\ \\ \\____\\ \\ \\|____|\\ \\ \\ \\\\ \\ \\
|
|
13
|
+
\\ \\_______\\ \\_______\\ \\_______\\ \\_______\\ \\__\\____\\_\\ \\ \\__\\\\ \\__\\
|
|
14
|
+
\\|_______|\\|_______|\\|_______|\\|_______|\\|__|\\_________\\|__| \\|__|
|
|
15
|
+
\\|_________|
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
`)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { TransactionBlock } from "@mysten/sui.js/transactions";
|
|
2
|
+
import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
|
|
3
|
+
import {
|
|
4
|
+
getFullnodeUrl,
|
|
5
|
+
SuiClient,
|
|
6
|
+
SuiTransactionBlockResponse,
|
|
7
|
+
} from "@mysten/sui.js/client";
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { ObeliskCliError } from "./errors";
|
|
11
|
+
import {
|
|
12
|
+
updateVersionInFile,
|
|
13
|
+
saveContractData,
|
|
14
|
+
validatePrivateKey,
|
|
15
|
+
} from "./utils";
|
|
16
|
+
|
|
17
|
+
export async function publishHandler(
|
|
18
|
+
name: string,
|
|
19
|
+
network: "mainnet" | "testnet" | "devnet" | "localnet"
|
|
20
|
+
) {
|
|
21
|
+
const path = process.cwd();
|
|
22
|
+
const projectPath = `${path}/contracts/${name}`;
|
|
23
|
+
|
|
24
|
+
const privateKey = process.env.PRIVATE_KEY;
|
|
25
|
+
if (!privateKey)
|
|
26
|
+
throw new ObeliskCliError(
|
|
27
|
+
`Missing PRIVATE_KEY environment variable.
|
|
28
|
+
Run 'echo "PRIVATE_KEY=YOUR_PRIVATE_KEY" > .env'
|
|
29
|
+
in your contracts directory to use the default sui private key.`
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const privateKeyFormat = validatePrivateKey(privateKey);
|
|
33
|
+
if (privateKeyFormat === false) {
|
|
34
|
+
throw new ObeliskCliError(`Please check your privateKey.`);
|
|
35
|
+
}
|
|
36
|
+
const privateKeyRaw = Buffer.from(privateKeyFormat as string, "hex");
|
|
37
|
+
const keypair = Ed25519Keypair.fromSecretKey(privateKeyRaw);
|
|
38
|
+
const client = new SuiClient({
|
|
39
|
+
url: getFullnodeUrl(network),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Set version 1
|
|
43
|
+
await updateVersionInFile(projectPath, "1");
|
|
44
|
+
let modules: any, dependencies: any;
|
|
45
|
+
try {
|
|
46
|
+
const { modules: extractedModules, dependencies: extractedDependencies } =
|
|
47
|
+
JSON.parse(
|
|
48
|
+
execSync(
|
|
49
|
+
`sui move build --dump-bytecode-as-base64 --path ${projectPath}`,
|
|
50
|
+
{
|
|
51
|
+
encoding: "utf-8",
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
modules = extractedModules;
|
|
56
|
+
dependencies = extractedDependencies;
|
|
57
|
+
} catch (error: any) {
|
|
58
|
+
console.error(chalk.red("Error executing sui move build:"));
|
|
59
|
+
console.error(error.stdout);
|
|
60
|
+
process.exit(1); // You might want to exit with a non-zero status code to indicate an error
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(chalk.blue(`Account: ${keypair.toSuiAddress()}`));
|
|
64
|
+
|
|
65
|
+
const tx = new TransactionBlock();
|
|
66
|
+
const [upgradeCap] = tx.publish({
|
|
67
|
+
modules,
|
|
68
|
+
dependencies,
|
|
69
|
+
});
|
|
70
|
+
tx.transferObjects(
|
|
71
|
+
[upgradeCap],
|
|
72
|
+
tx.pure(keypair.getPublicKey().toSuiAddress())
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
let result: SuiTransactionBlockResponse;
|
|
76
|
+
try {
|
|
77
|
+
result = await client.signAndExecuteTransactionBlock({
|
|
78
|
+
signer: keypair,
|
|
79
|
+
transactionBlock: tx,
|
|
80
|
+
options: {
|
|
81
|
+
showObjectChanges: true,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
} catch (error: any) {
|
|
85
|
+
console.error(chalk.red(`Failed to execute publish, please republish`));
|
|
86
|
+
console.error(error.message);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (result.effects?.status.status === "failure") {
|
|
91
|
+
console.log(chalk.red(`Failed to execute publish, please republish`));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let version = 1;
|
|
96
|
+
let packageId = "";
|
|
97
|
+
let worldId = "";
|
|
98
|
+
let upgradeCapId = "";
|
|
99
|
+
let adminCapId = "";
|
|
100
|
+
result.objectChanges!.map((object) => {
|
|
101
|
+
if (object.type === "published") {
|
|
102
|
+
console.log(chalk.blue(`${name} PackageId: ${object.packageId}`));
|
|
103
|
+
packageId = object.packageId;
|
|
104
|
+
}
|
|
105
|
+
if (
|
|
106
|
+
object.type === "created" &&
|
|
107
|
+
object.objectType.endsWith("::world::World")
|
|
108
|
+
) {
|
|
109
|
+
console.log(chalk.blue(`${name} WorldId: ${object.objectId}`));
|
|
110
|
+
worldId = object.objectId;
|
|
111
|
+
}
|
|
112
|
+
if (
|
|
113
|
+
object.type === "created" &&
|
|
114
|
+
object.objectType === "0x2::package::UpgradeCap"
|
|
115
|
+
) {
|
|
116
|
+
console.log(chalk.blue(`${name} UpgradeCap: ${object.objectId}`));
|
|
117
|
+
upgradeCapId = object.objectId;
|
|
118
|
+
}
|
|
119
|
+
if (
|
|
120
|
+
object.type === "created" &&
|
|
121
|
+
object.objectType.endsWith("::world::AdminCap")
|
|
122
|
+
) {
|
|
123
|
+
console.log(chalk.blue(`${name} AdminCapId: ${object.objectId}`));
|
|
124
|
+
adminCapId = object.objectId;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
console.log(chalk.green(`Publish transaction digest: ${result.digest}`));
|
|
129
|
+
|
|
130
|
+
saveContractData(
|
|
131
|
+
name,
|
|
132
|
+
network,
|
|
133
|
+
packageId,
|
|
134
|
+
worldId,
|
|
135
|
+
upgradeCapId,
|
|
136
|
+
adminCapId,
|
|
137
|
+
version
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
console.log("Executing the deployHook: ");
|
|
141
|
+
const delay = (ms: number) =>
|
|
142
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
143
|
+
await delay(5000);
|
|
144
|
+
|
|
145
|
+
const deployHookTx = new TransactionBlock();
|
|
146
|
+
|
|
147
|
+
deployHookTx.moveCall({
|
|
148
|
+
target: `${packageId}::deploy_hook::run`,
|
|
149
|
+
arguments: [deployHookTx.object(worldId), deployHookTx.object(adminCapId)],
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
let deployHookResult: SuiTransactionBlockResponse;
|
|
153
|
+
try {
|
|
154
|
+
deployHookResult = await client.signAndExecuteTransactionBlock({
|
|
155
|
+
signer: keypair,
|
|
156
|
+
transactionBlock: deployHookTx,
|
|
157
|
+
options: {
|
|
158
|
+
showEffects: true,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
} catch (error: any) {
|
|
162
|
+
console.error(
|
|
163
|
+
chalk.red(
|
|
164
|
+
`Failed to execute deployHook, please republish or manually call deploy_hook::run`
|
|
165
|
+
)
|
|
166
|
+
);
|
|
167
|
+
console.error(error.message);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (deployHookResult.effects?.status.status === "success") {
|
|
172
|
+
console.log(
|
|
173
|
+
chalk.green(
|
|
174
|
+
`Successful auto-execution of deployHook, please check the transaction digest: ${deployHookResult.digest}`
|
|
175
|
+
)
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
console.log(
|
|
179
|
+
chalk.yellow(
|
|
180
|
+
`Failed to execute deployHook, please republish or manually call deploy_hook::run`
|
|
181
|
+
)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { TransactionBlock, UpgradePolicy } from "@mysten/sui.js/transactions";
|
|
2
|
+
import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
|
|
3
|
+
import { getFullnodeUrl, SuiClient } from "@mysten/sui.js/client";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { ObeliskCliError, UpgradeError } from "./errors";
|
|
7
|
+
import {
|
|
8
|
+
updateVersionInFile,
|
|
9
|
+
getOldPackageId,
|
|
10
|
+
getVersion,
|
|
11
|
+
getWorldId,
|
|
12
|
+
getUpgradeCap,
|
|
13
|
+
saveContractData,
|
|
14
|
+
validatePrivateKey,
|
|
15
|
+
getAdminCap,
|
|
16
|
+
} from "./utils";
|
|
17
|
+
|
|
18
|
+
type ObjectContent = {
|
|
19
|
+
type: string;
|
|
20
|
+
fields: Record<string, any>;
|
|
21
|
+
hasPublicTransfer: boolean;
|
|
22
|
+
dataType: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function upgradeHandler(
|
|
26
|
+
name: string,
|
|
27
|
+
network: "mainnet" | "testnet" | "devnet" | "localnet",
|
|
28
|
+
schemaNames: string[]
|
|
29
|
+
) {
|
|
30
|
+
const path = process.cwd();
|
|
31
|
+
const projectPath = `${path}/contracts/${name}`;
|
|
32
|
+
const privateKey = process.env.PRIVATE_KEY;
|
|
33
|
+
if (!privateKey)
|
|
34
|
+
throw new ObeliskCliError(
|
|
35
|
+
`Missing PRIVATE_KEY environment variable.
|
|
36
|
+
Run 'echo "PRIVATE_KEY=YOUR_PRIVATE_KEY" > .env'
|
|
37
|
+
in your contracts directory to use the default sui private key.`
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const privateKeyFormat = validatePrivateKey(privateKey);
|
|
41
|
+
if (privateKeyFormat === false) {
|
|
42
|
+
throw new ObeliskCliError(`Please check your privateKey.`);
|
|
43
|
+
}
|
|
44
|
+
const privateKeyRaw = Buffer.from(privateKeyFormat as string, "hex");
|
|
45
|
+
const keypair = Ed25519Keypair.fromSecretKey(privateKeyRaw);
|
|
46
|
+
|
|
47
|
+
const client = new SuiClient({
|
|
48
|
+
url: getFullnodeUrl(network),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
let oldVersion = Number(await getVersion(projectPath, network));
|
|
52
|
+
let oldPackageId = await getOldPackageId(projectPath, network);
|
|
53
|
+
let worldId = await getWorldId(projectPath, network);
|
|
54
|
+
let upgradeCap = await getUpgradeCap(projectPath, network);
|
|
55
|
+
let adminCap = await getAdminCap(projectPath, network);
|
|
56
|
+
|
|
57
|
+
const newVersion = oldVersion + 1;
|
|
58
|
+
await updateVersionInFile(projectPath, newVersion.toString());
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
let modules: any, dependencies: any, digest: any;
|
|
62
|
+
try {
|
|
63
|
+
const {
|
|
64
|
+
modules: extractedModules,
|
|
65
|
+
dependencies: extractedDependencies,
|
|
66
|
+
digest: extractedDigest,
|
|
67
|
+
} = JSON.parse(
|
|
68
|
+
execSync(
|
|
69
|
+
`sui move build --dump-bytecode-as-base64 --path ${path}/contracts/${name}`,
|
|
70
|
+
{
|
|
71
|
+
encoding: "utf-8",
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
modules = extractedModules;
|
|
77
|
+
dependencies = extractedDependencies;
|
|
78
|
+
digest = extractedDigest;
|
|
79
|
+
} catch (error: any) {
|
|
80
|
+
throw new UpgradeError(error.stdout);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const tx = new TransactionBlock();
|
|
84
|
+
const ticket = tx.moveCall({
|
|
85
|
+
target: "0x2::package::authorize_upgrade",
|
|
86
|
+
arguments: [
|
|
87
|
+
tx.object(upgradeCap),
|
|
88
|
+
tx.pure(UpgradePolicy.COMPATIBLE),
|
|
89
|
+
tx.pure(digest),
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const receipt = tx.upgrade({
|
|
94
|
+
modules,
|
|
95
|
+
dependencies,
|
|
96
|
+
packageId: oldPackageId,
|
|
97
|
+
ticket,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
tx.moveCall({
|
|
101
|
+
target: "0x2::package::commit_upgrade",
|
|
102
|
+
arguments: [tx.object(upgradeCap), receipt],
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
tx.transferObjects(
|
|
106
|
+
[tx.object(upgradeCap)],
|
|
107
|
+
tx.pure(keypair.getPublicKey().toSuiAddress())
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const result = await client.signAndExecuteTransactionBlock({
|
|
111
|
+
signer: keypair,
|
|
112
|
+
transactionBlock: tx,
|
|
113
|
+
options: {
|
|
114
|
+
showObjectChanges: true,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log("");
|
|
119
|
+
console.log(`${name} WorldId: ${worldId}`);
|
|
120
|
+
|
|
121
|
+
let newPackageId = "";
|
|
122
|
+
let newUpgradeCap = "";
|
|
123
|
+
result.objectChanges!.map((object) => {
|
|
124
|
+
if (object.type === "published") {
|
|
125
|
+
console.log(chalk.blue(`${name} PackageId: ${object.packageId}`));
|
|
126
|
+
newPackageId = object.packageId;
|
|
127
|
+
}
|
|
128
|
+
if (
|
|
129
|
+
object.type === "mutated" &&
|
|
130
|
+
object.objectType === "0x2::package::UpgradeCap"
|
|
131
|
+
) {
|
|
132
|
+
console.log(chalk.blue(`${name} UpgradeCap: ${object.objectId}`));
|
|
133
|
+
newUpgradeCap = object.objectId;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
console.log(chalk.green(`Upgrade Transaction Digest: ${result.digest}`));
|
|
138
|
+
|
|
139
|
+
saveContractData(
|
|
140
|
+
name,
|
|
141
|
+
network,
|
|
142
|
+
newPackageId,
|
|
143
|
+
worldId,
|
|
144
|
+
newUpgradeCap,
|
|
145
|
+
adminCap,
|
|
146
|
+
newVersion
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
oldPackageId = newPackageId;
|
|
150
|
+
upgradeCap = newUpgradeCap;
|
|
151
|
+
oldVersion = newVersion;
|
|
152
|
+
|
|
153
|
+
console.log("\nExecuting the migrate: ");
|
|
154
|
+
const delay = (ms: number) =>
|
|
155
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
156
|
+
await delay(5000);
|
|
157
|
+
|
|
158
|
+
const migrateTx = new TransactionBlock();
|
|
159
|
+
migrateTx.moveCall({
|
|
160
|
+
target: `${newPackageId}::world::migrate`,
|
|
161
|
+
arguments: [migrateTx.object(worldId), migrateTx.object(adminCap)],
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
let newWorldObject = await client.getObject({
|
|
165
|
+
id: worldId,
|
|
166
|
+
options: {
|
|
167
|
+
showContent: true,
|
|
168
|
+
showDisplay: true,
|
|
169
|
+
showType: true,
|
|
170
|
+
showOwner: true,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
let newObjectContent = newWorldObject.data!.content as ObjectContent;
|
|
174
|
+
|
|
175
|
+
const uniqueSchema: string[] = schemaNames.filter(
|
|
176
|
+
(item) => !newObjectContent.fields["schema_names"].includes(item)
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
console.log("new schema:", uniqueSchema);
|
|
180
|
+
let needRegisterSchema = [];
|
|
181
|
+
for (const newSchema of uniqueSchema) {
|
|
182
|
+
migrateTx.moveCall({
|
|
183
|
+
target: `${newPackageId}::${newSchema}_schema::register`,
|
|
184
|
+
arguments: [migrateTx.object(worldId), migrateTx.object(adminCap)],
|
|
185
|
+
});
|
|
186
|
+
needRegisterSchema.push(`${newSchema}_schema`);
|
|
187
|
+
}
|
|
188
|
+
const migrateResult = await client.signAndExecuteTransactionBlock({
|
|
189
|
+
signer: keypair,
|
|
190
|
+
transactionBlock: migrateTx,
|
|
191
|
+
options: {
|
|
192
|
+
showEffects: true,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (migrateResult.effects?.status.status === "success") {
|
|
197
|
+
console.log(
|
|
198
|
+
chalk.green(
|
|
199
|
+
`${name} migrate world success, new world version is: ${newObjectContent.fields["version"]}, package version is ${newVersion}`
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
if (needRegisterSchema.length !== 0) {
|
|
203
|
+
console.log(
|
|
204
|
+
chalk.green(
|
|
205
|
+
`new schema: ${needRegisterSchema.toString()} register success.`
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log(
|
|
211
|
+
chalk.blue(
|
|
212
|
+
`\n${name} world schemas is ${newObjectContent.fields["schema_names"]}`
|
|
213
|
+
)
|
|
214
|
+
);
|
|
215
|
+
} else {
|
|
216
|
+
console.log(
|
|
217
|
+
chalk.red(
|
|
218
|
+
`${name} migrate world failed, world version is: ${newObjectContent.fields["version"]}, package version is ${newVersion}`
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
} catch (error: any) {
|
|
223
|
+
console.log(chalk.red("Upgrade failed!"));
|
|
224
|
+
console.error(error.message);
|
|
225
|
+
|
|
226
|
+
saveContractData(
|
|
227
|
+
name,
|
|
228
|
+
network,
|
|
229
|
+
oldPackageId,
|
|
230
|
+
worldId,
|
|
231
|
+
upgradeCap,
|
|
232
|
+
adminCap,
|
|
233
|
+
oldVersion
|
|
234
|
+
);
|
|
235
|
+
await updateVersionInFile(projectPath, oldVersion.toString());
|
|
236
|
+
}
|
|
237
|
+
}
|