@axelar-network/axelar-cgp-sui
Version:
Axelar Sui Move contracts
212 lines • 9.67 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDefinedSuiVersion = exports.getInstalledSuiVersion = void 0;
exports.prepareMoveBuild = prepareMoveBuild;
exports.getContractBuild = getContractBuild;
exports.writeInterchainToken = writeInterchainToken;
exports.removeFile = removeFile;
exports.addFile = addFile;
exports.updateMoveToml = updateMoveToml;
exports.copyMovePackage = copyMovePackage;
exports.newInterchainToken = newInterchainToken;
exports.getLocalDependencies = getLocalDependencies;
exports.getDeploymentOrder = getDeploymentOrder;
const child_process_1 = require("child_process");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const smol_toml_1 = __importDefault(require("smol-toml"));
/**
* Prepare a move build by creating a temporary directory to store the compiled move code
* @returns {tmpdir: string, rmTmpDir: () => void}
* - tmpdir is the path to the temporary directory
* - rmTmpDir is a function to remove the temporary directory
*/
function prepareMoveBuild(tmpDir) {
const tmpdir = fs_1.default.mkdtempSync(path_1.default.join(tmpDir, '.move-build-'));
const rmTmpDir = () => fs_1.default.rmSync(tmpdir, { recursive: true });
return {
tmpdir,
rmTmpDir,
};
}
const getInstalledSuiVersion = () => {
const suiVersion = (0, child_process_1.execSync)('sui --version').toString().trim();
return parseVersion(suiVersion);
};
exports.getInstalledSuiVersion = getInstalledSuiVersion;
const getDefinedSuiVersion = () => {
const version = fs_1.default.readFileSync(`${__dirname}/../../version.json`, 'utf8');
const suiVersion = JSON.parse(version).SUI_VERSION;
return parseVersion(suiVersion);
};
exports.getDefinedSuiVersion = getDefinedSuiVersion;
const parseVersion = (version) => {
const versionMatch = version.match(/\d+\.\d+\.\d+/);
return versionMatch === null || versionMatch === void 0 ? void 0 : versionMatch[0];
};
function getContractBuild(packageName, moveDir) {
const emptyPackageId = '0x0';
updateMoveToml(packageName, emptyPackageId, moveDir);
const { tmpdir, rmTmpDir } = prepareMoveBuild(path_1.default.dirname(moveDir));
try {
const { modules, dependencies, digest } = JSON.parse((0, child_process_1.execSync)(`sui move build --dump-bytecode-as-base64 --path ${path_1.default.join(moveDir, packageName)} --install-dir ${tmpdir}`, {
encoding: 'utf-8',
stdio: 'pipe',
}));
return { modules, dependencies, digest };
}
finally {
rmTmpDir();
}
}
function writeInterchainToken(moveDir, options) {
const templateFilePath = `${moveDir}/interchain_token/sources/interchain_token.move`;
const templateContent = fs_1.default.readFileSync(templateFilePath, 'utf8');
const { filePath, content } = newInterchainToken(templateFilePath, options);
fs_1.default.writeFileSync(filePath, content, 'utf8');
return { templateFilePath, filePath, templateContent };
}
function removeFile(filePath) {
fs_1.default.rmSync(filePath);
}
function addFile(filePath, content) {
fs_1.default.writeFileSync(filePath, content, 'utf8');
}
function updateMoveToml(packageName, packageId, moveDir = `${__dirname}/../../move`, prepToml = undefined) {
// Path to the Move.toml file for the package
const movePath = `${moveDir}/${packageName}/Move.toml`;
// Check if the Move.toml file exists
if (!fs_1.default.existsSync(movePath)) {
throw new Error(`Move.toml file not found for given path: ${movePath}`);
}
// Read the Move.toml file
const moveRaw = fs_1.default.readFileSync(movePath, 'utf8');
// Parse the Move.toml file as JSON
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let moveJson = smol_toml_1.default.parse(moveRaw);
// Update the published-at field under the package section e.g. published-at = "0x01"
moveJson.package['published-at'] = packageId;
// Update the package address under the addresses section e.g. gas_service = "0x1"
moveJson.addresses[packageName] = packageId;
if (prepToml) {
moveJson = prepToml(moveJson);
}
fs_1.default.writeFileSync(movePath, smol_toml_1.default.stringify(moveJson));
}
function copyMovePackage(packageName, fromDir, toDir) {
if (fromDir == null) {
fromDir = `${__dirname}/../../move`;
}
if (fs_1.default.existsSync(`${toDir}/${packageName}`)) {
fs_1.default.rmSync(`${toDir}/${packageName}`, { recursive: true });
}
fs_1.default.cpSync(`${fromDir}/${packageName}`, `${toDir}/${packageName}`, { recursive: true });
}
function newInterchainToken(templateFilePath, options) {
let content = fs_1.default.readFileSync(templateFilePath, 'utf8');
const defaultFilePath = path_1.default.join(path_1.default.dirname(templateFilePath), `${options.symbol.toLowerCase()}.move`);
const filePath = options.filePath || defaultFilePath;
const structRegex = new RegExp(`struct\\s+Q\\s+has\\s+drop\\s+{}`, 'g');
// replace the module name with the token symbol in lowercase
content = content.replace(/(module\s+)([^:]+)(::)([^{]+)/, `$1interchain_token$3${options.symbol.toLowerCase()}`);
// replace the struct name with the token symbol in uppercase
content = content.replace(structRegex, `struct ${options.symbol.toUpperCase()} has drop {}`);
// replace the witness type with the token symbol in uppercase
content = content.replace(/(fun\s+init\s*\()witness:\s*Q/, `$1witness: ${options.symbol.toUpperCase()}`);
// replace the decimals with the given decimals
content = content.replace(/(witness,\s*)(\d+)/, `$1${options.decimals}`);
// replace the symbol with the given symbol
content = content.replace(/(b")(Q)(")/, `$1${options.symbol.toUpperCase()}$3`);
// replace the name with the given name
content = content.replace(/(b")(Quote)(")/, `$1${options.name}$3`);
// replace the generic type with the given symbol
content = content.replace(/<Q>/g, `<${options.symbol.toUpperCase()}>`);
return {
filePath,
content,
};
}
/**
* Get the local dependencies of a package from the Move.toml file.
* @param packageName The name of the package.
* @param baseMoveDir The parent directory of the Move.toml file.
* @returns An array of objects containing the name and path of the local dependencies.
*/
function getLocalDependencies(packageName, baseMoveDir) {
const movePath = `${baseMoveDir}/${packageName}/Move.toml`;
if (!fs_1.default.existsSync(movePath)) {
throw new Error(`Move.toml file not found for given path: ${movePath}`);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { dependencies } = smol_toml_1.default.parse(fs_1.default.readFileSync(movePath, 'utf8'));
const localDependencies = Object.keys(dependencies).filter((key) => dependencies[key].local);
return localDependencies.map((key) => ({
name: key,
path: `${baseMoveDir}/${path_1.default.resolve(path_1.default.dirname(movePath), dependencies[key].local)}`,
directory: dependencies[key].local.split('/').slice(-1)[0],
}));
}
/**
* Determines the deployment order of Move packages based on their dependencies.
*
* @param packageDir - The directory of the main package to start the dependency resolution from.
* @param baseMoveDir - The base directory where all Move packages are located.
* @returns An array of package directory names in the order they should be deployed.
* The array is sorted such that packages with no dependencies come first,
* followed by packages whose dependencies have already appeared in the array.
*
* @description
* This function performs the following steps:
* 1. Recursively builds a dependency map starting from the given package.
* 2. Performs a topological sort on the dependency graph.
* 3. Returns the sorted list of package directories.
*
* The function handles circular dependencies and will include each package only once in the output.
* If a package has multiple dependencies, it will appear in the list after all its dependencies.
*
* @example
* const deploymentOrder = getDeploymentOrder('myPackage', '/path/to/move');
* console.log(deploymentOrder);
* Might output: ['dependency1', 'dependency2', 'myPackage']
*/
function getDeploymentOrder(packageDir, baseMoveDir) {
const dependencyMap = {};
function recursiveDependencies(pkgDir) {
if (dependencyMap[pkgDir]) {
return;
}
const dependencies = getLocalDependencies(pkgDir, baseMoveDir);
dependencyMap[pkgDir] = {
name: pkgDir,
directory: pkgDir,
path: `${baseMoveDir}/${pkgDir}`,
dependencies: dependencies.map((dep) => dep.directory),
};
for (const dependency of dependencies) {
recursiveDependencies(dependency.directory);
}
}
recursiveDependencies(packageDir);
// Topological sort
const sorted = [];
const visited = {};
function visit(name) {
if (visited[name]) {
return;
}
visited[name] = true;
const node = dependencyMap[name];
for (const depName of node.dependencies) {
visit(depName);
}
sorted.push(node);
}
for (const name in dependencyMap) {
visit(name);
}
return sorted.map((dep) => dep.directory);
}
//# sourceMappingURL=node-utils.js.map