50 | 'use strict'
51 | /* eslint-disable */
52 | /* legacy API interface, not cleaned up with new eslint rules */
53 |
54 | const fetch = require('node-fetch')
55 | const debug = require('debug')('bfx:rest1')
56 | const { genAuthSig, nonce } = require('bfx-api-node-util')
57 | const API_URL = 'https://api.bitfinex.com'
58 |
59 | /**
60 | * Communicates with v1 of the Bitfinex HTTP API
61 | */
62 | class RESTv1 {
63 | /**
64 | * Instantiate a new REST v1 transport.
65 | *
66 | * @param {Object} opts
67 | * @param {string?} opts.apiKey
68 | * @param {string?} opts.apiSecret
69 | * @param {string?} opts.url - endpoint URL
70 | * @param {Object?} opts.agent - optional node agent for connection (proxy)
71 | * @param {Method?} opts.nonceGenerator - optional, should return a nonce
72 | */
73 | constructor (opts = {}) {
74 | this._url = opts.url || API_URL
75 | this._apiKey = opts.apiKey || ''
76 | this._apiSecret = opts.apiSecret || ''
77 | this._agent = opts.agent
78 | this._generateNonce = (typeof opts.nonceGenerator === 'function')
79 | ? opts.nonceGenerator
80 | : nonce
81 | }
82 |
83 | /**
84 | * @param {string} body - raw JSON
85 | * @param {Method} cb
86 | * @private
87 | */
88 | _parse_req_body (result, cb) {
89 | if (typeof result.message === 'string') {
90 | if (result.message.indexOf('Nonce is too small') !== -1) {
91 | result.message += ' See https://github.com/bitfinexcom/bitfinex-api-node/blob/master/README.md#nonce-too-small for help'
92 | }
93 |
94 | return cb(new Error(result.message))
95 | }
96 |
97 | return cb(null, result)
98 | }
99 |
100 | /**
101 | * @param {string} path
102 | * @param {Object} params
103 | * @param {Method} cb
104 | * @private
105 | */
106 | async make_request (path, params, cb) {
107 | if (!this._apiKey || !this._apiSecret) {
108 | return cb(new Error('missing api key or secret'))
109 | }
110 | if (!path) {
111 | return cb(new Error('path is missing'))
112 | }
113 |
114 | const payload = Object.assign({
115 | request: `/v1/${path}`,
116 | nonce: JSON.stringify(this._generateNonce())
117 | }, params)
118 |
119 | const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString('base64')
120 | const { sig } = genAuthSig(this._apiSecret, payloadBase64)
121 | const url = `${this._url}/v1/${path}`
122 |
123 | debug('POST %s', url)
124 |
125 | const reqOpts = {
126 | method: 'POST',
127 | timeout: 15000,
128 | agent: this._agent,
129 | headers: {
130 | 'X-BFX-APIKEY': this._apiKey,
131 | 'X-BFX-PAYLOAD': payloadBase64,
132 | 'X-BFX-SIGNATURE': sig
133 | }
134 | }
135 |
136 | try {
137 | const resp = await fetch(url, reqOpts)
138 | if (!resp.ok && +resp.status !== 400) {
139 | throw new Error(`HTTP code ${resp.status} ${resp.statusText || ''}`)
140 | }
141 | const json = await resp.json()
142 | return this._parse_req_body(json, cb)
143 | } catch (err) {
144 | return cb(err)
145 | }
146 | }
147 |
148 | /**
149 | * @param {string} path
150 | * @param {Method} cb
151 | * @private
152 | */
153 | async make_public_request (path, cb) {
154 | if (!path) {
155 | return cb(new Error('path is missing'))
156 | }
157 |
158 | const url = `${this._url}/v1/${path}`
159 |
160 | debug('GET %s', url)
161 |
162 | const reqOpts = {
163 | method: 'GET',
164 | agent: this._agent,
165 | timeout: 15000
166 | }
167 |
168 | try {
169 | const resp = await fetch(url, reqOpts)
170 | if (!resp.ok && +resp.status !== 400) {
171 | throw new Error(`HTTP code ${resp.status} ${resp.statusText || ''}`)
172 | }
173 | const json = await resp.json()
174 | return this._parse_req_body(json, cb)
175 | } catch (err) {
176 | return cb(err)
177 | }
178 | }
179 |
180 | /**
181 | * @param {string} symbol
182 | * @param {Method} cb
183 | * @see https://docs.bitfinex.com/v1/reference#rest-public-ticker
184 | */
185 | ticker (symbol = 'BTCUSD', cb) {
186 | if (!cb) {
187 | cb = (err, data) => {
188 | if (err) {
189 | console.error(err)
190 | }
191 |
192 | console.log(data)
193 | }
194 | }
195 |
196 | return this.make_public_request(`pubticker/${symbol}`, cb)
197 | }
198 |
199 | /**
200 | * @param {string} symbol
201 | * @param {Method} cb
202 | */
203 | today (symbol, cb) {
204 | return this.make_public_request(`today/${symbol}`, cb)
205 | }
206 |
207 | /**
208 | * @param {string} symbol
209 | * @param {Method} cb
210 | * @see https://docs.bitfinex.com/v1/reference#rest-public-stats
211 | */
212 | stats (symbol, cb) {
213 | return this.make_public_request(`stats/${symbol}`, cb)
214 | }
215 |
216 | /**
217 | * @param {string} currency
218 | * @param {Object} options
219 | * @param {Method} cb
220 | * @see https://docs.bitfinex.com/v1/reference#rest-public-fundingbook
221 | */
222 | fundingbook (currency, options, cb) {
223 | let uri = `lendbook/${currency}`
224 |
225 | if (typeof options === 'function') {
226 | cb = options
227 | } else {
228 | const keys = Object.keys(options)
229 |
230 | for (let i = 0; i < keys.length; i++) {
231 | uri += `${i === 0 ? '/?' : '&'}${keys[i]}=${options[keys[i]]}`
232 | }
233 | }
234 |
235 | return this.make_public_request(uri, cb)
236 | }
237 |
238 | /**
239 | * @param {string} symbol
240 | * @param {Object} options
241 | * @param {Method} cb
242 | * @see https://docs.bitfinex.com/v1/reference#rest-public-orderbook
243 | */
244 | orderbook (symbol, options, cb) {
245 | let uri = `book/${symbol}`
246 |
247 | if (typeof options === 'function') {
248 | cb = options
249 | } else {
250 | const keys = Object.keys(options)
251 |
252 | for (let i = 0; i < keys.length; i++) {
253 | uri += `${i === 0 ? '/?' : '&'}${keys[i]}=${options[keys[i]]}`
254 | }
255 | }
256 |
257 | return this.make_public_request(uri, cb)
258 | }
259 |
260 | /**
261 | * @param {string} symbol
262 | * @param {Method} cb
263 | * @see https://docs.bitfinex.com/v1/reference#rest-public-trades
264 | */
265 | trades (symbol, cb) {
266 | return this.make_public_request('trades/' + symbol, cb)
267 | }
268 |
269 | /**
270 | * @param {string} symbol
271 | * @param {Method} cb
272 | * @see https://docs.bitfinex.com/v1/reference#rest-public-lends
273 | */
274 | lends (currency, cb) {
275 | return this.make_public_request('lends/' + currency, cb)
276 | }
277 |
278 | /**
279 | * @param {Method} cb
280 | * @see https://docs.bitfinex.com/v1/reference#rest-public-symbols
281 | */
282 | get_symbols (cb) {
283 | return this.make_public_request('symbols', cb)
284 | }
285 |
286 | /**
287 | * @param {Method} cb
288 | * @see https://docs.bitfinex.com/v1/reference#rest-public-symbol-details
289 | */
290 | symbols_details (cb) {
291 | return this.make_public_request('symbols_details', cb)
292 | }
293 |
294 | /**
295 | * @param {string} symbol
296 | * @param {number} amount
297 | * @param {number} price
298 | * @param {string} exchange
299 | * @param {string} side
300 | * @param {string} type
301 | * @param {boolean} is_hidden
302 | * @param {boolean} postOnly
303 | * @param {Method} cb
304 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-new-order
305 | */
306 | new_order (symbol, amount, price, exchange, side, type, is_hidden, postOnly, cb) {
307 | if (typeof is_hidden === 'function') {
308 | cb = is_hidden
309 | is_hidden = false
310 | }
311 |
312 | if (typeof postOnly === 'function') {
313 | cb = postOnly
314 | postOnly = false
315 | }
316 |
317 | const params = {
318 | symbol,
319 | amount,
320 | price,
321 | exchange,
322 | side,
323 | type
324 | }
325 |
326 | if (postOnly) params.post_only = true
327 | if (is_hidden) params.is_hidden = true
328 |
329 | return this.make_request('order/new', params, cb)
330 | }
331 |
332 | /**
333 | * @param {Object[]} orders
334 | * @param {Method} cb
335 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-multiple-new-orders
336 | */
337 | multiple_new_orders (orders, cb) {
338 | return this.make_request('order/new/multi', { orders }, cb)
339 | }
340 |
341 | /**
342 | * @param {number} order_id
343 | * @param {Method} cb
344 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-order
345 | */
346 | cancel_order (order_id, cb) {
347 | return this.make_request('order/cancel', {
348 | order_id: parseInt(order_id)
349 | }, cb)
350 | }
351 |
352 | /**
353 | * @param {Method} cb
354 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-all-orders
355 | */
356 | cancel_all_orders (cb) {
357 | return this.make_request('order/cancel/all', {}, cb)
358 | }
359 |
360 | /**
361 | * @param {number[]} order_ids
362 | * @param {Method} cb
363 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-multiple-orders
364 | */
365 | cancel_multiple_orders (order_ids, cb) {
366 | return this.make_request('order/cancel/multi', {
367 | order_ids: order_ids.map(id => parseInt(id))
368 | }, cb)
369 | }
370 |
371 | /**
372 | * @param {number} order_id
373 | * @param {string} symbol
374 | * @param {number} amount
375 | * @param {number} price
376 | * @param {string} exchange
377 | * @param {string} side
378 | * @param {string} type
379 | * @param {Method} cb
380 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-replace-order
381 | */
382 | replace_order (order_id, symbol, amount, price, exchange, side, type, cb) {
383 | return this.make_request('order/cancel/replace', {
384 | order_id: parseInt(order_id),
385 | symbol,
386 | amount,
387 | price,
388 | exchange,
389 | side,
390 | type
391 | }, cb)
392 | }
393 |
394 | /**
395 | * @param {string} order_id
396 | * @param {Method} cb
397 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-order-status
398 | */
399 | order_status (order_id, cb) {
400 | return this.make_request('order/status', {
401 | order_id: parseInt(order_id)
402 | }, cb)
403 | }
404 |
405 | /**
406 | * @param {Method} cb
407 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-orders
408 | */
409 | active_orders (cb) {
410 | return this.make_request('orders', {}, cb)
411 | }
412 |
413 | /**
414 | * @param {Method} cb
415 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-orders-history
416 | */
417 | orders_history (cb) {
418 | return this.make_request('orders/hist', {}, cb)
419 | }
420 |
421 | /**
422 | * @param {Method} cb
423 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-positions
424 | */
425 | active_positions (cb) {
426 | return this.make_request('positions', {}, cb)
427 | }
428 |
429 | /**
430 | * @param {string} position_id
431 | * @param {number} amount
432 | * @param {Method} cb
433 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-claim-position
434 | */
435 | claim_position (position_id, amount, cb) {
436 | return this.make_request('position/claim', {
437 | position_id: parseInt(position_id),
438 | amount
439 | }, cb)
440 | }
441 |
442 | /**
443 | * @param {string} currency
444 | * @param {Object} options
445 | * @param {Method} cb
446 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-balance-history
447 | */
448 | balance_history (currency, options, cb) {
449 | const params = { currency }
450 |
451 | if (typeof options === 'function') {
452 | cb = options
453 | } else if (options && options.constructor.name === 'Object') {
454 | Object.assign(params, options)
455 | }
456 |
457 | return this.make_request('history', params, cb)
458 | }
459 |
460 | /**
461 | * @param {string} currency
462 | * @param {Object} options
463 | * @param {Method} cb
464 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-deposit-withdrawal-history
465 | */
466 | movements (currency, options, cb) {
467 | const params = { currency }
468 |
469 | if (typeof options === 'function') {
470 | cb = options
471 | } else if (options && options.constructor.name === 'Object') {
472 | Object.assign(params, options)
473 | }
474 |
475 | return this.make_request('history/movements', params, cb)
476 | }
477 |
478 | /**
479 | * @param {string} symbol
480 | * @param {Object} options
481 | * @param {Method} cb
482 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-past-trades
483 | */
484 | past_trades (symbol, options, cb) {
485 | const params = { symbol }
486 |
487 | if (typeof options === 'function') {
488 | cb = options
489 | } else if (options && options.constructor.name === 'Object') {
490 | Object.assign(params, options)
491 | }
492 |
493 | return this.make_request('mytrades', params, cb)
494 | }
495 |
496 | /**
497 | * @param {string} currency
498 | * @param {string} method
499 | * @param {string} wallet_name
500 | * @param {Method} cb
501 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-deposit
502 | */
503 | new_deposit (currency, method, wallet_name, cb) {
504 | return this.make_request('deposit/new', {
505 | currency,
506 | method,
507 | wallet_name
508 | }, cb)
509 | }
510 |
511 | /**
512 | * @param {string} currency
513 | * @param {number} amount
514 | * @param {number} rate
515 | * @param {number} period
516 | * @param {string} direction
517 | * @param {Method} cb
518 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-new-offer
519 | */
520 | new_offer (currency, amount, rate, period, direction, cb) {
521 | return this.make_request('offer/new', {
522 | currency,
523 | amount,
524 | rate,
525 | period,
526 | direction
527 | }, cb)
528 | }
529 |
530 | /**
531 | * @param {string} offer_id
532 | * @param {Method} cb
533 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-offer
534 | */
535 | cancel_offer (offer_id, cb) {
536 | return this.make_request('offer/cancel', {
537 | offer_id: parseInt(offer_id)
538 | }, cb)
539 | }
540 |
541 | /**
542 | * @param {string} offer_id
543 | * @param {Method} cb
544 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-offer-status
545 | */
546 | offer_status (offer_id, cb) {
547 | return this.make_request('offer/status', {
548 | offer_id: parseInt(offer_id)
549 | }, cb)
550 | }
551 |
552 | /**
553 | * @param {Method} cb
554 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-offers
555 | */
556 | active_offers (cb) {
557 | return this.make_request('offers', {}, cb)
558 | }
559 |
560 | /**
561 | * @param {Method} cb
562 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-credits
563 | */
564 | active_credits (cb) {
565 | return this.make_request('credits', {}, cb)
566 | }
567 |
568 | /**
569 | * @param {Method} cb
570 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-wallet-balances
571 | */
572 | wallet_balances (cb) {
573 | return this.make_request('balances', {}, cb)
574 | }
575 |
576 | /**
577 | * @param {Method} cb
578 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-funding-used-in-a-margin-position
579 | */
580 | taken_swaps (cb) {
581 | return this.make_request('taken_funds', {}, cb)
582 | }
583 |
584 | /**
585 | * @param {Method} cb
586 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-total-taken-funds
587 | */
588 | total_taken_swaps (cb) {
589 | return this.make_request('total_taken_funds', {}, cb)
590 | }
591 |
592 | /**
593 | * @param {string} swap_id
594 | * @param {Method} cb
595 | */
596 | close_swap (swap_id, cb) {
597 | return this.make_request('swap/close', {
598 | swap_id: parseInt(swap_id)
599 | }, cb)
600 | }
601 |
602 | /**
603 | * @param {Method} cb
604 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-account-info
605 | */
606 | account_infos (cb) {
607 | return this.make_request('account_infos', {}, cb)
608 | }
609 |
610 | /**
611 | * @param {Method} cb
612 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-margin-information
613 | */
614 | margin_infos (cb) {
615 | return this.make_request('margin_infos', {}, cb)
616 | }
617 |
618 | /**
619 | * POST /v1/withdraw
620 | *
621 | * @param {string} withdrawType "bitcoin", "litecoin", "darkcoin" or "mastercoin"
622 | * @param {string} walletSelected origin of the wallet to withdraw from, can be "trading", "exchange", or "deposit"
623 | * @param {number} amount amount to withdraw
624 | * @param {string} address destination address for withdrawal
625 | */
626 | withdraw (withdrawType, walletSelected, amount, address, cb) {
627 | return this.make_request('withdraw', {
628 | withdrawType,
629 | walletSelected,
630 | amount,
631 | address
632 | }, cb)
633 | }
634 |
635 | /**
636 | * POST /v1/transfer
637 | *
638 | * @param {number} amount amount to transfer
639 | * @param {string} currency currency of funds to transfer
640 | * @param {string} walletFrom wallet to transfer from
641 | * @param {string} walletTo wallet to transfer to
642 | */
643 | transfer (amount, currency, walletFrom, walletTo, cb) {
644 | return this.make_request('transfer', {
645 | amount,
646 | currency,
647 | walletFrom,
648 | walletTo
649 | }, cb)
650 | }
651 | }
652 |
653 | module.exports = RESTv1
654 |
655 |
656 |