import {default as xdr} from "./generated/digitalbits-xdr_generated";
import {Keypair} from "./keypair";
import {UnsignedHyper, Hyper} from "js-xdr";
import {hash} from "./hashing";
import {StrKey} from "./strkey";
import {Asset} from "./asset";
import BigNumber from 'bignumber.js';
import {best_r} from "./util/continued_fraction";
import padEnd from 'lodash/padEnd';
import trimEnd from 'lodash/trimEnd';
import isEmpty from 'lodash/isEmpty';
import isUndefined from 'lodash/isUndefined';
import isString from 'lodash/isString';
import isNumber from 'lodash/isNumber';
import isFinite from 'lodash/isFinite';
const ONE = 10000000;
const MAX_INT64 = '9223372036854775807';
/**
* When set using `{@link Operation.setOptions}` option, requires the issuing account to
* give other accounts permission before they can hold the issuing account’s credit.
* @constant
* @see [Account flags](https://developer.digitalbits.io/guides/concepts/accounts.html#flags)
*/
export const AuthRequiredFlag = 1 << 0;
/**
* When set using `{@link Operation.setOptions}` option, allows the issuing account to
* revoke its credit held by other accounts.
* @constant
* @see [Account flags](https://developer.digitalbits.io/guides/concepts/accounts.html#flags)
*/
export const AuthRevocableFlag = 1 << 1;
/**
* When set using `{@link Operation.setOptions}` option, then none of the authorization flags
* can be set and the account can never be deleted.
* @constant
* @see [Account flags](https://developer.digitalbits.io/guides/concepts/accounts.html#flags)
*/
export const AuthImmutableFlag = 1 << 2;
/**
* `Operation` class represents [operations](https://developer.digitalbits.io/learn/concepts/operations.html) in DigitalBits network.
* Use one of static methods to create operations:
* * `{@link Operation.createAccount}`
* * `{@link Operation.payment}`
* * `{@link Operation.pathPayment}`
* * `{@link Operation.manageOffer}`
* * `{@link Operation.createPassiveOffer}`
* * `{@link Operation.setOptions}`
* * `{@link Operation.changeTrust}`
* * `{@link Operation.allowTrust}`
* * `{@link Operation.accountMerge}`
* * `{@link Operation.inflation}`
* * `{@link Operation.manageData}`
*
* @class Operation
*/
export class Operation {
/**
* Create and fund a non existent account.
* @param {object} opts
* @param {string} opts.destination - Destination account ID to create an account for.
* @param {string} opts.startingBalance - Amount in XLM the account should be funded for. Must be greater
* than the [reserve balance amount](https://developer.digitalbits.io/learn/concepts/fees.html).
* @param {string} [opts.source] - The source account for the payment. Defaults to the transaction's source account.
* @returns {xdr.CreateAccountOp}
*/
static createAccount(opts) {
if (!StrKey.isValidEd25519PublicKey(opts.destination)) {
throw new Error("destination is invalid");
}
if (!this.isValidAmount(opts.startingBalance)) {
throw new TypeError(Operation.constructAmountRequirementsError('startingBalance'));
}
let attributes = {};
attributes.destination = Keypair.fromPublicKey(opts.destination).xdrAccountId();
attributes.startingBalance = this._toXDRAmount(opts.startingBalance);
let createAccount = new xdr.CreateAccountOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.createAccount(createAccount);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* Create a payment operation.
* @param {object} opts
* @param {string} opts.destination - The destination account ID.
* @param {Asset} opts.asset - The asset to send.
* @param {string} opts.amount - The amount to send.
* @param {string} [opts.source] - The source account for the payment. Defaults to the transaction's source account.
* @returns {xdr.PaymentOp}
*/
static payment(opts) {
if (!StrKey.isValidEd25519PublicKey(opts.destination)) {
throw new Error("destination is invalid");
}
if (!opts.asset) {
throw new Error("Must provide an asset for a payment operation");
}
if (!this.isValidAmount(opts.amount)) {
throw new TypeError(Operation.constructAmountRequirementsError('amount'));
}
let attributes = {};
attributes.destination = Keypair.fromPublicKey(opts.destination).xdrAccountId();
attributes.asset = opts.asset.toXDRObject();
attributes.amount = this._toXDRAmount(opts.amount);
let payment = new xdr.PaymentOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.payment(payment);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* Returns a XDR PaymentOp. A "payment" operation send the specified amount to the
* destination account, optionally through a path. XLM payments create the destination
* account if it does not exist.
* @param {object} opts
* @param {Asset} opts.sendAsset - The asset to pay with.
* @param {string} opts.sendMax - The maximum amount of sendAsset to send.
* @param {string} opts.destination - The destination account to send to.
* @param {Asset} opts.destAsset - The asset the destination will receive.
* @param {string} opts.destAmount - The amount the destination receives.
* @param {Asset[]} opts.path - An array of Asset objects to use as the path.
* @param {string} [opts.source] - The source account for the payment. Defaults to the transaction's source account.
* @returns {xdr.PathPaymentOp}
*/
static pathPayment(opts) {
if (!opts.sendAsset) {
throw new Error("Must specify a send asset");
}
if (!this.isValidAmount(opts.sendMax)) {
throw new TypeError(Operation.constructAmountRequirementsError('sendMax'));
}
if (!StrKey.isValidEd25519PublicKey(opts.destination)) {
throw new Error("destination is invalid");
}
if (!opts.destAsset) {
throw new Error("Must provide a destAsset for a payment operation");
}
if (!this.isValidAmount(opts.destAmount)) {
throw new TypeError(Operation.constructAmountRequirementsError('destAmount'));
}
let attributes = {};
attributes.sendAsset = opts.sendAsset.toXDRObject();
attributes.sendMax = this._toXDRAmount(opts.sendMax);
attributes.destination = Keypair.fromPublicKey(opts.destination).xdrAccountId();
attributes.destAsset = opts.destAsset.toXDRObject();
attributes.destAmount = this._toXDRAmount(opts.destAmount);
let path = opts.path ? opts.path : [];
attributes.path = [];
for (let i in path) {
attributes.path.push(path[i].toXDRObject());
}
let payment = new xdr.PathPaymentOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.pathPayment(payment);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* Returns an XDR ChangeTrustOp. A "change trust" operation adds, removes, or updates a
* trust line for a given asset from the source account to another. The issuer being
* trusted and the asset code are in the given Asset object.
* @param {object} opts
* @param {Asset} opts.asset - The asset for the trust line.
* @param {string} [opts.limit] - The limit for the asset, defaults to max int64.
* If the limit is set to "0" it deletes the trustline.
* @param {string} [opts.source] - The source account (defaults to transaction source).
* @returns {xdr.ChangeTrustOp}
*/
static changeTrust(opts) {
let attributes = {};
attributes.line = opts.asset.toXDRObject();
if (!isUndefined(opts.limit) && !this.isValidAmount(opts.limit, true)) {
throw new TypeError(Operation.constructAmountRequirementsError('limit'));
}
if (opts.limit) {
attributes.limit = this._toXDRAmount(opts.limit);
} else {
attributes.limit = Hyper.fromString(new BigNumber(MAX_INT64).toString());
}
if (opts.source) {
attributes.source = opts.source ? opts.source.masterKeypair : null;
}
let changeTrustOP = new xdr.ChangeTrustOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.changeTrust(changeTrustOP);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* Returns an XDR AllowTrustOp. An "allow trust" operation authorizes another
* account to hold your account's credit for a given asset.
* @param {object} opts
* @param {string} opts.trustor - The trusting account (the one being authorized)
* @param {string} opts.assetCode - The asset code being authorized.
* @param {boolean} opts.authorize - True to authorize the line, false to deauthorize.
* @param {string} [opts.source] - The source account (defaults to transaction source).
* @returns {xdr.AllowTrustOp}
*/
static allowTrust(opts) {
if (!StrKey.isValidEd25519PublicKey(opts.trustor)) {
throw new Error("trustor is invalid");
}
let attributes = {};
attributes.trustor = Keypair.fromPublicKey(opts.trustor).xdrAccountId();
if (opts.assetCode.length <= 4) {
let code = padEnd(opts.assetCode, 4, '\0');
attributes.asset = xdr.AllowTrustOpAsset.assetTypeCreditAlphanum4(code);
} else if (opts.assetCode.length <= 12) {
let code = padEnd(opts.assetCode, 12, '\0');
attributes.asset = xdr.AllowTrustOpAsset.assetTypeCreditAlphanum12(code);
} else {
throw new Error("Asset code must be 12 characters at max.");
}
attributes.authorize = opts.authorize;
let allowTrustOp = new xdr.AllowTrustOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.allowTrust(allowTrustOp);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* Returns an XDR SetOptionsOp. A "set options" operations set or clear account flags,
* set the account's inflation destination, and/or add new signers to the account.
* The flags used in `opts.clearFlags` and `opts.setFlags` can be the following:
* - `{@link AuthRequiredFlag}`
* - `{@link AuthRevocableFlag}`
* - `{@link AuthImmutableFlag}`
*
* It's possible to set/clear multiple flags at once using logical or.
* @param {object} opts
* @param {string} [opts.inflationDest] - Set this account ID as the account's inflation destination.
* @param {(number|string)} [opts.clearFlags] - Bitmap integer for which account flags to clear.
* @param {(number|string)} [opts.setFlags] - Bitmap integer for which account flags to set.
* @param {number|string} [opts.masterWeight] - The master key weight.
* @param {number|string} [opts.lowThreshold] - The sum weight for the low threshold.
* @param {number|string} [opts.medThreshold] - The sum weight for the medium threshold.
* @param {number|string} [opts.highThreshold] - The sum weight for the high threshold.
* @param {object} [opts.signer] - Add or remove a signer from the account. The signer is
* deleted if the weight is 0. Only one of `ed25519PublicKey`, `sha256Hash`, `preAuthTx` should be defined.
* @param {string} [opts.signer.ed25519PublicKey] - The ed25519 public key of the signer.
* @param {Buffer|string} [opts.signer.sha256Hash] - sha256 hash (Buffer or hex string) of preimage that will unlock funds. Preimage should be used as signature of future transaction.
* @param {Buffer|string} [opts.signer.preAuthTx] - Hash (Buffer or hex string) of transaction that will unlock funds.
* @param {number|string} [opts.signer.weight] - The weight of the new signer (0 to delete or 1-255)
* @param {string} [opts.homeDomain] - sets the home domain used for reverse federation lookup.
* @param {string} [opts.source] - The source account (defaults to transaction source).
* @returns {xdr.SetOptionsOp}
* @see [Account flags](https://developer.digitalbits.io/guides/concepts/accounts.html#flags)
*/
static setOptions(opts) {
let attributes = {};
if (opts.inflationDest) {
if (!StrKey.isValidEd25519PublicKey(opts.inflationDest)) {
throw new Error("inflationDest is invalid");
}
attributes.inflationDest = Keypair.fromPublicKey(opts.inflationDest).xdrAccountId();
}
let weightCheckFunction = (value, name) => {
if (value >= 0 && value <= 255) {
return true;
} else {
throw new Error(`${name} value must be between 0 and 255`);
}
};
attributes.clearFlags = this._checkUnsignedIntValue("clearFlags", opts.clearFlags);
attributes.setFlags = this._checkUnsignedIntValue("setFlags", opts.setFlags);
attributes.masterWeight = this._checkUnsignedIntValue("masterWeight", opts.masterWeight, weightCheckFunction);
attributes.lowThreshold = this._checkUnsignedIntValue("lowThreshold", opts.lowThreshold, weightCheckFunction);
attributes.medThreshold = this._checkUnsignedIntValue("medThreshold", opts.medThreshold, weightCheckFunction);
attributes.highThreshold = this._checkUnsignedIntValue("highThreshold", opts.highThreshold, weightCheckFunction);
if (!isUndefined(opts.homeDomain) && !isString(opts.homeDomain)) {
throw new TypeError('homeDomain argument must be of type String');
}
attributes.homeDomain = opts.homeDomain;
if (opts.signer) {
let weight = this._checkUnsignedIntValue("signer.weight", opts.signer.weight, weightCheckFunction);
let key;
let setValues = 0;
if (opts.signer.ed25519PublicKey) {
if (!StrKey.isValidEd25519PublicKey(opts.signer.ed25519PublicKey)) {
throw new Error("signer.ed25519PublicKey is invalid.");
}
let rawKey = StrKey.decodeEd25519PublicKey(opts.signer.ed25519PublicKey);
key = new xdr.SignerKey.signerKeyTypeEd25519(rawKey);
setValues++;
}
if (opts.signer.preAuthTx) {
if (isString(opts.signer.preAuthTx)) {
opts.signer.preAuthTx = Buffer.from(opts.signer.preAuthTx, "hex");
}
if (!(Buffer.isBuffer(opts.signer.preAuthTx) && opts.signer.preAuthTx.length == 32)) {
throw new Error("signer.preAuthTx must be 32 bytes Buffer.");
}
key = new xdr.SignerKey.signerKeyTypePreAuthTx(opts.signer.preAuthTx);
setValues++;
}
if (opts.signer.sha256Hash) {
if (isString(opts.signer.sha256Hash)) {
opts.signer.sha256Hash = Buffer.from(opts.signer.sha256Hash, "hex");
}
if (!(Buffer.isBuffer(opts.signer.sha256Hash) && opts.signer.sha256Hash.length == 32)) {
throw new Error("signer.sha256Hash must be 32 bytes Buffer.");
}
key = new xdr.SignerKey.signerKeyTypeHashX(opts.signer.sha256Hash);
setValues++;
}
if (setValues != 1) {
throw new Error("Signer object must contain exactly one of signer.ed25519PublicKey, signer.sha256Hash, signer.preAuthTx.");
}
attributes.signer = new xdr.Signer({key, weight});
}
let setOptionsOp = new xdr.SetOptionsOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.setOption(setOptionsOp);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* Returns a XDR ManageOfferOp. A "manage offer" operation creates, updates, or
* deletes an offer.
* @param {object} opts
* @param {Asset} opts.selling - What you're selling.
* @param {Asset} opts.buying - What you're buying.
* @param {string} opts.amount - The total amount you're selling. If 0, deletes the offer.
* @param {number|string|BigNumber|Object} opts.price - Price of 1 unit of `selling` in terms of `buying`.
* @param {number} opts.price.n - If `opts.price` is an object: the price numerator
* @param {number} opts.price.d - If `opts.price` is an object: the price denominator
* @param {number|string} [opts.offerId ]- If `0`, will create a new offer (default). Otherwise, edits an exisiting offer.
* @param {string} [opts.source] - The source account (defaults to transaction source).
* @throws {Error} Throws `Error` when the best rational approximation of `price` cannot be found.
* @returns {xdr.ManageOfferOp}
*/
static manageOffer(opts) {
let attributes = {};
attributes.selling = opts.selling.toXDRObject();
attributes.buying = opts.buying.toXDRObject();
if (!this.isValidAmount(opts.amount, true)) {
throw new TypeError(Operation.constructAmountRequirementsError('amount'));
}
attributes.amount = this._toXDRAmount(opts.amount);
if (isUndefined(opts.price)) {
throw new TypeError('price argument is required');
}
attributes.price = this._toXDRPrice(opts.price);
if (!isUndefined(opts.offerId)) {
opts.offerId = opts.offerId.toString();
} else {
opts.offerId = '0';
}
attributes.offerId = UnsignedHyper.fromString(opts.offerId);
let manageOfferOp = new xdr.ManageOfferOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.manageOffer(manageOfferOp);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* Returns a XDR CreatePasiveOfferOp. A "create passive offer" operation creates an
* offer that won't consume a counter offer that exactly matches this offer. This is
* useful for offers just used as 1:1 exchanges for path payments. Use manage offer
* to manage this offer after using this operation to create it.
* @param {object} opts
* @param {Asset} opts.selling - What you're selling.
* @param {Asset} opts.buying - What you're buying.
* @param {string} opts.amount - The total amount you're selling. If 0, deletes the offer.
* @param {number|string|BigNumber|Object} opts.price - Price of 1 unit of `selling` in terms of `buying`.
* @param {number} opts.price.n - If `opts.price` is an object: the price numerator
* @param {number} opts.price.d - If `opts.price` is an object: the price denominator
* @param {string} [opts.source] - The source account (defaults to transaction source).
* @throws {Error} Throws `Error` when the best rational approximation of `price` cannot be found.
* @returns {xdr.CreatePassiveOfferOp}
*/
static createPassiveOffer(opts) {
let attributes = {};
attributes.selling = opts.selling.toXDRObject();
attributes.buying = opts.buying.toXDRObject();
if (!this.isValidAmount(opts.amount)) {
throw new TypeError(Operation.constructAmountRequirementsError('amount'));
}
attributes.amount = this._toXDRAmount(opts.amount);
if (isUndefined(opts.price)) {
throw new TypeError('price argument is required');
}
attributes.price = this._toXDRPrice(opts.price);
let createPassiveOfferOp = new xdr.CreatePassiveOfferOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.createPassiveOffer(createPassiveOfferOp);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* Transfers native balance to destination account.
* @param {object} opts
* @param {string} opts.destination - Destination to merge the source account into.
* @param {string} [opts.source] - The source account (defaults to transaction source).
* @returns {xdr.AccountMergeOp}
*/
static accountMerge(opts) {
let opAttributes = {};
if (!StrKey.isValidEd25519PublicKey(opts.destination)) {
throw new Error("destination is invalid");
}
opAttributes.body = xdr.OperationBody.accountMerge(
Keypair.fromPublicKey(opts.destination).xdrAccountId()
);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* This operation generates the inflation.
* @param {object} [opts]
* @param {string} [opts.source] - The optional source account.
* @returns {xdr.InflationOp}
*/
static inflation(opts={}) {
let opAttributes = {};
opAttributes.body = xdr.OperationBody.inflation();
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
/**
* This operation adds data entry to the ledger.
* @param {object} opts
* @param {string} opts.name - The name of the data entry.
* @param {string|Buffer} opts.value - The value of the data entry.
* @param {string} [opts.source] - The optional source account.
* @returns {xdr.ManageDataOp}
*/
static manageData(opts) {
let attributes = {};
if (!(isString(opts.name) && opts.name.length <= 64)) {
throw new Error("name must be a string, up to 64 characters");
}
attributes.dataName = opts.name;
if (!isString(opts.value) && !Buffer.isBuffer(opts.value) && opts.value !== null) {
throw new Error("value must be a string, Buffer or null");
}
if (isString(opts.value)) {
attributes.dataValue = new Buffer(opts.value);
} else {
attributes.dataValue = opts.value;
}
if (attributes.dataValue !== null && attributes.dataValue.length > 64) {
throw new Error("value cannot be longer that 64 bytes");
}
let manageDataOp = new xdr.ManageDataOp(attributes);
let opAttributes = {};
opAttributes.body = xdr.OperationBody.manageDatum(manageDataOp);
this.setSourceAccount(opAttributes, opts);
return new xdr.Operation(opAttributes);
}
static setSourceAccount(opAttributes, opts) {
if (opts.source) {
if (!StrKey.isValidEd25519PublicKey(opts.source)) {
throw new Error("Source address is invalid");
}
opAttributes.sourceAccount = Keypair.fromPublicKey(opts.source).xdrAccountId();
}
}
/**
* Converts the XDR Operation object to the opts object used to create the XDR
* operation.
* @param {xdr.Operation} operation - An XDR Operation.
* @return {Operation}
*/
static fromXDRObject(operation) {
function accountIdtoAddress(accountId) {
return StrKey.encodeEd25519PublicKey(accountId.ed25519());
}
let result = {};
if (operation.sourceAccount()) {
result.source = accountIdtoAddress(operation.sourceAccount());
}
let attrs = operation.body().value();
switch (operation.body().switch().name) {
case "createAccount":
result.type = "createAccount";
result.destination = accountIdtoAddress(attrs.destination());
result.startingBalance = this._fromXDRAmount(attrs.startingBalance());
break;
case "payment":
result.type = "payment";
result.destination = accountIdtoAddress(attrs.destination());
result.asset = Asset.fromOperation(attrs.asset());
result.amount = this._fromXDRAmount(attrs.amount());
break;
case "pathPayment":
result.type = "pathPayment";
result.sendAsset = Asset.fromOperation(attrs.sendAsset());
result.sendMax = this._fromXDRAmount(attrs.sendMax());
result.destination = accountIdtoAddress(attrs.destination());
result.destAsset = Asset.fromOperation(attrs.destAsset());
result.destAmount = this._fromXDRAmount(attrs.destAmount());
let path = attrs.path();
result.path = [];
for (let i in path) {
result.path.push(Asset.fromOperation(path[i]));
}
break;
case "changeTrust":
result.type = "changeTrust";
result.line = Asset.fromOperation(attrs.line());
result.limit = this._fromXDRAmount(attrs.limit());
break;
case "allowTrust":
result.type = "allowTrust";
result.trustor = accountIdtoAddress(attrs.trustor());
result.assetCode = attrs.asset().value().toString();
result.assetCode = trimEnd(result.assetCode, "\0");
result.authorize = attrs.authorize();
break;
case "setOption":
result.type = "setOptions";
if (attrs.inflationDest()) {
result.inflationDest = accountIdtoAddress(attrs.inflationDest());
}
result.clearFlags = attrs.clearFlags();
result.setFlags = attrs.setFlags();
result.masterWeight = attrs.masterWeight();
result.lowThreshold = attrs.lowThreshold();
result.medThreshold = attrs.medThreshold();
result.highThreshold = attrs.highThreshold();
result.homeDomain = attrs.homeDomain();
if (attrs.signer()) {
let signer = {};
let arm = attrs.signer().key().arm();
if (arm == "ed25519") {
signer.ed25519PublicKey = accountIdtoAddress(attrs.signer().key());
} else if (arm == "preAuthTx") {
signer.preAuthTx = attrs.signer().key().preAuthTx();
} else if (arm == "hashX") {
signer.sha256Hash = attrs.signer().key().hashX();
}
signer.weight = attrs.signer().weight();
result.signer = signer;
}
break;
case "manageOffer":
result.type = "manageOffer";
result.selling = Asset.fromOperation(attrs.selling());
result.buying = Asset.fromOperation(attrs.buying());
result.amount = this._fromXDRAmount(attrs.amount());
result.price = this._fromXDRPrice(attrs.price());
result.offerId = attrs.offerId().toString();
break;
case "createPassiveOffer":
result.type = "createPassiveOffer";
result.selling = Asset.fromOperation(attrs.selling());
result.buying = Asset.fromOperation(attrs.buying());
result.amount = this._fromXDRAmount(attrs.amount());
result.price = this._fromXDRPrice(attrs.price());
break;
case "accountMerge":
result.type = "accountMerge";
result.destination = accountIdtoAddress(attrs);
break;
case "manageDatum":
result.type = "manageData";
result.name = attrs.dataName();
result.value = attrs.dataValue();
break;
case "inflation":
result.type = "inflation";
break;
default:
throw new Error("Unknown operation");
}
return result;
}
static isValidAmount(value, allowZero = false) {
if (!isString(value)) {
return false;
}
let amount;
try {
amount = new BigNumber(value);
} catch (e) {
return false;
}
// == 0
if (!allowZero && amount.isZero()) {
return false;
}
// < 0
if (amount.isNegative()) {
return false;
}
// > Max value
if (amount.times(ONE).greaterThan(new BigNumber(MAX_INT64).toString())) {
return false;
}
// Decimal places (max 7)
if (amount.decimalPlaces() > 7) {
return false;
}
// Infinity
if (!amount.isFinite()) {
return false;
}
// NaN
if (amount.isNaN()) {
return false;
}
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);
}
if (!isNumber(value) || !isFinite(value) || value % 1 !== 0) {
throw new Error(`${name} value is invalid`);
}
if (value < 0) {
throw new Error(`${name} value must be unsigned`);
}
if (!isValidFunction ||
(isValidFunction && isValidFunction(value, name))) {
return value;
}
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;
}
}