PHP WebShell

Текущая директория: /opt/BitGoJS/node_modules/@aptos-labs/ts-sdk/src/cli

Просмотр файла: move.ts

import { spawn } from "child_process";
import { platform } from "os";

import { AccountAddress } from "../core";
import { Network } from "../utils";

/**
 * Class representing a Move package management utility for the Aptos blockchain.
 * This class provides methods to initialize directories, compile packages, run tests, publish modules, create objects, upgrade
 * packages, build transaction payloads, and run scripts.
 */
export class Move {
  /**
   * Initialize the current directory for Aptos by configuring the necessary settings.
   * Configuration will be pushed into .aptos/config.yaml.
   *
   * @param args - The arguments for initialization.
   * @param args.network - Optional Network type argument to use for default settings; defaults to local.
   * @param args.profile - Optional Profile to use from the config file; defaults to 'default'. This will override associated
   * settings such as the REST URL, the Faucet URL, and the private key arguments.
   * @param args.extraArguments - Optional extra arguments to include in the form of an array of strings.
   * Ex. ["--assume-yes","--gas-unit-price=10"]
   * @returns stdout
   */
  async init(args: {
    network?: Network;
    profile?: string;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ output: string }> {
    const { network, profile, extraArguments, showStdout } = args;
    const cliArgs = ["aptos", "init", `--network=${network ?? "local"}`, `--profile=${profile ?? "default"}`];

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    return this.runCommand(cliArgs, showStdout);
  }

  /**
   * Compile a Move package located at the specified directory path.
   * This function helps in preparing the Move package for deployment or further processing.
   *
   * @param args - The arguments for compiling the package.
   * @param args.packageDirectoryPath - Path to a Move package (the folder with a Move.toml file).
   * @param args.namedAddresses - Named addresses for the move binary. Ex. { alice: 0x1234, bob: 0x5678 }
   * @param args.extraArguments - Optional extra arguments to include in the form of an array of strings.
   * Ex. ["--assume-yes","--gas-unit-price=10"]
   * @returns stdout
   */
  async compile(args: {
    packageDirectoryPath: string;
    namedAddresses: Record<string, AccountAddress>;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ output: string }> {
    const { packageDirectoryPath, namedAddresses, extraArguments, showStdout } = args;
    const cliArgs = ["aptos", "move", "compile", "--package-dir", packageDirectoryPath];

    const addressesMap = this.parseNamedAddresses(namedAddresses);

    cliArgs.push(...this.prepareNamedAddresses(addressesMap));

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    return this.runCommand(cliArgs, showStdout);
  }

  /**
   * Run Move unit tests for a specified package.
   *
   * @param args - The arguments for running the tests.
   * @param args.packageDirectoryPath - The path to a Move package (the folder containing a Move.toml file).
   * @param args.namedAddresses - Named addresses for the move binary. Ex. { alice: 0x1234, bob: 0x5678 }
   * @param args.extraArguments - Optional extra arguments to include in the form of an array of strings.
   * Ex. ["--assume-yes","--gas-unit-price=10"]
   * @returns The stdout output from running the tests.
   */
  async test(args: {
    packageDirectoryPath: string;
    namedAddresses: Record<string, AccountAddress>;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ output: string }> {
    const { packageDirectoryPath, namedAddresses, extraArguments, showStdout } = args;
    const cliArgs = ["aptos", "move", "test", "--package-dir", packageDirectoryPath];

    const addressesMap = this.parseNamedAddresses(namedAddresses);

    cliArgs.push(...this.prepareNamedAddresses(addressesMap));

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    return this.runCommand(cliArgs, showStdout);
  }

  /**
   * Publishes the modules to the publisher account on the Aptos blockchain.
   *
   * @param args - The arguments for publishing the modules.
   * @param args.packageDirectoryPath - The path to a move package (the folder with a Move.toml file).
   * @param args.namedAddresses - Named addresses for the move binary. Ex. { alice: 0x1234, bob: 0x5678 }
   * @param args.profile - Optional profile to use from the config file.
   * @param args.extraArguments - Optional extra arguments to include in the form of an array of strings.
   * Ex. ["--assume-yes","--gas-unit-price=10"]
   * @returns stdout
   */
  async publish(args: {
    packageDirectoryPath: string;
    namedAddresses: Record<string, AccountAddress>;
    profile?: string;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ output: string }> {
    const { packageDirectoryPath, namedAddresses, profile, extraArguments, showStdout } = args;
    const cliArgs = [
      "aptos",
      "move",
      "publish",
      "--package-dir",
      packageDirectoryPath,
      `--profile=${profile ?? "default"}`,
    ];

    const addressesMap = this.parseNamedAddresses(namedAddresses);

    cliArgs.push(...this.prepareNamedAddresses(addressesMap));

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    return this.runCommand(cliArgs, showStdout);
  }

  /**
   * Create a new object and publish the Move package to it on the Aptos blockchain.
   *
   * @param args - The arguments for creating the object and publishing the package.
   * @param args.packageDirectoryPath - Path to a Move package (the folder with a Move.toml file).
   * @param args.addressName - Address name for the Move package.
   * @param args.namedAddresses - Named addresses for the Move binary.
   * @param args.profile - Optional profile to use from the config file.
   * @param args.extraArguments - Optional extra arguments to include in the form of an array of strings.
   * Ex. ["--assume-yes","--gas-unit-price=10"]
   * @returns The object address.
   *
   * A complete example in CLI:
   * aptos move create-object-and-publish-package \
   * --package-dir path_to_directory_that_has_move.toml \
   * --address-name launchpad_addr \
   * --named-addresses "launchpad_addr=0x123,initial_creator_addr=0x456" \
   * --profile my_profile \
   * --assume-yes
   */
  async createObjectAndPublishPackage(args: {
    packageDirectoryPath: string;
    addressName: string;
    namedAddresses: Record<string, AccountAddress>;
    profile?: string;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ objectAddress: string }> {
    const { packageDirectoryPath, addressName, namedAddresses, profile, extraArguments, showStdout } = args;
    const cliArgs = [
      "aptos",
      "move",
      "create-object-and-publish-package",
      "--package-dir",
      packageDirectoryPath,
      "--address-name",
      addressName,
      `--profile=${profile ?? "default"}`,
    ];

    const addressesMap = this.parseNamedAddresses(namedAddresses);

    cliArgs.push(...this.prepareNamedAddresses(addressesMap));

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    const { output } = await this.runCommand(cliArgs, showStdout);
    return { objectAddress: this.extractAddressFromOutput(output) };
  }

  /**
   * Upgrade a Move package previously published to an object on the Aptos blockchain.
   * The caller must be the object owner to execute this function.
   *
   * @param args - The arguments for upgrading the object package.
   * @param args.packageDirectoryPath - Path to a Move package (the folder with a Move.toml file).
   * @param args.objectAddress - Address of the object that the Move package published to. Ex. 0x1000
   * @param args.namedAddresses - Named addresses for the move binary. Ex. { alice: 0x1234, bob: 0x5678 }
   * @param args.profile - Optional profile to use from the config file.
   * @param args.extraArguments - Optional extra arguments to include in the form of an array of strings.
   * Ex. ["--assume-yes","--gas-unit-price=10"]
   * @returns stdout
   */
  async upgradeObjectPackage(args: {
    packageDirectoryPath: string;
    objectAddress: string;
    namedAddresses: Record<string, AccountAddress>;
    profile?: string;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ output: string }> {
    const { packageDirectoryPath, objectAddress, namedAddresses, profile, extraArguments, showStdout } = args;
    const cliArgs = [
      "aptos",
      "move",
      "upgrade-object-package",
      "--package-dir",
      packageDirectoryPath,
      "--object-address",
      objectAddress,
      `--profile=${profile ?? "default"}`,
    ];

    const addressesMap = this.parseNamedAddresses(namedAddresses);

    cliArgs.push(...this.prepareNamedAddresses(addressesMap));

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    return this.runCommand(cliArgs, showStdout);
  }

  /**
   * Build a publication transaction payload and store it in a JSON output file.
   *
   * @param args - The arguments for building the publishing payload.
   * @param args.packageDirectoryPath - Path to a move package (the folder with a Move.toml file).
   * @param args.outputFile - Output file to write the publication transaction to.
   * @param args.namedAddresses - Named addresses for the move binary. Ex. { alice: 0x1234, bob: 0x5678 }
   * @param args.extraArguments - Optional extra arguments to include in the form of an array of strings.
   * Ex. ["--assume-yes","--gas-unit-price=10"]   *
   * @returns stdout
   */
  async buildPublishPayload(args: {
    packageDirectoryPath: string;
    outputFile: string;
    namedAddresses: Record<string, AccountAddress>;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ output: string }> {
    const { outputFile, packageDirectoryPath, namedAddresses, extraArguments, showStdout } = args;
    const cliArgs = [
      "aptos",
      "move",
      "build-publish-payload",
      "--json-output-file",
      outputFile,
      "--package-dir",
      packageDirectoryPath,
    ];

    const addressesMap = this.parseNamedAddresses(namedAddresses);

    cliArgs.push(...this.prepareNamedAddresses(addressesMap));

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    return this.runCommand(cliArgs, showStdout);
  }

  /**
   * Runs a Move script using the provided compiled script path and optional parameters. Ensure that the script is compiled
   * before executing this function.
   *
   * @param args - The arguments for running the script.
   * @param args.compiledScriptPath - Path to a compiled Move script bytecode file.
   * Ex. "build/my_package/bytecode_scripts/my_move_script.mv"
   * @param args.profile - Optional profile to use from the config file.
   * @param args.extraArguments - Optional extra arguments to include in the form of an array of strings.
   * Ex. ["--assume-yes","--gas-unit-price=10"]
   *
   * @returns The standard output from running the script.
   */
  async runScript(args: {
    compiledScriptPath: string;
    profile?: string;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ output: string }> {
    const { compiledScriptPath, profile, extraArguments, showStdout } = args;
    const cliArgs = [
      "aptos",
      "move",
      "run-script",
      "--compiled-script-path",
      compiledScriptPath,
      `--profile=${profile ?? "default"}`,
    ];

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    return this.runCommand(cliArgs, showStdout);
  }

  async gasProfile(args: {
    network: string;
    transactionId: string;
    extraArguments?: Array<string>;
    showStdout?: boolean;
  }): Promise<{ output: string; result?: any }> {
    const { network, transactionId, extraArguments, showStdout } = args;
    const cliArgs = ["aptos", "move", "replay", "--profile-gas", "--network", network, "--txn-id", transactionId];

    if (extraArguments) {
      cliArgs.push(...extraArguments);
    }

    return this.runCommand(cliArgs, showStdout);
  }

  /**
   * Run a command with the specified arguments and return the output.
   *
   * @param args - An array of strings representing the command-line arguments to be passed to the command.
   * @param showStdout - Show the standard output generated by the command.
   * @returns The standard output generated by the command.
   */
  // eslint-disable-next-line class-methods-use-this
  private async runCommand(args: Array<string>, showStdout: boolean = true): Promise<{ result?: any; output: string }> {
    return new Promise((resolve, reject) => {
      const currentPlatform = platform();
      let childProcess;
      let stdout = "";
      // CLI final stdout is the Result/Error JSON string output
      // so we need to keep track of the last stdout
      let lastStdout = "";

      // Check if current OS is windows
      if (currentPlatform === "win32") {
        childProcess = spawn("npx", args, { shell: true });
      } else {
        childProcess = spawn("npx", args);
      }

      childProcess.stdout.on("data", (data) => {
        lastStdout = data.toString();
        stdout += data.toString();
      });

      if (showStdout) {
        childProcess.stdout.pipe(process.stdout);
        childProcess.stderr.pipe(process.stderr);
      }
      process.stdin.pipe(childProcess.stdin);

      childProcess.on("close", (code) => {
        if (code === 0) {
          try {
            // parse the last stdout as it might be the result
            const parsed = JSON.parse(lastStdout);
            if (parsed.Error) {
              reject(new Error(`Error: ${parsed.Error}`)); // Reject if the "Error" key exists
            } else if (parsed.Result) {
              resolve({ result: parsed.Result, output: stdout }); // Resolve if the "Result" key exists
            }
          } catch (error: any) {
            // resolve the stdout as it might be just a stdout
            resolve({ output: stdout });
          }
        } else {
          reject(new Error(`Child process exited with code ${code}`)); // Reject with an error if the child process exits with an error code
        }
      });
    });
  }

  /**
   * Convert named addresses from a Map into an array separated by a comma.
   *
   * @param namedAddresses - A Map where the key is a string representing the name and the value is an AccountAddress.
   * Ex. {'alice' => '0x123', 'bob' => '0x456'}
   * @returns An array of named addresses formatted as strings separated by a comma. Ex. "alice=0x123,bob=0x456"
   */
  // eslint-disable-next-line class-methods-use-this
  private prepareNamedAddresses(namedAddresses: Map<string, AccountAddress>): Array<string> {
    const totalNames = namedAddresses.size;
    const newArgs: Array<string> = [];

    if (totalNames === 0) {
      return newArgs;
    }

    newArgs.push("--named-addresses");

    const names: Array<string> = [];
    namedAddresses.forEach((value, key) => {
      const toAppend = `${key}=${value.toString()}`;
      names.push(toAppend);
    });
    newArgs.push(names.join(","));
    return newArgs;
  }

  /**
   * Parse named addresses from a Record type into a Map.
   *
   * This function transforms a collection of named addresses into a more accessible format by mapping each name to its
   * corresponding address.
   *
   * @param namedAddresses - A record containing named addresses where the key is the name and the value is the AccountAddress.
   * @returns A Map where each key is a name and each value is the corresponding address.
   */
  // eslint-disable-next-line class-methods-use-this
  private parseNamedAddresses(namedAddresses: Record<string, AccountAddress>): Map<string, AccountAddress> {
    const addressesMap = new Map();

    Object.keys(namedAddresses).forEach((key) => {
      const address = namedAddresses[key];
      addressesMap.set(key, address);
    });

    return addressesMap;
  }

  /**
   * Extracts the object address from the provided output string.
   *
   * @param output - The output string containing the object address.
   * @returns The extracted object address.
   * @throws Error if the object address cannot be extracted from the output.
   */
  // eslint-disable-next-line class-methods-use-this
  private extractAddressFromOutput(output: string): string {
    const match = output.match("Code was successfully deployed to object address (0x[0-9a-fA-F]+)");
    if (match) {
      return match[1];
    }
    throw new Error("Failed to extract object address from output");
  }
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!