mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 12:15:51 +00:00
[FEATURE] add offer autobridging
This commit is contained in:
435
src/js/ripple/autobridgecalculator.js
Normal file
435
src/js/ripple/autobridgecalculator.js
Normal file
@@ -0,0 +1,435 @@
|
||||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var assert = require('assert');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Amount = require('./amount').Amount;
|
||||
var Utils = require('./orderbookutils');
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
assert(!_.isNull(number) && !isNaN(number), message);
|
||||
}
|
||||
|
||||
function assertValidLegOneOffer(legOneOffer, message) {
|
||||
assert(legOneOffer);
|
||||
assert.strictEqual(typeof legOneOffer, 'object', message);
|
||||
assert.strictEqual(typeof legOneOffer.TakerPays, 'object', message);
|
||||
assertValidNumber(legOneOffer.TakerGets, message);
|
||||
}
|
||||
|
||||
function AutobridgeCalculator(currencyGets, currencyPays,
|
||||
legOneOffers, legTwoOffers) {
|
||||
this._currencyGets = currencyGets;
|
||||
this._currencyPays = currencyPays;
|
||||
this.legOneOffers = _.cloneDeep(legOneOffers);
|
||||
this.legTwoOffers = _.cloneDeep(legTwoOffers);
|
||||
|
||||
this._ownerFundsLeftover = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates an ordered array of autobridged offers by quality
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.calculate = function() {
|
||||
var legOnePointer = 0;
|
||||
var legTwoPointer = 0;
|
||||
|
||||
var offersAutobridged = [];
|
||||
|
||||
this.clearOwnerFundsLeftover();
|
||||
|
||||
while (this.legOneOffers[legOnePointer] && this.legTwoOffers[legTwoPointer]) {
|
||||
var legOneOffer = this.legOneOffers[legOnePointer];
|
||||
var legTwoOffer = this.legTwoOffers[legTwoPointer];
|
||||
var leftoverFunds = this.getLeftoverOwnerFunds(legOneOffer.Account);
|
||||
var autobridgedOffer;
|
||||
|
||||
if (legOneOffer.Account === legTwoOffer.Account) {
|
||||
this.unclampLegOneOwnerFunds(legOneOffer);
|
||||
} else if (!legOneOffer.is_fully_funded && !leftoverFunds.is_zero()) {
|
||||
this.adjustLegOneFundedAmount(legOneOffer);
|
||||
}
|
||||
|
||||
var legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
|
||||
var legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
|
||||
|
||||
if (legOneTakerGetsFunded.is_zero()) {
|
||||
legOnePointer++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (legTwoTakerPaysFunded.is_zero()) {
|
||||
legTwoPointer++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (legOneTakerGetsFunded.compareTo(legTwoTakerPaysFunded) > 0) {
|
||||
autobridgedOffer = this.getAutobridgedOfferWithClampedLegOne(
|
||||
legOneOffer,
|
||||
legTwoOffer
|
||||
);
|
||||
|
||||
legTwoPointer++;
|
||||
} else if (legTwoTakerPaysFunded.compareTo(legOneTakerGetsFunded) > 0) {
|
||||
autobridgedOffer = this.getAutobridgedOfferWithClampedLegTwo(
|
||||
legOneOffer,
|
||||
legTwoOffer
|
||||
);
|
||||
|
||||
legOnePointer++;
|
||||
} else {
|
||||
autobridgedOffer = this.getAutobridgedOfferWithoutClamps(
|
||||
legOneOffer,
|
||||
legTwoOffer
|
||||
);
|
||||
|
||||
legOnePointer++;
|
||||
legTwoPointer++;
|
||||
}
|
||||
|
||||
offersAutobridged.push(autobridgedOffer);
|
||||
}
|
||||
|
||||
return offersAutobridged;
|
||||
};
|
||||
|
||||
/**
|
||||
* In this case, the output from leg one is greater than the input to leg two.
|
||||
* Therefore, we must effectively clamp leg one output to leg two input.
|
||||
*
|
||||
* @param {Object} legOneOffer
|
||||
* @param {Object} legTwoOffer
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegOne =
|
||||
function(legOneOffer, legTwoOffer) {
|
||||
var legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
|
||||
var legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
|
||||
var legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
|
||||
|
||||
var autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer);
|
||||
var autobridgedTakerPays = legTwoTakerPaysFunded.multiply(legOneQuality);
|
||||
|
||||
if (legOneOffer.Account === legTwoOffer.Account) {
|
||||
var legOneTakerGets = Utils.getOfferTakerGets(legOneOffer);
|
||||
var updatedTakerGets = legOneTakerGets.subtract(legTwoTakerPaysFunded);
|
||||
|
||||
this.setLegOneTakerGets(legOneOffer, updatedTakerGets);
|
||||
|
||||
this.clampLegOneOwnerFunds(legOneOffer);
|
||||
} else {
|
||||
// Update funded amount since leg one offer was not completely consumed
|
||||
var updatedTakerGetsFunded = legOneTakerGetsFunded
|
||||
.subtract(legTwoTakerPaysFunded);
|
||||
|
||||
this.setLegOneTakerGetsFunded(legOneOffer, updatedTakerGetsFunded);
|
||||
}
|
||||
|
||||
return this.formatAutobridgedOffer(
|
||||
autobridgedTakerGets,
|
||||
autobridgedTakerPays
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* In this case, the input from leg two is greater than the output to leg one.
|
||||
* Therefore, we must effectively clamp leg two input to leg one output.
|
||||
*
|
||||
* @param {Object} legOneOffer
|
||||
* @param {Object} legTwoOffer
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegTwo =
|
||||
function(legOneOffer, legTwoOffer) {
|
||||
var legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
|
||||
var legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
|
||||
var legTwoQuality = Utils.getOfferQuality(legTwoOffer, this._currencyGets);
|
||||
|
||||
var autobridgedTakerGets = legOneTakerGetsFunded.divide(legTwoQuality);
|
||||
var autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer);
|
||||
|
||||
// Update funded amount since leg two offer was not completely consumed
|
||||
legTwoOffer.taker_gets_funded = Utils.getOfferTakerGetsFunded(legTwoOffer)
|
||||
.subtract(autobridgedTakerGets)
|
||||
.to_text();
|
||||
legTwoOffer.taker_pays_funded = legTwoTakerPaysFunded
|
||||
.subtract(legOneTakerGetsFunded)
|
||||
.to_text();
|
||||
|
||||
return this.formatAutobridgedOffer(
|
||||
autobridgedTakerGets,
|
||||
autobridgedTakerPays
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* In this case, the output from leg one and the input to leg two are the same.
|
||||
* We do not need to clamp either.
|
||||
* @param {Object} legOneOffer
|
||||
* @param {Object} legTwoOffer
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.getAutobridgedOfferWithoutClamps =
|
||||
function(legOneOffer, legTwoOffer) {
|
||||
var autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer);
|
||||
var autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer);
|
||||
|
||||
return this.formatAutobridgedOffer(
|
||||
autobridgedTakerGets,
|
||||
autobridgedTakerPays
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear owner funds leftovers
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.clearOwnerFundsLeftover = function() {
|
||||
this._ownerFundsLeftover = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset owner funds leftovers for an account to 0
|
||||
*
|
||||
* @param {String} account
|
||||
*
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.resetOwnerFundsLeftover = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
this._ownerFundsLeftover[account] = Utils.normalizeAmount('0');
|
||||
|
||||
return this._ownerFundsLeftover[account];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve leftover funds found after clamping leg one by account
|
||||
*
|
||||
* @param {String} account
|
||||
*
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.getLeftoverOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
var amount = this._ownerFundsLeftover[account];
|
||||
|
||||
if (!amount) {
|
||||
amount = Utils.normalizeAmount('0');
|
||||
}
|
||||
|
||||
return amount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add funds to account's leftover funds
|
||||
*
|
||||
* @param {String} account
|
||||
* @param {Amount} amount
|
||||
*
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.addLeftoverOwnerFunds =
|
||||
function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
assert(amount instanceof Amount, 'Amount is invalid');
|
||||
|
||||
this._ownerFundsLeftover[account] = this.getLeftoverOwnerFunds(account)
|
||||
.add(amount);
|
||||
|
||||
return this._ownerFundsLeftover[account];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set account's leftover funds
|
||||
*
|
||||
* @param {String} account
|
||||
* @param {Amount} amount
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.setLeftoverOwnerFunds =
|
||||
function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
assert(amount instanceof Amount, 'Amount is invalid');
|
||||
|
||||
this._ownerFundsLeftover[account] = amount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format an autobridged offer and compute synthetic values (e.g. quality)
|
||||
*
|
||||
* @param {Amount} takerGets
|
||||
* @param {Amount} takerPays
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.formatAutobridgedOffer =
|
||||
function(takerGets, takerPays) {
|
||||
assert(takerGets instanceof Amount, 'Autobridged taker gets is invalid');
|
||||
assert(takerPays instanceof Amount, 'Autobridged taker pays is invalid');
|
||||
|
||||
var autobridgedOffer = {};
|
||||
var quality = takerPays.divide(takerGets);
|
||||
|
||||
autobridgedOffer.TakerGets = {
|
||||
value: takerGets.to_text(),
|
||||
currency: this._currencyGets.to_hex(),
|
||||
issuer: this._issuerGets
|
||||
};
|
||||
|
||||
autobridgedOffer.TakerPays = {
|
||||
value: takerPays.to_text(),
|
||||
currency: this._currencyPays.to_hex(),
|
||||
issuer: this._issuerPays
|
||||
};
|
||||
|
||||
autobridgedOffer.quality = quality.to_text();
|
||||
|
||||
autobridgedOffer.taker_gets_funded = autobridgedOffer.TakerGets.value;
|
||||
autobridgedOffer.taker_pays_funded = autobridgedOffer.TakerPays.value;
|
||||
|
||||
autobridgedOffer.autobridged = true;
|
||||
|
||||
autobridgedOffer.BookDirectory = Utils.convertOfferQualityToHex(quality);
|
||||
|
||||
return autobridgedOffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove funds clamp on leg one offer. This is necessary when the two offers
|
||||
* are owned by the same account. In this case, it doesn't matter if offer one
|
||||
* is not fully funded. Leg one out goes to leg two in and since its the same
|
||||
* account, an infinite amount can flow.
|
||||
*
|
||||
* @param {Object} legOneOffer - IOU:XRP offer
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
|
||||
legOneOffer.initTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
|
||||
|
||||
this.setLegOneTakerGetsFunded(
|
||||
legOneOffer,
|
||||
Utils.getOfferTakerGets(legOneOffer)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply clamp back on leg one offer after a round of autobridge calculation
|
||||
* completes. We must reapply clamps that have been removed because we cannot
|
||||
* guarantee that the next offer from leg two will also be from the same
|
||||
* account.
|
||||
*
|
||||
* When we reapply, it could happen that the amount of TakerGets left after
|
||||
* the autobridge calculation is less than the original funded amount. In this
|
||||
* case, we have extra funds we can use towards unfunded offers with worse
|
||||
* quality by the same owner.
|
||||
*
|
||||
* @param {Object} legOneOffer - IOU:XRP offer
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.clampLegOneOwnerFunds = function(legOneOffer) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
|
||||
var takerGets = Utils.getOfferTakerGets(legOneOffer);
|
||||
|
||||
if (takerGets.compareTo(legOneOffer.initTakerGetsFunded) > 0) {
|
||||
// After clamping, TakerGets is still greater than initial funded amount
|
||||
this.setLegOneTakerGetsFunded(legOneOffer, legOneOffer.initTakerGetsFunded);
|
||||
} else {
|
||||
var updatedLeftover = legOneOffer.initTakerGetsFunded.subtract(takerGets);
|
||||
|
||||
this.setLegOneTakerGetsFunded(legOneOffer, takerGets);
|
||||
this.addLeftoverOwnerFunds(legOneOffer.Account, updatedLeftover);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Increase leg one offer funded amount with extra funds found after applying
|
||||
* clamp.
|
||||
*
|
||||
* @param {Object} legOneOffer - IOU:XRP offer
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.adjustLegOneFundedAmount =
|
||||
function(legOneOffer) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
assert(!legOneOffer.is_fully_funded, 'Leg one offer cannot be fully funded');
|
||||
|
||||
var fundedSum = Utils.getOfferTakerGetsFunded(legOneOffer)
|
||||
.add(this.getLeftoverOwnerFunds(legOneOffer.Account));
|
||||
|
||||
if (fundedSum.compareTo(Utils.getOfferTakerGets(legOneOffer)) >= 0) {
|
||||
// There are enough extra funds to fully fund the offer
|
||||
var legOneTakerGets = Utils.getOfferTakerGets(legOneOffer);
|
||||
var updatedLeftover = fundedSum.subtract(legOneTakerGets);
|
||||
|
||||
this.setLegOneTakerGetsFunded(legOneOffer, legOneTakerGets);
|
||||
this.setLeftoverOwnerFunds(legOneOffer.Account, updatedLeftover);
|
||||
} else {
|
||||
// There are not enough extra funds to fully fund the offer
|
||||
this.setLegOneTakerGetsFunded(legOneOffer, fundedSum);
|
||||
this.resetOwnerFundsLeftover(legOneOffer.Account);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set taker gets funded amount for a IOU:XRP offer. Also calculates taker
|
||||
* pays funded using offer quality and updates is_fully_funded flag
|
||||
*
|
||||
* @param {Object} legOneOffer - IOU:XRP offer
|
||||
* @param {Amount} takerGetsFunded
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.setLegOneTakerGetsFunded =
|
||||
function setLegOneTakerGetsFunded(legOneOffer, takerGetsFunded) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
assert(takerGetsFunded instanceof Amount, 'Taker gets funded is invalid');
|
||||
|
||||
legOneOffer.taker_gets_funded = takerGetsFunded.to_text();
|
||||
legOneOffer.taker_pays_funded = takerGetsFunded
|
||||
.multiply(Utils.getOfferQuality(legOneOffer, this._currencyGets))
|
||||
.to_text();
|
||||
|
||||
if (legOneOffer.taker_gets_funded === legOneOffer.TakerGets.value) {
|
||||
legOneOffer.is_fully_funded = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set taker gets amount for a IOU:XRP offer. Also calculates taker pays
|
||||
* using offer quality
|
||||
*
|
||||
* @param {Object} legOneOffer - IOU:XRP offer
|
||||
* @param {Amount} takerGets
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.setLegOneTakerGets =
|
||||
function(legOneOffer, takerGets) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
assert(takerGets instanceof Amount, 'Taker gets funded is invalid');
|
||||
|
||||
var legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
|
||||
|
||||
legOneOffer.TakerGets = takerGets.to_text();
|
||||
legOneOffer.TakerPays = takerGets.multiply(legOneQuality);
|
||||
};
|
||||
|
||||
module.exports = AutobridgeCalculator;
|
||||
@@ -19,6 +19,8 @@ var EventEmitter = require('events').EventEmitter;
|
||||
var Amount = require('./amount').Amount;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Currency = require('./currency').Currency;
|
||||
var AutobridgeCalculator = require('./autobridgecalculator');
|
||||
var OrderBookUtils = require('./orderbookutils');
|
||||
var log = require('./log').internal.sub('orderbook');
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
@@ -35,21 +37,25 @@ function assertValidNumber(number, message) {
|
||||
* @param {String} orderbook key
|
||||
*/
|
||||
|
||||
function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
|
||||
function OrderBook(remote,
|
||||
currencyGets, issuerGets, currencyPays, issuerPays,
|
||||
key) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
var self = this;
|
||||
|
||||
this._remote = remote;
|
||||
this._currencyGets = Currency.from_json(getsC);
|
||||
this._issuerGets = getsI;
|
||||
this._currencyPays = Currency.from_json(paysC);
|
||||
this._issuerPays = paysI;
|
||||
this._currencyGets = Currency.from_json(currencyGets);
|
||||
this._issuerGets = issuerGets;
|
||||
this._currencyPays = Currency.from_json(currencyPays);
|
||||
this._issuerPays = issuerPays;
|
||||
this._key = key;
|
||||
this._subscribed = false;
|
||||
this._shouldSubscribe = true;
|
||||
this._listeners = 0;
|
||||
this._offers = [];
|
||||
this._offersAutobridged = [];
|
||||
this._mergedOffers = [];
|
||||
this._offerCounts = {};
|
||||
this._ownerFundsUnadjusted = {};
|
||||
this._ownerFunds = {};
|
||||
@@ -62,6 +68,37 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
|
||||
// Transfer rate of the taker gets currency issuer
|
||||
this._issuerTransferRate = null;
|
||||
|
||||
// When orderbook is IOU/IOU, there will be IOU/XRP and XRP/IOU
|
||||
// books that we must keep track of to compute autobridged offers
|
||||
this._legOneBook = null;
|
||||
this._legTwoBook = null;
|
||||
|
||||
this._isAutobridgeable = !this._currencyGets.is_native()
|
||||
&& !this._currencyPays.is_native();
|
||||
|
||||
function computeAutobridgedOffersWrapper() {
|
||||
self.computeAutobridgedOffers();
|
||||
self.mergeDirectAndAutobridgedBooks();
|
||||
}
|
||||
|
||||
if (this._isAutobridgeable) {
|
||||
this._legOneBook = remote.createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
currency_pays: currencyPays,
|
||||
issuer_pays: issuerPays
|
||||
});
|
||||
|
||||
this._legOneBook.on('model', computeAutobridgedOffersWrapper);
|
||||
|
||||
this._legTwoBook = remote.createOrderBook({
|
||||
currency_gets: currencyGets,
|
||||
issuer_gets: issuerGets,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
|
||||
this._legTwoBook.on('model', computeAutobridgedOffersWrapper);
|
||||
}
|
||||
|
||||
function listenersModified(action, event) {
|
||||
// Automatically subscribe and unsubscribe to orderbook
|
||||
// on the basis of existing event listeners
|
||||
@@ -129,8 +166,6 @@ OrderBook.EVENTS = [
|
||||
|
||||
OrderBook.DEFAULT_TRANSFER_RATE = 1000000000;
|
||||
|
||||
OrderBook.IOU_SUFFIX = '/000/rrrrrrrrrrrrrrrrrrrrrhoLvTp';
|
||||
|
||||
/**
|
||||
* Normalize offers from book_offers and transaction stream
|
||||
*
|
||||
@@ -247,7 +282,7 @@ OrderBook.prototype.requestOffers = function(callback) {
|
||||
|
||||
self.setOffers(res.offers);
|
||||
self._synchronized = true;
|
||||
self.emit('model', self._offers);
|
||||
self.notifyDirectOffersChanged();
|
||||
|
||||
callback(null, self._offers);
|
||||
}
|
||||
@@ -269,6 +304,48 @@ OrderBook.prototype.requestOffers = function(callback) {
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request transfer rate for this orderbook's issuer
|
||||
*
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
OrderBook.prototype.requestTransferRate = function(callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this._currencyGets.is_native()) {
|
||||
// Transfer rate is default for the native currency
|
||||
this._issuerTransferRate = OrderBook.DEFAULT_TRANSFER_RATE;
|
||||
|
||||
return callback(null, OrderBook.DEFAULT_TRANSFER_RATE);
|
||||
}
|
||||
|
||||
if (this._issuerTransferRate) {
|
||||
// Transfer rate has already been cached
|
||||
return callback(null, this._issuerTransferRate);
|
||||
}
|
||||
|
||||
function handleAccountInfo(err, info) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// When transfer rate is not explicitly set on account, it implies the
|
||||
// default transfer rate
|
||||
self._issuerTransferRate = info.account_data.TransferRate ||
|
||||
OrderBook.DEFAULT_TRANSFER_RATE;
|
||||
|
||||
callback(null, self._issuerTransferRate);
|
||||
}
|
||||
|
||||
this._remote.requestAccountInfo(
|
||||
{account: this._issuerGets},
|
||||
handleAccountInfo
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Subscribe to transactions stream
|
||||
*
|
||||
@@ -317,6 +394,19 @@ OrderBook.prototype.subscribeTransactions = function(callback) {
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles notifying listeners that direct offers have changed. For autobridged
|
||||
* books, an additional merge step is also performed
|
||||
*/
|
||||
|
||||
OrderBook.prototype.notifyDirectOffersChanged = function() {
|
||||
if (this._isAutobridgeable) {
|
||||
this.mergeDirectAndAutobridgedBooks();
|
||||
} else {
|
||||
this.emit('model', this._offers);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset cached owner's funds, offer counts, and offer sums
|
||||
*/
|
||||
@@ -354,6 +444,27 @@ OrderBook.prototype.setOwnerFunds = function(account, fundedAmount) {
|
||||
this._ownerFunds[account] = this.applyTransferRate(fundedAmount);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute adjusted balance that would be left after issuer's transfer fee is
|
||||
* deducted
|
||||
*
|
||||
* @param {String} balance
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.applyTransferRate = function(balance) {
|
||||
assert(!isNaN(balance), 'Balance is invalid');
|
||||
assertValidNumber(this._issuerTransferRate, 'Transfer rate is invalid');
|
||||
|
||||
var adjustedBalance = OrderBookUtils.normalizeAmount(balance)
|
||||
.divide(this._issuerTransferRate)
|
||||
.multiply(Amount.from_json(OrderBook.DEFAULT_TRANSFER_RATE))
|
||||
.to_json()
|
||||
.value;
|
||||
|
||||
return adjustedBalance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get owner's cached, transfer rate adjusted, funds
|
||||
*
|
||||
@@ -370,9 +481,7 @@ OrderBook.prototype.getOwnerFunds = function(account) {
|
||||
if (this._currencyGets.is_native()) {
|
||||
amount = Amount.from_json(this._ownerFunds[account]);
|
||||
} else {
|
||||
amount = Amount.from_json(
|
||||
this._ownerFunds[account] + OrderBook.IOU_SUFFIX
|
||||
);
|
||||
amount = OrderBookUtils.normalizeAmount(this._ownerFunds[account]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,6 +568,7 @@ OrderBook.prototype.decrementOwnerOfferCount = function(account) {
|
||||
|
||||
OrderBook.prototype.addOwnerOfferTotal = function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
var previousAmount = this.getOwnerOfferTotal(account);
|
||||
var currentAmount = previousAmount.add(Amount.from_json(amount));
|
||||
|
||||
@@ -478,6 +588,7 @@ OrderBook.prototype.addOwnerOfferTotal = function(account, amount) {
|
||||
|
||||
OrderBook.prototype.subtractOwnerOfferTotal = function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
var previousAmount = this.getOwnerOfferTotal(account);
|
||||
var newAmount = previousAmount.subtract(Amount.from_json(amount));
|
||||
this._ownerOffersTotal[account] = newAmount;
|
||||
@@ -503,7 +614,7 @@ OrderBook.prototype.getOwnerOfferTotal = function(account) {
|
||||
if (this._currencyGets.is_native()) {
|
||||
amount = Amount.from_json('0');
|
||||
} else {
|
||||
amount = Amount.from_json('0' + OrderBook.IOU_SUFFIX);
|
||||
amount = OrderBookUtils.normalizeAmount('0');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,12 +629,14 @@ OrderBook.prototype.getOwnerOfferTotal = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.resetOwnerOfferTotal = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
var amount;
|
||||
|
||||
if (this._currencyGets.is_native()) {
|
||||
amount = Amount.from_json('0');
|
||||
} else {
|
||||
amount = Amount.from_json('0' + OrderBook.IOU_SUFFIX);
|
||||
amount = OrderBookUtils.normalizeAmount('0');
|
||||
}
|
||||
|
||||
this._ownerOffersTotal[account] = amount;
|
||||
@@ -531,82 +644,6 @@ OrderBook.prototype.resetOwnerOfferTotal = function(account) {
|
||||
return amount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Casts and returns offer's taker gets funded amount as a default IOU amount
|
||||
*
|
||||
* @param {Object} offer
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getOfferTakerGetsFunded = function(offer) {
|
||||
assertValidNumber(offer.taker_gets_funded, 'Taker gets funded is invalid');
|
||||
|
||||
return Amount.from_json(offer.taker_gets_funded + OrderBook.IOU_SUFFIX);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute adjusted balance that would be left after issuer's transfer fee is
|
||||
* deducted
|
||||
*
|
||||
* @param {String} balance
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.applyTransferRate = function(balance) {
|
||||
assert(!isNaN(balance), 'Balance is invalid');
|
||||
assertValidNumber(this._issuerTransferRate, 'Transfer rate is invalid');
|
||||
|
||||
var adjustedBalance = Amount.from_json(balance + OrderBook.IOU_SUFFIX)
|
||||
.divide(this._issuerTransferRate)
|
||||
.multiply(Amount.from_json(OrderBook.DEFAULT_TRANSFER_RATE))
|
||||
.to_json()
|
||||
.value;
|
||||
|
||||
return adjustedBalance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request transfer rate for this orderbook's issuer
|
||||
*
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
OrderBook.prototype.requestTransferRate = function(callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this._currencyGets.is_native()) {
|
||||
// Transfer rate is default for the native currency
|
||||
this._issuerTransferRate = OrderBook.DEFAULT_TRANSFER_RATE;
|
||||
|
||||
return callback(null, OrderBook.DEFAULT_TRANSFER_RATE);
|
||||
}
|
||||
|
||||
if (this._issuerTransferRate) {
|
||||
// Transfer rate has already been cached
|
||||
return callback(null, this._issuerTransferRate);
|
||||
}
|
||||
|
||||
function handleAccountInfo(err, info) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// When transfer rate is not explicitly set on account, it implies the
|
||||
// default transfer rate
|
||||
self._issuerTransferRate = info.account_data.TransferRate ||
|
||||
OrderBook.DEFAULT_TRANSFER_RATE;
|
||||
|
||||
callback(null, self._issuerTransferRate);
|
||||
}
|
||||
|
||||
this._remote.requestAccountInfo(
|
||||
{account: this._issuerGets},
|
||||
handleAccountInfo
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set funded amount on offer with its owner's cached funds
|
||||
*
|
||||
@@ -635,8 +672,9 @@ OrderBook.prototype.setOfferFundedAmount = function(offer) {
|
||||
} else if (previousOfferSum.compareTo(fundedAmount) < 0) {
|
||||
offer.taker_gets_funded = fundedAmount.subtract(previousOfferSum).to_text();
|
||||
|
||||
var takerPaysFunded = this.getOfferQuality(offer).multiply(
|
||||
this.getOfferTakerGetsFunded(offer)
|
||||
var quality = OrderBookUtils.getOfferQuality(offer, this._currencyGets);
|
||||
var takerPaysFunded = quality.multiply(
|
||||
OrderBookUtils.getOfferTakerGetsFunded(offer)
|
||||
);
|
||||
|
||||
offer.taker_pays_funded = this._currencyPays.is_native()
|
||||
@@ -727,8 +765,8 @@ OrderBook.prototype.isBalanceChangeNode = function(node) {
|
||||
/**
|
||||
* Updates funded amounts/balances using modified balance nodes
|
||||
*
|
||||
* Update owner funds using modified AccountRoot and RippleState nodes.
|
||||
* Update funded amounts for offers in the orderbook using owner funds.
|
||||
* Update owner funds using modified AccountRoot and RippleState nodes
|
||||
* Update funded amounts for offers in the orderbook using owner funds
|
||||
*
|
||||
* @param {Object} transaction - transaction that holds meta nodes
|
||||
*/
|
||||
@@ -798,14 +836,15 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
|
||||
if (_.isString(offer.taker_gets_funded)) {
|
||||
// Offer is not new, so we should consider it for offer_changed and
|
||||
// offer_funds_changed events
|
||||
previousFundedGets = self.getOfferTakerGetsFunded(offer);
|
||||
previousFundedGets = OrderBookUtils.getOfferTakerGetsFunded(offer);
|
||||
}
|
||||
|
||||
self.setOfferFundedAmount(offer);
|
||||
self.addOwnerOfferTotal(offer.Account, offer.TakerGets);
|
||||
|
||||
var takerGetsFunded = OrderBookUtils.getOfferTakerGetsFunded(offer);
|
||||
var areFundsChanged = previousFundedGets
|
||||
&& !self.getOfferTakerGetsFunded(offer).equals(previousFundedGets);
|
||||
&& !takerGetsFunded.equals(previousFundedGets);
|
||||
|
||||
if (areFundsChanged) {
|
||||
self.emit('offer_changed', previousOffer, offer);
|
||||
@@ -894,7 +933,7 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
_.each(affectedNodes, handleNode);
|
||||
|
||||
this.emit('transaction', transaction);
|
||||
this.emit('model', this._offers);
|
||||
this.notifyDirectOffersChanged();
|
||||
if (!takerGetsTotal.is_zero()) {
|
||||
this.emit('trade', takerPaysTotal, takerGetsTotal);
|
||||
}
|
||||
@@ -924,11 +963,14 @@ OrderBook.prototype.insertOffer = function(node) {
|
||||
// We're safe to calculate quality for newly created offers
|
||||
offer.quality = takerPays.divide(takerGets).to_text();
|
||||
|
||||
var quality = this.getOfferQuality(offer);
|
||||
var originalLength = this._offers.length;
|
||||
|
||||
for (var i = 0; i < originalLength; i++) {
|
||||
var existingOfferQuality = this.getOfferQuality(this._offers[i]);
|
||||
var quality = OrderBookUtils.getOfferQuality(offer, this._currencyGets);
|
||||
var existingOfferQuality = OrderBookUtils.getOfferQuality(
|
||||
this._offers[i],
|
||||
this._currencyGets
|
||||
);
|
||||
|
||||
if (quality.compareTo(existingOfferQuality) <= 0) {
|
||||
this._offers.splice(i, 0, offer);
|
||||
@@ -948,29 +990,6 @@ OrderBook.prototype.insertOffer = function(node) {
|
||||
this.emit('offer_added', offer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve offer quality
|
||||
*
|
||||
* @param {Object} offer
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getOfferQuality = function(offer) {
|
||||
var amount;
|
||||
|
||||
if (this._currencyGets.has_interest()) {
|
||||
// XXX Should use Amount#from_quality
|
||||
amount = Amount.from_json(
|
||||
offer.TakerPays
|
||||
).ratio_human(offer.TakerGets, {
|
||||
reference_date: new Date()
|
||||
});
|
||||
} else {
|
||||
amount = Amount.from_json(offer.quality + OrderBook.IOU_SUFFIX);
|
||||
}
|
||||
|
||||
return amount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert any amount into default IOU
|
||||
*
|
||||
@@ -986,7 +1005,7 @@ OrderBook.prototype.normalizeAmount = function(currency, amountObj) {
|
||||
? amountObj
|
||||
: amountObj.value;
|
||||
|
||||
return Amount.from_json(value + OrderBook.IOU_SUFFIX);
|
||||
return OrderBookUtils.normalizeAmount(value);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1060,7 +1079,7 @@ OrderBook.prototype.deleteOffer = function(node, isOfferCancel) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.setOffers = function(offers) {
|
||||
assert(Array.isArray(offers), '');
|
||||
assert(Array.isArray(offers), 'Offers is not an array');
|
||||
|
||||
var self = this;
|
||||
|
||||
@@ -1176,4 +1195,46 @@ OrderBook.prototype.is_valid = function() {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute autobridged offers for an IOU:IOU orderbook by merging offers from
|
||||
* IOU:XRP and XRP:IOU books
|
||||
*/
|
||||
|
||||
OrderBook.prototype.computeAutobridgedOffers = function() {
|
||||
assert(!this._currencyGets.is_native() && !this._currencyPays.is_native(),
|
||||
'Autobridging is only for IOU:IOU orderbooks');
|
||||
|
||||
var autobridgeCalculator = new AutobridgeCalculator(
|
||||
this._currencyGets,
|
||||
this._currencyPays,
|
||||
this._legOneBook.getOffersSync(),
|
||||
this._legTwoBook.getOffersSync()
|
||||
);
|
||||
|
||||
this._offersAutobridged = autobridgeCalculator.calculate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Merge direct and autobridged offers into a combined orderbook
|
||||
*
|
||||
* @return [Array]
|
||||
*/
|
||||
|
||||
OrderBook.prototype.mergeDirectAndAutobridgedBooks = function() {
|
||||
var self = this;
|
||||
|
||||
this._mergedOffers = this._offers
|
||||
.concat(this._offersAutobridged)
|
||||
.sort(function(a, b) {
|
||||
var aQuality = OrderBookUtils.getOfferQuality(a, self._currencyGets);
|
||||
var bQuality = OrderBookUtils.getOfferQuality(b, self._currencyGets);
|
||||
|
||||
return aQuality.compareTo(bQuality);
|
||||
});
|
||||
|
||||
this.emit('model', this._mergedOffers);
|
||||
|
||||
return this._mergedOffers;
|
||||
};
|
||||
|
||||
exports.OrderBook = OrderBook;
|
||||
|
||||
106
src/js/ripple/orderbookutils.js
Normal file
106
src/js/ripple/orderbookutils.js
Normal file
@@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var assert = require('assert');
|
||||
var SerializedObject = require('./serializedobject').SerializedObject;
|
||||
var Types = require('./serializedtypes');
|
||||
var Amount = require('./amount').Amount;
|
||||
|
||||
var IOU_SUFFIX = '/000/rrrrrrrrrrrrrrrrrrrrrhoLvTp';
|
||||
var OrderBookUtils = {};
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
assert(!_.isNull(number) && !isNaN(number), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts and returns offer's taker gets funded amount as a default IOU amount
|
||||
*
|
||||
* @param {Object} offer
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBookUtils.getOfferTakerGetsFunded = function(offer) {
|
||||
assertValidNumber(offer.taker_gets_funded, 'Taker gets funded is invalid');
|
||||
|
||||
return Amount.from_json(offer.taker_gets_funded + IOU_SUFFIX);
|
||||
};
|
||||
|
||||
/**
|
||||
* Casts and returns offer's taker pays funded amount as a default IOU amount
|
||||
*
|
||||
* @param {Object} offer
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBookUtils.getOfferTakerPaysFunded = function(offer) {
|
||||
assertValidNumber(offer.taker_pays_funded, 'Taker gets funded is invalid');
|
||||
|
||||
return Amount.from_json(offer.taker_pays_funded + IOU_SUFFIX);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get offer taker gets amount
|
||||
*
|
||||
* @param {Object} offer
|
||||
*
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBookUtils.getOfferTakerGets = function(offer) {
|
||||
assert(typeof offer, 'object', 'Offer is invalid');
|
||||
|
||||
return Amount.from_json(offer.TakerGets + IOU_SUFFIX);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve offer quality
|
||||
*
|
||||
* @param {Object} offer
|
||||
* @param {Currency} currencyGets
|
||||
*/
|
||||
|
||||
OrderBookUtils.getOfferQuality = function(offer, currencyGets) {
|
||||
var amount;
|
||||
|
||||
if (currencyGets.has_interest()) {
|
||||
// XXX Should use Amount#from_quality
|
||||
amount = Amount.from_json(
|
||||
offer.TakerPays
|
||||
).ratio_human(offer.TakerGets, {
|
||||
reference_date: new Date()
|
||||
});
|
||||
} else {
|
||||
amount = Amount.from_json(offer.quality + IOU_SUFFIX);
|
||||
}
|
||||
|
||||
return amount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats an offer quality amount to a hex that can be parsed by
|
||||
* Amount.parse_quality
|
||||
*
|
||||
* @param {Amount} quality
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
OrderBookUtils.convertOfferQualityToHex = function(quality) {
|
||||
assert(quality instanceof Amount, 'Quality is not an amount');
|
||||
|
||||
var so = new SerializedObject();
|
||||
Types.Quality.serialize(so, quality.to_text() + IOU_SUFFIX);
|
||||
|
||||
return so.to_hex();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
OrderBookUtils.normalizeAmount = function(value) {
|
||||
return Amount.from_json(value + IOU_SUFFIX);
|
||||
};
|
||||
|
||||
module.exports = OrderBookUtils;
|
||||
@@ -399,6 +399,49 @@ var STCurrency = new SerializedType({
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Quality is encoded into 64 bits:
|
||||
* (8 bits offset) (56 bits mantissa)
|
||||
*
|
||||
* Quality differs from Amount because it does not need the first two bits
|
||||
* to represent non-native and non-negative
|
||||
*/
|
||||
exports.Quality = new SerializedType({
|
||||
serialize: function(so, val) {
|
||||
var amount = Amount.from_json(val);
|
||||
|
||||
if (!amount.is_valid()) {
|
||||
throw new Error('Not a valid Amount object.');
|
||||
}
|
||||
|
||||
var hi = 0, lo = 0;
|
||||
var value = new BigNumber(amount.to_text());
|
||||
var offset = value.e - 15;
|
||||
|
||||
if (!amount.is_zero()) {
|
||||
// First eight bits: offset/exponent
|
||||
hi |= ((100 + offset) & 0xff) << 24;
|
||||
|
||||
// Remaining 56 bits: mantissa
|
||||
var mantissaDecimal = utils.getMantissaDecimalString(value.abs());
|
||||
var mantissaHex = (new BigNumber(mantissaDecimal)).toString(16);
|
||||
assert(mantissaHex.length <= 16,
|
||||
'Mantissa hex representation ' + mantissaHex +
|
||||
' exceeds the maximum length of 16');
|
||||
hi |= parseInt(mantissaHex.slice(0, -8), 16) & 0xffffff;
|
||||
lo = parseInt(mantissaHex.slice(-8), 16);
|
||||
}
|
||||
|
||||
var valueBytes = sjcl.codec.bytes.fromBits([hi, lo]);
|
||||
|
||||
so.append(valueBytes);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Amount is encoded into 64 bits:
|
||||
* (1 bit non-native) (1 bit non-negative) (8 bits offset) (54 bits mantissa)
|
||||
*/
|
||||
var STAmount = exports.Amount = new SerializedType({
|
||||
serialize: function(so, val) {
|
||||
var amount = Amount.from_json(val);
|
||||
@@ -754,6 +797,7 @@ exports.STMemo = new SerializedType({
|
||||
output.parsed_memo_type = parsedType;
|
||||
}
|
||||
} catch (e) {
|
||||
// empty
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8
|
||||
// string
|
||||
// this is fine, we won't add the parsed_memo_type field
|
||||
@@ -764,6 +808,7 @@ exports.STMemo = new SerializedType({
|
||||
try {
|
||||
output.parsed_memo_format = convertHexToString(output.MemoFormat);
|
||||
} catch (e) {
|
||||
// empty
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8
|
||||
// string
|
||||
// this is fine, we won't add the parsed_memo_format field
|
||||
@@ -783,6 +828,7 @@ exports.STMemo = new SerializedType({
|
||||
output.parsed_memo_data = convertHexToString(output.MemoData);
|
||||
}
|
||||
} catch(e) {
|
||||
// empty
|
||||
// we'll fail in case the content does not match what the MemoFormat
|
||||
// described
|
||||
// this is fine, we won't add the parsed_memo_data, the user has to
|
||||
|
||||
116
test/fixtures/orderbook.js
vendored
116
test/fixtures/orderbook.js
vendored
@@ -156,12 +156,12 @@ module.exports.NATIVE_OFFERS = [
|
||||
PreviousTxnID: 'CD77500EF28984BFC123E8A257C10E44FF486EA8FC43E1356C42BD6DB853A602',
|
||||
PreviousTxnLgrSeq: 8265523,
|
||||
Sequence: 1139002,
|
||||
TakerGets: {
|
||||
TakerGets: '972251352',
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '4.9656112525'
|
||||
},
|
||||
TakerPays: '972251352',
|
||||
index: 'D3338DA77BA23122FB5647B74B53636AB54BE246D4B21707C9D6887DEB334252',
|
||||
owner_funds: '235.0194163432668',
|
||||
quality: '195796912.5171664'
|
||||
@@ -403,6 +403,118 @@ module.exports.DECIMAL_TAKER_PAYS_FUNDED_OFFERS = [
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.LEG_ONE_OFFERS = [
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D043654A0DBD245',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000078',
|
||||
PreviousTxnID: '27723DCE3E6DB324DBCE9F0C9110352DBBC04DD6BEFE2A57C4E524FD215144C9',
|
||||
PreviousTxnLgrSeq: 12024847,
|
||||
Sequence: 14532890,
|
||||
TakerGets: '31461561812',
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '373.019921005'
|
||||
},
|
||||
index: '7EEE980B0BD43C15504B9A89164D29EF02DBBD3807DA7936F51EA2CE3D0C6324',
|
||||
owner_funds: '210586312936',
|
||||
quality: '0.00000001185637010756165'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D043676B9DEA2FC',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000002',
|
||||
PreviousTxnID: '1B36F7DE44C96FBDB50F8F80D24D3FA11454CB837BA4E4D667C92E01AE9225F5',
|
||||
PreviousTxnLgrSeq: 12024788,
|
||||
Sequence: 244399,
|
||||
TakerGets: '25299728855',
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '300'
|
||||
},
|
||||
index: '5F8BDA3343CB792FA0DD55740F5827C5E050A287C96FDE4F7DFF548693420744',
|
||||
owner_funds: '1291056089559',
|
||||
quality: '0.00000001185783459259132'
|
||||
},
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D0437FF40E6F02A',
|
||||
BookNode: '0000000000000000',
|
||||
Expiration: 478636633,
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000165',
|
||||
PreviousTxnID: 'D42D81273BDC3ED611ED84DF07EA55E31703F4E05BC70CC12871715FCB58E160',
|
||||
PreviousTxnLgrSeq: 12024847,
|
||||
Sequence: 3858033,
|
||||
TakerGets: '18189943147',
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '216'
|
||||
},
|
||||
index: 'FD5E66163DFE67919E64F31D506A8F3E94802E6A0FFEBE7A6FD40A2F1135EDD4',
|
||||
owner_funds: '490342145233',
|
||||
quality: '0.0000000118746935190737'
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.LEG_TWO_OFFERS = [
|
||||
{
|
||||
Account: addresses.FOURTH_ACCOUNT,
|
||||
BookDirectory: 'DA36FDE1B8CE294B214BE4E4C958DAAF9C1F46DE1FCB44115D0A4929E095B160',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000003',
|
||||
PreviousTxnID: '97A8D6B2135231363EC1B3B509DF052D481A0045684464948E6DF2C2B9FC1E64',
|
||||
PreviousTxnLgrSeq: 12004045,
|
||||
Sequence: 384,
|
||||
TakerGets: {
|
||||
currency: 'EUR',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '17.07639524223001'
|
||||
},
|
||||
TakerPays: '4943947661',
|
||||
index: '5B00ACF35041983F070EAE2219C274D24A11D6FD6FE4306A4C72E7B769D4F914',
|
||||
owner_funds: '36.40299530003982',
|
||||
quality: '289519397.75'
|
||||
},
|
||||
{
|
||||
Account: addresses.FOURTH_ACCOUNT,
|
||||
BookDirectory: 'DA36FDE1B8CE294B214BE4E4C958DAAF9C1F46DE1FCB44115E12B2D070B5DBE0',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000006',
|
||||
PreviousTxnID: '425EBA467DD335602BAFBAB5329B1E7FC1ABB325AA5CD4495A5085860D09F2BE',
|
||||
PreviousTxnLgrSeq: 11802828,
|
||||
Sequence: 605,
|
||||
TakerGets: {
|
||||
currency: 'EUR',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '19.99999999954904'
|
||||
},
|
||||
TakerPays: '105263157889',
|
||||
index: '8715E674302D446EBD520FF11B48A0F64822F4F9266D62544987223CA16EDBB1',
|
||||
quality: '5263157894.7',
|
||||
taker_gets_funded: {
|
||||
currency: 'EUR',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '19.25393938854825'
|
||||
},
|
||||
taker_pays_funded: '101336523096'
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.bookOffersResponse = function(options) {
|
||||
options = options || {};
|
||||
_.defaults(options, {
|
||||
|
||||
753
test/orderbook-autobridge-test.js
Normal file
753
test/orderbook-autobridge-test.js
Normal file
@@ -0,0 +1,753 @@
|
||||
/*eslint-disable max-len */
|
||||
|
||||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var assert = require('assert-diff');
|
||||
var Remote = require('ripple-lib').Remote;
|
||||
var Currency = require('ripple-lib').Currency;
|
||||
var addresses = require('./fixtures/addresses');
|
||||
var fixtures = require('./fixtures/orderbook');
|
||||
|
||||
describe('OrderBook Autobridging', function() {
|
||||
this.timeout(0);
|
||||
|
||||
it('Initialize IOU/IOU', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
assert.deepEqual(book._legOneBook._currencyGets.to_hex(), Currency.from_json('XRP').to_hex());
|
||||
assert.deepEqual(book._legOneBook._currencyPays.to_hex(), Currency.from_json('USD').to_hex());
|
||||
assert.deepEqual(book._legTwoBook._currencyGets.to_hex(), Currency.from_json('EUR').to_hex());
|
||||
assert.deepEqual(book._legTwoBook._currencyPays.to_hex(), Currency.from_json('XRP').to_hex());
|
||||
});
|
||||
|
||||
it('Compute autobridged offers', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].taker_gets_funded, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].taker_pays_funded, '58.61727326122974');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg one partially funded', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
|
||||
|
||||
legOneOffers[0].owner_funds = '2105863129';
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '7.273651248813431');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '24.96789265329184');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg two partially funded', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
|
||||
|
||||
legTwoOffers[0].owner_funds = '10';
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '10');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.32649132449533');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg two transfer rate', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1002000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
|
||||
|
||||
legTwoOffers[0].owner_funds = '10';
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '9.980039920159681');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.25797537722665');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - taker funds < leg two in', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
|
||||
|
||||
legOneOffers[0].owner_funds = '33461561812';
|
||||
|
||||
legTwoOffers[0].owner_funds = '360';
|
||||
legTwoOffers[0].TakerGets.value = '170.7639524223001';
|
||||
legTwoOffers[0].TakerPays = '49439476610';
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg one partially funded - owners equal', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
|
||||
|
||||
legOneOffers[0].owner_funds = '2105863129';
|
||||
|
||||
legTwoOffers[0].Account = legOneOffers[0].Account;
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg one partially funded - owners equal - leg two in > leg one out', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
|
||||
|
||||
legOneOffers[0].owner_funds = '2105863129';
|
||||
|
||||
legTwoOffers[0].Account = legOneOffers[0].Account;
|
||||
legTwoOffers[0].owner_funds = '360';
|
||||
legTwoOffers[0].TakerGets.value = '170.7639524223001';
|
||||
legTwoOffers[0].TakerPays = '49439476610';
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.0199210049999');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg one consumes leg two fully', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5.038346688725268');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg two consumes first leg one offer fully', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 2));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
|
||||
|
||||
legTwoOffers[0].TakerGets.value = '170.7639524223001';
|
||||
legTwoOffers[0].TakerPays = '49439476610';
|
||||
legTwoOffers[0].owner_funds = '364.0299530003982';
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '62.0957179050155');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '213.1791399943838');
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - owners equal', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1002000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
|
||||
|
||||
legOneOffers[0].owner_funds = '2105863129';
|
||||
legTwoOffers[1].owner_funds = '19.32660005780981';
|
||||
|
||||
legTwoOffers[0].Account = legOneOffers[0].Account;
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '0.4001139945128008');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '24.96789265329184');
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - owners equal - leg one overfunded', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1002000000;
|
||||
|
||||
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
|
||||
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
|
||||
|
||||
legOneOffers[0].owner_funds = '41461561812';
|
||||
|
||||
legTwoOffers[0].Account = legOneOffers[0].Account;
|
||||
|
||||
legTwoOffers[1].owner_funds = '30';
|
||||
|
||||
book._legOneBook.setOffers(legOneOffers);
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5.038346688725268');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - TakerPays < Quality * TakerGets', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
book._legOneBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: '75',
|
||||
TakerPays: {
|
||||
value: '50',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'USD'
|
||||
},
|
||||
owner_funds: '50',
|
||||
quality: '1'
|
||||
}
|
||||
]);
|
||||
|
||||
book._legTwoBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '90',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '90',
|
||||
owner_funds: '150',
|
||||
quality: '1'
|
||||
}
|
||||
]);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - update funded amount', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
book._legOneBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: '100',
|
||||
TakerPays: {
|
||||
value: '100',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'USD'
|
||||
},
|
||||
owner_funds: '50',
|
||||
quality: '1'
|
||||
},
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: '50',
|
||||
TakerPays: {
|
||||
value: '100',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'USD'
|
||||
},
|
||||
quality: '2'
|
||||
}
|
||||
]);
|
||||
|
||||
book._legTwoBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '90',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '90',
|
||||
owner_funds: '150',
|
||||
quality: '1'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '30',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '60',
|
||||
owner_funds: '70',
|
||||
quality: '2'
|
||||
}
|
||||
]);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 3);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '20');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '80');
|
||||
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - update funded amount - owners equal', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
book._legOneBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: '100',
|
||||
TakerPays: {
|
||||
value: '100',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'USD'
|
||||
},
|
||||
owner_funds: '50',
|
||||
quality: '1'
|
||||
},
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: '20',
|
||||
TakerPays: {
|
||||
value: '100',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'USD'
|
||||
},
|
||||
quality: '5'
|
||||
}
|
||||
]);
|
||||
|
||||
book._legTwoBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '90',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '90',
|
||||
owner_funds: '150',
|
||||
quality: '1'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '30',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '60',
|
||||
owner_funds: '70',
|
||||
quality: '2'
|
||||
}
|
||||
]);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 3);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '10');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
|
||||
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - update funded amount - first two owners equal', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
book._legOneBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: '100',
|
||||
TakerPays: {
|
||||
value: '100',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'USD'
|
||||
},
|
||||
owner_funds: '50',
|
||||
quality: '1'
|
||||
},
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: '100',
|
||||
TakerPays: {
|
||||
value: '200',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'USD'
|
||||
},
|
||||
quality: '2'
|
||||
}
|
||||
]);
|
||||
|
||||
book._legTwoBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '90',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '90',
|
||||
owner_funds: '150',
|
||||
quality: '1'
|
||||
},
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '30',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '60',
|
||||
quality: '2'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '20',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '40',
|
||||
owner_funds: '70',
|
||||
quality: '2'
|
||||
}
|
||||
]);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 4);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '25');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
|
||||
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[3].TakerGets.value, '20');
|
||||
assert.strictEqual(book._offersAutobridged[3].TakerPays.value, '80');
|
||||
|
||||
assert(book._offersAutobridged[3].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - unfunded offer - owners equal', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._legOneBook._issuerTransferRate = 1000000000;
|
||||
book._legTwoBook._issuerTransferRate = 1000000000;
|
||||
|
||||
book._legOneBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: '75',
|
||||
TakerPays: {
|
||||
value: '75',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'USD'
|
||||
},
|
||||
owner_funds: '0',
|
||||
quality: '1'
|
||||
}
|
||||
]);
|
||||
|
||||
book._legTwoBook.setOffers([
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
TakerGets: {
|
||||
value: '90',
|
||||
issuer: addresses.ISSUER,
|
||||
currency: 'EUR'
|
||||
},
|
||||
TakerPays: '90',
|
||||
owner_funds: '150',
|
||||
quality: '1'
|
||||
}
|
||||
]);
|
||||
|
||||
book.computeAutobridgedOffers();
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
});
|
||||
@@ -2193,159 +2193,6 @@ describe('OrderBook', function() {
|
||||
assert.strictEqual(book._offers[2].taker_pays_funded, '0');
|
||||
});
|
||||
|
||||
it('Request offers', function(done) {
|
||||
var remote = new Remote();
|
||||
|
||||
var offers = {
|
||||
offers: fixtures.REQUEST_OFFERS
|
||||
};
|
||||
|
||||
remote.request = function(request) {
|
||||
switch (request.message.command) {
|
||||
case 'book_offers':
|
||||
assert.deepEqual(request.message, {
|
||||
command: 'book_offers',
|
||||
id: undefined,
|
||||
taker_gets: {
|
||||
currency: '0000000000000000000000004254430000000000',
|
||||
issuer: addresses.ISSUER
|
||||
},
|
||||
taker_pays: {
|
||||
currency: '0000000000000000000000005553440000000000',
|
||||
issuer: addresses.ISSUER
|
||||
},
|
||||
taker: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
});
|
||||
|
||||
setImmediate(function() {
|
||||
request.emit('success', offers);
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'BTC',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: addresses.ISSUER
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1002000000;
|
||||
|
||||
var expected = [
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 131072,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000000',
|
||||
Sequence: 195,
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '0.1129232560043778'
|
||||
},
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '56.06639660617357'
|
||||
},
|
||||
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
|
||||
owner_funds: '0.1129267125000245',
|
||||
taker_gets_funded: '0.112701309880264',
|
||||
taker_pays_funded: '55.95620035555106',
|
||||
is_fully_funded: false,
|
||||
quality: '496.4999999999999'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
|
||||
BookNode: '0000000000000000',
|
||||
Expiration: 461498565,
|
||||
Flags: 131072,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000144',
|
||||
Sequence: 29354,
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '0.2'
|
||||
},
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '99.72233516476456'
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
owner_funds: '0.950363009783092',
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '0.2',
|
||||
taker_pays_funded: '99.72233516476456',
|
||||
quality: '498.6116758238228'
|
||||
},
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
|
||||
BookNode: '0000000000000000',
|
||||
Expiration: 461498565,
|
||||
Flags: 131072,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000144',
|
||||
Sequence: 29356,
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '0.5'
|
||||
},
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '99.72233516476456'
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
owner_funds: '0.950363009783092',
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '0.5',
|
||||
taker_pays_funded: '99.72233516476456',
|
||||
quality: '498.6116758238228'
|
||||
},
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
|
||||
BookNode: '0000000000000000',
|
||||
Expiration: 461498565,
|
||||
Flags: 131078,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000144',
|
||||
Sequence: 29354,
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '0.5'
|
||||
},
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '99.72233516476456'
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
owner_funds: '0.950363009783092',
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '0.4484660776278363',
|
||||
taker_pays_funded: '89.44416900646082',
|
||||
quality: '199.4446703295291'
|
||||
}
|
||||
];
|
||||
|
||||
book.on('model', function(model) {
|
||||
assert.deepEqual(model, expected);
|
||||
assert.strictEqual(book._synchronized, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Request offers - native currency', function(done) {
|
||||
var remote = new Remote();
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
/*eslint-disable max-len */
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var BigNumber = require('bignumber.js');
|
||||
var SerializedObject = require('ripple-lib').SerializedObject;
|
||||
var types = require('ripple-lib').types;
|
||||
var Amount = require('ripple-lib').Amount;
|
||||
@@ -282,9 +287,9 @@ describe('Serialized types', function() {
|
||||
});
|
||||
it('Does not get confused when the high bit is set', function() {
|
||||
var so = new SerializedObject();
|
||||
types.Int64.serialize(so, "8B2386F26F8E232B");
|
||||
types.Int64.serialize(so, '8B2386F26F8E232B');
|
||||
assert.strictEqual(so.to_hex(), '8B2386F26F8E232B');
|
||||
var so = new SerializedObject("8B2386F26F8E232B");
|
||||
so = new SerializedObject('8B2386F26F8E232B');
|
||||
var num = types.Int64.parse(so);
|
||||
// We get a positive number
|
||||
assert.strictEqual(num.toString(), '0x8b2386f26f8e232b');
|
||||
@@ -301,7 +306,8 @@ describe('Serialized types', function() {
|
||||
});
|
||||
it('Serialize bn("FFEEDDCCBBAA9988")', function() {
|
||||
var so = new SerializedObject();
|
||||
types.Int64.serialize(so, new sjcl.bn('FFEEDDCCBBAA9988', 16));
|
||||
var BN = sjcl.bn;
|
||||
types.Int64.serialize(so, new BN('FFEEDDCCBBAA9988', 16));
|
||||
assert.strictEqual(so.to_hex(), 'FFEEDDCCBBAA9988');
|
||||
});
|
||||
it('Fail to serialize BigNumber("-1")', function() {
|
||||
@@ -341,7 +347,7 @@ describe('Serialized types', function() {
|
||||
});
|
||||
});
|
||||
it('Parse "0123456789ABCDEF"', function() {
|
||||
var so = new SerializedObject("0123456789ABCDEF");
|
||||
var so = new SerializedObject('0123456789ABCDEF');
|
||||
var num = types.Int64.parse(so);
|
||||
assert.strictEqual(num.toString(), '0x123456789abcdef');
|
||||
});
|
||||
@@ -481,6 +487,19 @@ describe('Serialized types', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Quality', function() {
|
||||
it('Serialize 1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function() {
|
||||
var so = new SerializedObject();
|
||||
types.Quality.serialize(so, '1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(so.to_hex(), '55038D7EA4C68000');
|
||||
});
|
||||
it('Serialize 87654321.12345678/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function() {
|
||||
var so = new SerializedObject();
|
||||
types.Quality.serialize(so, '87654321.12345678/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(so.to_hex(), '5C1F241D335BF24E');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Amount', function() {
|
||||
it('Serialize 0 XRP', function() {
|
||||
var so = new SerializedObject();
|
||||
@@ -534,9 +553,9 @@ describe('Serialized types', function() {
|
||||
// Transaction #A2AD66C93C7B7277CD5AEB718A4E82D88C7099129948BC66A394EE38B34657A9
|
||||
var so = new SerializedObject();
|
||||
types.Amount.serialize(so, {
|
||||
"value":"1000",
|
||||
"currency":"XRP",
|
||||
"issuer":"rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo"
|
||||
value: '1000',
|
||||
currency: 'XRP',
|
||||
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'
|
||||
});
|
||||
assert.strictEqual(so.to_hex(), 'D5438D7EA4C680000000000000000000000000005852500000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367');
|
||||
});
|
||||
@@ -544,9 +563,9 @@ describe('Serialized types', function() {
|
||||
it('Serialize 15/015841551A748AD23FEFFFFFFFEA028000000000/1', function() {
|
||||
var so = new SerializedObject();
|
||||
types.Amount.serialize(so, {
|
||||
"value":"1000",
|
||||
"currency":"015841551A748AD23FEFFFFFFFEA028000000000",
|
||||
"issuer":"rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo"
|
||||
value: '1000',
|
||||
currency: '015841551A748AD23FEFFFFFFFEA028000000000',
|
||||
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'
|
||||
});
|
||||
assert.strictEqual(so.to_hex(), 'D5438D7EA4C68000015841551A748AD23FEFFFFFFFEA028000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367');
|
||||
});
|
||||
@@ -576,7 +595,7 @@ describe('Serialized types', function() {
|
||||
// hex(1161981756646125568) = 1020304050607000
|
||||
var so = new SerializedObject('1020304050607000');
|
||||
types.Amount.parse(so).to_json();
|
||||
})
|
||||
});
|
||||
});
|
||||
it('Parse 1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function() {
|
||||
var so = new SerializedObject('D4838D7EA4C680000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
|
||||
@@ -602,33 +621,33 @@ describe('Serialized types', function() {
|
||||
var base58 = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp';
|
||||
var so = new SerializedObject();
|
||||
types.Account.serialize(so, base58);
|
||||
assert.strictEqual(so.to_hex(), "14"+hex);
|
||||
assert.strictEqual(so.to_hex(), '14' + hex);
|
||||
|
||||
so = new SerializedObject();
|
||||
types.Account.serialize(so, hex);
|
||||
assert.strictEqual(so.to_hex(), "14"+hex);
|
||||
assert.strictEqual(so.to_hex(), '14' + hex);
|
||||
});
|
||||
it('Serialize 1', function() {
|
||||
var hex = '0000000000000000000000000000000000000001';
|
||||
var base58 = 'rrrrrrrrrrrrrrrrrrrrBZbvji';
|
||||
var so = new SerializedObject();
|
||||
types.Account.serialize(so, base58);
|
||||
assert.strictEqual(so.to_hex(), "14"+hex);
|
||||
assert.strictEqual(so.to_hex(), '14' + hex);
|
||||
|
||||
so = new SerializedObject();
|
||||
types.Account.serialize(so, hex);
|
||||
assert.strictEqual(so.to_hex(), "14"+hex);
|
||||
assert.strictEqual(so.to_hex(), '14' + hex);
|
||||
});
|
||||
it('Serialize FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', function() {
|
||||
var hex = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
|
||||
var base58 = 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi';
|
||||
var so = new SerializedObject();
|
||||
types.Account.serialize(so, base58);
|
||||
assert.strictEqual(so.to_hex(), "14"+hex);
|
||||
assert.strictEqual(so.to_hex(), '14' + hex);
|
||||
|
||||
so = new SerializedObject();
|
||||
types.Account.serialize(so, hex);
|
||||
assert.strictEqual(so.to_hex(), "14"+hex);
|
||||
assert.strictEqual(so.to_hex(), '14' + hex);
|
||||
});
|
||||
it('Parse 0', function() {
|
||||
var val = '140000000000000000000000000000000000000000';
|
||||
@@ -679,16 +698,16 @@ describe('Serialized types', function() {
|
||||
var hex = '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF1000000000000000000000000000000000000000003100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100';
|
||||
var json = [
|
||||
[{
|
||||
account: "rrrrrrrrrrrrrrrrrrrrNxV3Xza",
|
||||
account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
|
||||
currency: 'USD',
|
||||
issuer: "rrrrrrrrrrrrrrrrrrrpYnYCNYf"
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf'
|
||||
}],
|
||||
[{
|
||||
currency: "XRP"
|
||||
currency: 'XRP'
|
||||
}, {
|
||||
account: "rrrrrrrrrrrrrrrrrrrpvQsW3V3",
|
||||
account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3',
|
||||
currency: 'EUR',
|
||||
issuer: "rrrrrrrrrrrrrrrrrrrdHRtqg2"
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2'
|
||||
}]
|
||||
];
|
||||
|
||||
@@ -725,17 +744,17 @@ describe('Serialized types', function() {
|
||||
var hex = '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF1000000000000000000000000058525000000000003100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100';
|
||||
var json = [
|
||||
[{
|
||||
account: "rrrrrrrrrrrrrrrrrrrrNxV3Xza",
|
||||
account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
|
||||
currency: 'USD',
|
||||
issuer: "rrrrrrrrrrrrrrrrrrrpYnYCNYf"
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf'
|
||||
}],
|
||||
[{
|
||||
currency: "XRP",
|
||||
currency: 'XRP',
|
||||
non_native: true
|
||||
}, {
|
||||
account: "rrrrrrrrrrrrrrrrrrrpvQsW3V3",
|
||||
account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3',
|
||||
currency: 'EUR',
|
||||
issuer: "rrrrrrrrrrrrrrrrrrrdHRtqg2"
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2'
|
||||
}]
|
||||
];
|
||||
|
||||
@@ -778,51 +797,51 @@ describe('Serialized types', function() {
|
||||
var hex = '31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367310A20B3C85F482532A9578DBB3950B85CA06594D100000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D13000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F33673115036E2D3F5437A83E5AC3CAEE34FF2C21DEB618000000000000000000000000425443000000000015036E2D3F5437A83E5AC3CAEE34FF2C21DEB6183000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C13157180C769B66D942EE69E6DCC940CA48D82337AD000000000000000000000000425443000000000057180C769B66D942EE69E6DCC940CA48D82337AD1000000000000000000000000058525000000000003000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D100';
|
||||
var json = [
|
||||
[{
|
||||
"account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K",
|
||||
"currency": "BTC",
|
||||
"issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K"
|
||||
account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'
|
||||
}, {
|
||||
"account": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo",
|
||||
"currency": "BTC",
|
||||
"issuer": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo"
|
||||
account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
|
||||
currency: 'BTC',
|
||||
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'
|
||||
}, {
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"currency": "BTC",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
currency: 'BTC',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
}, {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
}],
|
||||
[{
|
||||
"account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K",
|
||||
"currency": "BTC",
|
||||
"issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K"
|
||||
account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'
|
||||
}, {
|
||||
"account": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo",
|
||||
"currency": "BTC",
|
||||
"issuer": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo"
|
||||
account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
|
||||
currency: 'BTC',
|
||||
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'
|
||||
}, {
|
||||
"account": "rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi",
|
||||
"currency": "BTC",
|
||||
"issuer": "rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi"
|
||||
account: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi',
|
||||
currency: 'BTC',
|
||||
issuer: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi'
|
||||
}, {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
}],
|
||||
[{
|
||||
"account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K",
|
||||
"currency": "BTC",
|
||||
"issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K"
|
||||
account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'
|
||||
}, {
|
||||
"account": "r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn",
|
||||
"currency": "BTC",
|
||||
"issuer": "r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn"
|
||||
account: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn',
|
||||
currency: 'BTC',
|
||||
issuer: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn'
|
||||
}, {
|
||||
"currency": "XRP",
|
||||
"non_native": true
|
||||
currency: 'XRP',
|
||||
non_native: true
|
||||
}, {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
}]
|
||||
];
|
||||
|
||||
@@ -917,23 +936,35 @@ describe('Serialized types', function() {
|
||||
var so = new SerializedObject('31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF31000000000000000000000000000000000000007B000000000000000000000000425443000000000000000000000000000000000000000000000003153100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100');
|
||||
|
||||
var parsed_path = types.PathSet.parse(so);
|
||||
var comp = [ [ { account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
|
||||
var comp = [
|
||||
[
|
||||
{
|
||||
account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
|
||||
currency: 'USD',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf',
|
||||
type: 49,
|
||||
type_hex: '0000000000000031' } ],
|
||||
[ { account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
|
||||
type_hex: '0000000000000031'
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
|
||||
currency: 'BTC',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf',
|
||||
type: 49,
|
||||
type_hex: '0000000000000031' },
|
||||
{ account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3',
|
||||
type_hex: '0000000000000031'
|
||||
},
|
||||
{
|
||||
account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3',
|
||||
currency: 'EUR',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2',
|
||||
type: 49,
|
||||
type_hex: '0000000000000031' } ] ];
|
||||
type_hex: '0000000000000031'
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
assert.deepEqual(SerializedObject.jsonify_structure(parsed_path, ""), comp);
|
||||
assert.deepEqual(SerializedObject.jsonify_structure(parsed_path, ''), comp);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -943,16 +974,16 @@ describe('Serialized types', function() {
|
||||
var so = new SerializedObject(hex);
|
||||
var as_json = so.to_json();
|
||||
var expected_json = {
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"Owner": "rh6kN9s7spSb3vdv6H8ZGYzsddSLeEUGmc",
|
||||
"Flags": 0,
|
||||
"Indexes": [
|
||||
"081342A0AB45459A54D8E4FA1842339A102680216CF9A152BCE4F4CE467D8246"
|
||||
LedgerEntryType: 'DirectoryNode',
|
||||
Owner: 'rh6kN9s7spSb3vdv6H8ZGYzsddSLeEUGmc',
|
||||
Flags: 0,
|
||||
Indexes: [
|
||||
'081342A0AB45459A54D8E4FA1842339A102680216CF9A152BCE4F4CE467D8246'
|
||||
],
|
||||
"RootIndex": "000360186E008422E06B72D5B275E29EE3BE9D87A370F424E0E7BF613C465909"
|
||||
}
|
||||
RootIndex: '000360186E008422E06B72D5B275E29EE3BE9D87A370F424E0E7BF613C465909'
|
||||
};
|
||||
assert.deepEqual(as_json, expected_json);
|
||||
assert.strictEqual(SerializedObject.from_json(expected_json).to_hex(), hex)
|
||||
assert.strictEqual(SerializedObject.from_json(expected_json).to_hex(), hex);
|
||||
});
|
||||
it('Serialize empty object {}', function() {
|
||||
var so = new SerializedObject();
|
||||
@@ -961,7 +992,7 @@ describe('Serialized types', function() {
|
||||
});
|
||||
it('Parse empty object {}', function() {
|
||||
var so = new SerializedObject('E1');
|
||||
var parsed_object = types.Object.parse(so)
|
||||
var parsed_object = types.Object.parse(so);
|
||||
assert.deepEqual(parsed_object, {});
|
||||
});
|
||||
it('Serialize simple object {TakerPays:"87654321.12345678/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", TakerGets:213, Fee:"789"}', function() {
|
||||
@@ -977,26 +1008,37 @@ describe('Serialized types', function() {
|
||||
it('Parse same object', function() {
|
||||
var so = new SerializedObject('64D65F241D335BF24E0000000000000000000000004555520000000000B5F762798A53D543A014CAF8B297CFF8F2F937E86540000000000000D5684000000000000315E1');
|
||||
var parsed_object = types.Object.parse(so);
|
||||
var comp = { TakerPays:
|
||||
{ value: '87654321.12345678',
|
||||
var comp = {
|
||||
TakerPays: {
|
||||
value: '87654321.12345678',
|
||||
currency: 'EUR',
|
||||
issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh' },
|
||||
issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
|
||||
},
|
||||
TakerGets: '213',
|
||||
Fee: '789' };
|
||||
assert.deepEqual(SerializedObject.jsonify_structure(parsed_object, ""), comp);
|
||||
Fee: '789'
|
||||
};
|
||||
assert.deepEqual(SerializedObject.jsonify_structure(parsed_object, ''), comp);
|
||||
// TODO: Check independently.
|
||||
});
|
||||
|
||||
it('Serialize simple object {DestinationTag:123, QualityIn:456, QualityOut:789}', function() {
|
||||
var so = new SerializedObject();
|
||||
types.Object.serialize(so, {DestinationTag:123, QualityIn:456, QualityOut:789});
|
||||
types.Object.serialize(so, {
|
||||
DestinationTag: 123,
|
||||
QualityIn: 456,
|
||||
QualityOut: 789
|
||||
});
|
||||
assert.strictEqual(so.to_hex(), '2E0000007B2014000001C8201500000315E1');
|
||||
// TODO: Check independently.
|
||||
});
|
||||
it('Parse simple object {DestinationTag:123, QualityIn:456, QualityOut:789}', function() {// 2E0000007B22000001C82400000315E1 2E0000007B2002000001C8200200000315E1
|
||||
var so = new SerializedObject('2E0000007B2014000001C8201500000315E1');
|
||||
var parsed_object = types.Object.parse(so);
|
||||
assert.deepEqual(parsed_object, { DestinationTag:123, QualityIn:456, QualityOut:789 });
|
||||
assert.deepEqual(parsed_object, {
|
||||
DestinationTag: 123,
|
||||
QualityIn: 456,
|
||||
QualityOut: 789
|
||||
});
|
||||
// TODO: Check independently.
|
||||
});
|
||||
});
|
||||
@@ -1014,28 +1056,68 @@ describe('Serialized types', function() {
|
||||
});
|
||||
it('Serialize 3-length array [{TakerPays:123}); {TakerGets:456}, {Fee:789}]', function() {
|
||||
var so = new SerializedObject();
|
||||
types.Array.serialize(so, [{TakerPays:123}, {TakerGets:456}, {Fee:789}]);
|
||||
types.Array.serialize(so, [
|
||||
{
|
||||
TakerPays: 123
|
||||
},
|
||||
{
|
||||
TakerGets: 456
|
||||
},
|
||||
{
|
||||
Fee: 789
|
||||
}
|
||||
]);
|
||||
// TODO: Check this manually
|
||||
assert.strictEqual(so.to_hex(), '64400000000000007B6540000000000001C8684000000000000315F1');
|
||||
});
|
||||
it('Parse the same array', function() {
|
||||
var so = new SerializedObject('64400000000000007B6540000000000001C8684000000000000315F1');
|
||||
var parsed_object = types.Array.parse(so);
|
||||
var comp = [ { TakerPays: '123' }, { TakerGets: '456' }, { Fee: '789' } ];
|
||||
assert.deepEqual(SerializedObject.jsonify_structure(parsed_object, ""), comp);
|
||||
var comp = [
|
||||
{
|
||||
TakerPays: '123'
|
||||
},
|
||||
{
|
||||
TakerGets: '456'
|
||||
},
|
||||
{
|
||||
Fee: '789'
|
||||
}
|
||||
];
|
||||
assert.deepEqual(SerializedObject.jsonify_structure(parsed_object, ''), comp);
|
||||
});
|
||||
it('Serialize 3-length array [{DestinationTag:123}); {QualityIn:456}, {Fee:789}]', function() {
|
||||
var so = new SerializedObject();
|
||||
types.Array.serialize(so, [{DestinationTag:123}, {QualityIn:456}, {Fee:789}]);
|
||||
types.Array.serialize(so, [
|
||||
{
|
||||
DestinationTag: 123
|
||||
},
|
||||
{
|
||||
QualityIn: 456
|
||||
},
|
||||
{
|
||||
Fee: 789
|
||||
}
|
||||
]);
|
||||
// TODO: Check this manually
|
||||
assert.strictEqual(so.to_hex(), '2E0000007B2014000001C8684000000000000315F1');
|
||||
});
|
||||
it('Parse the same array 2', function() {
|
||||
var so = new SerializedObject('2E0000007B2014000001C8684000000000000315F1');
|
||||
var parsed_object = types.Array.parse(so);
|
||||
var comp = [ { DestinationTag: 123 }, { QualityIn: 456 }, { Fee: '789' } ];
|
||||
var comp = [
|
||||
{
|
||||
DestinationTag: 123
|
||||
},
|
||||
{
|
||||
QualityIn: 456
|
||||
},
|
||||
{
|
||||
Fee: '789'
|
||||
}
|
||||
];
|
||||
// TODO: Is this correct? Return some things as integers, and others as objects?
|
||||
assert.deepEqual( SerializedObject.jsonify_structure(parsed_object, ""), comp);
|
||||
assert.deepEqual(SerializedObject.jsonify_structure(parsed_object, ''), comp);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user