coinrayjs 1.9.12 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/coinrayjs.es.js +13220 -0
  2. package/dist/coinrayjs.es.js.map +1 -0
  3. package/dist/coinrayjs.js +52 -0
  4. package/dist/coinrayjs.js.map +1 -0
  5. package/package.json +50 -81
  6. package/dist/candles-cache.d.ts +0 -7
  7. package/dist/candles-cache.js +0 -57
  8. package/dist/candles-cache.js.map +0 -1
  9. package/dist/coinray-cache.d.ts +0 -47
  10. package/dist/coinray-cache.js +0 -249
  11. package/dist/coinray-cache.js.map +0 -1
  12. package/dist/coinray.d.ts +0 -183
  13. package/dist/coinray.js +0 -1118
  14. package/dist/coinray.js.map +0 -1
  15. package/dist/crypto.d.ts +0 -1
  16. package/dist/crypto.js +0 -57
  17. package/dist/crypto.js.map +0 -1
  18. package/dist/current-market.d.ts +0 -62
  19. package/dist/current-market.js +0 -285
  20. package/dist/current-market.js.map +0 -1
  21. package/dist/errors.d.ts +0 -25
  22. package/dist/errors.js +0 -50
  23. package/dist/errors.js.map +0 -1
  24. package/dist/event-emitter.d.ts +0 -9
  25. package/dist/event-emitter.js +0 -36
  26. package/dist/event-emitter.js.map +0 -1
  27. package/dist/exchange.d.ts +0 -55
  28. package/dist/exchange.js +0 -167
  29. package/dist/exchange.js.map +0 -1
  30. package/dist/i18n/locales.d.ts +0 -21
  31. package/dist/i18n/locales.js +0 -21
  32. package/dist/i18n/locales.js.map +0 -1
  33. package/dist/i18n.d.ts +0 -4
  34. package/dist/i18n.js +0 -16
  35. package/dist/i18n.js.map +0 -1
  36. package/dist/index.d.ts +0 -9
  37. package/dist/index.js +0 -10
  38. package/dist/index.js.map +0 -1
  39. package/dist/lru.d.ts +0 -26
  40. package/dist/lru.js +0 -93
  41. package/dist/lru.js.map +0 -1
  42. package/dist/market.d.ts +0 -65
  43. package/dist/market.js +0 -162
  44. package/dist/market.js.map +0 -1
  45. package/dist/orders/base.d.ts +0 -29
  46. package/dist/orders/base.js +0 -47
  47. package/dist/orders/base.js.map +0 -1
  48. package/dist/orders/index.d.ts +0 -6
  49. package/dist/orders/index.js +0 -7
  50. package/dist/orders/index.js.map +0 -1
  51. package/dist/orders/limit-ladder.d.ts +0 -87
  52. package/dist/orders/limit-ladder.js +0 -229
  53. package/dist/orders/limit-ladder.js.map +0 -1
  54. package/dist/orders/limit.d.ts +0 -47
  55. package/dist/orders/limit.js +0 -86
  56. package/dist/orders/limit.js.map +0 -1
  57. package/dist/orders/market.d.ts +0 -37
  58. package/dist/orders/market.js +0 -60
  59. package/dist/orders/market.js.map +0 -1
  60. package/dist/orders/oco.d.ts +0 -54
  61. package/dist/orders/oco.js +0 -70
  62. package/dist/orders/oco.js.map +0 -1
  63. package/dist/orders/stop-limit.d.ts +0 -42
  64. package/dist/orders/stop-limit.js +0 -33
  65. package/dist/orders/stop-limit.js.map +0 -1
  66. package/dist/orders/validate.d.ts +0 -2
  67. package/dist/orders/validate.js +0 -6
  68. package/dist/orders/validate.js.map +0 -1
  69. package/dist/trade.d.ts +0 -10
  70. package/dist/trade.js +0 -32
  71. package/dist/trade.js.map +0 -1
  72. package/dist/types.d.ts +0 -213
  73. package/dist/types.js +0 -59
  74. package/dist/types.js.map +0 -1
  75. package/dist/util.d.ts +0 -48
  76. package/dist/util.js +0 -407
  77. package/dist/util.js.map +0 -1
package/dist/coinray.js DELETED
@@ -1,1118 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import axios from "axios";
11
- import { Socket } from "phoenix";
12
- import { candleTime, createJWT, encryptPayload, getBucketStartDates, getTimeParams, jwkToPublicKey, jwtExpired, parseJWT, resolutionToBucketType, safeBigNumber, safeTime, signHMAC, toBucketEnd } from "./util";
13
- import _, { chunk } from "lodash";
14
- import I18n from "./i18n";
15
- const VERSION = require('../package.json').version;
16
- export class CoinrayError extends Error {
17
- constructor({ code, message, exchange }) {
18
- super("Request failed");
19
- this.name = "CoinrayError";
20
- this.errorCode = code;
21
- this.errorMessage = message;
22
- this.exchange = exchange;
23
- }
24
- }
25
- export default class Coinray {
26
- constructor(token, { apiEndpoint, orderEndpoint, websocketEndpoint } = {
27
- apiEndpoint: "https://api.coinray.eu",
28
- orderEndpoint: "https://api.coinray.eu",
29
- websocketEndpoint: "wss://ws.coinray.eu/v1",
30
- }) {
31
- this._tickerListeners = {};
32
- this._tradeListeners = {};
33
- this._tradeSnapshots = {};
34
- this._orderbookListeners = {};
35
- this._orderbookSnapshots = {};
36
- this._candleTradeListeners = {};
37
- this._candleListeners = {};
38
- this._candles = {};
39
- this._timers = {};
40
- this._channels = {};
41
- this.tickersSubscribed = false;
42
- this.tradesSubscribed = false;
43
- this.orderBookSubscribed = false;
44
- this.authenticateDevice = (credential, sessionKey) => {
45
- this._credential = credential;
46
- this._sessionKey = sessionKey;
47
- };
48
- this.destroy = () => {
49
- this._tradeListeners = {};
50
- this._orderbookListeners = {};
51
- this._tickerListeners = {};
52
- this._channels = {};
53
- if (this._timeOffsetTimeout) {
54
- clearTimeout(this._timeOffsetTimeout);
55
- }
56
- if (this._tokenCheckTimeout) {
57
- clearInterval(this._tokenCheckTimeout);
58
- }
59
- this.disconnect();
60
- };
61
- this.checkToken = (loop = true) => __awaiter(this, void 0, void 0, function* () {
62
- try {
63
- if (jwtExpired(this._token)) {
64
- console.log("Coinray token expired. Can refresh:", !!this._onTokenExpired);
65
- if (!this._onTokenExpired) {
66
- return false;
67
- }
68
- if (!this._refreshingToken) {
69
- this._refreshingToken = this._onTokenExpired();
70
- }
71
- try {
72
- this._token = yield this._refreshingToken;
73
- }
74
- catch (error) {
75
- console.log("Coinray token could not be refreshed", error);
76
- this._refreshingToken = undefined;
77
- return false;
78
- }
79
- this._refreshingToken = undefined;
80
- if (!jwtExpired(this._token)) {
81
- this.reconnect();
82
- }
83
- else {
84
- return false;
85
- }
86
- }
87
- }
88
- catch (e) {
89
- console.error(e);
90
- }
91
- if (loop) {
92
- this._tokenCheckTimeout = setTimeout(this.checkToken, 5000);
93
- }
94
- return true;
95
- });
96
- this.setTransport = (transport) => {
97
- this._transport = transport;
98
- };
99
- this.onTokenExpired = (callback) => {
100
- if (this._tokenCheckTimeout) {
101
- clearInterval(this._tokenCheckTimeout);
102
- }
103
- this._onTokenExpired = callback;
104
- this._tokenCheckTimeout = setTimeout(this.checkToken, 5000);
105
- };
106
- this.refreshToken = (token) => {
107
- this._token = token;
108
- };
109
- this.getNonce = () => {
110
- if (this._nonceOffset > 900) {
111
- this._nonceOffset = 0;
112
- }
113
- this._nonceOffset += 1;
114
- return this.getTime() + this._nonceOffset;
115
- };
116
- this.getTime = () => {
117
- return Math.floor(new Date().getTime() / 1000) * 1000 + this._timeOffset;
118
- };
119
- this.loadTimeOffset = () => __awaiter(this, void 0, void 0, function* () {
120
- try {
121
- const { result } = yield this.get("/coinray/time");
122
- this._timeOffset = result.time - new Date().getTime();
123
- }
124
- catch (e) {
125
- console.error(e);
126
- }
127
- this._timeOffsetTimeout = setTimeout(this.loadTimeOffset, 60 * 1000);
128
- });
129
- this.getToken = () => __awaiter(this, void 0, void 0, function* () {
130
- const tokenValid = yield this.checkToken(false);
131
- if (tokenValid) {
132
- return this._token;
133
- }
134
- else {
135
- throw new Error("Token is expired. call refreshToken in the onTokenExpired callback");
136
- }
137
- });
138
- this.reconnect = () => {
139
- if (this._connected) {
140
- // @ts-ignore
141
- this.socket.conn.close();
142
- }
143
- };
144
- this.disconnect = () => {
145
- if (this._socket) {
146
- this._socket.disconnect();
147
- this._socket = undefined;
148
- }
149
- this._connected = undefined;
150
- };
151
- this.connect = () => __awaiter(this, void 0, void 0, function* () {
152
- if (this._connected) {
153
- yield this._connected;
154
- return;
155
- }
156
- else {
157
- this._connected = new Promise((resolve) => {
158
- this.markConnected = resolve;
159
- });
160
- }
161
- // @ts-ignore
162
- this.socket.onOpen((event) => {
163
- if (this._onOpen) {
164
- this._onOpen(event);
165
- }
166
- });
167
- // @ts-ignore
168
- this.socket.onError((event) => {
169
- if (this._onError) {
170
- this._onError(event);
171
- }
172
- });
173
- this.socket.connect();
174
- this.markConnected();
175
- });
176
- this.onOpen = (callback) => {
177
- this._onOpen = callback;
178
- };
179
- this.onError = (callback) => {
180
- this._onError = callback;
181
- };
182
- this.resubscribe = () => {
183
- if (this._firstOpen) {
184
- this._firstOpen = false;
185
- return;
186
- }
187
- Object.keys(this._tickerListeners).forEach((exchangeCode) => {
188
- if (this._tickerListeners[exchangeCode].length > 0) {
189
- const channel = this.getChannel("tickers");
190
- channel.push("subscribe", { exchangeCodes: [exchangeCode] }, 5000);
191
- }
192
- });
193
- Object.keys(this._tradeListeners).forEach((coinraySymbol) => {
194
- if (this._tradeListeners[coinraySymbol].length > 0) {
195
- const channel = this.getChannel("trades");
196
- channel.push("subscribe", { symbols: coinraySymbol, snapshots: true }, 5000);
197
- }
198
- });
199
- Object.keys(this._orderbookListeners).forEach((coinraySymbol) => {
200
- if (this._orderbookListeners[coinraySymbol].length > 0) {
201
- const channel = this.getChannel("orderbooks");
202
- channel.push("subscribe", { symbols: coinraySymbol, snapshots: true }, 5000);
203
- }
204
- });
205
- };
206
- this.subscribeTickers = (exchangeCode, callback) => __awaiter(this, void 0, void 0, function* () {
207
- if (this._tickerListeners[exchangeCode] && this._tickerListeners[exchangeCode].length > 0) {
208
- this._tickerListeners[exchangeCode].push(callback);
209
- return callback;
210
- }
211
- // Run only once per coinraySymbol
212
- this._tickerListeners[exchangeCode] = [callback];
213
- yield this.connect();
214
- const channel = this.getChannel("tickers");
215
- channel.push("subscribe", { exchangeCodes: [exchangeCode] }, 5000);
216
- if (this.tickersSubscribed) {
217
- return callback;
218
- }
219
- this.tickersSubscribed = true;
220
- channel.on("update", (payload) => {
221
- let { exchange_code: exchangeCode, tickers } = payload;
222
- const callbacks = Object.values(this._tickerListeners[exchangeCode]);
223
- const parsedTickers = tickers.map(Coinray._parseTicker);
224
- callbacks.map((callback) => callback({
225
- type: "tickers:update",
226
- exchangeCode,
227
- tickers: parsedTickers
228
- }));
229
- });
230
- channel.on("error", (payload) => console.error(payload));
231
- return callback;
232
- });
233
- this.unsubscribeTickers = (exchangeCode, callback) => {
234
- if (callback && this._tickerListeners[exchangeCode]) {
235
- this._tickerListeners[exchangeCode] = this._tickerListeners[exchangeCode].filter((c) => c !== callback);
236
- if (this._tickerListeners[exchangeCode].length === 0) {
237
- this.getChannel("tickers")
238
- .push("unsubscribe", { exchangeCodes: [exchangeCode] }, 5000);
239
- }
240
- }
241
- else {
242
- this._tickerListeners[exchangeCode] = [];
243
- }
244
- };
245
- this.subscribeTrades = ({ coinraySymbol }, callback) => __awaiter(this, void 0, void 0, function* () {
246
- if (this._tradeListeners[coinraySymbol] && this._tradeListeners[coinraySymbol].length > 0) {
247
- if (!this._tradeListeners[coinraySymbol].includes(callback)) {
248
- this._tradeListeners[coinraySymbol].push(callback);
249
- }
250
- callback({
251
- type: "trades:snapshot",
252
- coinraySymbol: coinraySymbol,
253
- trades: this._tradeSnapshots[coinraySymbol]
254
- });
255
- return callback;
256
- }
257
- // Run only once per coinraySymbol
258
- this._tradeSnapshots[coinraySymbol] = [];
259
- this._tradeListeners[coinraySymbol] = [callback];
260
- yield this.connect();
261
- const channel = this.getChannel("trades");
262
- channel.push("subscribe", { symbols: coinraySymbol, snapshots: true }, 5000);
263
- if (this.tradesSubscribed) {
264
- return callback;
265
- }
266
- this.tradesSubscribed = true;
267
- channel.on("snapshot", ({ symbol, trades }) => {
268
- const callbacks = Object.values(this._tradeListeners[symbol]);
269
- const parsedTrades = trades.map(Coinray._parseTrade);
270
- this._tradeSnapshots[symbol] = parsedTrades;
271
- callbacks.map((callback) => callback({
272
- type: "trades:snapshot",
273
- coinraySymbol: symbol,
274
- trades: parsedTrades
275
- }));
276
- });
277
- channel.on("update", ({ symbol, trades }) => {
278
- const callbacks = Object.values(this._tradeListeners[symbol]);
279
- const parsedTrades = trades.map(Coinray._parseTrade);
280
- const snapshot = [...parsedTrades, ...this._tradeSnapshots[symbol]];
281
- const minTime = new Date(new Date().getTime() - 120000);
282
- const relevantSnapshot = snapshot.filter(({ time }) => time > minTime);
283
- if (relevantSnapshot.length > 100) {
284
- this._tradeSnapshots[symbol] = relevantSnapshot;
285
- }
286
- else {
287
- this._tradeSnapshots[symbol] = snapshot.slice(0, 100);
288
- }
289
- callbacks.map((callback) => callback({
290
- type: "trades:update",
291
- coinraySymbol: symbol,
292
- trades: parsedTrades
293
- }));
294
- });
295
- channel.on("error", (payload) => console.error(payload));
296
- return callback;
297
- });
298
- this.unsubscribeTrades = ({ coinraySymbol }, callback) => {
299
- if (callback && this._tradeListeners[coinraySymbol]) {
300
- this._tradeListeners[coinraySymbol] = this._tradeListeners[coinraySymbol].filter((c) => c !== callback);
301
- if (this._tradeListeners[coinraySymbol].length === 0) {
302
- this.getChannel("trades")
303
- .push("unsubscribe", { symbols: coinraySymbol }, 5000);
304
- }
305
- }
306
- else {
307
- this._tradeSnapshots[coinraySymbol] = [];
308
- this._tradeListeners[coinraySymbol] = [];
309
- }
310
- };
311
- this.subscribeOrderBook = ({ coinraySymbol }, callback) => __awaiter(this, void 0, void 0, function* () {
312
- if (this._orderbookListeners[coinraySymbol] && this._orderbookListeners[coinraySymbol].length > 0) {
313
- if (!this._orderbookListeners[coinraySymbol].includes(callback)) {
314
- this._orderbookListeners[coinraySymbol].push(callback);
315
- }
316
- callback({
317
- type: "orderBook:snapshot",
318
- coinraySymbol: coinraySymbol,
319
- orderBook: this._orderbookSnapshots[coinraySymbol]
320
- });
321
- return callback;
322
- }
323
- this._orderbookSnapshots[coinraySymbol] = { minSeq: 0, maxSeq: 0, bids: {}, asks: {} };
324
- this._orderbookListeners[coinraySymbol] = [callback];
325
- yield this.connect();
326
- const channel = this.getChannel("orderbooks");
327
- channel.push("subscribe", { symbols: coinraySymbol }, 10000);
328
- if (this.orderBookSubscribed) {
329
- return callback;
330
- }
331
- this.orderBookSubscribed = true;
332
- channel.on("snapshot", ({ orderbooks }) => {
333
- const incoming_symbols = Object.keys(orderbooks);
334
- incoming_symbols.forEach(symbol => {
335
- const callbacks = Object.values(this._orderbookListeners[symbol]);
336
- const parsedOrderBook = Coinray._parseOrderBookSnapshot(orderbooks[symbol]);
337
- this._orderbookSnapshots[symbol] = parsedOrderBook;
338
- callbacks.map((callback) => callback({
339
- type: "orderBook:snapshot",
340
- coinraySymbol: symbol,
341
- orderBook: parsedOrderBook
342
- }));
343
- });
344
- });
345
- channel.on("update", ({ orderbooks }) => {
346
- const incoming_symbols = Object.keys(orderbooks);
347
- if (incoming_symbols.length === 0) {
348
- return;
349
- }
350
- incoming_symbols.forEach(symbol => {
351
- const callbacks = Object.values(this._orderbookListeners[symbol]);
352
- const parsedOrderBook = Coinray._parseOrderBookUpdate(orderbooks[symbol]);
353
- this._orderbookSnapshots[symbol] = Coinray._updateOrderBook(this._orderbookSnapshots[symbol], parsedOrderBook);
354
- callbacks.map((callback) => callback({
355
- type: "orderBook:update",
356
- coinraySymbol: symbol,
357
- orderBook: parsedOrderBook
358
- }));
359
- });
360
- });
361
- channel.on("error", (payload) => console.error(payload));
362
- return callback;
363
- });
364
- this.unsubscribeOrderBook = ({ coinraySymbol }, callback) => {
365
- if (callback && this._orderbookListeners[coinraySymbol]) {
366
- this._orderbookListeners[coinraySymbol] = this._orderbookListeners[coinraySymbol].filter((c) => c !== callback);
367
- if (this._orderbookListeners[coinraySymbol].length === 0) {
368
- this.getChannel("orderbooks")
369
- .push("unsubscribe", { symbols: coinraySymbol }, 5000);
370
- }
371
- }
372
- else {
373
- this._orderbookSnapshots[coinraySymbol] = { minSeq: 0, maxSeq: 0, bids: {}, asks: {} };
374
- this._orderbookListeners[coinraySymbol] = [];
375
- }
376
- };
377
- this.subscribeCandles = ({ coinraySymbol, resolution, lastCandle }, callback) => __awaiter(this, void 0, void 0, function* () {
378
- const candleId = `${coinraySymbol}-${resolution}`;
379
- if (this._candleListeners[candleId] && this._candleListeners[candleId].length > 0) {
380
- this._candleListeners[candleId].push(callback);
381
- return callback;
382
- }
383
- this._candleListeners[candleId] = [callback];
384
- const candleCallback = ({ type, coinraySymbol, trades }) => {
385
- const callbacks = Object.values(this._candleListeners[candleId]);
386
- const lastCandles = Coinray._tradesToLastCandle(resolution, trades);
387
- const lastCandle = lastCandles[lastCandles.length - 1];
388
- if (lastCandle) {
389
- if (this._candles[candleId] && type === "trades:snapshot") {
390
- this._candles[candleId].skipVolume = true;
391
- }
392
- this._candles[candleId] = Coinray._mergeCandle(this._candles[candleId], lastCandle);
393
- callbacks.map((callback) => callback({
394
- coinraySymbol,
395
- resolution,
396
- candle: this._candles[candleId],
397
- previousCandles: lastCandles
398
- }));
399
- }
400
- };
401
- if (this._candleTradeListeners[coinraySymbol]) {
402
- this._candleTradeListeners[coinraySymbol][candleId] = candleCallback;
403
- }
404
- else {
405
- this._candleTradeListeners[coinraySymbol] = { [candleId]: candleCallback };
406
- }
407
- this._candles[candleId] = lastCandle || { time: new Date(0) };
408
- yield this.subscribeTrades({ coinraySymbol }, candleCallback);
409
- this.startNewCandleTimer({ candleId, resolution, coinraySymbol });
410
- return callback;
411
- });
412
- this.startNewCandleTimer = ({ candleId, resolution, coinraySymbol }) => {
413
- if (this._timers[candleId]) {
414
- clearTimeout(this._timers[candleId]);
415
- }
416
- let secondsToNextBucked = candleTime(0, resolution, new Date()) - new Date().getTime() / 1000;
417
- const callbacks = Object.values(this._candleListeners[candleId]);
418
- this._timers[candleId] = setTimeout(() => {
419
- let currentBucket = candleTime(1, resolution, new Date()) * 1000;
420
- let oldCandle = this._candles[candleId];
421
- if (oldCandle && currentBucket > oldCandle.time.getTime()) {
422
- this._candles[candleId] = {
423
- time: new Date(currentBucket),
424
- open: oldCandle.close,
425
- high: oldCandle.close,
426
- low: oldCandle.close,
427
- close: oldCandle.close,
428
- baseVolume: 0.0,
429
- quoteVolume: 0.0,
430
- numTrades: 0,
431
- };
432
- callbacks.map((callback) => callback({
433
- coinraySymbol,
434
- resolution,
435
- candle: this._candles[candleId],
436
- previousCandles: []
437
- }));
438
- }
439
- this.startNewCandleTimer({ candleId, resolution, coinraySymbol });
440
- }, secondsToNextBucked * 1000);
441
- };
442
- this.unsubscribeCandles = ({ coinraySymbol, resolution }, callback) => {
443
- const candleId = `${coinraySymbol}-${resolution}`;
444
- if (callback && this._candleListeners[candleId]) {
445
- this._candleListeners[candleId] = this._candleListeners[candleId].filter((c) => c !== callback);
446
- }
447
- else {
448
- this._candleListeners[candleId] = [];
449
- }
450
- if (this._candleListeners[candleId].length === 0) {
451
- this.unsubscribeTrades({ coinraySymbol }, this._candleTradeListeners[coinraySymbol][candleId]);
452
- if (this._timers[candleId]) {
453
- clearTimeout(this._timers[candleId]);
454
- delete this._timers[candleId];
455
- }
456
- delete this._candles[candleId];
457
- delete this._candleTradeListeners[coinraySymbol][candleId];
458
- }
459
- };
460
- this.fetchFirstCandleTime = ({ coinraySymbol, resolution }) => __awaiter(this, void 0, void 0, function* () {
461
- let { result: { startTime } } = yield this.get("candles/start-time", {
462
- version: "v1",
463
- params: {
464
- symbol: coinraySymbol, resolution
465
- }
466
- });
467
- return startTime;
468
- });
469
- this.fetchCandles = ({ coinraySymbol, resolution, start, end, useWebSocket }) => __awaiter(this, void 0, void 0, function* () {
470
- this.flushCache();
471
- if (resolution.endsWith("S")) {
472
- const snapshot = yield this.getWebsocketCandles(coinraySymbol, resolution);
473
- return snapshot.filter((candle) => {
474
- let time = candle.time.getTime() / 1000;
475
- return time >= start && time <= end;
476
- });
477
- }
478
- const bucketType = resolutionToBucketType(resolution);
479
- const currentTime = new Date().getTime() / 1000;
480
- const candles = [];
481
- let minTime = end;
482
- if (toBucketEnd(end, resolution) >= currentTime) {
483
- let openCandles = yield this.getOpenCandles(useWebSocket, {
484
- version: "v2", params: { symbol: coinraySymbol, resolution: resolution, }
485
- });
486
- if (openCandles.length > 0) {
487
- minTime = Math.min(openCandles[0].time.getTime() / 1000, minTime);
488
- candles.push(...openCandles);
489
- }
490
- else {
491
- return [];
492
- }
493
- }
494
- let newEnd = candleTime(2, resolution, minTime);
495
- let chunks = chunk(getBucketStartDates(start, newEnd, bucketType), 10);
496
- for (let i = 0; i < chunks.length; i += 1) {
497
- let requests = chunks[i].map((bucketStart) => {
498
- let timeParams = getTimeParams(bucketType, bucketStart);
499
- let getParams = {
500
- version: "v2",
501
- params: Object.assign({ symbol: coinraySymbol, resolution: resolution }, timeParams)
502
- };
503
- return this.getHistoryCandles(getParams);
504
- });
505
- let results = yield Promise.all(requests);
506
- let historyCandles = [];
507
- for (let j = 0; j < results.length; j += 1) {
508
- historyCandles = results[j].filter(({ time }) => time.getTime() / 1000 < minTime);
509
- if (historyCandles.length > 0) {
510
- minTime = Math.min(historyCandles[0].time.getTime() / 1000, minTime);
511
- candles.unshift(...historyCandles);
512
- }
513
- }
514
- if (historyCandles.length === 0) {
515
- break;
516
- }
517
- }
518
- return candles.filter(({ time }) => {
519
- let current = time.getTime() / 1000;
520
- return current >= start && current <= end;
521
- }).sort((left, right) => left.time.getTime() - right.time.getTime());
522
- });
523
- this.flushCache = () => {
524
- let currentTime = new Date().getTime() / 1000;
525
- // flush the whole candle response cache every 10 min
526
- if (!this._candleCacheCreatedAt || (currentTime - this._candleCacheCreatedAt) > 600) {
527
- this._candleCacheCreatedAt = currentTime;
528
- this._candleCache = new Map();
529
- }
530
- };
531
- this.getWebsocketCandles = (coinraySymbol, resolution) => __awaiter(this, void 0, void 0, function* () {
532
- const subscribe = new Promise(resolve => {
533
- let timeout = setTimeout(() => {
534
- this.unsubscribeCandles({ coinraySymbol, resolution }, onSnapshot);
535
- resolve([]);
536
- }, 0);
537
- const onSnapshot = ({ previousCandles }) => {
538
- clearTimeout(timeout);
539
- this.unsubscribeCandles({ coinraySymbol, resolution }, onSnapshot);
540
- resolve(previousCandles);
541
- };
542
- this.subscribeCandles({ coinraySymbol, resolution }, onSnapshot);
543
- });
544
- return yield subscribe;
545
- });
546
- this.getOpenCandles = (useWebSocket, getParams) => __awaiter(this, void 0, void 0, function* () {
547
- const cacheKeyOpen = this.candleCacheKey(Object.assign({ prefix: "O" }, getParams.params));
548
- let cached = this._candleCache.get(cacheKeyOpen);
549
- if (!useWebSocket && cached) {
550
- return cached;
551
- }
552
- const snapshot = yield this.getWebsocketCandles(getParams.params.symbol, getParams.params.resolution);
553
- const indexedSnapshot = _.keyBy(snapshot, ({ time }) => time.getTime());
554
- let { result } = yield this.get("candles/open", getParams); // this goes directly to the backend
555
- if (result.candles) {
556
- let candles = result.candles.map(Coinray._parseCandle);
557
- let merged = candles.map((candle) => {
558
- const snapshotCandle = indexedSnapshot[candle.time.getTime()];
559
- if (snapshotCandle) {
560
- return Coinray._mergeCandle(candle, snapshotCandle);
561
- }
562
- else {
563
- return candle;
564
- }
565
- });
566
- this._candleCache.set(cacheKeyOpen, merged);
567
- return merged;
568
- }
569
- else {
570
- return [];
571
- }
572
- });
573
- this.getHistoryCandles = (getParams) => __awaiter(this, void 0, void 0, function* () {
574
- const cacheKey = this.candleCacheKey(getParams.params);
575
- let cachedCandlesResponses = this._candleCache.get(cacheKey);
576
- if (cachedCandlesResponses) {
577
- return cachedCandlesResponses;
578
- }
579
- else {
580
- if (getParams.params.year < 2018) {
581
- return new Promise((resolve) => resolve([]));
582
- }
583
- let { result } = yield this.get("candles/history", getParams); // this goes to the cf worker / cache
584
- if (result.candles) {
585
- let candles = result.candles.map(Coinray._parseCandle);
586
- this._candleCache.set(cacheKey, candles);
587
- return candles;
588
- }
589
- else {
590
- return [];
591
- }
592
- }
593
- });
594
- this.candleCacheKey = ({ symbol, resolution, year, month = "", day = "", week = "", prefix = "C" }) => `${prefix}/${symbol}/${resolution}/${year}/${month}/${day}/${week}`;
595
- this.fetchExchanges = (callback, cachedExchanges) => __awaiter(this, void 0, void 0, function* () {
596
- let exchanges = cachedExchanges;
597
- if (!exchanges) {
598
- ({ result: { exchanges } } = yield this.get("exchanges", {
599
- version: "v1",
600
- }));
601
- }
602
- return exchanges.map(callback);
603
- });
604
- this.fetchMarkets = (exchange, cachedMarkets) => __awaiter(this, void 0, void 0, function* () {
605
- let markets = cachedMarkets;
606
- if (!markets) {
607
- ({ result: { markets } } = yield this.get("markets", {
608
- version: "v1",
609
- params: {
610
- exchange: exchange.code
611
- }
612
- }));
613
- }
614
- return markets;
615
- });
616
- this.createCredential = (deviceId, password) => __awaiter(this, void 0, void 0, function* () {
617
- const publicKey = yield this.publicKey();
618
- try {
619
- const encryptedPassword = yield encryptPayload(yield createJWT({ password }), publicKey);
620
- const { result } = yield this.post("credentials", {
621
- secret: password,
622
- body: {
623
- deviceId, encryptedPassword
624
- }
625
- });
626
- return result;
627
- }
628
- catch (error) {
629
- throw error;
630
- }
631
- });
632
- this.wrapApiKey = (apiKeySettings, preWrapped) => __awaiter(this, void 0, void 0, function* () {
633
- const publicKey = yield this.publicKey();
634
- let encryptedApiKey;
635
- try {
636
- if (preWrapped) {
637
- encryptedApiKey = apiKeySettings;
638
- }
639
- else {
640
- const apiKey = JSON.stringify(apiKeySettings);
641
- encryptedApiKey = yield encryptPayload(yield createJWT({ apiKey }), publicKey);
642
- }
643
- const { result } = yield this.post("credentials/wrap_api_key", {
644
- secret: this._sessionKey,
645
- body: {
646
- encryptedApiKey, credential: this._credential
647
- }
648
- });
649
- return result;
650
- }
651
- catch (error) {
652
- throw error;
653
- }
654
- });
655
- this.testAccount = (accountTest) => __awaiter(this, void 0, void 0, function* () {
656
- try {
657
- const { result } = yield this.post("account/test", {
658
- secret: this._sessionKey,
659
- apiEndpoint: this.config.orderEndpoint,
660
- body: Object.assign(Object.assign({}, accountTest), { credential: this._credential })
661
- });
662
- return result;
663
- }
664
- catch (error) {
665
- throw error;
666
- }
667
- });
668
- this.createSmartOrderSignature = (smartOrder) => __awaiter(this, void 0, void 0, function* () {
669
- try {
670
- const { result } = yield this.post("order/smart_order_signature", {
671
- secret: this._sessionKey,
672
- apiEndpoint: this.config.orderEndpoint,
673
- body: { smartOrder, credential: this._credential }
674
- });
675
- return result;
676
- }
677
- catch (error) {
678
- throw error;
679
- }
680
- });
681
- this.createOrder = (order) => __awaiter(this, void 0, void 0, function* () {
682
- try {
683
- const { result } = yield this.post("order", {
684
- secret: this._sessionKey,
685
- apiEndpoint: this.config.orderEndpoint,
686
- body: Object.assign(Object.assign({}, order), { credential: this._credential })
687
- });
688
- return result;
689
- }
690
- catch (error) {
691
- throw error;
692
- }
693
- });
694
- this.updateOrder = (order) => __awaiter(this, void 0, void 0, function* () {
695
- try {
696
- const { result } = yield this.patch("order", {
697
- secret: this._sessionKey,
698
- apiEndpoint: this.config.orderEndpoint,
699
- body: Object.assign(Object.assign({}, order), { credential: this._credential })
700
- });
701
- return result;
702
- }
703
- catch (error) {
704
- throw error;
705
- }
706
- });
707
- this.cancelOrder = (order) => __awaiter(this, void 0, void 0, function* () {
708
- try {
709
- const { result } = yield this.delete("order", {
710
- secret: this._sessionKey,
711
- apiEndpoint: this.config.orderEndpoint,
712
- body: Object.assign(Object.assign({}, order), { orderId: order.orderId.toString(), credential: this._credential })
713
- });
714
- return result;
715
- }
716
- catch (error) {
717
- throw error;
718
- }
719
- });
720
- this.createWebSocketToken = ({ exchangeCode, encryptedApiKey }) => __awaiter(this, void 0, void 0, function* () {
721
- try {
722
- const { result } = yield this.post("exchanges/ws_token", {
723
- secret: this._sessionKey,
724
- apiEndpoint: this.config.orderEndpoint,
725
- body: { encryptedApiKey, exchangeCode, credential: this._credential }
726
- });
727
- return result;
728
- }
729
- catch (error) {
730
- throw error;
731
- }
732
- });
733
- this.fetchPositions = ({ exchangeCode, encryptedApiKey }) => __awaiter(this, void 0, void 0, function* () {
734
- try {
735
- const { result } = yield this.get("futures/positions", {
736
- secret: this._sessionKey,
737
- apiEndpoint: this.config.orderEndpoint,
738
- params: {
739
- credential: this._credential,
740
- exchangeCode,
741
- encryptedApiKey
742
- }
743
- });
744
- return result;
745
- }
746
- catch (error) {
747
- throw error;
748
- }
749
- });
750
- this.getBalances = ({ exchangeCode, encryptedApiKey }) => __awaiter(this, void 0, void 0, function* () {
751
- try {
752
- const { result: { balances, positions } } = yield this.get("account/balances", {
753
- secret: this._sessionKey,
754
- apiEndpoint: this.config.orderEndpoint,
755
- params: {
756
- credential: this._credential,
757
- exchangeCode,
758
- encryptedApiKey
759
- }
760
- });
761
- return { balances: balances.map(Coinray._parseBalance), positions };
762
- }
763
- catch (error) {
764
- throw error;
765
- }
766
- });
767
- this.setLeverage = ({ coinraySymbol, leverage, encryptedApiKey }) => __awaiter(this, void 0, void 0, function* () {
768
- try {
769
- const { result } = yield this.post("futures/leverage", {
770
- secret: this._sessionKey,
771
- apiEndpoint: this.config.orderEndpoint,
772
- body: {
773
- credential: this._credential,
774
- coinraySymbol,
775
- leverage,
776
- encryptedApiKey
777
- }
778
- });
779
- return result;
780
- }
781
- catch (error) {
782
- throw error;
783
- }
784
- });
785
- this.setMarginType = ({ coinraySymbol, marginType, encryptedApiKey }) => __awaiter(this, void 0, void 0, function* () {
786
- try {
787
- const { result } = yield this.post("futures/margin_type", {
788
- secret: this._sessionKey,
789
- apiEndpoint: this.config.orderEndpoint,
790
- body: {
791
- credential: this._credential,
792
- coinraySymbol,
793
- marginType,
794
- encryptedApiKey
795
- }
796
- });
797
- return result;
798
- }
799
- catch (error) {
800
- throw error;
801
- }
802
- });
803
- this.get = (endpoint, { apiEndpoint = undefined, version = "v2", headers = {}, params = {}, secret = "" } = {}) => __awaiter(this, void 0, void 0, function* () {
804
- return yield this._request(endpoint, "GET", {
805
- version,
806
- apiEndpoint,
807
- headers,
808
- secret,
809
- params
810
- });
811
- });
812
- this.post = (endpoint, attributes) => __awaiter(this, void 0, void 0, function* () { return yield this._request(endpoint, "POST", attributes); });
813
- this.patch = (endpoint, attributes) => __awaiter(this, void 0, void 0, function* () { return yield this._request(endpoint, "PATCH", attributes); });
814
- this.delete = (endpoint, attributes) => __awaiter(this, void 0, void 0, function* () { return yield this._request(endpoint, "delete", attributes); });
815
- this._token = token;
816
- this._nonceOffset = 0;
817
- this._timeOffset = 0;
818
- this._firstOpen = true;
819
- this.config = {
820
- apiEndpoint: apiEndpoint || "https://api.coinray.eu",
821
- orderEndpoint: orderEndpoint || apiEndpoint || "https://api.coinray.eu",
822
- websocketEndpoint: websocketEndpoint || "wss://ws.coinray.eu/v1"
823
- };
824
- this._candleCache = new Map();
825
- this.loadTimeOffset().then();
826
- }
827
- getProxyList({ exchangeCode, credentialVersion }) {
828
- return __awaiter(this, void 0, void 0, function* () {
829
- let params = "";
830
- if (exchangeCode) {
831
- params += `?exchange=${exchangeCode.toUpperCase()}`;
832
- params += credentialVersion ? `&version=${credentialVersion}` : "";
833
- }
834
- const { result: { proxies } } = yield this.get("credentials/ips" + params);
835
- return proxies;
836
- });
837
- }
838
- publicKey() {
839
- return __awaiter(this, void 0, void 0, function* () {
840
- if (!this._publicKey) {
841
- const { result: { jwk } } = yield this.get("credentials/certificate");
842
- this._publicKey = yield jwkToPublicKey(jwk);
843
- }
844
- return this._publicKey;
845
- });
846
- }
847
- get clientId() {
848
- return parseJWT(this._token).header.kid;
849
- }
850
- get socket() {
851
- if (!this._token) {
852
- throw new Error("Token not present");
853
- }
854
- if (!this._socket) {
855
- this._socket = new Socket(this.config.websocketEndpoint, {
856
- heartbeatIntervalMs: 5000,
857
- transport: this._transport,
858
- reconnectAfterMs: (tries) => {
859
- if (jwtExpired(this._token)) {
860
- return 30000;
861
- }
862
- else {
863
- return 1000;
864
- }
865
- }
866
- });
867
- this.onOpen(this.resubscribe);
868
- }
869
- // @ts-ignore
870
- this._socket.params = () => ({ reconnect: true, token: this._token, client: "coinrayjs", version: VERSION });
871
- return this._socket;
872
- }
873
- getChannel(name) {
874
- this.connect();
875
- if (this._channels[name]) {
876
- return this._channels[name];
877
- }
878
- const channel = this.socket.channel(name, {});
879
- this._channels[name] = channel;
880
- channel.join();
881
- return channel;
882
- }
883
- _request(endpoint, method, { apiEndpoint, version = "v2", headers = {}, params = {}, body = {}, secret = "" }) {
884
- return __awaiter(this, void 0, void 0, function* () {
885
- const token = yield this.getToken();
886
- const paramString = Object.entries(params).length > 0 ? '?' + Object.entries(params).map(([key, val]) => val ? `${key}=${val}` : undefined).filter(Boolean).join('&') : "";
887
- const nonce = this.getNonce();
888
- const requestUri = `/api/${version}/${endpoint}${paramString}`;
889
- if (version === "v2" && !endpoint.startsWith("candles")) {
890
- const dataToSign = [nonce, method.toUpperCase(), requestUri, method === "GET" ? "" : JSON.stringify(body)].join("");
891
- const signature = signHMAC(dataToSign, secret);
892
- headers = Object.assign(Object.assign({}, headers), { "Cr-Access-Token": `${token}`, "Cr-Nonce": nonce, "Cr-Signature": signature });
893
- }
894
- const options = {
895
- method,
896
- url: (apiEndpoint || this.config.apiEndpoint) + requestUri,
897
- timeout: 20000,
898
- headers: Object.assign({ "Accept": "application/json", "Content-Type": "application/json", "Authorization": `Bearer ${token}`, "Cr-Client-version": VERSION }, headers),
899
- data: method === "GET" ? undefined : JSON.stringify(body)
900
- };
901
- try {
902
- const response = yield axios(options);
903
- let result;
904
- if (response.status === 200) {
905
- result = response.data;
906
- }
907
- else if (response.status === 204) {
908
- result = {};
909
- }
910
- else {
911
- result = response.data;
912
- }
913
- return { result, _headers: response.headers };
914
- }
915
- catch (error) {
916
- const { response, request } = error;
917
- if (response) {
918
- const { error } = response.data;
919
- throw new CoinrayError(error);
920
- }
921
- else {
922
- throw error;
923
- }
924
- }
925
- });
926
- }
927
- static _parseOrderBookSnapshot({ asks, bids, min_seq, max_seq }) {
928
- return {
929
- minSeq: min_seq,
930
- maxSeq: max_seq,
931
- asks: Coinray._parseBidAsk(asks),
932
- bids: Coinray._parseBidAsk(bids),
933
- };
934
- }
935
- static _parseOrderBookUpdate({ asks_diff, bids_diff, min_seq, max_seq }) {
936
- return {
937
- minSeq: min_seq,
938
- maxSeq: max_seq,
939
- asks: Coinray._parseBidAsk(asks_diff),
940
- bids: Coinray._parseBidAsk(bids_diff),
941
- };
942
- }
943
- static _updateOrderBook(orderBook, { asks, bids, min_seq, max_seq }) {
944
- const update = (side, updates) => {
945
- _.forEach(updates, (quantity, price) => {
946
- if (quantity > 0) {
947
- side[price] = quantity;
948
- }
949
- else {
950
- delete side[price];
951
- }
952
- });
953
- };
954
- orderBook.minSeq = min_seq;
955
- orderBook.maxSeq = max_seq;
956
- update(orderBook.bids, bids);
957
- update(orderBook.asks, asks);
958
- return orderBook;
959
- }
960
- static _parseBidAsk(bidAsk) {
961
- if (Array.isArray(bidAsk)) {
962
- return bidAsk.reduce((acc, [price, quantity]) => {
963
- acc[price] = parseFloat(quantity);
964
- return acc;
965
- }, {});
966
- }
967
- else {
968
- return Object.keys(bidAsk).reduce((acc, price) => {
969
- acc[price] = parseFloat(bidAsk[price]);
970
- return acc;
971
- }, {});
972
- }
973
- }
974
- static _parseCandle(result) {
975
- if (result) {
976
- const [time, open, high, low, close, baseVolume, quoteVolume] = result;
977
- return {
978
- time: safeTime(time),
979
- open: parseFloat(open),
980
- high: parseFloat(high),
981
- low: parseFloat(low),
982
- close: parseFloat(close),
983
- baseVolume: parseFloat(baseVolume),
984
- quoteVolume: parseFloat(quoteVolume),
985
- numTrades: 0,
986
- };
987
- }
988
- else {
989
- throw new Error("Candle");
990
- }
991
- }
992
- static _parseTrade([id, time, price, quantity, isBuy]) {
993
- return {
994
- id,
995
- time: safeTime(time),
996
- price: parseFloat(price),
997
- quantity: parseFloat(quantity),
998
- type: ['1', 'buy'].includes(isBuy.toString()) ? "buy" : "sell"
999
- };
1000
- }
1001
- static _parseTicker(ticker) {
1002
- return {
1003
- askPrice: safeBigNumber(ticker.a),
1004
- baseVolume: safeBigNumber(ticker.bv),
1005
- bidPrice: safeBigNumber(ticker.b),
1006
- btcVolume: safeBigNumber(ticker.B),
1007
- coinraySymbol: ticker.s,
1008
- highPrice1s: safeBigNumber(ticker.h),
1009
- highPrice24h: safeBigNumber(ticker.H),
1010
- lastPrice: safeBigNumber(ticker.c),
1011
- lowPrice1s: safeBigNumber(ticker.l),
1012
- lowPrice24h: safeBigNumber(ticker.L),
1013
- openPrice1s: safeBigNumber(ticker.o),
1014
- openPrice24h: safeBigNumber(ticker.O),
1015
- quoteVolume: safeBigNumber(ticker.qv),
1016
- usdVolume: safeBigNumber(ticker.U)
1017
- };
1018
- }
1019
- static _parseBalance(balance) {
1020
- let newBalance = {
1021
- currency: balance.currency,
1022
- available: safeBigNumber(balance.available),
1023
- inOrders: safeBigNumber(balance.inOrders),
1024
- total: safeBigNumber(balance.inOrders),
1025
- };
1026
- if (balance.initialMargin) {
1027
- newBalance = Object.assign(Object.assign({}, newBalance), {
1028
- initialMargin: safeBigNumber(balance.initialMargin),
1029
- openOrderInitialMargin: safeBigNumber(balance.openOrderInitialMargin),
1030
- positionInitialMargin: safeBigNumber(balance.positionInitialMargin),
1031
- unrealizedProfit: safeBigNumber(balance.unrealizedProfit),
1032
- crossWalletBalance: safeBigNumber(balance.crossWalletBalance),
1033
- crossUnrealizedProfit: safeBigNumber(balance.crossUnrealizedProfit),
1034
- });
1035
- }
1036
- return newBalance;
1037
- }
1038
- static _tradesToLastCandle(resolution, trades) {
1039
- if (trades.length > 0 && trades[0].time > trades[trades.length - 1].time) {
1040
- trades = trades.reverse();
1041
- }
1042
- const seconds = Coinray._resolutionToSeconds(resolution);
1043
- const groupedTrades = _.groupBy(trades.reverse(), ({ time }) => Math.floor(time.getTime() / seconds) * seconds);
1044
- return _.sortBy(Object.keys(groupedTrades), (date) => parseInt(date)).reduce((result, date) => {
1045
- const currentCandleTrades = groupedTrades[date];
1046
- const currentTime = currentCandleTrades[0].time.getTime();
1047
- const startDate = new Date(currentTime - (currentTime % seconds));
1048
- if (currentCandleTrades.length === 0) {
1049
- return result;
1050
- }
1051
- let first = currentCandleTrades[0];
1052
- let open, low, high, close;
1053
- let baseVolume = 0.0;
1054
- let quoteVolume = 0.0;
1055
- if (result.length === 0) {
1056
- open = low = high = close = first.price;
1057
- }
1058
- else {
1059
- open = low = high = close = result[result.length - 1].close;
1060
- }
1061
- currentCandleTrades.reverse().map(({ price, quantity }) => {
1062
- low = Math.min(low, price);
1063
- high = Math.max(high, price);
1064
- close = price;
1065
- baseVolume += quantity;
1066
- quoteVolume += quantity * price;
1067
- });
1068
- result.push({
1069
- time: new Date(startDate),
1070
- open,
1071
- high,
1072
- low,
1073
- close,
1074
- baseVolume,
1075
- quoteVolume,
1076
- numTrades: currentCandleTrades.length
1077
- });
1078
- return result;
1079
- }, []);
1080
- }
1081
- ;
1082
- static _mergeCandle(currentCandle, candle) {
1083
- if (!currentCandle || currentCandle.time < candle.time) {
1084
- return candle;
1085
- }
1086
- currentCandle.numTrades += candle.numTrades;
1087
- currentCandle.high = Math.max(currentCandle.high, candle.high);
1088
- currentCandle.low = Math.min(currentCandle.low, candle.low);
1089
- currentCandle.close = candle.close;
1090
- if (currentCandle.skipVolume) {
1091
- currentCandle.skipVolume = false;
1092
- }
1093
- else {
1094
- currentCandle.baseVolume += candle.baseVolume;
1095
- currentCandle.quoteVolume += candle.quoteVolume;
1096
- }
1097
- return currentCandle;
1098
- }
1099
- static _resolutionToSeconds(resolution) {
1100
- if (resolution.indexOf("W") > 0) {
1101
- return parseInt(resolution) * 24 * 60 * 60 * 1000;
1102
- }
1103
- else if (resolution.indexOf("D") > 0) {
1104
- return parseInt(resolution) * 24 * 60 * 60 * 1000;
1105
- }
1106
- else if (resolution.indexOf("S") > 0) {
1107
- return parseInt(resolution) * 1000;
1108
- }
1109
- else if (resolution === "D") {
1110
- return 24 * 60 * 60 * 1000;
1111
- }
1112
- else {
1113
- return parseInt(resolution) * 60 * 1000;
1114
- }
1115
- }
1116
- }
1117
- Coinray.I18n = I18n;
1118
- //# sourceMappingURL=coinray.js.map