PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-core/src/bitgo/baseCoin

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

/**
 * @prettier
 */
import * as crypto from 'crypto';
import { bip32 } from '@bitgo/utxo-lib';
import { BigNumber } from 'bignumber.js';

import * as utxolib from '@bitgo/utxo-lib';
import { BaseCoin as StaticsBaseCoin } from '@bitgo/statics';

import { InitiateRecoveryOptions } from '../recovery';
import { signMessage } from '../bip32util';
import { NotImplementedError } from '../../account-lib';
import { BitGoBase } from '../bitgoBase';
import { Enterprises } from '../enterprise';
import { Keychains, KeyIndices } from '../keychain';
import { Markets } from '../market';
import { PendingApprovals } from '../pendingApproval';
import { Wallets, IWallet, Wallet } from '../wallet';
import { Webhooks } from '../webhook';
import {
  ExtraPrebuildParamsOptions,
  FeeEstimateOptions,
  IBaseCoin,
  ParsedTransaction,
  ITransactionExplanation,
  KeychainsTriplet,
  KeyPair,
  MPCAlgorithm,
  ParseTransactionOptions,
  PrecreateBitGoOptions,
  PresignTransactionOptions,
  RecoverTokenTransaction,
  RecoverWalletTokenOptions,
  SignedTransaction,
  SignTransactionOptions,
  SupplementGenerateWalletOptions,
  TokenEnablementConfig,
  TransactionPrebuild,
  VerifyAddressOptions,
  VerifyTransactionOptions,
  BuildNftTransferDataOptions,
  BaseBroadcastTransactionOptions,
  BaseBroadcastTransactionResult,
  DeriveKeyWithSeedOptions,
  MultisigType,
} from './iBaseCoin';
import { IInscriptionBuilder } from '../inscriptionBuilder';
import { Hash } from 'crypto';
import { MPCSweepRecoveryOptions, MPCTxs, PopulatedIntent, PrebuildTransactionWithIntentOptions } from '../utils';

export abstract class BaseCoin implements IBaseCoin {
  protected readonly bitgo: BitGoBase;
  protected readonly _url: string;
  protected readonly _enterprises: Enterprises;
  protected readonly _wallets: Wallets;
  protected readonly _keychains: Keychains;
  protected readonly _webhooks: Webhooks;
  protected readonly _pendingApprovals: PendingApprovals;
  protected readonly _markets: Markets;
  protected static readonly _coinTokenPatternSeparator = ':';
  protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;

  protected constructor(bitgo: BitGoBase) {
    this.bitgo = bitgo;
    this._url = this.bitgo.url('/', 2);
    this._wallets = new Wallets(this.bitgo, this);
    this._keychains = new Keychains(this.bitgo, this);
    this._webhooks = new Webhooks(this.bitgo, this);
    this._pendingApprovals = new PendingApprovals(this.bitgo, this);
    this._enterprises = new Enterprises(this.bitgo, this);
    this._markets = new Markets(this.bitgo, this);
  }

  public url(suffix: string): string {
    return this._url + this.getChain() + suffix;
  }

  public wallets(): Wallets {
    return this._wallets;
  }

  public enterprises(): Enterprises {
    return this._enterprises;
  }

  public keychains(): Keychains {
    return this._keychains;
  }

  public webhooks(): Webhooks {
    return this._webhooks;
  }

  public pendingApprovals(): PendingApprovals {
    return this._pendingApprovals;
  }

  public markets(): Markets {
    return this._markets;
  }

  public static get coinTokenPatternSeparator(): string {
    return this._coinTokenPatternSeparator;
  }

  public get type(): string {
    return this.getChain();
  }

  /**
   * Gets the statics coin object
   * @returns {Readonly<StaticsBaseCoin>} the statics coin object
   */
  getConfig(): Readonly<StaticsBaseCoin> {
    return this._staticsCoin;
  }

  /**
   * Name of the chain which supports this coin (eg, 'btc', 'eth')
   */
  abstract getChain(): string;

  /**
   * Name of the coin family (eg. for tbtc, this would be btc)
   */
  abstract getFamily(): string;

  /**
   * Human readable full name for the coin
   */
  abstract getFullName(): string;

  /**
   * Flag for sending value of 0.
   * @returns {boolean} True if okay to send 0 value, false otherwise
   */
  valuelessTransferAllowed(): boolean {
    return false;
  }

  /**
   * Use `sendMany()` to perform wallet sweep.
   * FIXME(BG-39738): add coin.sweepWallet() instead
   */
  sweepWithSendMany(): boolean {
    return false;
  }

  /**
   * Flag for sending data along with transactions
   * @returns {boolean} True if okay to send tx data (ETH), false otherwise
   */
  transactionDataAllowed(): boolean {
    return false;
  }

  /**
   * Flag for determining whether this coin supports account consolidations
   * from its receive addresses to the root address.
   * @returns {boolean} True if okay to consolidate over this coin; false, otherwise
   */
  allowsAccountConsolidations(): boolean {
    return false;
  }

  /**
   * Gets config for how token enablements work for this coin
   * @returns
   *    requiresTokenEnablement: True if tokens need to be enabled for this coin
   *    supportsMultipleTokenEnablements: True if multiple tokens can be enabled in one transaction
   */
  getTokenEnablementConfig(): TokenEnablementConfig {
    return {
      requiresTokenEnablement: false,
      supportsMultipleTokenEnablements: false,
    };
  }

  /**
   * Flag indicating if this coin supports TSS wallets.
   * @returns {boolean} True if TSS Wallets can be created for this coin
   */
  supportsTss(): boolean {
    return false;
  }

  /**
   * Flag indicating if this coin supports MultiSig wallets.
   * @return {boolean} True if MultiSig wallets can be created for this coin
   */
  supportsMultisig(): boolean {
    return false;
  }

  /**
   * It will return the default multisig type value for coin
   * @return {MultisigType} return 'tss' if coin supports only TSS not MultiSig
   * else if coin supports MultiSig return 'onchain'
   * if coin supports both return 'onchain'
   * else undefined
   */
  getDefaultMultisigType(): MultisigType | undefined {
    return undefined;
  }

  /**
   * Flag indicating if the coin supports deriving a key with a seed (keyID)
   * to the user/backup keys.
   */
  supportsDeriveKeyWithSeed(): boolean {
    return true;
  }

  /**
   * Flag indicating if this blockchain runs on EVM architecture.
   * @returns {boolean} True if the blockchain runs on EVM architecture.
   */
  isEVM(): boolean {
    return false;
  }

  /**
   * Flag indicating if this coin supports BLS-DKG wallets.
   * @returns {boolean} True if BLS-DKG Wallets can be created for this coin
   */
  supportsBlsDkg(): boolean {
    return false;
  }

  /**
   * Returns the factor between the base unit and its smallest subdivison
   * @return {number}
   */
  abstract getBaseFactor(): number | string;

  /**
   * Convert a currency amount represented in base units (satoshi, wei, atoms, drops, stroops)
   * to big units (btc, eth, xrp, xlm)
   */
  baseUnitsToBigUnits(baseUnits: string | number): string {
    BigNumber.set({ DECIMAL_PLACES: 24 });
    const dividend = this.getBaseFactor();
    const bigNumber = new BigNumber(baseUnits).dividedBy(dividend);
    // set the format so commas aren't added to large coin amounts
    return bigNumber.toFormat(null as any, null as any, { groupSeparator: '', decimalSeparator: '.' });
  }

  checkRecipient(recipient: { address: string; amount: string | number }): void {
    if (recipient.amount !== 'max') {
      const amount = new BigNumber(recipient.amount);
      if (amount.isNegative()) {
        throw new Error('invalid argument for amount - positive number greater than zero or numeric string expected');
      }
      if (!this.valuelessTransferAllowed() && amount.isZero()) {
        throw new Error('invalid argument for amount - positive number greater than zero or numeric string expected');
      }
    }
  }

  /**
   * Convert a currency amount represented in big units (btc, eth, xrp, xlm)
   * to base units (satoshi, wei, atoms, drops, stroops)
   * @param bigUnits
   */
  bigUnitsToBaseUnits(bigUnits: string | number): string {
    const multiplier = this.getBaseFactor();
    const bigNumber = new BigNumber(bigUnits).times(multiplier);
    if (!bigNumber.isInteger()) {
      throw new Error(`non-integer output resulted from multiplying ${bigUnits} by ${multiplier}`);
    }
    return bigNumber.toFixed(0);
  }

  /**
   * Preprocess the build parameters before sending to the API
   * @param buildParams
   */
  preprocessBuildParams(buildParams: Record<string, any>): Record<string, any> {
    return buildParams;
  }

  /**
   * Sign message with private key
   *
   * @param key
   * @param message
   */
  async signMessage(key: { prv: string }, message: string): Promise<Buffer> {
    return signMessage(message, bip32.fromBase58(key.prv), utxolib.networks.bitcoin);
  }

  /**
   * Create signatures for the backup and bitgo keys using the user key.
   * We can verify the signatures when fetching the keys from wallet-platform later.
   * Currently only `AbstractUtxoCoin` implements and uses the complementary `verifyKeySignature` method.
   * @param prv - the user private key
   * @param backupKeychain - contains the backup public key
   * @param bitgoKeychain - contains the bitgo public key
   */
  public async createKeySignatures(
    prv: string,
    backupKeychain: { pub: string },
    bitgoKeychain: { pub: string }
  ): Promise<{
    backup: string;
    bitgo: string;
  }> {
    return {
      backup: (await this.signMessage({ prv }, backupKeychain.pub)).toString('hex'),
      bitgo: (await this.signMessage({ prv }, bitgoKeychain.pub)).toString('hex'),
    };
  }

  /**
   * Decompose a raw transaction into useful information.
   * @param options - coin-specific
   */
  explainTransaction(options: Record<string, any>): Promise<ITransactionExplanation<any, string | number> | undefined> {
    throw new Error(`not implemented`);
  }

  /**
   * Verify that a transaction prebuild complies with the original intention
   */
  abstract verifyTransaction(params: VerifyTransactionOptions): Promise<boolean>;

  /**
   * @deprecated use {@see isWalletAddress} instead
   */
  verifyAddress(params: VerifyAddressOptions): Promise<boolean> {
    return this.isWalletAddress(params);
  }

  /**
   * @param params
   * @return true iff address is a wallet address. Must return false if address is outside wallet.
   */
  abstract isWalletAddress(params: VerifyAddressOptions): Promise<boolean>;

  /**
   * convert address into desired address format.
   * @param address
   * @param format
   */
  canonicalAddress(address: string, format?: unknown): string {
    return address;
  }

  /**
   * Check whether a coin supports blockTarget for transactions to be included in
   * @returns {boolean}
   */
  supportsBlockTarget() {
    return false;
  }

  /**
   * Check whether a coin supports lightning transactions
   * @returns {boolean}
   */
  supportsLightning() {
    return false;
  }

  /**
   * Check whether a coin supports message signing
   * @returns {boolean}
   */
  supportsMessageSigning(): boolean {
    return false;
  }

  /**
   * Check whether a coin supports signing of Typed data
   * @returns {boolean}
   */
  supportsSigningTypedData(): boolean {
    return false;
  }

  /**
   * Hook to add additional parameters to the wallet generation
   * @param walletParams
   * @param keychains
   * @return {*}
   */
  supplementGenerateWallet(walletParams: SupplementGenerateWalletOptions, keychains: KeychainsTriplet): Promise<any> {
    return Promise.resolve(walletParams);
  }

  /**
   * Get extra parameters for prebuilding a tx. Add things like hop transaction params
   */
  getExtraPrebuildParams(buildParams: ExtraPrebuildParamsOptions): Promise<Record<string, unknown>> {
    return Promise.resolve({});
  }

  /**
   * Modify prebuild after receiving it from the server. Add things like nlocktime
   */
  postProcessPrebuild(prebuildResponse: TransactionPrebuild): Promise<TransactionPrebuild> {
    return Promise.resolve(prebuildResponse);
  }

  /**
   * Coin-specific things done before signing a transaction, i.e. verification
   */
  presignTransaction(params: PresignTransactionOptions): Promise<PresignTransactionOptions> {
    return Promise.resolve(params);
  }

  /**
   * Create a new wallet object from a wallet data object
   * @param walletParams
   */
  newWalletObject(walletParams: any): IWallet {
    return new Wallet(this.bitgo, this, walletParams);
  }

  /**
   * Fetch fee estimate information from the server
   * @param {Object} params The params passed into the function
   * @param {Integer} params.numBlocks The number of blocks to target for conformation (Only works for btc)
   * @returns {Object} The info returned from the merchant server
   */
  async feeEstimate(params: FeeEstimateOptions): Promise<any> {
    const query: any = {};
    if (params && params.numBlocks) {
      query.numBlocks = params.numBlocks;
    }

    return this.bitgo.get(this.url('/tx/fee')).query(query).result();
  }

  /**
   * The cold wallet tool uses this function to derive an extended key that is based on the passed key and seed
   * @param key
   * @param seed
   * @returns {{key: string, derivationPath: string}}
   */
  static deriveKeyWithSeedBip32(
    key: utxolib.BIP32Interface,
    seed: string
  ): {
    key: utxolib.BIP32Interface;
    derivationPath: string;
  } {
    function sha256(input) {
      return crypto.createHash('sha256').update(input).digest();
    }
    const derivationPathInput = sha256(sha256(`${seed}`)).toString('hex');
    const derivationPathParts = [
      parseInt(derivationPathInput.slice(0, 7), 16),
      parseInt(derivationPathInput.slice(7, 14), 16),
    ];
    const derivationPath = 'm/999999/' + derivationPathParts.join('/');
    return {
      key: key.derivePath(derivationPath),
      derivationPath,
    };
  }

  /** {@see deriveKeyWithSeedBip32} */
  deriveKeyWithSeed(params: DeriveKeyWithSeedOptions): {
    key: string;
    derivationPath: string;
  } {
    const { key, derivationPath } = BaseCoin.deriveKeyWithSeedBip32(bip32.fromBase58(params.key), params.seed);
    return {
      key: key.toBase58(),
      derivationPath,
    };
  }

  /**
   * Specifies what key we will need for signing - right now we just need the
   * user key.
   */
  keyIdsForSigning(): number[] {
    return [KeyIndices.USER];
  }

  /**
   * Perform additional checks before adding a bitgo key. Base controller
   * is a no-op, but coin-specific controller may do something
   * @param params
   */
  preCreateBitGo(params: PrecreateBitGoOptions): void {
    return;
  }

  /**
   * @deprecated - use getBip32Keys() in conjunction with isValidAddress instead
   */
  initiateRecovery(params: InitiateRecoveryOptions): never {
    throw new Error('deprecated method');
  }

  /**
   * Only used in PendingApproval for comparing PAYGo fees purpose
   * @param params options for parsing
   */
  abstract parseTransaction(params: ParseTransactionOptions): Promise<ParsedTransaction>;

  /**
   * Generate a key pair on the curve used by the coin
   * @param {Buffer} seed - seed to use for key pair generation
   * @returns {KeyPair} the generated key pair
   */
  abstract generateKeyPair(seed?: Buffer): KeyPair;

  /**
   * Generate a root key pair on the curve used by the coin
   * @param {Buffer} seed - seed to use for key pair generation
   * @returns {KeyPair} the generated key pair
   */
  generateRootKeyPair(seed?: Buffer): KeyPair {
    throw new NotImplementedError('generateRootKeyPair is not supported for this coin');
  }

  /**
   * Return boolean indicating whether input is valid public key for the coin.
   *
   * @param {String} pub the pub to be checked
   * @returns {Boolean} is it valid?
   */
  abstract isValidPub(pub: string): boolean;

  /**
   * Return wether the given m of n wallet signers/ key amounts are valid for the coin
   */
  isValidMofNSetup({ m, n }: { m?: number; n?: number }): boolean {
    return m === 2 && n === 3;
  }

  /**
   * Check if `address` is a plausibly valid address for the given coin.
   *
   * Does not verify that the address belongs to a wallet. For that,
   * use [[verifyAddress]]
   * @param address
   */
  abstract isValidAddress(address: string): boolean;

  /**
   * Sign a transaction
   */
  abstract signTransaction(params: SignTransactionOptions): Promise<SignedTransaction>;

  /**
   * Returns the portion of the transaction that needs to be signed in Buffer format.
   * Only needed for coins that support adding signatures directly (e.g. TSS).
   *
   * @param {String} serializedTx - the unsigned transaction in broadcast format
   * @returns {Promise<Buffer>} - the portion of the transaction that needs to be signed
   */
  async getSignablePayload(serializedTx: string): Promise<Buffer> {
    return Buffer.from(serializedTx);
  }

  /**
   * Returns the MPC algorithm (ecdsa or eddsa) used for coins that support TSS
   */
  getMPCAlgorithm(): MPCAlgorithm {
    throw new Error('no MPC algorithm is defined for this coin');
  }

  async recoverToken(params: RecoverWalletTokenOptions): Promise<RecoverTokenTransaction> {
    throw new NotImplementedError('recoverToken is not supported for this coin');
  }

  getInscriptionBuilder(wallet: Wallet): IInscriptionBuilder {
    throw new NotImplementedError('Inscription Builder is not supported for this coin');
  }

  /**
   * Function to get coin specific hash function used to generate transaction digests.
   * @returns {@see Hash} hash function if implemented, otherwise throws exception
   */
  getHashFunction(): Hash {
    throw new NotImplementedError('getHashFunction is not supported for this coin');
  }

  buildNftTransferData(params: BuildNftTransferDataOptions): string {
    throw new NotImplementedError('buildNftTransferData is not supported for this coin');
  }

  /**
   * Broadcast a transaction to the network
   * @param params options for broadcasting
   * @returns {Promise<BaseBroadcastTransactionResult>} result of broadcast
   * @throws {NotImplementedError} if not implemented
   */
  broadcastTransaction(params: BaseBroadcastTransactionOptions): Promise<BaseBroadcastTransactionResult> {
    throw new NotImplementedError('broadcastTransaction is not supported for this coin');
  }

  /**
   * Creates funds sweep recovery transaction(s) without BitGo
   *
   * @param {MPCSweepRecoveryOptions} params parameters needed to combine the signatures
   * and transactions to create broadcastable transactions
   *
   * @returns {MPCTxs} array of the serialized transaction hex strings and indices
   * of the addresses being swept
   */
  async createBroadcastableSweepTransaction(params: MPCSweepRecoveryOptions): Promise<MPCTxs> {
    throw new NotImplementedError('createBroadcastableSweepTransaction is not supported for this coin');
  }

  /**
   * Sets coinspecific fields in intent from input params.
   * This method should be overridden in coin-specific classes
   * to configure these fields in the intent
   * @param intent - intent in which coinspecific fields are to be set
   * @param params
   */
  setCoinSpecificFieldsInIntent(intent: PopulatedIntent, params: PrebuildTransactionWithIntentOptions): void {
    return;
  }
}

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


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