PHP WebShell

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

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

import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { BuildTransactionError, NotSupported, TransactionType } from '@bitgo/sdk-core';
import {
  EVMConstants,
  Tx as EVMTx,
  ImportTx,
  UnsignedTx,
  SECPTransferInput,
  SelectCredentialClass,
  TransferableInput,
  EVMOutput,
  AmountInput,
} from 'avalanche/dist/apis/evm';
import { costImportTx } from 'avalanche/dist/utils';
import { BN } from 'avalanche';
import { Credential } from 'avalanche/dist/common';
import { deprecatedRecoverUtxos, utxoToInput } from './utxoEngine';
import { DeprecatedBaseTx, DeprecatedTx } from './iface';
import { AtomicInCTransactionBuilder } from './atomicInCTransactionBuilder';
import utils from './utils';

export class ImportInCTxBuilder extends AtomicInCTransactionBuilder {
  constructor(_coinConfig: Readonly<CoinConfig>) {
    super(_coinConfig);
  }

  /**
   * C-chain address who is target of the import.
   * Address format is eth like
   * @param {string} cAddress
   */
  to(cAddress: string): this {
    this.transaction._to = [utils.parseAddress(cAddress)];
    return this;
  }

  protected get transactionType(): TransactionType {
    return TransactionType.Import;
  }

  /** @inheritdoc */
  initBuilder(tx: DeprecatedTx): this {
    const baseTx: DeprecatedBaseTx = tx.getUnsignedTx().getTransaction();
    if (
      baseTx.getNetworkID() !== this.transaction._networkID ||
      !baseTx.getBlockchainID().equals(this.transaction._blockchainID)
    ) {
      throw new Error('Network or blockchain is not equals');
    }

    if (!this.verifyTxType(baseTx)) {
      throw new NotSupported('Transaction cannot be parsed or has an unsupported transaction type');
    }

    // The outputs is a signler C-Chain address result.
    // It's expected to have only one outputs to the destination C-Chain address.
    const outputs = baseTx.getOuts();
    if (outputs.length !== 1) {
      throw new BuildTransactionError('Transaction can have one output');
    }
    const output = outputs[0];

    if (!output.getAssetID().equals(this.transaction._assetId)) {
      throw new Error('AssetID are not equals');
    }
    this.transaction._to = [output.getAddress()];

    const input = baseTx.getImportInputs();

    this.transaction._utxos = deprecatedRecoverUtxos(input);

    const totalInputAmount = input.reduce((t, i) => t.add((i.getInput() as AmountInput).getAmount()), new BN(0));
    // it should be (output as AmountOutput).getAmount(), but it's not working.
    const totalOutputAmount = new BN((output as any).amount);
    const feeSize = costImportTx(tx.getUnsignedTx() as UnsignedTx);
    const fee = totalInputAmount.sub(totalOutputAmount);
    const feeRate = fee.divn(feeSize);
    this.transaction._fee = {
      fee: fee.toString(),
      feeRate: feeRate.toNumber(),
      size: feeSize,
    };
    this.transaction.setTransaction(tx);
    return this;
  }

  static verifyTxType(baseTx: DeprecatedBaseTx): baseTx is ImportTx {
    return baseTx.getTypeID() === EVMConstants.IMPORTTX;
  }

  verifyTxType(baseTx: DeprecatedBaseTx): baseTx is ImportTx {
    return ImportInCTxBuilder.verifyTxType(baseTx);
  }

  /**
   * Build the import in C-chain transaction
   * @protected
   */
  protected buildAvaxTransaction(): void {
    // if tx has credentials, tx shouldn't change
    if (this.transaction.hasCredentials) return;
    if (this.transaction._to.length !== 1) {
      throw new Error('to is required');
    }
    if (!this.transaction._fee.feeRate) {
      throw new Error('fee rate is required');
    }
    const { inputs, amount, credentials } = this.createInputs();

    const feeRate = new BN(this.transaction._fee.feeRate);
    const feeSize = costImportTx(
      new UnsignedTx(
        new ImportTx(this.transaction._networkID, this.transaction._blockchainID, this._externalChainId, inputs, [
          new EVMOutput(this.transaction._to[0], amount, this.transaction._assetId),
        ])
      )
    );
    const fee = feeRate.muln(feeSize);
    this.transaction._fee.fee = fee.toString();
    this.transaction._fee.size = feeSize;
    this.transaction.setTransaction(
      new EVMTx(
        new UnsignedTx(
          new ImportTx(
            this.transaction._networkID,
            this.transaction._blockchainID,
            this._externalChainId,
            inputs,
            [new EVMOutput(this.transaction._to[0], amount.sub(fee), this.transaction._assetId)],
            fee
          )
        ),
        credentials
      )
    );
  }

  /**
   * Create inputs by mapping {@see utxoEngine.utxoToInput} result.
   * Reorder sender to handle recover signer.
   * TransferableInput is a EVM Tx.
   * @return {
   *     inputs: TransferableInput[];
   *     credentials: Credential[];
   *     amount: BN;
   *   } where amount is the sum of inputs amount and credentials has signer address to be replaced with correct signature.
   * @protected
   *
   */
  protected createInputs(): {
    inputs: TransferableInput[];
    credentials: Credential[];
    amount: BN;
  } {
    const sender = this.transaction._fromAddresses.slice();
    if (this.recoverSigner) {
      // switch first and last signer.
      const tmp = sender.pop();
      sender.push(sender[0]);
      if (tmp) {
        sender[0] = tmp;
      }
    }
    const { inputs, amount } = utxoToInput(this.transaction._utxos, sender);
    const result: {
      inputs: TransferableInput[];
      credentials: Credential[];
    } = { inputs: [], credentials: [] };

    inputs.forEach((input) => {
      const secpTransferInput = new SECPTransferInput(input.amount);
      input.signaturesIdx.forEach((signatureIdx, arrayIndex) =>
        secpTransferInput.addSignatureIdx(signatureIdx, sender[arrayIndex])
      );
      result.inputs.push(
        new TransferableInput(input.txidBuf, input.outputIdx, this.transaction._assetId, secpTransferInput)
      );

      result.credentials.push(SelectCredentialClass(secpTransferInput.getCredentialID(), input.signatures));
    });

    return { ...result, amount };
  }
}

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


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