bitcore-wallet-service
Version:
A service for Mutisig HD Bitcoin Wallets
1,051 lines (1,014 loc) • 30.6 kB
JavaScript
;
var async = require('async');
var chai = require('chai');
var sinon = require('sinon');
var should = chai.should();
var log = require('npmlog');
log.debug = log.verbose;
log.level = 'info';
var { Common } = require('../../ts_build/lib/common');
var Defaults = Common.Defaults;
var Constants = Common.Constants;
var helpers = require('./helpers');
var { FiatRateService } = require('../../ts_build/lib/fiatrateservice');
describe('Fiat rate service', function() {
this.timeout(5000);
var service, request;
before(function(done) {
helpers.before(res => {
done();
});
});
after(function(done) {
helpers.after(done);
});
beforeEach(function(done) {
helpers.beforeEach(function() {
service = new FiatRateService();
request = sinon.stub();
request.get = sinon.stub();
service.init(
{
storage: helpers.getStorage(),
request: request
},
function(err) {
should.not.exist(err);
service.startCron({}, done);
}
);
});
});
describe('#getRate', function() {
it('should get current rate', function(done) {
service.storage.storeFiatRate(
'bch',
[
{
code: 'USD',
value: 123.45
}
],
function(err) {
should.not.exist(err);
service.getRate(
{
coin: 'bch',
code: 'USD'
},
function(err, res) {
should.not.exist(err);
res.rate.should.equal(123.45);
done();
}
);
}
);
});
it('should get current rate for different currency', function(done) {
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 123.45
}
],
function(err) {
should.not.exist(err);
service.storage.storeFiatRate(
'btc',
[
{
code: 'EUR',
value: 345.67
}
],
function(err) {
should.not.exist(err);
service.getRate(
{
code: 'EUR'
},
function(err, res) {
should.not.exist(err);
res.rate.should.equal(345.67);
done();
}
);
}
);
}
);
});
it('should get current rate for different coin', function(done) {
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 100.0
}
],
function(err) {
should.not.exist(err);
service.storage.storeFiatRate(
'bch',
[
{
code: 'USD',
value: 200.0
}
],
function(err) {
should.not.exist(err);
service.getRate(
{
code: 'USD'
},
function(err, res) {
should.not.exist(err);
res.rate.should.equal(100.0, 'Should use default coin');
service.getRate(
{
code: 'USD',
coin: 'bch'
},
function(err, res) {
should.not.exist(err);
res.rate.should.equal(200.0);
done();
}
);
}
);
}
);
}
);
});
it('should get rate for specific ts', function(done) {
var clock = sinon.useFakeTimers({ toFake: ['Date'] });
clock.tick(20);
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 123.45
}
],
function(err) {
should.not.exist(err);
clock.tick(100);
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 345.67
}
],
function(err) {
should.not.exist(err);
service.getRate(
{
code: 'USD',
ts: 50
},
function(err, res) {
should.not.exist(err);
res.ts.should.equal(50);
res.rate.should.equal(123.45);
res.fetchedOn.should.equal(20);
clock.restore();
done();
}
);
}
);
}
);
});
it('should get rates for a series of ts', function(done) {
var clock = sinon.useFakeTimers({ toFake: ['Date'] });
async.each(
[1.0, 2.0, 3.0, 4.0],
function(value, next) {
clock.tick(100);
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: value
},
{
code: 'EUR',
value: value
}
],
next
);
},
function(err) {
should.not.exist(err);
service.getRate(
{
code: 'USD',
ts: [50, 100, 199, 500]
},
function(err, res) {
should.not.exist(err);
res.length.should.equal(4);
res[0].ts.should.equal(50);
should.not.exist(res[0].rate);
should.not.exist(res[0].fetchedOn);
res[1].ts.should.equal(100);
res[1].rate.should.equal(1.0);
res[1].fetchedOn.should.equal(100);
res[2].ts.should.equal(199);
res[2].rate.should.equal(1.0);
res[2].fetchedOn.should.equal(100);
res[3].ts.should.equal(500);
res[3].rate.should.equal(4.0);
res[3].fetchedOn.should.equal(400);
clock.restore();
done();
}
);
}
);
});
it('should get historical rates from ts to now', function(done) {
const coins = ['btc', 'bch', 'eth', 'matic', 'xrp', 'doge', 'ltc'];
var clock = sinon.useFakeTimers({ toFake: ['Date'] });
async.each(
[1.0, 2.0, 3.0, 4.0, 5.0],
function(value, next) {
clock.tick(100);
async.map(
coins,
(coin, cb) => {
service.storage.storeFiatRate(
coin,
[
{
code: 'USD',
value: value
},
{
code: 'EUR',
value: value
}
],
cb
);
},
() => {
return next();
}
);
},
function(err) {
should.not.exist(err);
service.getHistoricalRates(
{
code: 'USD',
ts: 100
},
function(err, res) {
should.not.exist(err);
should.exist(res);
for (const coin of coins) {
res[coin].length.should.equal(5);
res[coin][4].ts.should.equal(100);
res[coin][4].rate.should.equal(1.0);
res[coin][3].ts.should.equal(200);
res[coin][3].rate.should.equal(2.0);
res[coin][2].ts.should.equal(300);
res[coin][2].rate.should.equal(3.0);
res[coin][1].ts.should.equal(400);
res[coin][1].rate.should.equal(4.0);
res[coin][0].ts.should.equal(500);
res[coin][0].rate.should.equal(5.0);
}
clock.restore();
done();
}
);
}
);
});
it('should not throw if missing historical rates for a coin', function(done) {
var clock = sinon.useFakeTimers({ toFake: ['Date'] });
async.each(
[1.0, 2.0, 3.0, 4.0],
function(value, next) {
clock.tick(100);
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: value
},
{
code: 'EUR',
value: value
}
],
next
);
},
function(err) {
should.not.exist(err);
service.getHistoricalRates(
{
code: 'USD',
ts: 100
},
function(err, res) {
should.not.exist(err);
should.exist(res);
res['btc'].length.should.equal(4);
should.not.exist(res['bch']);
should.not.exist(res['eth']);
should.not.exist(res['matic']);
should.not.exist(res['xrp']);
should.not.exist(res['doge']);
should.not.exist(res['ltc']);
res['btc'][3].ts.should.equal(100);
res['btc'][3].rate.should.equal(1.0);
res['btc'][2].ts.should.equal(200);
res['btc'][2].rate.should.equal(2.0);
res['btc'][1].ts.should.equal(300);
res['btc'][1].rate.should.equal(3.0);
res['btc'][0].ts.should.equal(400);
res['btc'][0].rate.should.equal(4.0);
clock.restore();
done();
}
);
}
);
});
it('should return current rates if missing opts.ts when fetching historical rates', function(done) {
const coins = ['btc', 'bch', 'eth', 'xrp', 'doge', 'ltc'];
var clock = sinon.useFakeTimers({ toFake: ['Date'] });
async.each(
[1.0, 2.0, 3.0, 4.0],
function(value, next) {
clock.tick(11 * 60 * 1000);
async.map(
coins,
(coin, cb) => {
service.storage.storeFiatRate(
coin,
[
{
code: 'USD',
value: value
},
{
code: 'EUR',
value: value
}
],
cb
);
},
() => {
return next();
}
);
},
function(err) {
should.not.exist(err);
service.getHistoricalRates(
{
code: 'USD'
},
function(err, res) {
should.not.exist(err);
for (const coin of coins) {
res[coin].length.should.equal(1);
res[coin][0].ts.should.equal(2640000);
res[coin][0].rate.should.equal(4.0);
}
clock.restore();
done();
}
);
}
);
});
it('should not get rate older than 2hs', function(done) {
var clock = sinon.useFakeTimers({ toFake: ['Date'] });
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 123.45
}
],
function(err) {
should.not.exist(err);
clock.tick(24 * 3600 * 1000); // Some time in the future
service.getRate(
{
ts: 2 * 3600 * 1000 - 1, // almost 2 hours
code: 'USD'
},
function(err, res) {
should.not.exist(err);
res.rate.should.equal(123.45);
res.fetchedOn.should.equal(0);
service.getRate(
{
ts: 2 * 3600 * 1000 + 1, // just past 2 hours
code: 'USD'
},
function(err, res) {
should.not.exist(err);
should.not.exist(res.rate);
clock.restore();
done();
}
);
}
);
}
);
});
});
describe('#fetch', function() {
it('should fetch rates from all coins', function(done) {
var clock = sinon.useFakeTimers({ now: 100, toFake: ['Date'] });
var btc = [
{
code: 'USD',
rate: 123.45
},
{
code: 'EUR',
rate: 234.56
}
];
var bch = [
{
code: 'USD',
rate: 120
},
{
code: 'EUR',
rate: 120
}
];
var eth = [
{
code: 'USD',
rate: 121
},
{
code: 'EUR',
rate: 121
}
];
var matic = [
{
code: 'USD',
rate: 1.19
},
{
code: 'EUR',
rate: 1.19
}
];
var xrp = [
{
code: 'USD',
rate: 0.222222
},
{
code: 'EUR',
rate: 0.211111
}
];
var doge = [
{
code: 'USD',
rate: 0.05
},
{
code: 'EUR',
rate: 0.04
}
];
var ltc = [
{
code: 'USD',
rate: 150
},
{
code: 'EUR',
rate: 170
}
];
var shib = [
{
code: 'USD',
rate: 0.00003678
},
{
code: 'EUR',
rate: 0.00003276
}
];
var ape = [
{
code: 'USD',
rate: 6.66
},
{
code: 'EUR',
rate: 6.64
}
];
var sol = [
{
code: 'USD',
rate: 150
},
{
code: 'EUR',
rate: 156
}
];
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/BTC?p=bws',
json: true
})
.yields(null, null, btc);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/BCH?p=bws',
json: true
})
.yields(null, null, bch);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/ETH?p=bws',
json: true
})
.yields(null, null, eth);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/MATIC?p=bws',
json: true
})
.yields(null, null, matic);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/XRP?p=bws',
json: true
})
.yields(null, null, xrp);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/DOGE?p=bws',
json: true
})
.yields(null, null, doge);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/LTC?p=bws',
json: true
})
.yields(null, null, ltc);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/SHIB?p=bws',
json: true
})
.yields(null, null, shib);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/APE?p=bws',
json: true
})
.yields(null, null, ape);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/SOL?p=bws',
json: true
})
.yields(null, null, sol);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/WBTC?p=bws',
json: true
})
.yields(null, null, btc);
request.get
.withArgs({
url: 'https://bitpay.com/api/rates/WETH?p=bws',
json: true
})
.yields(null, null, eth);
service._fetch(function(err) {
should.not.exist(err);
service.getRate(
{
code: 'USD'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(123.45);
service.getRate(
{
code: 'USD',
coin: 'bch'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(120.0);
service.getRate(
{
code: 'USD',
coin: 'eth'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(121);
service.getRate(
{
code: 'USD',
coin: 'matic'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(1.19);
service.getRate(
{
code: 'USD',
coin: 'xrp'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(0.222222);
service.getRate(
{
code: 'USD',
coin: 'doge'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(0.05);
service.getRate(
{
code: 'USD',
coin: 'ltc'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(150);
service.getRate(
{
code: 'EUR'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(234.56);
service.getRate(
{
code: 'USD',
coin: 'shib'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(0.00003678);
service.getRate(
{
code: 'USD',
coin: 'ape'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(6.66);
service.getRate(
{
code: 'USD',
coin: 'usdc'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(1);
service.getRate(
{
code: 'USD',
coin: 'weth'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(121);
service.getRate(
{
code: 'USD',
coin: 'sol'
},
function(err, res) {
should.not.exist(err);
res.fetchedOn.should.equal(100);
res.rate.should.equal(150);
clock.restore();
done();
}
);
}
);
}
);
}
);
}
);
}
);
}
);
}
);
}
);
}
);
}
);
}
);
}
);
});
});
});
describe('#getRates', function() {
it('should get the rates of each coin in all supported fiat currencies', function(done) {
service.getRates({}, function(err, res) {
should.not.exist(err);
Object.keys(res).forEach(key => {
res[key].length.should.equal(Defaults.FIAT_CURRENCIES.length);
});
done();
});
});
it('should get the rates of all coins supported', function(done) {
service.getRates({}, function(err, res) {
should.not.exist(err);
Object.keys(res).length.should.equal( Object.keys(Constants.BITPAY_SUPPORTED_COINS).length)
done();
});
});
it('should get rates of all coins in the specified fiat currency if it is supported', function(done) {
service.getRates(
{
code: 'EUR'
},
function(err, res) {
should.not.exist(err);
Object.keys(res).length.should.equal( Object.keys(Constants.BITPAY_SUPPORTED_COINS).length)
Object.keys(res).forEach(key=>{
res[key].length.should.equal(1);
});
done();
}
);
});
it('should throw error if the specified fiat currency code is not supported', function(done) {
service.getRates(
{
code: 'AOA'
},
function(err) {
should.exist(err);
err.should.equal('AOA is not supported');
done();
}
);
});
it('should get rate for specific ts', function(done) {
var clock = sinon.useFakeTimers({ toFake: ['Date'] });
clock.tick(20);
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 123.45
}
],
function(err) {
should.not.exist(err);
clock.tick(100);
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 345.67
}
],
function(err) {
should.not.exist(err);
service.getRates(
{
coin: 'btc',
code: 'USD',
ts: 50
},
function(err, res) {
should.not.exist(err);
Object.keys(res).forEach(key => {
res[key][0].ts.should.equal(50);
});
clock.restore();
done();
}
);
}
);
}
);
});
});
describe('#getRatesByCoin', function() {
const bchRates = [
{ code: 'USD', value: 268.94 },
{ code: 'INR', value: 19680.35 },
{ code: 'EUR', value: 226.37 },
{ code: 'CAD', value: 352.33 },
{ code: 'COP', value: 1026617.26 },
{ code: 'NGN', value: 104201.93 },
{ code: 'GBP', value: 201.62 },
{ code: 'ARS', value: 19900.21 },
{ code: 'AUD', value: 365.8 },
{ code: 'BRL', value: 1456.93 },
{ code: 'JPY', value: 1124900.43 },
{ code: 'NZD', value: 16119.66 }
];
const btcRates = [
{ code: 'USD', value: 44173.54 },
{ code: 'INR', value: 3306411.94 },
{ code: 'GBP', value: 32558.07 },
{ code: 'EUR', value: 38642.15 },
{ code: 'CAD', value: 56070.58 },
{ code: 'COP', value: 173924929.36 },
{ code: 'NGN', value: 18409323.7 },
{ code: 'BRL', value: 233457.17 },
{ code: 'ARS', value: 4678684.28 },
{ code: 'AUD', value: 61503.4 },
{ code: 'JPY', value: 5099004.98 },
{ code: 'NZD', value: 50966068.64 }
];
it('should get rates for all the supported fiat currencies of the specified coin', function(done) {
service.storage.storeFiatRate('bch', bchRates, function(err) {
should.not.exist(err);
service.getRatesByCoin(
{
coin: 'bch'
},
function(err, res) {
should.not.exist(err);
res.length.should.equal(bchRates.length);
done();
}
);
});
});
it('should get rate for the specified coin and currency if they are supported', function(done) {
service.storage.storeFiatRate('bch', bchRates, function(err) {
should.not.exist(err);
service.getRatesByCoin(
{
coin: 'bch',
code: 'EUR'
},
function(err, res) {
should.not.exist(err);
res[0].rate.should.equal(226.37);
done();
}
);
});
});
it('should throw error if the specified currency code is not supported', function(done) {
service.storage.storeFiatRate('bch', bchRates, function(err) {
should.not.exist(err);
service.getRatesByCoin(
{
coin: 'bch',
code: 'AOA'
},
function(err) {
should.exist(err);
err.should.equal('AOA is not supported');
done();
}
);
});
});
it('should get fiat rates for a USD stablecoin', function(done) {
sinon.spy(service, 'getRatesForStablecoin');
service.storage.storeFiatRate('btc', btcRates, function(err) {
should.not.exist(err);
service.getRatesByCoin({ coin: 'gusd' }, function(err, res) {
should.not.exist(err);
should.exist(res);
res
.reduce((rates, { code, rate }) => ({ ...rates, [code]: rate }), {})
.should.deep.equal({
ARS: 105.92,
AUD: 1.39,
BRL: 5.29,
CAD: 1.27,
COP: 3937.31,
EUR: 0.87,
GBP: 0.74,
INR: 74.85,
JPY: 115.43,
NGN: 416.75,
NZD: 1153.77,
USD: 1
});
service.getRatesForStablecoin
.calledWith(
sinon.match({
code: 'USD',
ts: sinon.match.number
})
)
.should.equal(true);
done();
});
});
});
it('should get rate for specific ts', function(done) {
var clock = sinon.useFakeTimers({ toFake: ['Date'] });
clock.tick(20);
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 123.45
}
],
function(err) {
should.not.exist(err);
clock.tick(100);
service.storage.storeFiatRate(
'btc',
[
{
code: 'USD',
value: 345.67
}
],
function(err) {
should.not.exist(err);
service.getRatesByCoin(
{
coin: 'btc',
code: 'USD',
ts: 50
},
function(err, res) {
should.not.exist(err);
res[0].ts.should.equal(50);
res[0].rate.should.equal(123.45);
res[0].fetchedOn.should.equal(20);
clock.restore();
done();
}
);
}
);
}
);
});
});
});