PHP WebShell

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

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

import assert from 'assert';
import * as hex from '@stablelib/hex';
import * as tronweb from 'tronweb';
import { protocol } from '../../resources/protobuf/tron';

import { UtilsError } from '@bitgo/sdk-core';
import { TronErc20Coin, coins } from '@bitgo/statics';
import {
  TransferContract,
  RawData,
  AccountPermissionUpdateContract,
  TransactionReceipt,
  Permission,
  TriggerSmartContract,
} from './iface';
import { ContractType, PermissionType } from './enum';
import { AbiCoder, hexConcat } from 'ethers/lib/utils';

const ADDRESS_PREFIX_REGEX = /^(41)/;
const ADDRESS_PREFIX = '41';

const getTronTokens = (network = 'mainnet') => {
  return (
    coins
      .filter((coin) => coin.family === 'trx')
      .filter((trx) => trx.network.type === network && trx.isToken) as unknown as TronErc20Coin[]
  ).map((coins) => coins.contractAddress.toString());
};

export const tokenMainnetContractAddresses = getTronTokens('mainnet');
export const tokenTestnetContractAddresses = getTronTokens('testnet');
/**
 * Tron-specific helper functions
 */
export type TronBinaryLike = ByteArray | Buffer | Uint8Array | string;
export type ByteArray = number[];

/**
 * @param address
 */
export function isBase58Address(address: string): boolean {
  return tronweb.utils.crypto.isAddressValid(address);
}

/**
 * @param str
 */
export function getByteArrayFromHexAddress(str: string): ByteArray {
  return tronweb.utils.code.hexStr2byteArray(str.replace('0x', ''));
}

/**
 * @param arr
 */
export function getHexAddressFromByteArray(arr: ByteArray): string {
  return tronweb.utils.code.byteArray2hexStr(arr);
}

/**
 * @param messageToVerify
 * @param base58Address
 * @param sigHex
 * @param useTronHeader
 */
export function verifySignature(
  messageToVerify: string,
  base58Address: string,
  sigHex: string,
  useTronHeader = true
): boolean {
  if (!isValidHex(sigHex)) {
    throw new UtilsError('signature is not in a valid format, needs to be hexadecimal');
  }

  if (!isValidHex(messageToVerify)) {
    throw new UtilsError('message is not in a valid format, needs to be hexadecimal');
  }

  if (!isBase58Address(base58Address)) {
    throw new UtilsError('address needs to be base58 encoded');
  }

  return tronweb.Trx.verifySignature(messageToVerify, base58Address, sigHex, useTronHeader);
}

/**
 * @param base58
 */
export function getHexAddressFromBase58Address(base58: string): string {
  // pulled from: https://github.com/TRON-US/tronweb/blob/dcb8efa36a5ebb65c4dab3626e90256a453f3b0d/src/utils/help.js#L17
  // but they don't surface this call in index.js
  const bytes = tronweb.utils.crypto.decodeBase58Address(base58);
  return getHexAddressFromByteArray(bytes);
}

/**
 * @param privateKey
 */
export function getPubKeyFromPriKey(privateKey: TronBinaryLike): ByteArray {
  return tronweb.utils.crypto.getPubKeyFromPriKey(privateKey);
}

/**
 * @param privateKey
 */
export function getAddressFromPriKey(privateKey: TronBinaryLike): ByteArray {
  return tronweb.utils.crypto.getAddressFromPriKey(privateKey);
}

/**
 * @param address
 */
export function getBase58AddressFromByteArray(address: ByteArray): string {
  return tronweb.utils.crypto.getBase58CheckAddress(address);
}

/**
 * @param hex
 */
export function getBase58AddressFromHex(hex: string): string {
  const arr = getByteArrayFromHexAddress(hex);
  return getBase58AddressFromByteArray(arr);
}

/**
 * @param privateKey
 * @param transaction
 */
export function signTransaction(privateKey: string | ByteArray, transaction: TransactionReceipt): TransactionReceipt {
  return tronweb.utils.crypto.signTransaction(privateKey, transaction);
}

/**
 * @param message
 * @param privateKey
 * @param useTronHeader
 */
export function signString(message: string, privateKey: string | ByteArray, useTronHeader = true): string {
  return tronweb.Trx.signString(message, privateKey, useTronHeader);
}

/**
 * @param pubBytes
 */
export function getRawAddressFromPubKey(pubBytes: TronBinaryLike): ByteArray {
  return tronweb.utils.crypto.computeAddress(pubBytes);
}

/**
 * Decodes a hex encoded transaction in its protobuf representation.
 *
 * @param hexString raw_data_hex field from tron transactions
 */
export function decodeTransaction(hexString: string): RawData {
  const rawTransaction = decodeRawTransaction(hexString);

  // there should not be multiple contracts in this data
  if (rawTransaction.contracts.length !== 1) {
    throw new UtilsError('Number of contracts is greater than 1.');
  }

  let contract: TransferContract[] | AccountPermissionUpdateContract[] | TriggerSmartContract[];
  let contractType: ContractType;
  // ensure the contract type is supported
  switch (rawTransaction.contracts[0].parameter.type_url) {
    case 'type.googleapis.com/protocol.TransferContract':
      contractType = ContractType.Transfer;
      contract = exports.decodeTransferContract(rawTransaction.contracts[0].parameter.value);
      break;
    case 'type.googleapis.com/protocol.AccountPermissionUpdateContract':
      contractType = ContractType.AccountPermissionUpdate;
      contract = exports.decodeAccountPermissionUpdateContract(rawTransaction.contracts[0].parameter.value);
      break;
    case 'type.googleapis.com/protocol.TriggerSmartContract':
      contractType = ContractType.TriggerSmartContract;
      contract = exports.decodeTriggerSmartContract(rawTransaction.contracts[0].parameter.value);
      break;
    default:
      throw new UtilsError('Unsupported contract type');
  }

  return {
    contractType,
    contract,
    expiration: rawTransaction.expiration,
    timestamp: rawTransaction.timestamp,
    ref_block_bytes: rawTransaction.blockBytes,
    ref_block_hash: rawTransaction.blockHash,
    fee_limit: +rawTransaction.feeLimit,
  };
}

/**
 * Decodes a transaction's raw field from a base64 encoded string. This is a protobuf representation.
 *
 * @param hexString this is the raw hexadecimal encoded string. Doc found in the following link.
 * @example
 * @see {@link https://github.com/BitGo/bitgo-account-lib/blob/5f282588701778a4421c75fa61f42713f56e95b9/resources/protobuf/tron.proto#L319}
 */
export function decodeRawTransaction(hexString: string): {
  expiration: number;
  timestamp: number;
  contracts: Array<any>;
  blockBytes: string;
  blockHash: string;
  feeLimit: string;
} {
  const bytes = Buffer.from(hexString, 'hex');

  let raw;
  try {
    // we need to decode our raw_data_hex field first
    raw = protocol.Transaction.raw.decode(bytes);
  } catch (e) {
    throw new UtilsError('There was an error decoding the initial raw_data_hex from the serialized tx.');
  }

  return {
    expiration: Number(raw.expiration),
    timestamp: Number(raw.timestamp),
    contracts: raw.contract,
    blockBytes: toHex(raw.refBlockBytes),
    feeLimit: raw.feeLimit,
    blockHash: toHex(raw.refBlockHash),
  };
}

/**
 * Indicates whether the passed string is a safe hex string for tron's purposes.
 *
 * @param hex A valid hex string must be a string made of numbers and characters and has an even length.
 */
export function isValidHex(hex: string): boolean {
  return /^(0x)?([0-9a-f]{2})+$/i.test(hex);
}

/** Deserialize the segment of the txHex which corresponds with the details of the transfer
 *
 * @param transferHex is the value property of the "parameter" field of contractList[0]
 * */
export function decodeTransferContract(transferHex: string): TransferContract[] {
  const contractBytes = Buffer.from(transferHex, 'base64');
  let transferContract;

  try {
    transferContract = protocol.TransferContract.decode(contractBytes);
  } catch (e) {
    throw new UtilsError('There was an error decoding the transfer contract in the transaction.');
  }

  if (!transferContract.ownerAddress) {
    throw new UtilsError('Owner address does not exist in this transfer contract.');
  }

  if (!transferContract.toAddress) {
    throw new UtilsError('Destination address does not exist in this transfer contract.');
  }

  if (!transferContract.hasOwnProperty('amount')) {
    throw new UtilsError('Amount does not exist in this transfer contract.');
  }

  // deserialize attributes
  const owner_address = getBase58AddressFromByteArray(
    getByteArrayFromHexAddress(Buffer.from(transferContract.ownerAddress, 'base64').toString('hex'))
  );
  const to_address = getBase58AddressFromByteArray(
    getByteArrayFromHexAddress(Buffer.from(transferContract.toAddress, 'base64').toString('hex'))
  );
  const amount = transferContract.amount;

  return [
    {
      parameter: {
        value: {
          amount: Number(amount),
          owner_address,
          to_address,
        },
      },
    },
  ];
}

/**
 * Deserialize the segment of the txHex corresponding with trigger smart contract
 *
 * @param {string} base64
 * @returns {AccountPermissionUpdateContract}
 */
export function decodeTriggerSmartContract(base64: string): TriggerSmartContract[] {
  let contractCallDecoded;
  try {
    contractCallDecoded = protocol.TriggerSmartContract.decode(Buffer.from(base64, 'base64')).toJSON();
  } catch (e) {
    throw new UtilsError('There was an error decoding the contract call in the transaction.');
  }

  if (!contractCallDecoded.ownerAddress) {
    throw new UtilsError('Owner address does not exist in this contract call.');
  }

  if (!contractCallDecoded.contractAddress) {
    throw new UtilsError('Destination contract address does not exist in this contract call.');
  }

  if (!contractCallDecoded.data) {
    throw new UtilsError('Data does not exist in this contract call.');
  }

  // deserialize attributes
  const owner_address = getBase58AddressFromByteArray(
    getByteArrayFromHexAddress(Buffer.from(contractCallDecoded.ownerAddress, 'base64').toString('hex'))
  );
  const contract_address = getBase58AddressFromByteArray(
    getByteArrayFromHexAddress(Buffer.from(contractCallDecoded.contractAddress, 'base64').toString('hex'))
  );
  const data = contractCallDecoded.data;
  return [
    {
      parameter: {
        value: {
          data: data,
          owner_address,
          contract_address,
        },
      },
    },
  ];
}

/**
 * Deserialize the segment of the txHex corresponding with the details of the contract which updates
 * account permission
 *
 * @param {string} base64
 * @returns {AccountPermissionUpdateContract}
 */
export function decodeAccountPermissionUpdateContract(base64: string): AccountPermissionUpdateContract {
  const accountUpdateContract = protocol.AccountPermissionUpdateContract.decode(Buffer.from(base64, 'base64')).toJSON();
  assert(accountUpdateContract.ownerAddress);
  assert(accountUpdateContract.owner);
  assert(accountUpdateContract.hasOwnProperty('actives'));

  const ownerAddress = getBase58AddressFromByteArray(
    getByteArrayFromHexAddress(Buffer.from(accountUpdateContract.ownerAddress, 'base64').toString('hex'))
  );
  const owner: Permission = createPermission(accountUpdateContract.owner);
  let witness: Permission | undefined = undefined;
  if (accountUpdateContract.witness) {
    witness = createPermission(accountUpdateContract.witness);
  }
  const activeList = accountUpdateContract.actives.map((active) => createPermission(active));

  return {
    ownerAddress,
    owner,
    witness,
    actives: activeList,
  };
}

/**
 * @param raw
 */
function createPermission(raw: { permissionName: string; threshold: number }): Permission {
  let permissionType: PermissionType;
  const permission = raw.permissionName.toLowerCase().trim();
  if (permission === 'owner') {
    permissionType = PermissionType.Owner;
  } else if (permission === 'witness') {
    permissionType = PermissionType.Witness;
  } else if (permission.substr(0, 6) === 'active') {
    permissionType = PermissionType.Active;
  } else {
    throw new UtilsError('Permission type not parseable.');
  }
  return { type: permissionType, threshold: raw.threshold };
}

/**
 * @param rawTransaction
 */
export function isValidTxJsonString(rawTransaction: string): boolean {
  const transaction = JSON.parse(rawTransaction);
  return transaction.hasOwnProperty('txID');
}

/**
 * Returns whether the provided raw transaction accommodates to bitgo's preferred format
 *
 * @param {any} rawTransaction - The raw transaction to be checked
 * @returns {boolean} the validation result
 */
export function isValidRawTransactionFormat(rawTransaction: any): boolean {
  if (typeof rawTransaction === 'string' && (isValidHex(rawTransaction) || isValidTxJsonString(rawTransaction))) {
    return true;
  }
  return false;
}

/**
 * Returns an hex string of the given buffer
 *
 * @param {Buffer | Uint8Array} buffer - the buffer to be converted to hex
 * @returns {string} - the hex value
 */
export function toHex(buffer: Buffer | Uint8Array): string {
  return hex.encode(buffer, true);
}

/**
 * Returns a Keccak-256 encoded string of the parameters
 *
 * @param types - strings describing the types of the values
 * @param values - value to encode
 * @param methodId - the first 4 bytes of the function selector
 */
export function encodeDataParams(types: string[], values: any[], methodId?: string): string {
  types.forEach((type, index) => {
    if (type == 'address') {
      values[index] = values[index].replace(ADDRESS_PREFIX_REGEX, '0x');
    }
  });

  const abiCoder = new AbiCoder();
  let data;
  try {
    data = abiCoder.encode(types, values);
  } catch (e) {
    throw new UtilsError(`There was an error encoding the data params. Error = ${JSON.stringify(e)}`);
  }
  if (methodId) {
    return hexConcat([methodId, data]).replace(/^(0x)/, '');
  } else {
    return data.replace(/^(0x)/, '');
  }
}

/**
 * Returns the decoded values according to the array of types
 *
 * @param types - strings describing the types of the values
 * @param data - encoded string
 */
export function decodeDataParams(types: string[], data: string): any[] {
  const abiCoder = new AbiCoder();
  data = '0x' + data.substring(8);
  return abiCoder.decode(types, data).reduce((obj, arg, index) => {
    if (types[index] == 'address') arg = ADDRESS_PREFIX + arg.substr(2).toLowerCase();
    obj.push(arg);
    return obj;
  }, []);
}

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


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