Source: contr_operation.js

import {default as xdr} from "./generated/stellar-xdr_generated";
import {Keypair} from "./keypair";
import {hash} from "./hashing";
import {StrKey} from "./strkey";
import {Hyper} from "js-xdr";
import {Asset} from "./asset";
import BigNumber from 'bignumber.js';
import {best_r} from "./util/continued_fraction";
import trimEnd  from 'lodash/trimEnd';
import isUndefined from 'lodash/isUndefined';
import isString from 'lodash/isString';
import isNumber from 'lodash/isNumber';
import isFinite from 'lodash/isFinite';
import * as ops from './contr_operations/index';

const ONE = 10000000;
const MAX_INT64 = '9223372036854775807';

/**
 * `ContrOperation` class represents [contrOperations] in Stellar network.
 * Use one of static methods to create contract operations:
 * * thuannd start
* * `{@link ContrOperation.check}`
* * `{@link ContrOperation.invoke}`
* * `{@link ContrOperation.query}`
* * `{@link ContrOperation.transfer}`
 * * thuannd end
 *
 * @class ContrOperation
 */
export class ContrOperation {

  /**
   * Converts the XDR Contract Operation object to the opts object used to create the XDR
   * contract operation.
   * @param {xdr.ContrOperation} contrOperation - An XDR contract operation.
   * @return {ContrOperation}
   */
  static fromXDRObject(contrOperation) {
    function accountIdtoAddress(accountId) {
      return StrKey.encodeEd25519PublicKey(accountId.ed25519());
    }

    // thuannd start
    function contractIdtoAddress(contractId) {
      return StrKey.encodeContractKey(contractId.contract());
    }
    // thuannd end

    let result = {};

    let attrs = contrOperation.body().value();
    switch (contrOperation.body().switch().name) {
      case "check":
      result.type = "check";
      result.sourceAccId = accountIdtoAddress(attrs.sourceAccId());
      result.op = attrs.op().toString();
      result.amount = this._fromXDRAmount(attrs.amount());

      break;
      case "invoke":
      result.type = "invoke";
      result.newState = attrs.newState().toString();

      break;
      case "query":
      result.type = "query";
      result.data = attrs.data().toString();

      break;
      case "transfer":
      result.destination = accountIdtoAddress(attrs.destination());
      result.asset = Asset.fromOperation(attrs.asset());
      result.amount = this._fromXDRAmount(attrs.amount());

      break;
      default:
      throw new Error("Unknown contract opration");
    }
    return result;
  }

  static isValidAmount(value, allowZero = false) {
    if (!isString(value)) {
      return false;
    }

    let amount;
    try {
      amount = new BigNumber(value);
    } catch (e) {
      return false;
    }

    switch (true) {
      // == 0
      case !allowZero && amount.isZero():
      // < 0
      case amount.isNegative():
      // > Max value
      case amount.times(ONE).greaterThan(new BigNumber(MAX_INT64).toString()):
       // Decimal places (max 7)
      case amount.decimalPlaces() > 7:
      // NaN or Infinity
      case (amount.isNaN() || !amount.isFinite()):
        return false;
      default:
       return true;
    }
  }

  static constructAmountRequirementsError(arg) {
    return `${arg} argument must be of type String, represent a positive number and have at most 7 digits after the decimal`;
  }

  /**
   * Returns value converted to uint32 value or undefined.
   * If `value` is not `Number`, `String` or `Undefined` then throws an error.
   * Used in {@link Operation.setOptions}.
   * @private
   * @param {string} name Name of the property (used in error message only)
   * @param {*} value Value to check
   * @param {function(value, name)} isValidFunction Function to check other constraints (the argument will be a `Number`)
   * @returns {undefined|Number}
   * @private
   */
  static _checkUnsignedIntValue(name, value, isValidFunction = null) {
    if (isUndefined(value)) {
      return undefined;
    }

    if (isString(value)) {
      value = parseFloat(value);
    }

    switch (true) {
      case !isNumber(value) || !isFinite(value) || value % 1 !== 0:
        throw new Error(`${name} value is invalid`);
      case value < 0:
        throw new Error(`${name} value must be unsigned`);
      case !isValidFunction || (isValidFunction && isValidFunction(value, name)):
        return value;
      default:
        throw new Error(`${name} value is invalid`);
    }
  }

  /**
   * @private
   */
  static _toXDRAmount(value) {
    let amount = new BigNumber(value).mul(ONE);
    return Hyper.fromString(amount.toString());
  }

  /**
   * @private
   */
  static _fromXDRAmount(value) {
    return new BigNumber(value).div(ONE).toString();
  }

  /**
   * @private
   */
  static _fromXDRPrice(price) {
    let n = new BigNumber(price.n());
    return n.div(new BigNumber(price.d())).toString();
  }

  /**
   * @private
   */
  static _toXDRPrice(price) {
    let xdrObject;
    if (price.n && price.d) {
      xdrObject = new xdr.Price(price);
    } else {
      price = new BigNumber(price);
      let approx = best_r(price);
      xdrObject = new xdr.Price({
        n: parseInt(approx[0]),
        d: parseInt(approx[1])
      });
    }

    if (xdrObject.n() < 0 || xdrObject.d() < 0) {
      throw new Error('price must be positive');
    }

    return xdrObject;
  }
}

// Attach all imported operations as static methods on the Operation class
// thuannd start
ContrOperation.check = ops.check;
ContrOperation.invoke = ops.invoke;
ContrOperation.query = ops.query;
ContrOperation.transfer = ops.transfer;
// thuannd end