UNPKG

divvy-lib-orderbook

Version:
1,736 lines (1,427 loc) 75.3 kB
/* eslint-disable max-len, indent, no-unused-vars */ 'use strict' // eslint-disable-line strict const assert = require('assert-diff') const addresses = require('./fixtures/addresses') const fixtures = require('./fixtures/orderbook') const {XDVValue, IOUValue} = require('divvy-lib-value') const DivvyAPI = require('divvy-lib').DivvyAPI const OrderBook = require('../src/orderbook').OrderBook const OrderBookUtils = require('../src/orderbookutils') const EventEmitter = require('events').EventEmitter describe('OrderBook', function() { function createOrderBook(options) { const api = new DivvyAPI() const orderbook = OrderBook.createOrderBook(api, options) return orderbook } it('toJSON', function() { let book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) assert.deepEqual(book.toJSON(), { taker_gets: { currency: 'XDV' }, taker_pays: { currency: 'BTC', issuer: addresses.ISSUER } }) book = createOrderBook({ issuer_gets: addresses.ISSUER, currency_gets: 'BTC', currency_pays: 'XDV' }) assert.deepEqual(book.toJSON(), { taker_gets: { currency: 'BTC', issuer: addresses.ISSUER }, taker_pays: { currency: 'XDV' } }) }) it('Check orderbook validity', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) assert(book.isValid()) }) it('Automatic subscription (based on listeners)', function(done) { this.timeout(100) const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._api.isConnected = function() { return true } book._api.connection.request = function(message) { const response = {} if (message.command === 'account_info') { response.account_data = { TransferRate: 1002000000 } } else if (message.command === 'book_offers') { response.offers = [] } return Promise.resolve(response) } book.on('model', function(offers) { assert.strictEqual(offers.length, 0) done() }) }) it('Automatic subscription (based on listeners) - autobridged', function(done) { this.timeout(200) const book = createOrderBook({ currency_pays: 'USD', issuer_pays: addresses.ISSUER, currency_gets: 'BTC', issuer_gets: addresses.ISSUER }) let subscribed = false book._api.isConnected = function() { return true } book._api.connection.request = function(message) { const response = {} if (message.command === 'account_info') { response.account_data = { TransferRate: 1002000000 } } else if (message.command === 'subscribe') { if (message.streams && message.streams[0] === 'transactions') { subscribed = true } } else if (message.command === 'book_offers') { response.offers = [] response.ledger_index = 32571 } return Promise.resolve(response) } book.on('model', function(offers) { assert(subscribed) assert.strictEqual(offers.length, 0) done() }) }) it('Automatic unsubscription - remove all listeners', function(done) { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._subscribe = function(subscribe) { if (!subscribe) { done() } } book.on('model', function() {}) book.removeAllListeners('model') }) it('Automatic unsubscription - once listener', function(done) { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._subscribe = function(subscribe) { if (!subscribe) { done() } } book.once('model', function() {}) book.emit('model', {}) }) it('Automatic unsubscription - check unsubscribed', function(done) { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._issuerTransferRate = new IOUValue('1.000000000') book._api.isConnected = function() { return true } console.log('---- ', EventEmitter.listenerCount(book._api.connection, 'transaction')) console.log('---- ', EventEmitter.listenerCount(book._api.connection, 'connected')) assert.strictEqual(EventEmitter.listenerCount(book._api.connection, 'transaction'), 0) assert.strictEqual(EventEmitter.listenerCount(book._api.connection, 'connected'), 0) function noop() {} book.on('model', noop) setTimeout(() => { console.log('2 ---- ', EventEmitter.listenerCount(book._api.connection, 'transaction')) console.log('2 ---- ', EventEmitter.listenerCount(book._api.connection, 'connected')) assert.strictEqual(EventEmitter.listenerCount(book._api.connection, 'transaction'), 1) assert.strictEqual(EventEmitter.listenerCount(book._api.connection, 'connected'), 1) book.removeListener('model', noop) setTimeout(() => { console.log('3 ---- ', EventEmitter.listenerCount(book._api.connection, 'transaction')) console.log('3 ---- ', EventEmitter.listenerCount(book._api.connection, 'connected')) assert.strictEqual(EventEmitter.listenerCount(book._api.connection, 'transaction'), 0) assert.strictEqual(EventEmitter.listenerCount(book._api.connection, 'connected'), 0) done() }, 2) }, 1) }) it('Subscribe to transactions on reconnect', function(done) { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) let subscribeRequests = 0 let unSubscribeRequests = 0 book._api.isConnected = function() { return true } book._api.connection.request = function(message) { const response = {} if (message.command === 'account_info') { response.account_data = { TransferRate: 1002000000 } } else if (message.command === 'unsubscribe') { unSubscribeRequests++ } else if (message.command === 'subscribe') { subscribeRequests++ } else if (message.command === 'book_offers') { response.offers = [] } return Promise.resolve(response) } let models = 0 book.on('model', function(offers) { assert.strictEqual(offers.length, 0) if (models++ === 1) { assert.strictEqual(subscribeRequests, 2) assert.strictEqual(unSubscribeRequests, 1) done() } }) book._api.connection.emit('connected') }) it('Set owner funds', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._issuerTransferRate = new IOUValue('1.000000000') book._setOwnerFunds(addresses.ACCOUNT, '1') assert.strictEqual(book._getOwnerFunds(addresses.ACCOUNT).toString(), '1') }) it('Set owner funds - unadjusted funds', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._issuerTransferRate = new IOUValue('1.002000000') book._setOwnerFunds(addresses.ACCOUNT, '1') assert.strictEqual(book._ownerFundsUnadjusted[addresses.ACCOUNT], '1') }) it('Set owner funds - invalid account', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) assert.throws(function() { book._setOwnerFunds('0rrrrrrrrrrrrrrrrrrrrBZbvji', '1') }) }) it('Set owner funds - invalid amount', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) assert.throws(function() { book._setOwnerFunds(addresses.ACCOUNT, null) }) }) it('Has owner funds', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._ownerFunds[addresses.ACCOUNT] = '1' assert(book._hasOwnerFunds(addresses.ACCOUNT)) }) it('Delete owner funds', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._ownerFunds[addresses.ACCOUNT] = '1' assert(book._hasOwnerFunds(addresses.ACCOUNT)) book._deleteOwnerFunds(addresses.ACCOUNT) assert(!book._hasOwnerFunds(addresses.ACCOUNT)) }) it('Increment owner offer count', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) assert.strictEqual(book._incrementOwnerOfferCount(addresses.ACCOUNT), 1) assert.strictEqual(book._offerCounts[addresses.ACCOUNT], 1) }) it('Decrement owner offer count', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._incrementOwnerOfferCount(addresses.ACCOUNT) assert.strictEqual(book._decrementOwnerOfferCount(addresses.ACCOUNT), 0) assert.strictEqual(book._offerCounts[addresses.ACCOUNT], 0) }) it('Decrement owner offer count - no more offers', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._incrementOwnerOfferCount(addresses.ACCOUNT) assert.strictEqual(book._decrementOwnerOfferCount(addresses.ACCOUNT), 0) assert.strictEqual(book._offerCounts[addresses.ACCOUNT], 0) assert.throws(function() { book._getOwnerFunds(addresses.ACCOUNT) }) }) it('Subtract owner offer total', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._ownerOffersTotal[addresses.ACCOUNT] = new IOUValue('3') const newAmount = book._subtractOwnerOfferTotal(addresses.ACCOUNT, { value: 2, currency: 'BTC', issuer: addresses.ISSUER }) const offerTotal = new IOUValue('1') assert(newAmount.equals(offerTotal)) }) it('Subtract owner offer total - negative total', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) assert.throws(function() { book._subtractOwnerOfferTotal(addresses.ACCOUNT, new IOUValue('2')) }) }) it('Get owner offer total', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._ownerOffersTotal[addresses.ACCOUNT] = new IOUValue('3') assert.strictEqual( book._getOwnerOfferTotal(addresses.ACCOUNT).toString(), '3') }) it('Get owner offer total - native', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'XDV' }) book._ownerOffersTotal[addresses.ACCOUNT] = new IOUValue('3') assert.strictEqual( book._getOwnerOfferTotal(addresses.ACCOUNT).toString(), '3') }) it('Get owner offer total - no total', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) assert.strictEqual( book._getOwnerOfferTotal(addresses.ACCOUNT).toString(), '0') }) it('Get owner offer total - native - no total', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) assert(book._getOwnerOfferTotal(addresses.ACCOUNT).toString(), '0') }) it('Apply transfer rate - cached transfer rate', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') assert.strictEqual(book._applyTransferRate('1'), '0.9980039920159681') }) it('Apply transfer rate - native currency', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._issuerTransferRate = new IOUValue('1.000000000') assert.strictEqual(book._applyTransferRate('0.9980039920159681'), '0.9980039920159681') }) it('Apply transfer rate - invalid balance', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) assert.throws(function() { book._applyTransferRate('asdf') }) }) it('Apply transfer rate - invalid transfer rate', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) assert.throws(function() { book._applyTransferRate('1') }) }) it('Request transfer rate', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._api.connection.request = function(request) { assert.deepEqual(request, { command: 'account_info', ledger_index: 'validated', account: addresses.ISSUER }) return Promise.resolve({ account_data: { TransferRate: 1002000000 } }) } return book._requestTransferRate().then(rate => { assert(rate.equals(new IOUValue('1.002000000'))) }) }) it('Request transfer rate - not set', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._api.connection.request = function(request) { assert.deepEqual(request, { command: 'account_info', ledger_index: 'validated', account: addresses.ISSUER }) return Promise.resolve({ account_data: { } }) } return book._requestTransferRate().then(rate => { assert(rate.equals(new IOUValue('1.000000000'))) }) }) it('Request transfer rate - cached transfer rate', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') book._api.connection.request = function() { assert(false, 'Must not be called') } return book._requestTransferRate().then(rate => { assert(rate.equals(new IOUValue('1.002000000'))) }) }) it('Request transfer rate - native currency', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._api.connection.request = function() { assert(false, 'Must not be called') } return book._requestTransferRate().then(rate => { assert(rate.equals(new IOUValue('1.000000000'))) assert(book._issuerTransferRate.equals(new IOUValue('1.000000000'))) }) }) it('Set offer funded amount - iou/xdv - fully funded', function() { const book = createOrderBook({ currency_gets: 'BTC', currency_pays: 'XDV', issuer_gets: addresses.ISSUER }) book._issuerTransferRate = new IOUValue('1.000000000') const offer = { Account: addresses.ACCOUNT, TakerGets: { value: '100', currency: 'BTC', issuer: addresses.ISSUER }, TakerPays: '123456' } book._setOwnerFunds(addresses.ACCOUNT, '100.1234') book._setOfferFundedAmount(offer) const expected = { Account: addresses.ACCOUNT, TakerGets: offer.TakerGets, TakerPays: offer.TakerPays, is_fully_funded: true, taker_gets_funded: '100', taker_pays_funded: '123456', owner_funds: '100.1234' } assert.deepEqual(offer, expected) }) it('Set offer funded amount - iou/xdv - unfunded', function() { const book = createOrderBook({ currency_gets: 'BTC', currency_pays: 'XDV', issuer_gets: addresses.ISSUER }) book._issuerTransferRate = new IOUValue('1.000000000') const offer = { Account: addresses.ACCOUNT, TakerGets: { value: '100', currency: 'BTC', issuer: addresses.ISSUER }, TakerPays: '123456', quality: '1234.56' } book._setOwnerFunds(addresses.ACCOUNT, '99') book._setOfferFundedAmount(offer) const expected = { Account: addresses.ACCOUNT, TakerGets: offer.TakerGets, TakerPays: offer.TakerPays, is_fully_funded: false, taker_gets_funded: '99', taker_pays_funded: '122221', owner_funds: '99', quality: '1234.56' } assert.deepEqual(offer, expected) }) it('Set offer funded amount - xdv/iou - funded', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._issuerTransferRate = new IOUValue('1.000000000') const offer = { Account: addresses.ACCOUNT, TakerGets: '100', TakerPays: { value: '123.456', currency: 'BTC', issuer: addresses.ISSUER } } book._setOwnerFunds(addresses.ACCOUNT, '100100000') book._setOfferFundedAmount(offer) const expected = { Account: addresses.ACCOUNT, TakerGets: offer.TakerGets, TakerPays: offer.TakerPays, is_fully_funded: true, taker_gets_funded: '100', taker_pays_funded: '123.456', owner_funds: '100100000' } assert.deepEqual(offer, expected) }) it('Set offer funded amount - xdv/iou - unfunded', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._issuerTransferRate = new IOUValue('1.000000000') const offer = { Account: addresses.ACCOUNT, TakerGets: '100', TakerPays: { value: '123.456', currency: 'BTC', issuer: addresses.ISSUER }, quality: '1.23456' } book._setOwnerFunds(addresses.ACCOUNT, '99') book._setOfferFundedAmount(offer) const expected = { Account: addresses.ACCOUNT, TakerGets: offer.TakerGets, TakerPays: offer.TakerPays, is_fully_funded: false, taker_gets_funded: '99', taker_pays_funded: '122.22144', owner_funds: '99', quality: '1.23456' } assert.deepEqual(offer, expected) }) it('Set offer funded amount - zero funds', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) book._issuerTransferRate = new IOUValue('1.000000000') const offer = { Account: addresses.ACCOUNT, TakerPays: { value: '100', currency: 'BTC', issuer: addresses.ISSUER }, TakerGets: '123456' } book._setOwnerFunds(addresses.ACCOUNT, '0') book._setOfferFundedAmount(offer) assert.deepEqual(offer, { Account: addresses.ACCOUNT, TakerGets: offer.TakerGets, TakerPays: offer.TakerPays, is_fully_funded: false, taker_gets_funded: '0', taker_pays_funded: '0', owner_funds: '0' }) }) describe('Metadata', function() { it('Check is balance change node', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) const meta = { AffectedNodes: [{ ModifiedNode: { FinalFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '-1' }, Flags: 131072, HighLimit: { currency: 'USD', issuer: addresses.ISSUER, value: '100' }, HighNode: '0000000000000000', LowLimit: { currency: 'USD', issuer: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH', value: '0' }, LowNode: '0000000000000000' }, LedgerEntryType: 'DivvyState', LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959', PreviousFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '0' } }, PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8', PreviousTxnLgrSeq: 343570 } }] } assert(book._isBalanceChangeNode( OrderBookUtils.getAffectedNodes(meta)[0])) }) it('Check is balance change node - not balance change', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) const meta = { AffectedNodes: [{ ModifiedNode: { FinalFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '-1' }, Flags: 131072, HighLimit: { currency: 'USD', issuer: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', value: '100' }, HighNode: '0000000000000000', LowLimit: { currency: 'USD', issuer: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH', value: '0' }, LowNode: '0000000000000000' }, LedgerEntryType: 'DivvyState', LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959', PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8', PreviousTxnLgrSeq: 343570 } }] } assert(!book._isBalanceChangeNode( OrderBookUtils.getAffectedNodes(meta)[0])) }) it('Check is balance change node - different currency', function() { const book = createOrderBook({ currency_gets: 'BTC', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) const meta = { AffectedNodes: [{ ModifiedNode: { FinalFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '-1' }, Flags: 131072, HighLimit: { currency: 'USD', issuer: addresses.ISSUER, value: '100' }, HighNode: '0000000000000000', LowLimit: { currency: 'USD', issuer: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH', value: '0' }, LowNode: '0000000000000000' }, LedgerEntryType: 'DivvyState', LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959', PreviousFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '0' } }, PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8', PreviousTxnLgrSeq: 343570 } }] } assert(!book._isBalanceChangeNode( OrderBookUtils.getAffectedNodes(meta)[0])) }) it('Check is balance change node - different issuer', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) const meta = { AffectedNodes: [{ ModifiedNode: { FinalFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '-1' }, Flags: 131072, HighLimit: { currency: 'USD', issuer: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', value: '100' }, HighNode: '0000000000000000', LowLimit: { currency: 'USD', issuer: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH', value: '0' }, LowNode: '0000000000000000' }, LedgerEntryType: 'DivvyState', LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959', PreviousFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '0' } }, PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8', PreviousTxnLgrSeq: 343570 } }] } assert(!book._isBalanceChangeNode( OrderBookUtils.getAffectedNodes(meta)[0])) }) it('Check is balance change node - native currency', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) const meta = { AffectedNodes: [{ ModifiedNode: { FinalFields: { Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', Balance: '9999999990', Flags: 0, OwnerCount: 1, Sequence: 2 }, LedgerEntryType: 'AccountRoot', LedgerIndex: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05', PreviousFields: { Balance: '10000000000', OwnerCount: 0, Sequence: 1 }, PreviousTxnID: 'B24159F8552C355D35E43623F0E5AD965ADBF034D482421529E2703904E1EC09', PreviousTxnLgrSeq: 16154 } }] } assert(book._isBalanceChangeNode( OrderBookUtils.getAffectedNodes(meta)[0])) }) it('Check is balance change node - native currency - not balance change', function() { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'BTC' }) const meta = { AffectedNodes: [{ ModifiedNode: { FinalFields: { Account: 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV', Balance: '78991384535796', Flags: 0, OwnerCount: 3, Sequence: 188 }, LedgerEntryType: 'AccountRoot', LedgerIndex: 'B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A', PreviousTxnID: 'E9E1988A0F061679E5D14DE77DB0163CE0BBDC00F29E396FFD1DA0366E7D8904', PreviousTxnLgrSeq: 195455 } }] } assert(!book._isBalanceChangeNode( OrderBookUtils.getAffectedNodes(meta)[0])) }) it('Parse account balance from node', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) const meta = { AffectedNodes: [ { ModifiedNode: { FinalFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '10' }, Flags: 131072, HighLimit: { currency: 'USD', issuer: addresses.ISSUER, value: '100' }, HighNode: '0000000000000000', LowLimit: { currency: 'USD', issuer: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH', value: '0' }, LowNode: '0000000000000000' }, LedgerEntryType: 'DivvyState', LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959', PreviousFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '0' } }, PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8', PreviousTxnLgrSeq: 343570 } }, { ModifiedNode: { FinalFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '-10' }, Flags: 131072, HighLimit: { currency: 'USD', issuer: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH', value: '100' }, HighNode: '0000000000000000', LowLimit: { currency: 'USD', issuer: addresses.ISSUER, value: '0' }, LowNode: '0000000000000000' }, LedgerEntryType: 'DivvyState', LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959', PreviousFields: { Balance: { currency: 'USD', issuer: addresses.ISSUER, value: '0' } }, PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8', PreviousTxnLgrSeq: 343570 } } ] } assert.deepEqual(book._parseAccountBalanceFromNode( OrderBookUtils.getAffectedNodes(meta)[0]), { account: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH', balance: '10' }) assert.deepEqual(book._parseAccountBalanceFromNode( OrderBookUtils.getAffectedNodes(meta)[1]), { account: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH', balance: '10' }) }) it('Parse account balance from node - native currency', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) const meta = { AffectedNodes: [{ ModifiedNode: { FinalFields: { Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', Balance: '9999999990', Flags: 0, OwnerCount: 1, Sequence: 2 }, LedgerEntryType: 'AccountRoot', LedgerIndex: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05', PreviousFields: { Balance: '10000000000', OwnerCount: 0, Sequence: 1 }, PreviousTxnID: 'B24159F8552C355D35E43623F0E5AD965ADBF034D482421529E2703904E1EC09', PreviousTxnLgrSeq: 16154 } }] } assert.deepEqual(book._parseAccountBalanceFromNode( OrderBookUtils.getAffectedNodes(meta)[0]), { account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', balance: '9999999990' }) }) }) it('Update funded amounts', function(done) { let receivedChangedEvents = 0 let receivedFundsChangedEvents = 0 const message = fixtures.transactionWithDivvyState() const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.000000000') book._offers = fixtures.fiatOffers() book.on('offer_changed', function() { receivedChangedEvents += 1 }) book.on('offer_funds_changed', function(offer, previousFunds, newFunds) { assert.strictEqual(previousFunds, '100') assert.strictEqual(newFunds, offer.taker_gets_funded) assert.notStrictEqual(previousFunds, newFunds) switch (++receivedFundsChangedEvents) { case 1: assert.strictEqual(offer.is_fully_funded, false) assert.strictEqual(offer.taker_gets_funded, '10') assert.strictEqual(offer.taker_pays_funded, '1954238072') break case 2: assert.strictEqual(offer.is_fully_funded, false) assert.strictEqual(offer.taker_gets_funded, '0') assert.strictEqual(offer.taker_pays_funded, '0') break } }) book._ownerFunds[addresses.ACCOUNT] = '20' book._updateFundedAmounts(message) setImmediate(function() { assert.strictEqual(book._getOwnerFunds(addresses.ACCOUNT).toString(), fixtures.FIAT_BALANCE) assert.strictEqual(receivedChangedEvents, 2) assert.strictEqual(receivedFundsChangedEvents, 2) done() }) }) it('Update funded amounts - increase funds', function() { let receivedFundsChangedEvents = 0 const message = fixtures.transactionWithDivvyState({ balance: '50' }) const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.000000000') book._setOffers(fixtures.fiatOffers({ account_funds: '19' })) book.on('offer_funds_changed', function(offer, previousFunds, newFunds) { assert.strictEqual(newFunds, offer.taker_gets_funded) assert.notStrictEqual(previousFunds, newFunds) switch (++receivedFundsChangedEvents) { case 1: assert.strictEqual(previousFunds, '19') assert.strictEqual(offer.is_fully_funded, true) assert.strictEqual(offer.taker_gets_funded, fixtures.TAKER_GETS) assert.strictEqual(offer.taker_pays_funded, fixtures.TAKER_PAYS) break case 2: assert.strictEqual(previousFunds, '0') assert.strictEqual(offer.is_fully_funded, true) assert.strictEqual(offer.taker_gets_funded, '4.9656112525') assert.strictEqual(offer.taker_pays_funded, '972251352') break } }) book._updateFundedAmounts(message) }) it('Update funded amounts - owner_funds', function(done) { const message = fixtures.transactionWithDivvyState() const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') book._offers = fixtures.fiatOffers() book._ownerFunds[addresses.ACCOUNT] = '100' book._updateFundedAmounts(message) setImmediate(function() { assert.strictEqual(book._offers[0].owner_funds, fixtures.FIAT_BALANCE) assert.strictEqual(book._offers[1].owner_funds, fixtures.FIAT_BALANCE) done() }) }) it('Update funded amounts - issuer transfer rate set', function(done) { const message = fixtures.transactionWithDivvyState() const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') book._ownerFunds[addresses.ACCOUNT] = '100' book._offers = fixtures.fiatOffers() book._updateFundedAmounts(message) setImmediate(function() { assert.strictEqual(book._getOwnerFunds(addresses.ACCOUNT).toString(), '9.980039920159681') done() }) }) it('Update funded amounts - native currency', function(done) { let receivedChangedEvents = 0 let receivedFundsChangedEvents = 0 const message = fixtures.transactionWithAccountRoot() const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'USD' }) book._offers = fixtures.NATIVE_OFFERS book.on('offer_changed', function() { receivedChangedEvents += 1 }) book.on('offer_funds_changed', function(offer, previousFunds, newFunds) { assert.strictEqual(previousFunds, fixtures.NATIVE_BALANCE_PREVIOUS) assert.strictEqual(newFunds, offer.taker_gets_funded) assert.notStrictEqual(previousFunds, newFunds) switch (++receivedFundsChangedEvents) { case 1: assert(offer.is_fully_funded) break case 2: assert(!offer.is_fully_funded) break } }) book._ownerFunds[addresses.ACCOUNT] = fixtures.NATIVE_BALANCE_PREVIOUS book._updateFundedAmounts(message) setImmediate(function() { book._getOwnerFunds(addresses.ACCOUNT, fixtures.NATIVE_BALANCE) assert.strictEqual(receivedChangedEvents, 2) assert.strictEqual(receivedFundsChangedEvents, 2) done() }) }) it('Update funded amounts - no affected account', function(done) { const message = fixtures.transactionWithAccountRoot({ account: addresses.ACCOUNT }) const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'USD' }) book._offers = fixtures.NATIVE_OFFERS book._offers.__defineGetter__(0, function() { assert(false, 'Iteration of offers for unaffected account') }) book.on('offer_changed', function() { assert(false, 'offer_changed event emitted') }) book.on('offer_funds_changed', function() { assert(false, 'offer_funds_changed event emitted') }) book._updateFundedAmounts(message) setImmediate(done) }) it('Update funded amounts - no balance change', function(done) { const book = createOrderBook({ currency_gets: 'XDV', issuer_pays: addresses.ISSUER, currency_pays: 'USD' }) const message = fixtures.transactionWithInvalidAccountRoot() book._offers = fixtures.NATIVE_OFFERS book.on('offer_changed', function() { assert(false, 'offer_changed event emitted') }) book.on('offer_funds_changed', function() { assert(false, 'offer_funds_changed event emitted') }) assert.strictEqual(typeof book._parseAccountBalanceFromNode, 'function') book.parseAccountBalanceFromNode = function() { assert(false, 'getBalanceChange should not be called') } book._ownerFunds[addresses.ACCOUNT] = '100' book._updateFundedAmounts(message) setImmediate(done) }) it('Update funded amounts - deferred TransferRate', function(done) { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) const message = fixtures.transactionWithDivvyState() book._api.connection.request = function(request) { assert.deepEqual(request, { command: 'account_info', ledger_index: 'validated', account: addresses.ISSUER }) setImmediate(() => { assert(book._issuerTransferRate.equals(new IOUValue(fixtures.TRANSFER_RATE))) done() }) return Promise.resolve(fixtures.accountInfoResponse()) } book._ownerFunds[addresses.ACCOUNT] = '100' book._updateFundedAmounts(message) }) it('Set offers - issuer transfer rate set - iou/xdv', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') const offers = fixtures.bookOffersResponse().offers book._setOffers(offers) assert.strictEqual(book._offers.length, 5) assert.strictEqual(book._getOwnerOfferTotal(addresses.ACCOUNT).toString(), '275.85192574') assert.strictEqual(book._getOwnerOfferTotal(addresses.OTHER_ACCOUNT).toString(), '24.060765960393') assert.strictEqual(book._getOwnerOfferTotal(addresses.THIRD_ACCOUNT).toString(), '712.60995') assert.strictEqual(book._getOwnerOfferTotal(addresses.FOURTH_ACCOUNT).toString(), '288.08') assert.strictEqual(book._offerCounts[addresses.ACCOUNT], 2) assert.strictEqual(book._offerCounts[addresses.OTHER_ACCOUNT], 1) assert.strictEqual(book._offerCounts[addresses.THIRD_ACCOUNT], 1) assert.strictEqual(book._offerCounts[addresses.FOURTH_ACCOUNT], 1) assert.strictEqual(book._getOwnerFunds(addresses.ACCOUNT).toString(), '2006.015671538605') assert.strictEqual(book._getOwnerFunds(addresses.OTHER_ACCOUNT).toString(), '24.01284027983332') assert.strictEqual(book._getOwnerFunds(addresses.THIRD_ACCOUNT).toString(), '9053.294314019701') assert.strictEqual(book._getOwnerFunds(addresses.FOURTH_ACCOUNT).toString(), '7229.594289344439') }) it('Set offers - issuer transfer rate set - iou/xdv - funded amounts', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') const offers = fixtures.bookOffersResponse({ account_funds: '233.13532' }).offers book._setOffers(offers) const offerOneTakerGetsFundedExpected = '79.39192374' assert.strictEqual(book._offers[0].taker_gets_funded, offerOneTakerGetsFundedExpected) assert(book._offers[0].is_fully_funded) const offerTwoTakerGetsFundedExpected = '24.01284027983332' const offerTwoTakerPaysFundedExpected = '1661400177' assert.strictEqual(book._offers[1].taker_gets_funded, offerTwoTakerGetsFundedExpected) assert.strictEqual(book._offers[1].taker_pays_funded, offerTwoTakerPaysFundedExpected) assert.strictEqual(book._offers[1].is_fully_funded, false) const offerFiveTakerGetsFundedExpected = '153.2780562999202' const offerFiveTakerPaysFundedExpected = '10684615137' assert.strictEqual(book._offers[4].taker_gets_funded, offerFiveTakerGetsFundedExpected) assert.strictEqual(book._offers[4].taker_pays_funded, offerFiveTakerPaysFundedExpected) assert.strictEqual(book._offers[4].is_fully_funded, false) }) it('Set offers - multiple calls', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') const offers = fixtures.bookOffersResponse().offers book._setOffers(offers) book._setOffers(offers) assert.strictEqual(book._offers.length, 5) assert.strictEqual(book._getOwnerOfferTotal(addresses.ACCOUNT).toString(), '275.85192574') assert.strictEqual(book._getOwnerOfferTotal(addresses.OTHER_ACCOUNT).toString(), '24.060765960393') assert.strictEqual(book._getOwnerOfferTotal(addresses.THIRD_ACCOUNT).toString(), '712.60995') assert.strictEqual(book._getOwnerOfferTotal(addresses.FOURTH_ACCOUNT).toString(), '288.08') assert.strictEqual(book._offerCounts[addresses.ACCOUNT], 2) assert.strictEqual(book._offerCounts[addresses.OTHER_ACCOUNT], 1) assert.strictEqual(book._offerCounts[addresses.THIRD_ACCOUNT], 1) assert.strictEqual(book._offerCounts[addresses.FOURTH_ACCOUNT], 1) assert.strictEqual(book._getOwnerFunds(addresses.ACCOUNT).toString(), '2006.015671538605') assert.strictEqual(book._getOwnerFunds(addresses.OTHER_ACCOUNT).toString(), '24.01284027983332') assert.strictEqual(book._getOwnerFunds(addresses.THIRD_ACCOUNT).toString(), '9053.294314019701') assert.strictEqual(book._getOwnerFunds(addresses.FOURTH_ACCOUNT).toString(), '7229.594289344439') }) it('Set offers - incorrect taker pays funded', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') const offers = fixtures.DECIMAL_TAKER_PAYS_FUNDED_OFFERS book._setOffers(offers) assert.strictEqual(book._offers.length, 1) assert.strictEqual(book._offers[0].taker_gets_funded, '9261.514125778347') assert.strictEqual(book._offers[0].taker_pays_funded, '1704050437125') }) it('Notify - created node', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') book._subscribed = book._synced = true const message = fixtures.transactionWithCreatedOffer() book._processTransaction(message) assert.strictEqual(book._offers.length, 1) assert.strictEqual(book._getOwnerOfferTotal(addresses.ACCOUNT).toString(), '1.9951') assert.strictEqual(book._offerCounts[addresses.ACCOUNT], 1) assert.strictEqual(book._getOwnerFunds(addresses.ACCOUNT).toString(), '2006.015671538605') }) it('Notify - created nodes - correct sorting', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._issuerTransferRate = new IOUValue('1.002000000') book._subscribed = book._synced = true const offer = fixtures.transactionWithCreatedOffer() const lowQualityOffer = fixtures.transactionWithCreatedOffer({ account: addresses.OTHER_ACCOUNT, amount: '1.5' }) const highQualityOffer = fixtures.transactionWithCreatedOffer({ account: addresses.THIRD_ACCOUNT, amount: '3.83' }) book._processTransaction(offer) book._processTransaction(lowQualityOffer) book._processTransaction(highQualityOffer) assert.strictEqual(book._offers.length, 3) assert.strictEqual(book._offers[0].Account, addresses.THIRD_ACCOUNT) assert.strictEqual(book._offers[1].Account, addresses.ACCOUNT) assert.strictEqual(book._offers[2].Account, addresses.OTHER_ACCOUNT) }) it('Notify - created nodes - events', function(done) { let numTransactionEvents = 0 let numModelEvents = 0 let numOfferAddedEvents = 0 const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._api.connection.request = function() { return Promise.resolve({}) } book.on('transaction', function() { numTransactionEvents += 1 }) book.on('model', function() { numModelEvents += 1 }) book.on('offer_added', function() { numOfferAddedEvents += 1 }) book._issuerTransferRate = new IOUValue('1.002000000') book._subscribed = book._synced = true book._waitingForOffers = false const offer = fixtures.transactionWithCreatedOffer() const offer2 = fixtures.transactionWithCreatedOffer() const offer3 = fixtures.transactionWithCreatedOffer() book._api.emit('ledger', {transactionCount: 3}) book._api.connection.emit('transaction', offer) book._api.connection.emit('transaction', offer2) book._api.connection.emit('transaction', offer3) setTimeout(function() { assert.strictEqual(numTransactionEvents, 3) assert.strictEqual(numOfferAddedEvents, 3) assert.strictEqual(numModelEvents, 1) done() }, 300) }) it('Notify - deleted node', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._api.connection.request = function() { return Promise.resolve({}) } book._subscribed = true book._issuerTransferRate = new IOUValue('1.000000000') book._waitingForOffers = false book._setOffers(fixtures.fiatOffers()) const message = fixtures.transactionWithDeletedOffer() book._processTransaction(message) assert.strictEqual(book._offers.length, 2) assert.strictEqual(book._getOwnerOfferTotal(addresses.ACCOUNT).toString(), '4.9656112525') assert.strictEqual(book._offerCounts[addresses.ACCOUNT], 1) }) it('Notify - deleted node - last offer', function() { const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book._subscribed = true book._issuerTransferRate = new IOUValue('1.000000000') book._setOffers(fixtures.fiatOffers().slice(0, 1)) const message = fixtures.transactionWithDeletedOffer() book._processTransaction(message) assert.strictEqual(book._offers.length, 0) assert.throws(() => { book._getOwnerFunds(addresses.ACCOUNT) }) }) it('Notify - deleted node - events', function(done) { let numTransactionEvents = 0 let numModelEvents = 0 let numTradeEvents = 0 let numOfferRemovedEvents = 0 const book = createOrderBook({ currency_gets: 'USD', issuer_gets: addresses.ISSUER, currency_pays: 'XDV' }) book.on('transaction', function() { numTransactionEvents += 1 }) book.on('model', function() { numModelEvents += 1 }) book.on('trade', function() { numTradeEvents += 1 }) book.on('offer_removed', function() { numOfferRemovedEvents += 1 }) book._subscribed = true book._waitingForOffers = false book._issuerTransferRate = new IOUValue('1.000000000') book._setOffers(fixtures.fiatOffers()) const message = fixtures.transactionWithDeletedOffer() book._api.emit('ledger', {transactionCount: 1}) book._api.connection.emit('transaction', message) assert.strictEqual(numTransactionEvents, 1) assert.strictEqual(numTradeEvents, 1) assert.strictEqual(numOfferRemovedEvents, 1) setTimeout(function() { assert.strictEqual(numModelEvents, 1) done() }, 300) }) it('Notify - deleted node - t