diff --git a/app/utils/fetch-exchange-rate.js b/app/utils/fetch-exchange-rate.js new file mode 100644 index 0000000..4eaf97d --- /dev/null +++ b/app/utils/fetch-exchange-rate.js @@ -0,0 +1,20 @@ +const bitstampBaseUrl = 'https://www.bitstamp.net/api/v2'; + +async function fetchFromBitstamp(currencyPair) { + try { + const res = await fetch(`${bitstampBaseUrl}/ticker/${currencyPair}/`).then(r => r.json()); + return res.vwap; // Last 24 hours volume weighted average price + } catch(e) { + console.error('Could not fetch exchange rate from Bitstamp:', e); + return 0; + } +} + +export default function fetchExchangeRate(currencyPair, source='bitstamp') { + if (!currencyPair) throw 'Currency pair required'; + + switch(source) { + case 'bitstamp': + return fetchFromBitstamp(currencyPair); + } +} diff --git a/package-lock.json b/package-lock.json index fdfe8d9..0af0729 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16191,6 +16191,63 @@ "bser": "2.1.1" } }, + "fetch-mock": { + "version": "9.10.7", + "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.10.7.tgz", + "integrity": "sha512-YkiMHSL8CQ0vlWYpqGvlaZjViFk0Kar9jonPjSvaWoztkeHH6DENqUzBIsffzjVKhwchPI74SZRLRpIsEyNcZQ==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "core-js": "^3.0.0", + "debug": "^4.1.1", + "glob-to-regexp": "^0.4.0", + "is-subset": "^0.1.1", + "lodash.isequal": "^4.5.0", + "path-to-regexp": "^2.2.1", + "querystring": "^0.2.0", + "whatwg-url": "^6.5.0" + }, + "dependencies": { + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "dev": true + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "path-to-regexp": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", + "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", + "dev": true + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, "figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", @@ -16930,6 +16987,12 @@ } } }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, "global": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", @@ -18352,6 +18415,12 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "dev": true + }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -19495,6 +19564,12 @@ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, "lodash.isfunction": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-2.3.0.tgz", diff --git a/package.json b/package.json index 387ebff..c755e29 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "eslint-plugin-ember": "^8.5.2", "eslint-plugin-node": "^11.1.0", "ethers": "^4.0.47", + "fetch-mock": "^9.10.7", "kredits-contracts": "github:67P/kredits-contracts#feature/expenses", "loader.js": "^4.7.0", "localforage": "^1.7.3", diff --git a/tests/unit/utils/fetch-exchange-rate-test.js b/tests/unit/utils/fetch-exchange-rate-test.js new file mode 100644 index 0000000..b2007ce --- /dev/null +++ b/tests/unit/utils/fetch-exchange-rate-test.js @@ -0,0 +1,31 @@ +import fetchExchangeRate from 'kredits-web/utils/fetch-exchange-rate'; +import { module, test } from 'qunit'; +import fetchMock from 'fetch-mock'; + +fetchMock.get('https://www.bitstamp.net/api/v2/ticker/btceur/', { + "high": "9258.43", "last": "9165.14", "timestamp": "1601455909", "bid": + "9159.93", "vwap": "9167.57", "volume": "1542.54764854", "low": "9080.20", + "ask": "9165.14", "open": "9240.84" +}); + +fetchMock.get('https://www.bitstamp.net/api/v2/ticker/btcusd/', { + "high": "10865.00", "last": "10714.62", "timestamp": "1601455914", "bid": + "10711.31", "vwap": "10749.70", "volume": "4460.32091975", "low": "10636.66", + "ask": "10715.99", "open": "10840.00" +}); + +module('Unit | Utility | fetch-exchange-rate', function() { + test('it requires a currency pair', function(assert) { + assert.throws(fetchExchangeRate); + }); + + test('it fetches BTCEUR from Bitstamp', async function(assert) { + let result = await fetchExchangeRate('btceur'); + assert.equal(result, 9167.57, 'returns 24-hour volume-weighted average price'); + }); + + test('it fetches BTCUSD from Bitstamp', async function(assert) { + let result = await fetchExchangeRate('btcusd'); + assert.equal(result, 10749.70, 'returns 24-hour volume-weighted average price'); + }); +});