divvy-lib-orderbook
Version:
Convenient Orderbook class.
1,736 lines (1,427 loc) • 75.3 kB
JavaScript
/* 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