From a38e17beed98bc13d72f3830565bbae476911f92 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 19 Feb 2013 17:35:42 +0100 Subject: [PATCH 1/2] JS: Added class OrderBook for listening to order books. --- src/js/orderbook.js | 104 ++++++++++++++++++++++++++++++++++++++++++++ src/js/remote.js | 35 +++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 src/js/orderbook.js diff --git a/src/js/orderbook.js b/src/js/orderbook.js new file mode 100644 index 000000000..c78674e07 --- /dev/null +++ b/src/js/orderbook.js @@ -0,0 +1,104 @@ +// Routines for working with an orderbook. +// +// Events: + +// var network = require("./network.js"); + +var EventEmitter = require('events').EventEmitter; +var Amount = require('./amount').Amount; +var UInt160 = require('./uint160').UInt160; +var Currency = require('./currency').Currency; + +var extend = require('extend'); + +var OrderBook = function (remote, + currency_out, issuer_out, + currency_in, issuer_in) { + var self = this; + + this._remote = remote; + this._currency_out = currency_out; + this._issuer_out = issuer_out; + this._currency_in = currency_in; + this._issuer_in = issuer_in; + + this._subs = 0; + + // Ledger entry object + // Important: This must never be overwritten, only extend()-ed + this._entry = {}; + + this.on('newListener', function (type, listener) { + if (OrderBook.subscribe_events.indexOf(type) !== -1) { + if (!self._subs && 'open' === self._remote._online_state) { + self._remote.request_subscribe() + .books([self.to_json()]) + .request(); + } + self._subs += 1; + } + }); + + this.on('removeListener', function (type, listener) { + if (OrderBook.subscribe_events.indexOf(type) !== -1) { + self._subs -= 1; + + if (!self._subs && 'open' === self._remote._online_state) { + self._remote.request_unsubscribe() + .books([self.to_json()]) + .request(); + } + } + }); + + this._remote.on('connect', function () { + if (self._subs) { + self._remote.request_subscribe() + .books([self.to_json()]) + .request(); + } + }); + + return this; +}; + +OrderBook.prototype = new EventEmitter; + +/** + * List of events that require a remote subscription to the orderbook. + */ +OrderBook.subscribe_events = ['transaction']; + +OrderBook.prototype.to_json = function () +{ + var json = { + "CurrencyOut": this._currency_out, + "CurrencyIn": this._currency_in + }; + + if (json["CurrencyOut"] !== "XRP") json["IssuerOut"] = this._issuer_out; + if (json["CurrencyIn"] !== "XRP") json["IssuerIn"] = this._issuer_in; + + return json; +}; + +/** + * Whether the OrderBook is valid. + * + * Note: This only checks whether the parameters (currencies and issuer) are + * syntactically valid. It does not check anything against the ledger. + */ +OrderBook.prototype.is_valid = function () +{ + return ( + Currency.is_valid(this._currency_in) && + (this._currency_in !== "XRP" && UInt160.is_valid(this._issuer_in)) && + Currency.is_valid(this._currency_out) && + (this._currency_out !== "XRP" && UInt160.is_valid(this._issuer_out)) && + !(this._currency_in === "XRP" && this._currency_out === "XRP") + ); +}; + +exports.OrderBook = OrderBook; + +// vim:sw=2:sts=2:ts=8:et diff --git a/src/js/remote.js b/src/js/remote.js index 9fdd70f66..8507c4a3f 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -22,6 +22,7 @@ var UInt160 = require('./amount').UInt160; var Transaction = require('./transaction').Transaction; var Account = require('./account').Account; var Meta = require('./meta').Meta; +var OrderBook = require('./orderbook').OrderBook; var utils = require('./utils'); var config = require('./config'); @@ -181,6 +182,31 @@ Request.prototype.rt_accounts = function (accounts) { return this.accounts(accounts, true); }; +Request.prototype.books = function (books) { + var procBooks = []; + + for (var i = 0, l = books.length; i < l; i++) { + var book = books[i]; + + var json = { + "CurrencyOut": Currency.json_rewrite(book["CurrencyOut"]), + "CurrencyIn": Currency.json_rewrite(book["CurrencyIn"]) + }; + + if (json["CurrencyOut"] !== "XRP") { + json["IssuerOut"] = UInt160.json_rewrite(book["IssuerOut"]); + } + if (json["CurrencyIn"] !== "XRP") { + json["IssuerIn"] = UInt160.json_rewrite(book["IssuerIn"]); + } + + procBooks.push(json); + } + this.message.books = procBooks; + + return this; +}; + // // Remote - access to a remote Ripple server via websocket. // @@ -1036,6 +1062,15 @@ Remote.prototype.account = function (accountId) { return this._accounts[account.to_json()] = account; }; +Remote.prototype.book = function (currency_out, issuer_out, + currency_in, issuer_in) { + var book = new OrderBook(this, + currency_out, issuer_out, + currency_in, issuer_in); + + return book; +} + // Return the next account sequence if possible. // <-- undefined or Sequence Remote.prototype.account_seq = function (account, advance) { From aa77f433e44bacbd96e720a6a11e40e105a78aa8 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Thu, 21 Feb 2013 23:06:12 +0100 Subject: [PATCH 2/2] JS: Failing test. Amount#equals does not compare exponent. --- test/amount-test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/amount-test.js b/test/amount-test.js index 550dc2963..42674b91b 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -218,6 +218,11 @@ buster.testCase("Amount", { "Divide EUR by XRP, neg, <1" : function () { buster.assert.equals("-0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").divide(Amount.from_json("2000")).to_text_full()); } + }, + "Amount comparisons" : { + "10 USD != 100 USD" : function () { + buster.refute(Amount.from_json("10/USD/rNDKeo9RrCiRdfsMG8AdoZvNZxHASGzbZL").equals(Amount.from_json("100/USD/rNDKeo9RrCiRdfsMG8AdoZvNZxHASGzbZL"))); + } } });