<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

import { HexString, MaybeHexString } from "./hex_string";
import { clear, fixNodeUrl, Memoize, sleep } from "./utils";
import { AptosAccount } from "./aptos_account";
import * as Gen from "./generated/index";
import {
  TxnBuilderTypes,
  TransactionBuilderEd25519,
  TransactionBuilderRemoteABI,
  RemoteABIBuilderConfig,
} from "./transaction_builder";
import {
  bcsSerializeBytes,
  bcsSerializeU8,
  bcsToBytes,
  Bytes,
  Seq,
  Serializer,
  serializeVector,
  Uint64,
  AnyNumber,
} from "./bcs";

export interface OptionalTransactionArgs {
  maxGasAmount?: Uint64;
  gasUnitPrice?: Uint64;
  expireTimestamp?: Uint64;
}

interface PaginationArgs {
  start?: AnyNumber;
  limit?: number;
}

/**
 * Provides methods for retrieving data from Aptos node.
 * For more detailed API specification see {@link https://fullnode.devnet.aptoslabs.com/v1/spec}
 */
export class AptosClient {
  client: Gen.AptosGeneratedClient;

  /**
   * Build a client configured to connect to an Aptos node at the given URL.
   *
   * Note: If you forget to append `/v1` to the URL, the client constructor
   * will automatically append it. If you don't want this URL processing to
   * take place, set doNotFixNodeUrl to true.
   *
   * @param nodeUrl URL of the Aptos Node API endpoint.
   * @param config Additional configuration options for the generated Axios client.
   */
  constructor(nodeUrl: string, config?: Partial<Gen.OpenAPIConfig>, doNotFixNodeUrl: boolean = false) {
    if (!nodeUrl) {
      throw new Error("Node URL cannot be empty.");
    }
    const conf = config === undefined || config === null ? {} : { ...config };
    if (doNotFixNodeUrl) {
      conf.BASE = nodeUrl;
    } else {
      conf.BASE = fixNodeUrl(nodeUrl);
    }

    // Do not carry cookies when `WITH_CREDENTIALS` is explicitly set to `false`. By default, cookies will be sent
    if (config?.WITH_CREDENTIALS === false) {
      conf.WITH_CREDENTIALS = false;
    } else {
      conf.WITH_CREDENTIALS = true;
    }

    this.client = new Gen.AptosGeneratedClient(conf);
  }

  /**
   * Queries an Aptos account by address
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @returns Core account resource, used for identifying account and transaction execution
   * @example An example of the returned account
   * ```
   * {
   *    sequence_number: "1",
   *    authentication_key: "<KEY>"
   * }
   * ```
   */
  @parseApiError
  async getAccount(accountAddress: MaybeHexString): Promise<Gen.AccountData> {
    return this.client.accounts.getAccount(HexString.ensure(accountAddress).hex());
  }

  /**
   * Queries transactions sent by given account
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param query Optional pagination object
   * @param query.start The start transaction version of the page. Default is the latest ledger version
   * @param query?.limit The max number of transactions should be returned for the page. Default is 25.
   * @returns An array of on-chain transactions, sent by account
   */
  @parseApiError
  async getAccountTransactions(accountAddress: MaybeHexString, query?: PaginationArgs): Promise<Gen.Transaction[]> {
    return this.client.transactions.getAccountTransactions(
      HexString.ensure(accountAddress).hex(),
      query?.start?.toString(),
      query?.limit,
    );
  }

  /**
   * Queries modules associated with given account
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param query.version Specifies ledger version of transactions. By default latest version will be used
   * @returns Account modules array for a specific ledger version.
   * Module is represented by MoveModule interface. It contains module `bytecode` and `abi`,
   * which is JSON representation of a module
   */
  @parseApiError
  async getAccountModules(
    accountAddress: MaybeHexString,
    query?: { ledgerVersion?: AnyNumber },
  ): Promise<Gen.MoveModuleBytecode[]> {
    return this.client.accounts.getAccountModules(
      HexString.ensure(accountAddress).hex(),
      query?.ledgerVersion?.toString(),
    );
  }

  /**
   * Queries module associated with given account by module name
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param moduleName The name of the module
   * @param query.version Specifies ledger version of transactions. By default latest version will be used
   * @returns Specified module.
   * Module is represented by MoveModule interface. It contains module `bytecode` and `abi`,
   * which JSON representation of a module
   */
  @parseApiError
  async getAccountModule(
    accountAddress: MaybeHexString,
    moduleName: string,
    query?: { ledgerVersion?: AnyNumber },
  ): Promise<Gen.MoveModuleBytecode> {
    return this.client.accounts.getAccountModule(
      HexString.ensure(accountAddress).hex(),
      moduleName,
      query?.ledgerVersion?.toString(),
    );
  }

  /**
   * Queries all resources associated with given account
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param query.version Specifies ledger version of transactions. By default latest version will be used
   * @returns Account resources for a specific ledger version
   * @example An example of an account resource
   * ```
   * {
   *    type: "0x1::AptosAccount::Coin",
   *    data: { value: 6 }
   * }
   * ```
   */
  @parseApiError
  async getAccountResources(
    accountAddress: MaybeHexString,
    query?: { ledgerVersion?: AnyNumber },
  ): Promise<Gen.MoveResource[]> {
    return this.client.accounts.getAccountResources(
      HexString.ensure(accountAddress).hex(),
      query?.ledgerVersion?.toString(),
    );
  }

  /**
   * Queries resource associated with given account by resource type
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param resourceType String representation of an on-chain Move struct type
   * @param query.version Specifies ledger version of transactions. By default latest version will be used
   * @returns Account resource of specified type and ledger version
   * @example An example of an account resource
   * ```
   * {
   *    type: "0x1::AptosAccount::Coin",
   *    data: { value: 6 }
   * }
   * ```
   */
  @parseApiError
  async getAccountResource(
    accountAddress: MaybeHexString,
    resourceType: Gen.MoveStructTag,
    query?: { ledgerVersion?: AnyNumber },
  ): Promise<Gen.MoveResource> {
    return this.client.accounts.getAccountResource(
      HexString.ensure(accountAddress).hex(),
      resourceType,
      query?.ledgerVersion?.toString(),
    );
  }

  /** Generates a signed transaction that can be submitted to the chain for execution. */
  static generateBCSTransaction(accountFrom: AptosAccount, rawTxn: TxnBuilderTypes.RawTransaction): Uint8Array {
    const txnBuilder = new TransactionBuilderEd25519((signingMessage: TxnBuilderTypes.SigningMessage) => {
      // @ts-ignore
      const sigHexStr = accountFrom.signBuffer(signingMessage);
      return new TxnBuilderTypes.Ed25519Signature(sigHexStr.toUint8Array());
    }, accountFrom.pubKey().toUint8Array());

    return txnBuilder.sign(rawTxn);
  }

  /**
   * Note: Unless you have a specific reason for using this, it'll probably be simpler
   * to use `simulateTransaction`.
   *
   * Generates a BCS transaction that can be submitted to the chain for simulation.
   *
   * @param accountFrom The account that will be used to send the transaction
   * for simulation.
   * @param rawTxn The raw transaction to be simulated, likely created by calling
   * the `generateTransaction` function.
   * @returns The BCS encoded signed transaction, which you should then pass into
   * the `submitBCSSimulation` function.
   */
  static generateBCSSimulation(accountFrom: AptosAccount, rawTxn: TxnBuilderTypes.RawTransaction): Uint8Array {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const txnBuilder = new TransactionBuilderEd25519((_signingMessage: TxnBuilderTypes.SigningMessage) => {
      // @ts-ignore
      const invalidSigBytes = new Uint8Array(64);
      return new TxnBuilderTypes.Ed25519Signature(invalidSigBytes);
    }, accountFrom.pubKey().toUint8Array());

    return txnBuilder.sign(rawTxn);
  }

  /** Generates a transaction request that can be submitted to produce a raw transaction that
   * can be signed, which upon being signed can be submitted to the blockchain
   * @param sender Hex-encoded 32 byte Aptos account address of transaction sender
   * @param payload Transaction payload. It depends on transaction type you want to send
   * @param options Options allow to overwrite default transaction options.
   * Defaults are:
   * ```bash
   *   {
   *     sender: senderAddress.hex(),
   *     sequence_number: account.sequence_number,
   *     max_gas_amount: "1000",
   *     gas_unit_price: "1",
   *     // Unix timestamp, in seconds + 10 seconds
   *     expiration_timestamp_secs: (Math.floor(Date.now() / 1000) + 10).toString(),
   *   }
   * ```
   * @returns A transaction object
   */
  async generateTransaction(
    sender: MaybeHexString,
    payload: Gen.EntryFunctionPayload,
    options?: Partial<Gen.SubmitTransactionRequest>,
  ): Promise<TxnBuilderTypes.RawTransaction> {
    const config: RemoteABIBuilderConfig = { sender };
    if (options?.sequence_number) {
      config.sequenceNumber = options.sequence_number;
    }

    if (options?.gas_unit_price) {
      config.gasUnitPrice = options.gas_unit_price;
    }

    if (options?.max_gas_amount) {
      config.maxGasAmount = options.max_gas_amount;
    }

    if (options?.expiration_timestamp_secs) {
      const timestamp = Number.parseInt(options.expiration_timestamp_secs, 10);
      config.expSecFromNow = timestamp - Math.floor(Date.now() / 1000);
    }

    const builder = new TransactionBuilderRemoteABI(this, config);
    return builder.build(payload.function, payload.type_arguments, payload.arguments);
  }

  /** Converts a transaction request produced by `generateTransaction` into a properly
   * signed transaction, which can then be submitted to the blockchain
   * @param accountFrom AptosAccount of transaction sender
   * @param rawTransaction A raw transaction generated by `generateTransaction` method
   * @returns A transaction, signed with sender account
   */
  // eslint-disable-next-line class-methods-use-this
  async signTransaction(
    accountFrom: AptosAccount,
    rawTransaction: TxnBuilderTypes.RawTransaction,
  ): Promise<Uint8Array> {
    return Promise.resolve(AptosClient.generateBCSTransaction(accountFrom, rawTransaction));
  }

  /**
   * @deprecated Use `getEventsByCreationNumber` instead. This will be removed in the next release.
   *
   * Queries events by event key
   * @param eventKey Event key for an event stream. It is BCS serialized bytes
   * of `guid` field in the Move struct `EventHandle`
   * @returns Array of events assotiated with given key
   */
  @parseApiError
  async getEventsByEventKey(eventKey: string): Promise<Gen.Event[]> {
    return this.client.events.getEventsByEventKey(eventKey);
  }

  /**
   * Event types are globally identifiable by an account `address` and
   * monotonically increasing `creation_number`, one per event type emitted
   * to the given account. This API returns events corresponding to that
   * that event type.
   * @param address Hex-encoded 32 byte Aptos account, with or without a `0x` prefix,
   * for which events are queried. This refers to the account that events were emitted
   * to, not the account hosting the move module that emits that event type.
   * @param creationNumber Creation number corresponding to the event type.
   * @returns Array of events assotiated with the given account and creation number.
   */
  @parseApiError
  async getEventsByCreationNumber(
    address: MaybeHexString,
    creationNumber: AnyNumber | string,
    query?: PaginationArgs,
  ): Promise<Gen.Event[]> {
    return this.client.events.getEventsByCreationNumber(
      HexString.ensure(address).hex(),
      creationNumber.toString(),
      query?.start?.toString(),
      query?.limit,
    );
  }

  /**
   * This API uses the given account `address`, `eventHandle`, and `fieldName`
   * to build a key that can globally identify an event types. It then uses this
   * key to return events emitted to the given account matching that event type.
   * @param address Hex-encoded 32 byte Aptos account, with or without a `0x` prefix,
   * for which events are queried. This refers to the account that events were emitted
   * to, not the account hosting the move module that emits that event type.
   * @param eventHandleStruct String representation of an on-chain Move struct type.
   * (e.g. `0x1::Coin::CoinStore<0x1::aptos_coin::AptosCoin>`)
   * @param fieldName The field name of the EventHandle in the struct
   * @param query Optional query object
   * @param query.start The start sequence number in the EVENT STREAM, defaulting to the latest event.
   * The events are returned in the reverse order of sequence number
   * @param query?.limit The number of events to be returned for the page default is 5
   * @returns Array of events
   */
  @parseApiError
  async getEventsByEventHandle(
    address: MaybeHexString,
    eventHandleStruct: Gen.MoveStructTag,
    fieldName: string,
    query?: PaginationArgs,
  ): Promise<Gen.Event[]> {
    return this.client.events.getEventsByEventHandle(
      HexString.ensure(address).hex(),
      eventHandleStruct,
      fieldName,
      query?.start?.toString(),
      query?.limit,
    );
  }

  /**
   * Submits a signed transaction to the transaction endpoint.
   * @param signedTxn A transaction, signed by `signTransaction` method
   * @returns Transaction that is accepted and submitted to mempool
   */
  async submitTransaction(signedTxn: Uint8Array): Promise<Gen.PendingTransaction> {
    return this.submitSignedBCSTransaction(signedTxn);
  }

  /**
   * Generates and submits a transaction to the transaction simulation
   * endpoint. For this we generate a transaction with a fake signature.
   *
   * @param accountFrom The account that will be used to send the transaction
   * for simulation.
   * @param rawTransaction The raw transaction to be simulated, likely created
   * by calling the `generateTransaction` function.
   * @param query?.estimateGasUnitPrice If set to true, the gas unit price in the
   * transaction will be ignored and the estimated value will be used.
   * @param query?.estimateMaxGasAmount If set to true, the max gas value in the
   * transaction will be ignored and the maximum possible gas will be used.
   * @returns The BCS encoded signed transaction, which you should then provide
   *
   */
  async simulateTransaction(
    accountFrom: AptosAccount,
    rawTransaction: TxnBuilderTypes.RawTransaction,
    query?: { estimateGasUnitPrice?: boolean; estimateMaxGasAmount?: boolean },
  ): Promise<Gen.UserTransaction[]> {
    const signedTxn = AptosClient.generateBCSSimulation(accountFrom, rawTransaction);
    return this.submitBCSSimulation(signedTxn, query);
  }

  /**
   * Submits a signed transaction to the the endpoint that takes BCS payload
   *
   * @param signedTxn A BCS transaction representation
   * @returns Transaction that is accepted and submitted to mempool
   */
  async submitSignedBCSTransaction(signedTxn: Uint8Array): Promise<Gen.PendingTransaction> {
    // Need to construct a customized post request for transactions in BCS payload
    return this.client.request.request<Gen.PendingTransaction>({
      url: "/transactions",
      method: "POST",
      body: signedTxn,
      mediaType: "application/x.aptos.signed_transaction+bcs",
    });
  }

  /**
   * Submits the BCS serialization of a signed transaction to the simulation endpoint.
   *
   * @param signedTxn The output of `generateBCSSimulation`.
   * @param query?.estimateGasUnitPrice If set to true, the gas unit price in the
   * transaction will be ignored and the estimated value will be used.
   * @param query?.estimateMaxGasAmount If set to true, the max gas value in the
   * transaction will be ignored and the maximum possible gas will be used.
   * @returns Simulation result in the form of UserTransaction.
   */
  async submitBCSSimulation(
    bcsBody: Uint8Array,
    query?: { estimateGasUnitPrice?: boolean; estimateMaxGasAmount?: boolean },
  ): Promise<Gen.UserTransaction[]> {
    // Need to construct a customized post request for transactions in BCS payload.
    const queryParams = {
      estimate_gas_unit_price: query?.estimateGasUnitPrice ?? false,
      estimate_max_gas_amount: query?.estimateMaxGasAmount ?? false,
    };
    return this.client.request.request<Gen.UserTransaction[]>({
      url: "/transactions/simulate",
      query: queryParams,
      method: "POST",
      body: bcsBody,
      mediaType: "application/x.aptos.signed_transaction+bcs",
    });
  }

  /**
   * Queries on-chain transactions
   * @param query Optional pagination object
   * @param query.start The start transaction version of the page. Default is the latest ledger version
   * @param query?.limit The max number of transactions should be returned for the page. Default is 25
   * @returns Array of on-chain transactions
   */
  @parseApiError
  async getTransactions(query?: PaginationArgs): Promise<Gen.Transaction[]> {
    return this.client.transactions.getTransactions(query?.start?.toString(), query?.limit);
  }

  /**
   * @param txnHashOrVersion - Transaction hash should be hex-encoded bytes string with 0x prefix.
   * Transaction version is an uint64 number.
   * @returns Transaction from mempool or on-chain transaction
   */
  @parseApiError
  async getTransactionByHash(txnHash: string): Promise<Gen.Transaction> {
    return this.client.transactions.getTransactionByHash(txnHash);
  }

  /**
   * @param txnHashOrVersion - Transaction hash should be hex-encoded bytes string with 0x prefix.
   * Transaction version is an uint64 number.
   * @returns Transaction from mempool or on-chain transaction
   */
  @parseApiError
  async getTransactionByVersion(txnVersion: BigInt | number): Promise<Gen.Transaction> {
    return this.client.transactions.getTransactionByVersion(txnVersion.toString());
  }

  /**
   * Defines if specified transaction is currently in pending state
   * @param txnHash A hash of transaction
   *
   * To create a transaction hash:
   *
   * 1. Create hash message bytes: "Aptos::Transaction" bytes + BCS bytes of Transaction.
   * 2. Apply hash algorithm SHA3-256 to the hash message bytes.
   * 3. Hex-encode the hash bytes with 0x prefix.
   *
   * @returns `true` if transaction is in pending state and `false` otherwise
   */
  async transactionPending(txnHash: string): Promise<boolean> {
    try {
      const response = await this.client.transactions.getTransactionByHash(txnHash);
      return response.type === "pending_transaction";
    } catch (e) {
      if (e?.status === 404) {
        return true;
      }
      throw e;
    }
  }

  /**
   * Wait for a transaction to move past pending state.
   *
   * There are 4 possible outcomes:
   * 1. Transaction is processed and successfully committed to the blockchain.
   * 2. Transaction is rejected for some reason, and is therefore not committed
   *    to the blockchain.
   * 3. Transaction is committed but execution failed, meaning no changes were
   *    written to the blockchain state.
   * 4. Transaction is not processed within the specified timeout.
   *
   * In case 1, this function resolves with the transaction response returned
   * by the API.
   *
   * In case 2, the function will throw an ApiError, likely with an HTTP status
   * code indicating some problem with the request (e.g. 400).
   *
   * In case 3, if `checkSuccess` is false (the default), this function returns
   * the transaction response just like in case 1, in which the `success` field
   * will be false. If `checkSuccess` is true, it will instead throw a
   * FailedTransactionError.
   *
   * In case 4, this function throws a WaitForTransactionError.
   *
   * @param txnHash The hash of a transaction previously submitted to the blockchain.
   * @param timeoutSecs Timeout in seconds. Defaults to 10 seconds.
   * @param checkSuccess See above. Defaults to false.
   * @returns See above.
   *
   * @example
   * ```
   * const rawTransaction = await this.generateRawTransaction(sender.address(), payload, extraArgs);
   * const bcsTxn = AptosClient.generateBCSTransaction(sender, rawTransaction);
   * const pendingTransaction = await this.submitSignedBCSTransaction(bcsTxn);
   * const transasction = await this.aptosClient.waitForTransactionWithResult(pendingTransaction.hash);
   * ```
   */
  async waitForTransactionWithResult(
    txnHash: string,
    extraArgs?: { timeoutSecs?: number; checkSuccess?: boolean },
  ): Promise<Gen.Transaction> {
    const timeoutSecs = extraArgs?.timeoutSecs ?? 20;
    const checkSuccess = extraArgs?.checkSuccess ?? false;

    let isPending = true;
    let count = 0;
    let lastTxn: Gen.Transaction | undefined;
    while (isPending) {
      if (count >= timeoutSecs) {
        break;
      }
      try {
        // eslint-disable-next-line no-await-in-loop
        lastTxn = await this.client.transactions.getTransactionByHash(txnHash);
        isPending = lastTxn.type === "pending_transaction";
        if (!isPending) {
          break;
        }
      } catch (e) {
        // In short, this means we will retry if it was an ApiError and the code was 404 or 5xx.
        const isApiError = e instanceof Gen.ApiError;
        const isRequestError = isApiError && e.status !== 404 && e.status >= 400 && e.status < 500;
        if (!isApiError || isRequestError) {
          throw e;
        }
      }
      // eslint-disable-next-line no-await-in-loop
      await sleep(1000);
      count += 1;
    }
    if (isPending) {
      throw new WaitForTransactionError(
        `Waiting for transaction ${txnHash} timed out after ${timeoutSecs} seconds`,
        lastTxn,
      );
    }
    if (!checkSuccess) {
      return lastTxn;
    }
    if (!(lastTxn as any)?.success) {
      throw new FailedTransactionError(
        `Transaction ${lastTxn.hash} committed to the blockchain but execution failed`,
        lastTxn,
      );
    }
    return lastTxn;
  }

  /**
   * This function works the same as `waitForTransactionWithResult` except it
   * doesn't return the transaction in those cases, it returns nothing. For
   * more information, see the documentation for `waitForTransactionWithResult`.
   */
  async waitForTransaction(
    txnHash: string,
    extraArgs?: { timeoutSecs?: number; checkSuccess?: boolean },
  ): Promise<void> {
    await this.waitForTransactionWithResult(txnHash, extraArgs);
  }

  /**
   * Queries the latest ledger information
   * @param params Request params
   * @returns Latest ledger information
   * @example Example of returned data
   * ```
   * {
   *   chain_id: 15,
   *   epoch: 6,
   *   ledgerVersion: "2235883",
   *   ledger_timestamp:"1654580922321826"
   * }
   * ```
   */
  @parseApiError
  async getLedgerInfo(): Promise<Gen.IndexResponse> {
    return this.client.general.getLedgerInfo();
  }

  /**
   * @returns Current chain id
   */
  @Memoize()
  async getChainId(): Promise<number> {
    const result = await this.getLedgerInfo();
    return result.chain_id;
  }

  /**
   * Gets a table item for a table identified by the handle and the key for the item.
   * Key and value types need to be passed in to help with key serialization and value deserialization.
   * @param handle A pointer to where that table is stored
   * @param data Object, that describes table item
   * @param data.key_type Move type of table key (e.g. `vector<u8>`)
   * @param data.value_type Move type of table value (e.g. `u64`)
   * @param data.key Value of table key
   * @param params Request params
   * @returns Table item value rendered in JSON
   */
  @parseApiError
  async getTableItem(handle: string, data: Gen.TableItemRequest, query?: { ledgerVersion?: AnyNumber }): Promise<any> {
    const tableItem = await this.client.tables.getTableItem(handle, data, query?.ledgerVersion?.toString());
    return tableItem;
  }
<commit_msg>fix: clean up the TS doc - in progress<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

import { HexString, MaybeHexString } from "./hex_string";
import { clear, fixNodeUrl, Memoize, sleep } from "./utils";
import { AptosAccount } from "./aptos_account";
import * as Gen from "./generated/index";
import {
  TxnBuilderTypes,
  TransactionBuilderEd25519,
  TransactionBuilderRemoteABI,
  RemoteABIBuilderConfig,
} from "./transaction_builder";
import {
  bcsSerializeBytes,
  bcsSerializeU8,
  bcsToBytes,
  Bytes,
  Seq,
  Serializer,
  serializeVector,
  Uint64,
  AnyNumber,
} from "./bcs";

export interface OptionalTransactionArgs {
  maxGasAmount?: Uint64;
  gasUnitPrice?: Uint64;
  expireTimestamp?: Uint64;
}

interface PaginationArgs {
  start?: AnyNumber;
  limit?: number;
}

/**
 * Provides methods for retrieving data from Aptos node.
 * For more detailed API specification see {@link https://fullnode.devnet.aptoslabs.com/v1/spec}
 */
export class AptosClient {
  client: Gen.AptosGeneratedClient;

  /**
   * Build a client configured to connect to an Aptos node at the given URL.
   *
   * Note: If you forget to append `/v1` to the URL, the client constructor
   * will automatically append it. If you don't want this URL processing to
   * take place, set doNotFixNodeUrl to true.
   *
   * @param nodeUrl URL of the Aptos Node API endpoint.
   * @param config Additional configuration options for the generated Axios client.
   */
  constructor(nodeUrl: string, config?: Partial<Gen.OpenAPIConfig>, doNotFixNodeUrl: boolean = false) {
    if (!nodeUrl) {
      throw new Error("Node URL cannot be empty.");
    }
    const conf = config === undefined || config === null ? {} : { ...config };
    if (doNotFixNodeUrl) {
      conf.BASE = nodeUrl;
    } else {
      conf.BASE = fixNodeUrl(nodeUrl);
    }

    // Do not carry cookies when `WITH_CREDENTIALS` is explicitly set to `false`. By default, cookies will be sent
    if (config?.WITH_CREDENTIALS === false) {
      conf.WITH_CREDENTIALS = false;
    } else {
      conf.WITH_CREDENTIALS = true;
    }

    this.client = new Gen.AptosGeneratedClient(conf);
  }

  /**
   * Queries an Aptos account by address
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @returns Core account resource, used for identifying account and transaction execution
   * @example An example of the returned account
   * ```
   * {
   *    sequence_number: "1",
   *    authentication_key: "<KEY>"
   * }
   * ```
   */
  @parseApiError
  async getAccount(accountAddress: MaybeHexString): Promise<Gen.AccountData> {
    return this.client.accounts.getAccount(HexString.ensure(accountAddress).hex());
  }

  /**
   * Queries transactions sent by given account
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param query Optional pagination object
   * @param query.start The start transaction version of the page. Default is the latest ledger version
   * @param query.limit The max number of transactions should be returned for the page. Default is 25.
   * @returns An array of on-chain transactions, sent by account
   */
  @parseApiError
  async getAccountTransactions(accountAddress: MaybeHexString, query?: PaginationArgs): Promise<Gen.Transaction[]> {
    return this.client.transactions.getAccountTransactions(
      HexString.ensure(accountAddress).hex(),
      query?.start?.toString(),
      query?.limit,
    );
  }

  /**
   * Queries modules associated with given account
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param query.ledgerVersion Specifies ledger version of transactions. By default latest version will be used
   * @returns Account modules array for a specific ledger version.
   * Module is represented by MoveModule interface. It contains module `bytecode` and `abi`,
   * which is JSON representation of a module
   */
  @parseApiError
  async getAccountModules(
    accountAddress: MaybeHexString,
    query?: { ledgerVersion?: AnyNumber },
  ): Promise<Gen.MoveModuleBytecode[]> {
    return this.client.accounts.getAccountModules(
      HexString.ensure(accountAddress).hex(),
      query?.ledgerVersion?.toString(),
    );
  }

  /**
   * Queries module associated with given account by module name
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param moduleName The name of the module
   * @param query.ledgerVersion Specifies ledger version of transactions. By default latest version will be used
   * @returns Specified module.
   * Module is represented by MoveModule interface. It contains module `bytecode` and `abi`,
   * which JSON representation of a module
   */
  @parseApiError
  async getAccountModule(
    accountAddress: MaybeHexString,
    moduleName: string,
    query?: { ledgerVersion?: AnyNumber },
  ): Promise<Gen.MoveModuleBytecode> {
    return this.client.accounts.getAccountModule(
      HexString.ensure(accountAddress).hex(),
      moduleName,
      query?.ledgerVersion?.toString(),
    );
  }

  /**
   * Queries all resources associated with given account
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param query.ledgerVersion Specifies ledger version of transactions. By default latest version will be used
   * @returns Account resources for a specific ledger version
   */
  @parseApiError
  async getAccountResources(
    accountAddress: MaybeHexString,
    query?: { ledgerVersion?: AnyNumber },
  ): Promise<Gen.MoveResource[]> {
    return this.client.accounts.getAccountResources(
      HexString.ensure(accountAddress).hex(),
      query?.ledgerVersion?.toString(),
    );
  }

  /**
   * Queries resource associated with given account by resource type
   * @param accountAddress Hex-encoded 32 byte Aptos account address
   * @param resourceType String representation of an on-chain Move struct type
   * @param query.ledgerVersion Specifies ledger version of transactions. By default latest version will be used
   * @returns Account resource of specified type and ledger version
   * @example An example of an account resource
   * ```
   * {
   *    type: "0x1::AptosAccount::Coin",
   *    data: { value: 6 }
   * }
   * ```
   */
  @parseApiError
  async getAccountResource(
    accountAddress: MaybeHexString,
    resourceType: Gen.MoveStructTag,
    query?: { ledgerVersion?: AnyNumber },
  ): Promise<Gen.MoveResource> {
    return this.client.accounts.getAccountResource(
      HexString.ensure(accountAddress).hex(),
      resourceType,
      query?.ledgerVersion?.toString(),
    );
  }

  /** Generates a signed transaction that can be submitted to the chain for execution. */
  static generateBCSTransaction(accountFrom: AptosAccount, rawTxn: TxnBuilderTypes.RawTransaction): Uint8Array {
    const txnBuilder = new TransactionBuilderEd25519((signingMessage: TxnBuilderTypes.SigningMessage) => {
      // @ts-ignore
      const sigHexStr = accountFrom.signBuffer(signingMessage);
      return new TxnBuilderTypes.Ed25519Signature(sigHexStr.toUint8Array());
    }, accountFrom.pubKey().toUint8Array());

    return txnBuilder.sign(rawTxn);
  }

  /**
   * Note: Unless you have a specific reason for using this, it'll probably be simpler
   * to use `simulateTransaction`.
   *
   * Generates a BCS transaction that can be submitted to the chain for simulation.
   *
   * @param accountFrom The account that will be used to send the transaction
   * for simulation.
   * @param rawTxn The raw transaction to be simulated, likely created by calling
   * the `generateTransaction` function.
   * @returns The BCS encoded signed transaction, which you should then pass into
   * the `submitBCSSimulation` function.
   */
  static generateBCSSimulation(accountFrom: AptosAccount, rawTxn: TxnBuilderTypes.RawTransaction): Uint8Array {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const txnBuilder = new TransactionBuilderEd25519((_signingMessage: TxnBuilderTypes.SigningMessage) => {
      // @ts-ignore
      const invalidSigBytes = new Uint8Array(64);
      return new TxnBuilderTypes.Ed25519Signature(invalidSigBytes);
    }, accountFrom.pubKey().toUint8Array());

    return txnBuilder.sign(rawTxn);
  }

  /** Generates a transaction request that can be submitted to produce a raw transaction that
   * can be signed, which upon being signed can be submitted to the blockchain
   * @param sender Hex-encoded 32 byte Aptos account address of transaction sender
   * @param payload Transaction payload. It depends on transaction type you want to send
   * @param options Options allow to overwrite default transaction options.
   * @returns A transaction object
   */
  async generateTransaction(
    sender: MaybeHexString,
    payload: Gen.EntryFunctionPayload,
    options?: Partial<Gen.SubmitTransactionRequest>,
  ): Promise<TxnBuilderTypes.RawTransaction> {
    const config: RemoteABIBuilderConfig = { sender };
    if (options?.sequence_number) {
      config.sequenceNumber = options.sequence_number;
    }

    if (options?.gas_unit_price) {
      config.gasUnitPrice = options.gas_unit_price;
    }

    if (options?.max_gas_amount) {
      config.maxGasAmount = options.max_gas_amount;
    }

    if (options?.expiration_timestamp_secs) {
      const timestamp = Number.parseInt(options.expiration_timestamp_secs, 10);
      config.expSecFromNow = timestamp - Math.floor(Date.now() / 1000);
    }

    const builder = new TransactionBuilderRemoteABI(this, config);
    return builder.build(payload.function, payload.type_arguments, payload.arguments);
  }

  /** Converts a transaction request produced by `generateTransaction` into a properly
   * signed transaction, which can then be submitted to the blockchain
   * @param accountFrom AptosAccount of transaction sender
   * @param rawTransaction A raw transaction generated by `generateTransaction` method
   * @returns A transaction, signed with sender account
   */
  // eslint-disable-next-line class-methods-use-this
  async signTransaction(
    accountFrom: AptosAccount,
    rawTransaction: TxnBuilderTypes.RawTransaction,
  ): Promise<Uint8Array> {
    return Promise.resolve(AptosClient.generateBCSTransaction(accountFrom, rawTransaction));
  }

  /**
   * @deprecated Use `getEventsByCreationNumber` instead. This will be removed in the next release.
   *
   * Queries events by event key
   * @param eventKey Event key for an event stream. It is BCS serialized bytes
   * of `guid` field in the Move struct `EventHandle`
   * @returns Array of events assotiated with given key
   */
  @parseApiError
  async getEventsByEventKey(eventKey: string): Promise<Gen.Event[]> {
    return this.client.events.getEventsByEventKey(eventKey);
  }

  /**
   * Event types are globally identifiable by an account `address` and
   * monotonically increasing `creation_number`, one per event type emitted
   * to the given account. This API returns events corresponding to that
   * that event type.
   * @param address Hex-encoded 32 byte Aptos account, with or without a `0x` prefix,
   * for which events are queried. This refers to the account that events were emitted
   * to, not the account hosting the move module that emits that event type.
   * @param creationNumber Creation number corresponding to the event type.
   * @returns Array of events assotiated with the given account and creation number.
   */
  @parseApiError
  async getEventsByCreationNumber(
    address: MaybeHexString,
    creationNumber: AnyNumber | string,
    query?: PaginationArgs,
  ): Promise<Gen.Event[]> {
    return this.client.events.getEventsByCreationNumber(
      HexString.ensure(address).hex(),
      creationNumber.toString(),
      query?.start?.toString(),
      query?.limit,
    );
  }

  /**
   * This API uses the given account `address`, `eventHandle`, and `fieldName`
   * to build a key that can globally identify an event types. It then uses this
   * key to return events emitted to the given account matching that event type.
   * @param address Hex-encoded 32 byte Aptos account, with or without a `0x` prefix,
   * for which events are queried. This refers to the account that events were emitted
   * to, not the account hosting the move module that emits that event type.
   * @param eventHandleStruct String representation of an on-chain Move struct type.
   * (e.g. `0x1::Coin::CoinStore<0x1::aptos_coin::AptosCoin>`)
   * @param fieldName The field name of the EventHandle in the struct
   * @param query Optional query object
   * @param query.start The start sequence number in the EVENT STREAM, defaulting to the latest event.
   * The events are returned in the reverse order of sequence number
   * @param query.limit The number of events to be returned for the page default is 5
   * @returns Array of events
   */
  @parseApiError
  async getEventsByEventHandle(
    address: MaybeHexString,
    eventHandleStruct: Gen.MoveStructTag,
    fieldName: string,
    query?: PaginationArgs,
  ): Promise<Gen.Event[]> {
    return this.client.events.getEventsByEventHandle(
      HexString.ensure(address).hex(),
      eventHandleStruct,
      fieldName,
      query?.start?.toString(),
      query?.limit,
    );
  }

  /**
   * Submits a signed transaction to the transaction endpoint.
   * @param signedTxn A transaction, signed by `signTransaction` method
   * @returns Transaction that is accepted and submitted to mempool
   */
  async submitTransaction(signedTxn: Uint8Array): Promise<Gen.PendingTransaction> {
    return this.submitSignedBCSTransaction(signedTxn);
  }

  /**
   * Generates and submits a transaction to the transaction simulation
   * endpoint. For this we generate a transaction with a fake signature.
   *
   * @param accountFrom The account that will be used to send the transaction
   * for simulation.
   * @param rawTransaction The raw transaction to be simulated, likely created
   * by calling the `generateTransaction` function.
   * @param query.estimateGasUnitPrice If set to true, the gas unit price in the
   * transaction will be ignored and the estimated value will be used.
   * @param query.estimateMaxGasAmount If set to true, the max gas value in the
   * transaction will be ignored and the maximum possible gas will be used.
   * @returns The BCS encoded signed transaction, which you should then provide
   *
   */
  async simulateTransaction(
    accountFrom: AptosAccount,
    rawTransaction: TxnBuilderTypes.RawTransaction,
    query?: { estimateGasUnitPrice?: boolean; estimateMaxGasAmount?: boolean },
  ): Promise<Gen.UserTransaction[]> {
    const signedTxn = AptosClient.generateBCSSimulation(accountFrom, rawTransaction);
    return this.submitBCSSimulation(signedTxn, query);
  }

  /**
   * Submits a signed transaction to the the endpoint that takes BCS payload
   *
   * @param signedTxn A BCS transaction representation
   * @returns Transaction that is accepted and submitted to mempool
   */
  async submitSignedBCSTransaction(signedTxn: Uint8Array): Promise<Gen.PendingTransaction> {
    // Need to construct a customized post request for transactions in BCS payload
    return this.client.request.request<Gen.PendingTransaction>({
      url: "/transactions",
      method: "POST",
      body: signedTxn,
      mediaType: "application/x.aptos.signed_transaction+bcs",
    });
  }

  /**
   * Submits the BCS serialization of a signed transaction to the simulation endpoint.
   *
   * @param bcsBody The output of `generateBCSSimulation`.
   * @param query?.estimateGasUnitPrice If set to true, the gas unit price in the
   * transaction will be ignored and the estimated value will be used.
   * @param query?.estimateMaxGasAmount If set to true, the max gas value in the
   * transaction will be ignored and the maximum possible gas will be used.
   * @returns Simulation result in the form of UserTransaction.
   */
  async submitBCSSimulation(
    bcsBody: Uint8Array,
    query?: { estimateGasUnitPrice?: boolean; estimateMaxGasAmount?: boolean },
  ): Promise<Gen.UserTransaction[]> {
    // Need to construct a customized post request for transactions in BCS payload.
    const queryParams = {
      estimate_gas_unit_price: query?.estimateGasUnitPrice ?? false,
      estimate_max_gas_amount: query?.estimateMaxGasAmount ?? false,
    };
    return this.client.request.request<Gen.UserTransaction[]>({
      url: "/transactions/simulate",
      query: queryParams,
      method: "POST",
      body: bcsBody,
      mediaType: "application/x.aptos.signed_transaction+bcs",
    });
  }

  /**
   * Queries on-chain transactions
   * @param query Optional pagination object
   * @param query.start The start transaction version of the page. Default is the latest ledger version
   * @param query.limit The max number of transactions should be returned for the page. Default is 25
   * @returns Array of on-chain transactions
   */
  @parseApiError
  async getTransactions(query?: PaginationArgs): Promise<Gen.Transaction[]> {
    return this.client.transactions.getTransactions(query?.start?.toString(), query?.limit);
  }

  /**
   * @param txnHash - Transaction hash should be hex-encoded bytes string with 0x prefix.
   * @returns Transaction from mempool or on-chain transaction
   */
  @parseApiError
  async getTransactionByHash(txnHash: string): Promise<Gen.Transaction> {
    return this.client.transactions.getTransactionByHash(txnHash);
  }

  /**
   * @param txnVersion - Transaction version is an uint64 number.
   * @returns Transaction from mempool or on-chain transaction
   */
  @parseApiError
  async getTransactionByVersion(txnVersion: AnyNumber): Promise<Gen.Transaction> {
    return this.client.transactions.getTransactionByVersion(txnVersion.toString());
  }

  /**
   * Defines if specified transaction is currently in pending state
   * @param txnHash A hash of transaction
   *
   * To create a transaction hash:
   *
   * 1. Create hash message bytes: "Aptos::Transaction" bytes + BCS bytes of Transaction.
   * 2. Apply hash algorithm SHA3-256 to the hash message bytes.
   * 3. Hex-encode the hash bytes with 0x prefix.
   *
   * @returns `true` if transaction is in pending state and `false` otherwise
   */
  async transactionPending(txnHash: string): Promise<boolean> {
    try {
      const response = await this.client.transactions.getTransactionByHash(txnHash);
      return response.type === "pending_transaction";
    } catch (e) {
      if (e?.status === 404) {
        return true;
      }
      throw e;
    }
  }

  /**
   * Wait for a transaction to move past pending state.
   *
   * There are 4 possible outcomes:
   * 1. Transaction is processed and successfully committed to the blockchain.
   * 2. Transaction is rejected for some reason, and is therefore not committed
   *    to the blockchain.
   * 3. Transaction is committed but execution failed, meaning no changes were
   *    written to the blockchain state.
   * 4. Transaction is not processed within the specified timeout.
   *
   * In case 1, this function resolves with the transaction response returned
   * by the API.
   *
   * In case 2, the function will throw an ApiError, likely with an HTTP status
   * code indicating some problem with the request (e.g. 400).
   *
   * In case 3, if `checkSuccess` is false (the default), this function returns
   * the transaction response just like in case 1, in which the `success` field
   * will be false. If `checkSuccess` is true, it will instead throw a
   * FailedTransactionError.
   *
   * In case 4, this function throws a WaitForTransactionError.
   *
   * @param txnHash The hash of a transaction previously submitted to the blockchain.
   * @param extraArgs.timeoutSecs Timeout in seconds. Defaults to 20 seconds.
   * @param extraArgs.checkSuccess See above. Defaults to false.
   * @returns See above.
   *
   * @example
   * ```
   * const rawTransaction = await this.generateRawTransaction(sender.address(), payload, extraArgs);
   * const bcsTxn = AptosClient.generateBCSTransaction(sender, rawTransaction);
   * const pendingTransaction = await this.submitSignedBCSTransaction(bcsTxn);
   * const transasction = await this.aptosClient.waitForTransactionWithResult(pendingTransaction.hash);
   * ```
   */
  async waitForTransactionWithResult(
    txnHash: string,
    extraArgs?: { timeoutSecs?: number; checkSuccess?: boolean },
  ): Promise<Gen.Transaction> {
    const timeoutSecs = extraArgs?.timeoutSecs ?? 20;
    const checkSuccess = extraArgs?.checkSuccess ?? false;

    let isPending = true;
    let count = 0;
    let lastTxn: Gen.Transaction | undefined;
    while (isPending) {
      if (count >= timeoutSecs) {
        break;
      }
      try {
        // eslint-disable-next-line no-await-in-loop
        lastTxn = await this.client.transactions.getTransactionByHash(txnHash);
        isPending = lastTxn.type === "pending_transaction";
        if (!isPending) {
          break;
        }
      } catch (e) {
        // In short, this means we will retry if it was an ApiError and the code was 404 or 5xx.
        const isApiError = e instanceof Gen.ApiError;
        const isRequestError = isApiError && e.status !== 404 && e.status >= 400 && e.status < 500;
        if (!isApiError || isRequestError) {
          throw e;
        }
      }
      // eslint-disable-next-line no-await-in-loop
      await sleep(1000);
      count += 1;
    }
    if (isPending) {
      throw new WaitForTransactionError(
        `Waiting for transaction ${txnHash} timed out after ${timeoutSecs} seconds`,
        lastTxn,
      );
    }
    if (!checkSuccess) {
      return lastTxn;
    }
    if (!(lastTxn as any)?.success) {
      throw new FailedTransactionError(
        `Transaction ${lastTxn.hash} committed to the blockchain but execution failed`,
        lastTxn,
      );
    }
    return lastTxn;
  }

  /**
   * This function works the same as `waitForTransactionWithResult` except it
   * doesn't return the transaction in those cases, it returns nothing. For
   * more information, see the documentation for `waitForTransactionWithResult`.
   */
  async waitForTransaction(
    txnHash: string,
    extraArgs?: { timeoutSecs?: number; checkSuccess?: boolean },
  ): Promise<void> {
    await this.waitForTransactionWithResult(txnHash, extraArgs);
  }

  /**
   * Queries the latest ledger information
   * @returns Latest ledger information
   * @example Example of returned data
   * ```
   * {
   *   chain_id: 15,
   *   epoch: 6,
   *   ledgerVersion: "2235883",
   *   ledger_timestamp:"1654580922321826"
   * }
   * ```
   */
  @parseApiError
  async getLedgerInfo(): Promise<Gen.IndexResponse> {
    return this.client.general.getLedgerInfo();
  }

  /**
   * @returns Current chain id
   */
  @Memoize()
  async getChainId(): Promise<number> {
    const result = await this.getLedgerInfo();
    return result.chain_id;
  }

  /**
   * Gets a table item for a table identified by the handle and the key for the item.
   * Key and value types need to be passed in to help with key serialization and value deserialization.
   * @param handle A pointer to where that table is stored
   * @param data Object, that describes table item
   * @param data.key_type Move type of table key (e.g. `vector<u8>`)
   * @param data.value_type Move type of table value (e.g. `u64`)
   * @param data.key Value of table key
   * @returns Table item value rendered in JSON
   */
  @parseApiError
  async getTableItem(handle: string, data: Gen.TableItemRequest, query?: { ledgerVersion?: AnyNumber }): Promise<any> {
    const tableItem = await this.client.tables.getTableItem(handle, data, query?.ledgerVersion?.toString());
    return tableItem;
  }

{'fraction_non_alphanumeric': 0.08904708380167353, 'fraction_numerical': 0.007389367636651264, 'mean_word_length': 3.4761949128854934, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 73, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6429427', 'n_tokens_mistral': 13667, 'n_tokens_neox': 13267, 'n_words': 4529}