Change this._value to be an instance of IOU or XRP Value

This commit is contained in:
Madeline Shortt
2015-07-02 17:53:33 -07:00
parent 13e9ad45f9
commit 6bffe06c3b
8 changed files with 186 additions and 171 deletions

View File

@@ -3,6 +3,7 @@
'use strict'; 'use strict';
const Value = require('./value').Value; const Value = require('./value').Value;
const XRPValue = require('./XRPValue').XRPValue;
const GlobalBigNumber = require('bignumber.js'); const GlobalBigNumber = require('bignumber.js');
const BigNumber = GlobalBigNumber.another({ const BigNumber = GlobalBigNumber.another({
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP, ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
@@ -11,33 +12,44 @@ const BigNumber = GlobalBigNumber.another({
class IOUValue extends Value { class IOUValue extends Value {
constructor(value: string | BigNumber) { constructor(value: string | BigNumber, roundingMode: ?number = null,
super(value); base: ?number = null) {
super(new BigNumber(value, base).toDigits(16, roundingMode));
} }
multiplyByXRP(multiplicand: {_value: BigNumber, _bi_xns_unit: number}) { multiply(multiplicand: Value) {
const constant = new BigNumber((multiplicand._bi_xns_unit).toString()); if (multiplicand instanceof XRPValue) {
const value = new BigNumber(multiplicand._value); return super.multiply(
multiplicand._value = (value).times(constant); new IOUValue(
multiplicand._value.times(multiplicand.rippleUnits)));
}
return super.multiply(multiplicand); return super.multiply(multiplicand);
} }
divideByXRP(divisor: {_value: BigNumber, _bi_xns_unit: number}) { divide(divisor: Value) {
const constant = new BigNumber((divisor._bi_xns_unit).toString()); if (divisor instanceof XRPValue) {
const value = new BigNumber(divisor._value); return super.divide(
divisor._value = (value).times(constant); new IOUValue(divisor._value.times(divisor.rippleUnits)));
}
return super.divide(divisor); return super.divide(divisor);
} }
negate() {
return new IOUValue(this._value.neg());
}
_canonicalize(value) { _canonicalize(value) {
if (value.isNaN()) {
throw new Error('Invalid result');
}
return new IOUValue(value.toPrecision(16)); return new IOUValue(value.toPrecision(16));
} }
equals(comparator) { equals(comparator) {
return (comparator instanceof IOUValue) return (comparator instanceof IOUValue)
&& this._value.equals(comparator.value); && this._value.equals(comparator._value);
} }
} }
exports.IOUValue = IOUValue; exports.IOUValue = IOUValue;

View File

@@ -1,3 +1,5 @@
/* @flow */
'use strict'; 'use strict';
const GlobalBigNumber = require('bignumber.js'); const GlobalBigNumber = require('bignumber.js');
@@ -10,20 +12,48 @@ const Value = require('./value').Value;
class XRPValue extends Value { class XRPValue extends Value {
constructor(value, bi_xns_unit) { constructor(value: string | BigNumber) {
super(value); super(value);
this._bi_xns_unit = bi_xns_unit; this.rippleUnits = new BigNumber(1e6);
if (this._value.dp() > 6) {
throw new Error(
'Value has more than 6 digits of precision past the decimal point, '
+ 'an IOUValue may be being cast to an XRPValue'
);
}
}
multiply(multiplicand: Value) {
if (multiplicand instanceof XRPValue) {
return super.multiply(
new XRPValue(multiplicand._value.times(multiplicand.rippleUnits)));
}
return super.multiply(multiplicand);
}
divide(divisor: Value) {
if (divisor instanceof XRPValue) {
return super.divide(
new XRPValue(divisor._value.times(divisor.rippleUnits)));
}
return super.divide(divisor);
}
negate() {
return new XRPValue(this._value.neg());
} }
_canonicalize(value) { _canonicalize(value) {
if (value.isNaN()) {
throw new Error('Invalid result');
}
return new XRPValue(value.round(6, BigNumber.ROUND_DOWN)); return new XRPValue(value.round(6, BigNumber.ROUND_DOWN));
} }
equals(comparator) { equals(comparator) {
return (comparator instanceof XRPValue) return (comparator instanceof XRPValue)
&& this._value.equals(comparator.value); && this._value.equals(comparator._value);
} }
} }
exports.XRPValue = XRPValue; exports.XRPValue = XRPValue;

View File

@@ -13,17 +13,13 @@ const Value = require('./value').Value;
const IOUValue = require('./IOUValue').IOUValue; const IOUValue = require('./IOUValue').IOUValue;
const XRPValue = require('./XRPValue').XRPValue; const XRPValue = require('./XRPValue').XRPValue;
function Amount(value = new XRPValue(NaN)) {
function inverse(number) {
return new IOUValue(number).invert()._value;
}
function Amount() {
// Json format: // Json format:
// integer : XRP // integer : XRP
// { 'value' : ..., 'currency' : ..., 'issuer' : ...} // { 'value' : ..., 'currency' : ..., 'issuer' : ...}
assert(value instanceof Value);
this._value = new Value(NaN)._value; this._value = value;
this._is_native = true; // Default to XRP. Only valid if value is not NaN. this._is_native = true; // Default to XRP. Only valid if value is not NaN.
this._currency = new Currency(); this._currency = new Currency();
this._issuer = new UInt160(); this._issuer = new UInt160();
@@ -41,19 +37,11 @@ const consts = {
xns_precision: 6, xns_precision: 6,
// bi_ prefix refers to "big integer" // bi_ prefix refers to "big integer"
// TODO: we shouldn't expose our BigNumber library publicly // man refers to mantissa
bi_5: new Value(5)._value, bi_man_max_value: '9999999999999999',
bi_7: new Value(7)._value, bi_man_min_value: Number(1e15).toString(),
bi_10: new Value(10)._value, bi_xns_max: Number(1e17).toString(),
bi_1e14: new Value(1e14)._value, bi_xns_min: Number(-1e17).toString(),
bi_1e16: new Value(1e16)._value,
bi_1e17: new Value(1e17)._value,
bi_1e32: new Value(1e32)._value,
bi_man_max_value: new Value('9999999999999999')._value,
bi_man_min_value: new Value(1e15)._value,
bi_xns_max: new Value(1e17)._value,
bi_xns_min: new Value(-1e17)._value,
bi_xns_unit: new Value(1e6)._value,
cMinOffset: -96, cMinOffset: -96,
cMaxOffset: 80, cMaxOffset: 80,
@@ -65,9 +53,11 @@ const consts = {
min_value: '-1000000000000000e-96' min_value: '-1000000000000000e-96'
}; };
const MAX_XRP_VALUE = new Value(1e11)._value; const MAX_XRP_VALUE = new XRPValue(1e11);
const MAX_IOU_VALUE = new Value(consts.max_value)._value; const MAX_IOU_VALUE = new IOUValue(consts.max_value);
const MIN_IOU_VALUE = new Value(consts.min_value)._value.abs(); const MIN_IOU_VALUE = new IOUValue(consts.min_value).abs();
const bi_xns_unit = new IOUValue(1e6);
// Add constants to Amount class // Add constants to Amount class
extend(Amount, consts); extend(Amount, consts);
@@ -111,21 +101,15 @@ Amount.is_valid_full = function(j) {
Amount.NaN = function() { Amount.NaN = function() {
const result = new Amount(); const result = new Amount();
result._value = new Value(NaN)._value; // should have no effect result._value = new IOUValue(NaN); // should have no effect
return result; // but let's be careful return result; // but let's be careful
}; };
// be sure that _is_native is set properly BEFORE calling _set_value // be sure that _is_native is set properly BEFORE calling _set_value
Amount.prototype._set_value = function(value, roundingMode) { Amount.prototype._set_value = function(value: Value) {
assert(value instanceof Object);
this._value = this._value = value.isZero() && value.isNegative() ? this._value = value.isZero() && value.isNegative() ?
value.negated()._value : value._value; value.negate() : value;
if (!this._value) {
this._value = value.isZero() && value.isNegative() ?
value.negated() : value;
}
this.canonicalize(roundingMode);
this._check_limits(); this._check_limits();
}; };
@@ -133,8 +117,7 @@ Amount.prototype._set_value = function(value, roundingMode) {
// Returns a new value which is the absolute value of this. // Returns a new value which is the absolute value of this.
Amount.prototype.abs = function() { Amount.prototype.abs = function() {
const val = (new IOUValue(this._value)).abs(); return this._copy(this._value.abs());
return this._copy(val);
}; };
@@ -142,12 +125,10 @@ Amount.prototype.add = function(addend) {
const addendAmount = Amount.from_json(addend); const addendAmount = Amount.from_json(addend);
if (!this.is_comparable(addendAmount)) { if (!this.is_comparable(addendAmount)) {
return new Amount(NaN); return new Amount();
} }
const thisValue = new IOUValue(this._value); return this._copy(this._value.add(addendAmount._value));
const addendValue = new IOUValue(addendAmount._value);
return this._copy(thisValue.add(addendValue));
}; };
@@ -160,17 +141,8 @@ Amount.prototype.subtract = function(subtrahend) {
Amount.prototype.multiply = function(multiplicand) { Amount.prototype.multiply = function(multiplicand) {
const multiplicandAmount = Amount.from_json(multiplicand); const multiplicandAmount = Amount.from_json(multiplicand);
const thisValue = new IOUValue(this._value);
let multiplyBy; return this._copy(this._value.multiply(multiplicandAmount._value));
if (multiplicandAmount.is_native()) {
multiplyBy = new XRPValue(multiplicandAmount._value, Amount.bi_xns_unit);
return this._copy(thisValue.multiplyByXRP(multiplyBy)._value);
}
multiplyBy = new IOUValue(multiplicandAmount._value);
return this._copy(thisValue.multiply(multiplyBy));
}; };
@@ -180,17 +152,8 @@ Amount.prototype.scale = function(scaleFactor) {
Amount.prototype.divide = function(divisor) { Amount.prototype.divide = function(divisor) {
const divisorAmount = Amount.from_json(divisor); const divisorAmount = Amount.from_json(divisor);
const thisValue = new IOUValue(this._value);
let divideBy; return this._copy(this._value.divide(divisorAmount._value));
if (divisorAmount.is_native()) {
divideBy = new XRPValue(divisorAmount._value, Amount.bi_xns_unit);
return this._copy(thisValue.divideByXRP(divideBy)._value);
}
divideBy = new IOUValue(divisorAmount._value);
return this._copy(thisValue.divide(divideBy));
}; };
/** /**
@@ -248,7 +211,7 @@ Amount.prototype.ratio_human = function(denom, opts) {
// //
// To compensate, we multiply the numerator by 10^xns_precision. // To compensate, we multiply the numerator by 10^xns_precision.
if (denominator._is_native) { if (denominator._is_native) {
numerator._set_value(numerator.multiply(Amount.bi_xns_unit)); numerator._set_value(numerator.multiply(bi_xns_unit));
} }
return numerator.divide(denominator); return numerator.divide(denominator);
@@ -268,20 +231,20 @@ Amount.prototype.ratio_human = function(denom, opts) {
* *
* @see Amount#ratio_human * @see Amount#ratio_human
* *
* @param {Amount} fac The second factor of the product. * @param {Amount} factor The second factor of the product.
* @param {Object} opts Options for the calculation. * @param {Object} opts Options for the calculation.
* @param {Date|Number} opts.reference_date Date based on which * @param {Date|Number} opts.reference_date Date based on which
* demurrage/interest should be applied. Can be given as JavaScript Date or int * demurrage/interest should be applied. Can be given as JavaScript Date or int
* for Ripple epoch. * for Ripple epoch.
* @return {Amount} The product. Unit will be the same as the first factor. * @return {Amount} The product. Unit will be the same as the first factor.
*/ */
Amount.prototype.product_human = function(fac, options = {}) { Amount.prototype.product_human = function(factor, options = {}) {
let factor = Amount.from_json(fac); let fac = Amount.from_json(factor);
// If either operand is NaN, the result is NaN. // If either operand is NaN, the result is NaN.
if (!this.is_valid() || !factor.is_valid()) { if (!this.is_valid() || !fac.is_valid()) {
return new Amount(NaN); return new Amount();
} }
// Apply interest/demurrage // Apply interest/demurrage
@@ -289,17 +252,17 @@ Amount.prototype.product_human = function(fac, options = {}) {
// We only need to apply it to the second factor, because the currency unit of // We only need to apply it to the second factor, because the currency unit of
// the first factor will carry over into the result. // the first factor will carry over into the result.
if (options.reference_date) { if (options.reference_date) {
factor = factor.applyInterest(options.reference_date); fac = fac.applyInterest(options.reference_date);
} }
const product = this.multiply(factor); const product = this.multiply(fac);
// Special case: The second factor is a native (XRP) amount expressed as base // Special case: The second factor is a native (XRP) amount expressed as base
// units (1 XRP = 10^xns_precision base units). // units (1 XRP = 10^xns_precision base units).
// //
// See also Amount#ratio_human. // See also Amount#ratio_human.
if (factor._is_native) { if (fac._is_native) {
const quotient = product.divide(Amount.bi_xns_unit.toString()); const quotient = product.divide(bi_xns_unit.toString());
product._set_value(quotient._value); product._set_value(quotient._value);
} }
@@ -313,7 +276,7 @@ Amount.prototype.product_human = function(fac, options = {}) {
* @private * @private
*/ */
Amount.prototype._invert = function() { Amount.prototype._invert = function() {
this._set_value(inverse(this._value)); this._set_value(this._value.invert());
return this; return this;
}; };
@@ -328,7 +291,7 @@ Amount.prototype.invert = function() {
}; };
/** /**
* Canonicalize amount value * Canonicalize amount value is now taken care of in the Value classes
* *
* Mirrors rippled's internal Amount representation * Mirrors rippled's internal Amount representation
* From https://github.com/ripple/rippled/blob/develop/src/ripple/data * From https://github.com/ripple/rippled/blob/develop/src/ripple/data
@@ -357,19 +320,6 @@ Amount.prototype.invert = function() {
* bigger than supported * bigger than supported
*/ */
Amount.prototype.canonicalize = function(roundingMode) {
if (typeof this._value === 'undefined') {
throw new Error('undefined value');
}
if (this._is_native) {
this._value = this._value.round(6, Value.getBNRoundDown());
} else if (roundingMode) {
this._value = new Value(this._value.toPrecision(16, roundingMode))._value;
} else {
this._value = new Value(this._value.toPrecision(16))._value;
}
};
Amount.prototype._check_limits = function() { Amount.prototype._check_limits = function() {
if (!Amount.strict_mode) { if (!Amount.strict_mode) {
return this; return this;
@@ -377,7 +327,7 @@ Amount.prototype._check_limits = function() {
if (this._value.isNaN() || this._value.isZero()) { if (this._value.isNaN() || this._value.isZero()) {
return this; return this;
} }
const absval = this._value.absoluteValue(); const absval = this._value.abs();
if (this._is_native) { if (this._is_native) {
if (absval.greaterThan(MAX_XRP_VALUE)) { if (absval.greaterThan(MAX_XRP_VALUE)) {
throw new Error('Exceeding max value of ' + MAX_XRP_VALUE.toString()); throw new Error('Exceeding max value of ' + MAX_XRP_VALUE.toString());
@@ -406,7 +356,7 @@ Amount.prototype._copy = function(value) {
Amount.prototype.compareTo = function(to) { Amount.prototype.compareTo = function(to) {
const toAmount = Amount.from_json(to); const toAmount = Amount.from_json(to);
if (!this.is_comparable(toAmount)) { if (!this.is_comparable(toAmount)) {
return new Amount(NaN); return new Amount();
} }
return this._value.comparedTo(toAmount._value); return this._value.comparedTo(toAmount._value);
}; };
@@ -414,7 +364,7 @@ Amount.prototype.compareTo = function(to) {
// Make d a copy of this. Returns d. // Make d a copy of this. Returns d.
// Modification of objects internally refered to is not allowed. // Modification of objects internally refered to is not allowed.
Amount.prototype.copyTo = function(d, negate) { Amount.prototype.copyTo = function(d, negate) {
d._value = negate ? this._value.negated() : this._value; d._value = negate ? this._value.negate() : this._value;
d._is_native = this._is_native; d._is_native = this._is_native;
d._currency = this._currency; d._currency = this._currency;
d._issuer = this._issuer; d._issuer = this._issuer;
@@ -525,7 +475,7 @@ Amount.prototype.parse_human = function(j, options) {
value = words[0].slice(0, -3); value = words[0].slice(0, -3);
currency = words[0].slice(-3); currency = words[0].slice(-3);
if (!(isNumber(value) && currency.match(currency_RE))) { if (!(isNumber(value) && currency.match(currency_RE))) {
return new Amount(NaN); return new Amount();
} }
} }
} else if (words.length === 2) { } else if (words.length === 2) {
@@ -539,22 +489,25 @@ Amount.prototype.parse_human = function(j, options) {
value = words[0]; value = words[0];
currency = words[1]; currency = words[1];
} else { } else {
return new Amount(NaN); return new Amount();
} }
} else { } else {
return new Amount(NaN); return new Amount();
} }
currency = currency.toUpperCase(); currency = currency.toUpperCase();
this.set_currency(currency); this.set_currency(currency);
this._is_native = (currency === 'XRP'); this._is_native = (currency === 'XRP');
this._set_value(new Value(value)._value); const newValue =
(this._is_native ? new XRPValue(value) :
new IOUValue(value));
this._set_value(newValue);
// Apply interest/demurrage // Apply interest/demurrage
if (opts.reference_date && this._currency.has_interest()) { if (opts.reference_date && this._currency.has_interest()) {
const interest = this._currency.get_interest_at(opts.reference_date); const interest = this._currency.get_interest_at(opts.reference_date);
this._set_value( this._set_value(
new IOUValue(this._value).divide(new Value(interest.toString()))); this._value.divide(new IOUValue(interest.toString())));
} }
return this; return this;
@@ -608,11 +561,9 @@ function(quality, counterCurrency, counterIssuer, opts) {
const mantissa_hex = quality.substring(quality.length - 14); const mantissa_hex = quality.substring(quality.length - 14);
const offset_hex = quality.substring( const offset_hex = quality.substring(
quality.length - 16, quality.length - 14); quality.length - 16, quality.length - 14);
const mantissa = new Value(mantissa_hex, 16)._value; const mantissa = new IOUValue(mantissa_hex, null, 16);
const offset = parseInt(offset_hex, 16) - 100; const offset = parseInt(offset_hex, 16) - 100;
const valueStr = mantissa.toString() + 'e' + offset.toString();
const value = new Value(valueStr)._value;
this._currency = Currency.from_json(counterCurrency); this._currency = Currency.from_json(counterCurrency);
this._issuer = UInt160.from_json(counterIssuer); this._issuer = UInt160.from_json(counterIssuer);
this._is_native = this._currency.is_native(); this._is_native = this._currency.is_native();
@@ -634,9 +585,9 @@ function(quality, counterCurrency, counterIssuer, opts) {
quality as stored : 5 USD / 3000000 drops quality as stored : 5 USD / 3000000 drops
inverted : 3000000 drops / 5 USD inverted : 3000000 drops / 5 USD
*/ */
const adjusted = options.inverse ? inverse(value) : value; const valueStr = mantissa.toString() + 'e' + offset.toString();
let nativeAdjusted = this._is_native ? let nativeAdjusted = new IOUValue(valueStr);
new XRPValue(adjusted, Amount.bi_xns_unit) : new IOUValue(adjusted); nativeAdjusted = options.inverse ? nativeAdjusted.invert() : nativeAdjusted;
if (!options.xrp_as_drops) { if (!options.xrp_as_drops) {
// `In a currency exchange, the exchange rate is quoted as the units of the // `In a currency exchange, the exchange rate is quoted as the units of the
@@ -647,22 +598,26 @@ function(quality, counterCurrency, counterIssuer, opts) {
if (this._is_native) { if (this._is_native) {
// pay:$price drops get:1 X // pay:$price drops get:1 X
// pay:($price / 1,000,000) XRP get:1 X // pay:($price / 1,000,000) XRP get:1 X
nativeAdjusted = nativeAdjusted.divide(new Value(Amount.bi_xns_unit)); nativeAdjusted = nativeAdjusted.divide(bi_xns_unit);
} else if (baseCurrency.is_valid() && baseCurrency.is_native()) { } else if (baseCurrency.is_valid() && baseCurrency.is_native()) {
// pay:$price X get:1 drop // pay:$price X get:1 drop
// pay:($price * 1,000,000) X get:1 XRP // pay:($price * 1,000,000) X get:1 XRP
nativeAdjusted = nativeAdjusted.multiply(new Value(Amount.bi_xns_unit)); nativeAdjusted = nativeAdjusted.multiply(bi_xns_unit);
} }
} }
this._set_value(nativeAdjusted); if (this._is_native) {
this._set_value(
new XRPValue(nativeAdjusted.round(6, Value.getBNRoundDown())));
} else {
this._set_value(nativeAdjusted);
}
if (options.reference_date && baseCurrency.is_valid() if (options.reference_date && baseCurrency.is_valid()
&& baseCurrency.has_interest()) { && baseCurrency.has_interest()) {
const interest = baseCurrency.get_interest_at(options.reference_date); const interest = baseCurrency.get_interest_at(options.reference_date);
this._set_value( this._set_value(
new IOUValue(this._value).divide(new Value(interest.toString()))); this._value.divide(new IOUValue(interest.toString())));
} }
return this; return this;
}; };
@@ -670,7 +625,7 @@ Amount.prototype.parse_number = function(n) {
this._is_native = false; this._is_native = false;
this._currency = Currency.from_json(1); this._currency = Currency.from_json(1);
this._issuer = UInt160.from_json(1); this._issuer = UInt160.from_json(1);
this._set_value(new Value(n)._value); this._set_value(new IOUValue(n));
return this; return this;
}; };
@@ -721,7 +676,7 @@ Amount.prototype.parse_json = function(j) {
break; break;
default: default:
this._set_value(new Value(NaN)._value); this._set_value(new IOUValue(NaN));
} }
return this; return this;
@@ -736,11 +691,11 @@ Amount.prototype.parse_native = function(j) {
if (j.indexOf('.') >= 0) { if (j.indexOf('.') >= 0) {
throw new Error('Native amounts must be specified in integer drops'); throw new Error('Native amounts must be specified in integer drops');
} }
const value = new XRPValue(j, Amount.bi_xns_unit); const value = new XRPValue(j);
this._is_native = true; this._is_native = true;
this._set_value(value.divide(new Value(Amount.bi_xns_unit))); this._set_value(value.divide(bi_xns_unit));
} else { } else {
this._set_value(new Value(NaN)._value); this._set_value(new IOUValue(NaN));
} }
return this; return this;
@@ -750,7 +705,8 @@ Amount.prototype.parse_native = function(j) {
// Requires _currency to be set! // Requires _currency to be set!
Amount.prototype.parse_value = function(j) { Amount.prototype.parse_value = function(j) {
this._is_native = false; this._is_native = false;
this._set_value(new Value(j)._value, Value.getBNRoundDown()); const newValue = new IOUValue(j, Value.getBNRoundDown());
this._set_value(newValue);
return this; return this;
}; };
@@ -781,14 +737,14 @@ Amount.prototype.to_text = function() {
} }
if (this._is_native) { if (this._is_native) {
return new XRPValue(this._value).multiply( return this._value.multiply(bi_xns_unit).toString();
new Value(Amount.bi_xns_unit))._value.toString();
} }
// not native // not native
const offset = this._value.e - 15; const offset = this._value.getExponent() - 15;
const sign = this._value.isNegative() ? '-' : ''; const sign = this._value.isNegative() ? '-' : '';
const mantissa = utils.getMantissaDecimalString(this._value.absoluteValue()); const mantissa = utils.getMantissa16FromString(
this._value.abs().toString());
if (offset !== 0 && (offset < -25 || offset > -4)) { if (offset !== 0 && (offset < -25 || offset > -4)) {
// Use e notation. // Use e notation.
// XXX Clamp output. // XXX Clamp output.
@@ -825,7 +781,7 @@ Amount.prototype.applyInterest = function(referenceDate) {
} }
const interest = this._currency.get_interest_at(referenceDate); const interest = this._currency.get_interest_at(referenceDate);
return this._copy( return this._copy(
new IOUValue(this._value).multiply(new Value(interest.toString()))); this._value.multiply(new IOUValue(interest.toString())));
}; };
/** /**
@@ -1005,7 +961,7 @@ Amount.prototype.not_equals_why = function(d, ignore_issuer) {
} }
const type = this._is_native ? 'XRP' : 'Non-XRP'; const type = this._is_native ? 'XRP' : 'Non-XRP';
if (!this._value.isZero() && this._value.negated().equals(d._value)) { if (!this._value.isZero() && this._value.negate().equals(d._value)) {
return type + ' sign differs.'; return type + ' sign differs.';
} }
if (!this._value.equals(d._value)) { if (!this._value.equals(d._value)) {

View File

@@ -451,8 +451,7 @@ OrderBook.prototype.applyTransferRate = function(balance) {
const adjustedBalance = (new IOUValue(balance)) const adjustedBalance = (new IOUValue(balance))
.divide(new IOUValue(this._issuerTransferRate)) .divide(new IOUValue(this._issuerTransferRate))
.multiply(new IOUValue(OrderBook.DEFAULT_TRANSFER_RATE)) .multiply(new IOUValue(OrderBook.DEFAULT_TRANSFER_RATE)).toString();
._value.toString();
return adjustedBalance; return adjustedBalance;
}; };

View File

@@ -32,12 +32,7 @@ function createAmount(value, currency, counterparty) {
*/ */
function getCurrencyFromOffer(offer) { function getCurrencyFromOffer(offer) {
let currency = offer.TakerPays.currency; return offer.TakerPays.currency || offer.TakerGets.currency;
if (!currency) {
currency = offer.TakerGets.currency;
}
return currency;
} }
/** /**
@@ -47,12 +42,7 @@ function getCurrencyFromOffer(offer) {
*/ */
function getIssuerFromOffer(offer) { function getIssuerFromOffer(offer) {
let issuer = offer.TakerPays.issuer; return offer.TakerPays.issuer || offer.TakerGets.issuer;
if (!issuer) {
issuer = offer.TakerGets.issuer;
}
return issuer;
} }
/** /**

View File

@@ -1,8 +1,9 @@
'use strict'; 'use strict';
function getMantissaDecimalString(bignum) { // returns the mantissa from the passed in string,
let mantissa = bignum.toPrecision(16) // adding zeros until it has 16 sd
.replace(/\./, '') // remove decimal point function getMantissa16FromString(decimalString) {
let mantissa = decimalString.replace(/\./, '') // remove decimal point
.replace(/e.*/, '') // remove scientific notation .replace(/e.*/, '') // remove scientific notation
.replace(/^0*/, ''); // remove leading zeroes .replace(/^0*/, ''); // remove leading zeroes
while (mantissa.length < 16) { while (mantissa.length < 16) {
@@ -11,6 +12,10 @@ function getMantissaDecimalString(bignum) {
return mantissa; return mantissa;
} }
function getMantissaDecimalString(bignum) {
return getMantissa16FromString(bignum.toPrecision(16));
}
function trace(comment, func) { function trace(comment, func) {
return function() { return function() {
console.log('%s: %s', trace, arguments.toString); console.log('%s: %s', trace, arguments.toString);
@@ -156,6 +161,7 @@ exports.arrayUnique = arrayUnique;
exports.toTimestamp = toTimestamp; exports.toTimestamp = toTimestamp;
exports.fromTimestamp = fromTimestamp; exports.fromTimestamp = fromTimestamp;
exports.getMantissaDecimalString = getMantissaDecimalString; exports.getMantissaDecimalString = getMantissaDecimalString;
exports.getMantissa16FromString = getMantissa16FromString;
exports.sjcl = require('sjcl-extended'); exports.sjcl = require('sjcl-extended');

View File

@@ -9,10 +9,16 @@ const BigNumber = GlobalBigNumber.another({
DECIMAL_PLACES: 40 DECIMAL_PLACES: 40
}); });
const assert = require('assert');
class Value { class Value {
constructor(value: string | BigNumber, base: number) { constructor(value: string | BigNumber) {
this._value = new BigNumber(value, base); if (this.constructor === 'Value') {
throw new Error(
'Cannot instantiate Value directly, it is an abstract base class');
}
this._value = new BigNumber(value);
} }
static getBNRoundDown() { static getBNRoundDown() {
@@ -25,35 +31,23 @@ class Value {
} }
add(addend: Value) { add(addend: Value) {
assert(this.constructor === addend.constructor);
const result = this._value.plus(addend._value); const result = this._value.plus(addend._value);
return this._canonicalize(result); return this._canonicalize(result);
} }
subtract(subtrahend: Value) { subtract(subtrahend: Value) {
assert(this.constructor === subtrahend.constructor);
const result = this._value.minus(subtrahend._value); const result = this._value.minus(subtrahend._value);
return this._canonicalize(result); return this._canonicalize(result);
} }
multiply(multiplicand: Value) { multiply(multiplicand: Value) {
const val = this._value; const result = this._value.times(multiplicand._value);
const mult = multiplicand._value;
const result = (val).times(mult);
return this._canonicalize(result); return this._canonicalize(result);
} }
scale(scaleFactor: Value) {
const result = this._value.times(scaleFactor._value);
return this._canonicalize(result);
}
divide(divisor: Value) { divide(divisor: Value) {
if (this._value.isNaN()) {
throw new Error('Invalid dividend');
}
if (divisor.isNaN()) {
throw new Error('Invalid divisor');
}
if (divisor.isZero()) { if (divisor.isZero()) {
throw new Error('divide by zero'); throw new Error('divide by zero');
} }
@@ -66,6 +60,19 @@ class Value {
return this._canonicalize(result); return this._canonicalize(result);
} }
round(decimalPlaces: number, roundingMode: number) {
const result = this._value.round(decimalPlaces, roundingMode);
return this._canonicalize(result);
}
toFixed(decimalPlaces: number, roundingMode: number) {
return this._value.toFixed(decimalPlaces, roundingMode);
}
getExponent() {
return this._value.e;
}
isNaN() { isNaN() {
return this._value.isNaN(); return this._value.isNaN();
} }
@@ -78,8 +85,23 @@ class Value {
return this._value.isNegative(); return this._value.isNegative();
} }
negated() { toString() {
return this._value.neg(); return this._value.toString();
}
greaterThan(comparator: Value) {
assert(this.constructor === comparator.constructor);
return this._value.greaterThan(comparator._value);
}
lessThan(comparator: Value) {
assert(this.constructor === comparator.constructor);
return this._value.lessThan(comparator._value);
}
comparedTo(comparator: Value) {
assert(this.constructor === comparator.constructor);
return this._value.comparedTo(comparator._value);
} }
} }

View File

@@ -1186,19 +1186,19 @@ describe('Amount', function() {
describe('amount limits', function() { describe('amount limits', function() {
it('max JSON wire limite', function() { it('max JSON wire limite', function() {
assert.strictEqual(Amount.bi_xns_max.toString(), '100000000000000000'); assert.strictEqual(Amount.bi_xns_max, '100000000000000000');
}); });
it('max JSON wire limite', function() { it('max JSON wire limite', function() {
assert.strictEqual(Amount.bi_xns_min.toString(), '-100000000000000000'); assert.strictEqual(Amount.bi_xns_min, '-100000000000000000');
}); });
it('max mantissa value', function() { it('max mantissa value', function() {
assert.strictEqual(Amount.bi_man_max_value.toString(), '9999999999999999'); assert.strictEqual(Amount.bi_man_max_value, '9999999999999999');
}); });
it('min mantissa value', function() { it('min mantissa value', function() {
assert.strictEqual(Amount.bi_man_min_value.toString(), '1000000000000000'); assert.strictEqual(Amount.bi_man_min_value, '1000000000000000');
}); });
it('from_json minimum XRP', function() { it('from_json minimum XRP', function() {