PHP WebShell

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

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

import {
  BaseKey,
  BaseTransaction,
  InvalidTransactionError,
  PublicKey,
  Signature,
  TransactionRecipient,
  TransactionType,
} from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig, NetworkType } from '@bitgo/statics';
import {
  AccountAddress,
  AccountAuthenticator,
  AccountAuthenticatorEd25519,
  AccountAuthenticatorNoAccountAuthenticator,
  Aptos,
  AptosConfig,
  DEFAULT_MAX_GAS_AMOUNT,
  Ed25519PublicKey,
  Ed25519Signature,
  FeePayerRawTransaction,
  generateSigningMessage,
  generateUserTransactionHash,
  Hex,
  InputGenerateTransactionPayloadData,
  Network,
  RAW_TRANSACTION_SALT,
  RAW_TRANSACTION_WITH_DATA_SALT,
  RawTransaction,
  SignedTransaction,
  SimpleTransaction,
  TransactionAuthenticatorFeePayer,
  TransactionPayload,
} from '@aptos-labs/ts-sdk';
import { DEFAULT_GAS_UNIT_PRICE, UNAVAILABLE_TEXT } from '../constants';
import utils from '../utils';
import BigNumber from 'bignumber.js';
import { AptTransactionExplanation, TxData } from '../iface';
import assert from 'assert';

export abstract class Transaction extends BaseTransaction {
  protected _rawTransaction: RawTransaction;
  protected _senderSignature: Signature;
  protected _feePayerSignature: Signature;
  protected _sender: string;
  protected _recipients: TransactionRecipient[];
  protected _sequenceNumber: number;
  protected _maxGasAmount: number;
  protected _gasUnitPrice: number;
  protected _gasUsed: number;
  protected _expirationTime: number;
  protected _feePayerAddress: string;
  protected _assetId: string;
  protected _isSimulateTxn: boolean;

  static EMPTY_PUBLIC_KEY = Buffer.alloc(32);
  static EMPTY_SIGNATURE = Buffer.alloc(64);

  constructor(coinConfig: Readonly<CoinConfig>) {
    super(coinConfig);
    this._maxGasAmount = DEFAULT_MAX_GAS_AMOUNT;
    this._gasUnitPrice = DEFAULT_GAS_UNIT_PRICE;
    this._gasUsed = 0;
    this._expirationTime = utils.getTxnExpirationTimestamp();
    this._sequenceNumber = 0;
    this._sender = AccountAddress.ZERO.toString();
    this._recipients = [];
    this._assetId = AccountAddress.ZERO.toString();
    this._isSimulateTxn = false;
    this._senderSignature = {
      publicKey: {
        pub: Hex.fromHexInput(Transaction.EMPTY_PUBLIC_KEY).toString(),
      },
      signature: Transaction.EMPTY_SIGNATURE,
    };
    this._feePayerAddress = AccountAddress.ZERO.toString();
    this._feePayerSignature = {
      publicKey: {
        pub: Hex.fromHexInput(Transaction.EMPTY_PUBLIC_KEY).toString(),
      },
      signature: Transaction.EMPTY_SIGNATURE,
    };
  }

  /** @inheritDoc **/
  public get id(): string {
    this.generateTxnId();
    return this._id ?? UNAVAILABLE_TEXT;
  }

  get sender(): string {
    return this._sender;
  }

  set sender(value: string) {
    this._sender = value;
  }

  /**
   * @deprecated - use `recipients()`.
   */
  get recipient(): TransactionRecipient {
    assert(this._recipients.length > 0, 'No recipients available');
    return this._recipients[0];
  }

  /**
   * @deprecated - use `recipients()`.
   */
  set recipient(value: TransactionRecipient) {
    this.recipients = [value];
  }

  get recipients(): TransactionRecipient[] {
    return this._recipients;
  }

  set recipients(value: TransactionRecipient[]) {
    this._recipients = value;
  }

  get sequenceNumber(): number {
    return this._sequenceNumber;
  }

  set sequenceNumber(value: number) {
    this._sequenceNumber = value;
  }

  get maxGasAmount(): number {
    return this._maxGasAmount;
  }

  set maxGasAmount(value: number) {
    this._maxGasAmount = value;
  }

  get gasUnitPrice(): number {
    return this._gasUnitPrice;
  }

  set gasUnitPrice(value: number) {
    this._gasUnitPrice = value;
  }

  get gasUsed(): number {
    return this._gasUsed;
  }

  set gasUsed(value: number) {
    this._gasUsed = value;
  }

  get expirationTime(): number {
    return this._expirationTime;
  }

  set expirationTime(value: number) {
    this._expirationTime = value;
  }

  get feePayerAddress(): string {
    return this._feePayerAddress;
  }

  set transactionType(transactionType: TransactionType) {
    this._type = transactionType;
  }

  get assetId(): string {
    return this._assetId;
  }

  set assetId(value: string) {
    this._assetId = value;
  }

  get isSimulateTxn(): boolean {
    return this._isSimulateTxn;
  }

  set isSimulateTxn(value: boolean) {
    this._isSimulateTxn = value;
  }

  protected abstract getTransactionPayloadData(): InputGenerateTransactionPayloadData;

  protected abstract parseTransactionPayload(payload: TransactionPayload): void;

  fromDeserializedSignedTransaction(signedTxn: SignedTransaction): void {
    try {
      const rawTxn = signedTxn.raw_txn;
      this.parseTransactionPayload(rawTxn.payload);
      this._sender = rawTxn.sender.toString();
      this._sequenceNumber = utils.castToNumber(rawTxn.sequence_number);
      this._maxGasAmount = utils.castToNumber(rawTxn.max_gas_amount);
      this._gasUnitPrice = utils.castToNumber(rawTxn.gas_unit_price);
      this._expirationTime = utils.castToNumber(rawTxn.expiration_timestamp_secs);
      this._rawTransaction = rawTxn;

      this.loadInputsAndOutputs();
      const authenticator = signedTxn.authenticator as TransactionAuthenticatorFeePayer;
      this._feePayerAddress = authenticator.fee_payer.address.toString();
      const senderAuthenticator = authenticator.sender as AccountAuthenticatorEd25519;
      const senderSignature = Buffer.from(senderAuthenticator.signature.toUint8Array());
      this.addSenderSignature({ pub: senderAuthenticator.public_key.toString() }, senderSignature);

      const feePayerAuthenticator = authenticator.fee_payer.authenticator as AccountAuthenticatorEd25519;
      const feePayerSignature = Buffer.from(feePayerAuthenticator.signature.toUint8Array());
      this.addFeePayerSignature(
        { pub: utils.stripHexPrefix(feePayerAuthenticator.public_key.toString()) },
        feePayerSignature
      );
    } catch (e) {
      console.error('invalid signed transaction', e);
      throw new Error('invalid signed transaction');
    }
  }

  canSign(_key: BaseKey): boolean {
    return false;
  }

  toBroadcastFormat(): string {
    if (!this._rawTransaction) {
      throw new InvalidTransactionError('Empty transaction');
    }
    return this.serialize();
  }

  serialize(): string {
    let senderAuthenticator: AccountAuthenticator;
    let feePayerAuthenticator: AccountAuthenticator;
    if (this.isSimulateTxn) {
      senderAuthenticator = new AccountAuthenticatorNoAccountAuthenticator();
      feePayerAuthenticator = new AccountAuthenticatorNoAccountAuthenticator();
    } else {
      const senderPublicKeyBuffer = utils.getBufferFromHexString(this._senderSignature.publicKey.pub);
      const senderPublicKey = new Ed25519PublicKey(senderPublicKeyBuffer);
      const senderSignature = new Ed25519Signature(this._senderSignature.signature);
      senderAuthenticator = new AccountAuthenticatorEd25519(senderPublicKey, senderSignature);

      const feePayerPublicKeyBuffer = utils.getBufferFromHexString(this._feePayerSignature.publicKey.pub);
      const feePayerPublicKey = new Ed25519PublicKey(feePayerPublicKeyBuffer);
      const feePayerSignature = new Ed25519Signature(this._feePayerSignature.signature);
      feePayerAuthenticator = new AccountAuthenticatorEd25519(feePayerPublicKey, feePayerSignature);
    }

    const txnAuthenticator = new TransactionAuthenticatorFeePayer(senderAuthenticator, [], [], {
      address: AccountAddress.fromString(this._feePayerAddress),
      authenticator: feePayerAuthenticator,
    });

    const signedTxn = new SignedTransaction(this._rawTransaction, txnAuthenticator);
    return signedTxn.toString();
  }

  addSenderSignature(publicKey: PublicKey, signature: Buffer): void {
    this._signatures = [signature.toString('hex')];
    this._senderSignature = { publicKey, signature };
  }

  getFeePayerPubKey(): string {
    return this._feePayerSignature.publicKey.pub;
  }

  addFeePayerSignature(publicKey: PublicKey, signature: Buffer): void {
    this._feePayerSignature = { publicKey, signature };
  }

  addFeePayerAddress(address: string): void {
    this._feePayerAddress = address;
  }

  async build(): Promise<void> {
    await this.buildRawTransaction();
    this.generateTxnId();
    this.loadInputsAndOutputs();
  }

  loadInputsAndOutputs(): void {
    const totalAmount = this._recipients.reduce(
      (accumulator, current) => accumulator.plus(current.amount),
      new BigNumber('0')
    );
    this._inputs = [
      {
        address: this.sender,
        value: totalAmount.toString(),
        coin: this._coinConfig.name,
      },
    ];
    this._outputs = this._recipients.map((recipient) => {
      return {
        address: recipient.address,
        value: recipient.amount as string,
        coin: this._coinConfig.name,
      };
    });
  }

  fromRawTransaction(rawTransaction: string): void {
    let signedTxn: SignedTransaction;
    try {
      signedTxn = utils.deserializeSignedTransaction(rawTransaction);
    } catch (e) {
      console.error('invalid raw transaction', e);
      throw new Error('invalid raw transaction');
    }
    this.fromDeserializedSignedTransaction(signedTxn);
  }
  /**
   * Deserializes a signed transaction hex string
   * @param {string} signedRawTransaction
   * @returns {SignedTransaction} the aptos signed transaction
   */
  static deserializeSignedTransaction(signedRawTransaction: string): SignedTransaction {
    try {
      return utils.deserializeSignedTransaction(signedRawTransaction);
    } catch (e) {
      console.error('invalid raw transaction', e);
      throw new Error('invalid raw transaction');
    }
  }

  toJson(): TxData {
    return {
      id: this.id,
      sender: this.sender,
      recipient: this.recipient,
      recipients: this.recipients,
      sequenceNumber: this.sequenceNumber,
      maxGasAmount: this.maxGasAmount,
      gasUnitPrice: this.gasUnitPrice,
      gasUsed: this.gasUsed,
      expirationTime: this.expirationTime,
      feePayer: this.feePayerAddress,
      assetId: this.assetId,
    };
  }

  public getFee(): string {
    return new BigNumber(this.gasUsed).multipliedBy(this.gasUnitPrice).toString();
  }

  public get signablePayload(): Buffer {
    return this.feePayerAddress ? this.getSignablePayloadWithFeePayer() : this.getSignablePayloadWithoutFeePayer();
  }

  /** @inheritDoc */
  explainTransaction(): AptTransactionExplanation {
    const displayOrder = [
      'id',
      'outputs',
      'outputAmount',
      'changeOutputs',
      'changeAmount',
      'fee',
      'withdrawAmount',
      'sender',
      'type',
    ];

    const outputs: TransactionRecipient[] = this._recipients;
    const outputAmount = outputs
      .reduce((accumulator, current) => accumulator.plus(current.amount), new BigNumber('0'))
      .toString();
    return {
      displayOrder,
      id: this.id,
      outputs,
      outputAmount,
      changeOutputs: [],
      changeAmount: '0',
      fee: { fee: this.getFee() },
      sender: this.sender,
      type: this.type,
    };
  }

  protected async buildRawTransaction(): Promise<void> {
    const network: Network = this._coinConfig.network.type === NetworkType.MAINNET ? Network.MAINNET : Network.TESTNET;
    const aptos = new Aptos(new AptosConfig({ network }));
    const senderAddress = AccountAddress.fromString(this._sender);

    const simpleTxn = await aptos.transaction.build.simple({
      sender: senderAddress,
      data: this.getTransactionPayloadData() as InputGenerateTransactionPayloadData,
      options: {
        maxGasAmount: this.maxGasAmount,
        gasUnitPrice: this.gasUnitPrice,
        expireTimestamp: this.expirationTime,
        accountSequenceNumber: this.sequenceNumber,
      },
    });
    this._rawTransaction = simpleTxn.rawTransaction;
  }

  private getSignablePayloadWithFeePayer(): Buffer {
    const feePayerRawTxn = new FeePayerRawTransaction(
      this._rawTransaction,
      [],
      AccountAddress.fromString(this._feePayerAddress)
    );
    return Buffer.from(generateSigningMessage(feePayerRawTxn.bcsToBytes(), RAW_TRANSACTION_WITH_DATA_SALT));
  }

  private getSignablePayloadWithoutFeePayer(): Buffer {
    return Buffer.from(generateSigningMessage(this._rawTransaction.bcsToBytes(), RAW_TRANSACTION_SALT));
  }

  private generateTxnId() {
    if (
      !this._senderSignature ||
      !this._senderSignature.publicKey ||
      !this._senderSignature.signature ||
      !this._feePayerSignature ||
      !this._feePayerSignature.publicKey ||
      !this._feePayerSignature.signature ||
      !this._feePayerAddress
    ) {
      return;
    }
    const transaction = new SimpleTransaction(this._rawTransaction, AccountAddress.fromString(this._feePayerAddress));
    const senderPublicKey = new Ed25519PublicKey(utils.getBufferFromHexString(this._senderSignature.publicKey.pub));
    const senderSignature = new Ed25519Signature(this._senderSignature.signature);
    const senderAuthenticator = new AccountAuthenticatorEd25519(senderPublicKey, senderSignature);
    const feePayerPublicKey = new Ed25519PublicKey(utils.getBufferFromHexString(this._feePayerSignature.publicKey.pub));
    const feePayerSignature = new Ed25519Signature(this._feePayerSignature.signature);
    const feePayerAuthenticator = new AccountAuthenticatorEd25519(feePayerPublicKey, feePayerSignature);
    this._id = generateUserTransactionHash({ transaction, senderAuthenticator, feePayerAuthenticator });
  }
}

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


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