PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-coin-rune/src/lib

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

import { InvalidTransactionError } from '@bitgo/sdk-core';
import { Coin } from '@cosmjs/stargate';
import BigNumber from 'bignumber.js';

import { CosmosLikeTransaction, CosmosUtils, FeeData } from '@bitgo/abstract-cosmos';
import { MessageData } from './iface';
import * as constants from './constants';
import { NetworkType } from '@bitgo/statics';
import { DecodedTxRaw } from '@cosmjs/proto-signing';
import { MAINNET_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX } from './constants';
const bech32 = require('bech32-buffer');

export class RuneUtils extends CosmosUtils {
  private networkType: NetworkType;
  constructor(networkType: NetworkType = NetworkType.MAINNET) {
    super();
    this.networkType = networkType;
  }

  getSendMessageDataFromDecodedTx(decodedTx: DecodedTxRaw): MessageData[] {
    return decodedTx.body.messages.map((message) => {
      const value = this.registry.decode(message);
      return {
        value: {
          fromAddress: this.getEncodedAddress(value.fromAddress),
          toAddress: this.getEncodedAddress(value.toAddress),
          amount: value.amount,
        },
        typeUrl: message.typeUrl,
      };
    });
  }

  /** @inheritdoc */
  isValidAddress(address: string | Uint8Array): boolean {
    if (address === undefined || address === null) {
      return false;
    }
    if (address instanceof Uint8Array) {
      return this.isValidDecodedAddress(address);
    }
    if (typeof address === 'string') {
      return this.isValidEncodedAddress(address);
    }
    return false;
  }

  /**
   * Validates a decoded address in `Uint8Array` form by encoding it and
   * checking if the encoded version is valid
   *
   * @param address - The decoded address as a `Uint8Array`.
   * @returns `true` if the encoded address is valid, `false` otherwise.
   */
  private isValidDecodedAddress(address: Uint8Array): boolean {
    const encodedAddress = this.getEncodedAddress(address);
    return this.isValidEncodedAddress(encodedAddress);
  }

  /**
   * Validates an encoded address string against network-specific criteria.
   *
   * @param address - The encoded address as a `string`.
   * @returns `true` if the address meets network-specific validation criteria, `false` otherwise.
   */
  private isValidEncodedAddress(address: string): boolean {
    if (this.networkType === NetworkType.TESTNET) {
      return this.isValidCosmosLikeAddressWithMemoId(address, constants.testnetAccountAddressRegex);
    }
    return this.isValidCosmosLikeAddressWithMemoId(address, constants.mainnetAccountAddressRegex);
  }

  /**
   * Encodes a given address `Uint8Array` into a bech32 string format, based on the current network type.
   * Primarily serves as a utility to convert a `Uint8Array`-type address to a bech32 encoded string
   *
   * @param address - The address to be encoded, provided as a `Uint8Array`.
   * @returns A bech32-encoded string representing the address.
   * @throws Error - Throws an error if encoding fails
   */
  getEncodedAddress(address: Uint8Array): string {
    try {
      return this.networkType === NetworkType.TESTNET
        ? bech32.encode(TESTNET_ADDRESS_PREFIX, address)
        : bech32.encode(MAINNET_ADDRESS_PREFIX, address);
    } catch (error) {
      throw new Error(`Failed to encode address: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * Decodes a bech32-encoded address string back into a `Uint8Array`.
   * Primarily serves as a utility to convert a string-type address into its binary representation,
   *
   * @param address - The bech32-encoded address as a `string`.
   * @returns The decoded address as a `Uint8Array`.
   * @throws Error - Throws an error if decoding fails
   */
  getDecodedAddress(address: string): Uint8Array {
    try {
      return bech32.decode(address).data;
    } catch (error) {
      throw new Error(`Failed to decode address: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /** @inheritdoc */
  isValidValidatorAddress(address: string): boolean {
    if (this.networkType === NetworkType.TESTNET) {
      return this.isValidBech32AddressMatchingRegex(address, constants.testnetValidatorAddressRegex);
    }
    return this.isValidBech32AddressMatchingRegex(address, constants.mainnetValidatorAddressRegex);
  }

  /** @inheritdoc */
  validateAmount(amount: Coin): void {
    const amountBig = BigNumber(amount.amount);
    if (amountBig.isNaN() || amountBig.isLessThanOrEqualTo(0)) {
      throw new InvalidTransactionError('transactionBuilder: validateAmount: Invalid amount: ' + amount.amount);
    }
    this.validateDenomination(amount.denom);
  }

  /**
   * Validates the gas limit and gas amount for a transaction.
   * @param {FeeData} gasBudget - The gas budget to validate.
   * @throws {InvalidTransactionError} Throws an error if the gas budget is invalid.
   */
  validateGasBudget(gasBudget: FeeData): void {
    if (gasBudget.gasLimit <= 0) {
      throw new InvalidTransactionError('Invalid gas limit ' + gasBudget.gasLimit);
    }
    this.validateGasAmountData(gasBudget.amount);
  }

  /**
   * Validates an array of coin amounts.
   * @param {Coin[]} amountArray - The array of coin amounts to validate.
   */
  validateGasAmountData(amountArray: Coin[]): void {
    amountArray.forEach((coinAmount) => {
      this.validateGasAmount(coinAmount);
    });
  }

  validateGasAmount(amount: Coin): void {
    const amountBig = BigNumber(amount.amount);
    if (amountBig.isNaN() || amountBig.isLessThan(0)) {
      throw new InvalidTransactionError('transactionBuilder: validateAmount: Invalid amount: ' + amount.amount);
    }
    this.validateDenomination(amount.denom);
  }

  validateDenomination(amountDenom: string): void {
    if (
      (this.networkType === NetworkType.TESTNET &&
        !constants.testnetValidDenoms.find((denom) => denom === amountDenom)) ||
      (this.networkType === NetworkType.MAINNET && !constants.mainnetValidDenoms.find((denom) => denom === amountDenom))
    ) {
      throw new InvalidTransactionError('transactionBuilder: validateAmount: Invalid denom: ' + amountDenom);
    }
  }

  convertMessageAddressToUint8Array(messages: MessageData[]): MessageData[] {
    return messages.map((message) => {
      if ('fromAddress' in message.value && 'toAddress' in message.value) {
        const sendMessage = message.value;

        const decodedFrom =
          typeof sendMessage.fromAddress === 'string'
            ? bech32.decode(sendMessage.fromAddress).data
            : sendMessage.fromAddress;
        const decodedTo =
          typeof sendMessage.toAddress === 'string' ? bech32.decode(sendMessage.toAddress).data : sendMessage.toAddress;

        return {
          ...message,
          value: {
            ...sendMessage,
            fromAddress: decodedFrom,
            toAddress: decodedTo,
          },
        };
      }

      return message;
    });
  }

  createTransaction(
    sequence: number,
    messages: MessageData[],
    gasBudget: FeeData,
    publicKey?: string,
    memo?: string
  ): CosmosLikeTransaction {
    messages = this.convertMessageAddressToUint8Array(messages);
    const cosmosLikeTxn = {
      sequence: sequence,
      sendMessages: messages,
      gasBudget: gasBudget,
      publicKey: publicKey,
      memo: memo,
    };
    this.validateTransaction(cosmosLikeTxn);
    return cosmosLikeTxn;
  }

  getNetworkPrefix() {
    return this.networkType === NetworkType.TESTNET ? TESTNET_ADDRESS_PREFIX : MAINNET_ADDRESS_PREFIX;
  }
}

const runeUtils = new RuneUtils();

export default runeUtils;

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


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