poloniex.rb 0.0.0 → 0.1.0

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.
@@ -10,66 +10,137 @@ require 'openssl'
10
10
 
11
11
  require_relative '../Configuration'
12
12
  require_relative '../Error'
13
+ require_relative '../../Hash/x_www_form_urlencode'
14
+ require_relative '../../Object/inQ'
13
15
 
14
16
  module Poloniex
15
17
  module V1
16
18
  class Client
17
19
 
20
+ ALLOWABLE_VERBS = %w{GET POST PUT DELETE}
18
21
  API_HOST = 'api.poloniex.com'
19
22
 
20
- # Get candles for specific symbol
21
- def candles(symbol, interval: 'DAY_1', start_time: nil, end_time: nil, limit: nil)
22
- args = {interval: interval, startTime: start_time, endTime: end_time, limit: limit}
23
- response = get(path: "/markets/#{symbol}/candles", args: args)
23
+ class << self
24
+ def path_prefix
25
+ ''
26
+ end
27
+ end
28
+
29
+ # Public endpoints
30
+
31
+ ## Reference Data
32
+
33
+ ### Symbol Information
34
+ ### GET https://api.poloniex.com/markets
35
+ ### GET https://api.poloniex.com/markets/{symbol}
36
+ ### https://api-docs.poloniex.com/spot/api/public/reference-data#symbol-information
37
+ def markets(symbol = nil)
38
+ path = "/markets"
39
+ path += "/#{symbol}"if symbol
40
+ response = get(path: path)
24
41
  handle_response(response)
25
42
  end
26
43
 
27
- # Get all currencies if no currency specified.
28
- def currencies(currency = nil)
29
- path = "/currencies"
44
+ ### Currency Information
45
+ ### GET https://api.poloniex.com/currencies
46
+ ### GET https://api.poloniex.com/currencies/{currency}
47
+ ### https://api-docs.poloniex.com/spot/api/public/reference-data#currency-information
48
+ def currencies(currency = nil, include_multichain_currencies: nil)
49
+ args = {}
50
+ args.merge!(includeMultiChainCurrencies: include_multichain_currencies) if include_multichain_currencies
51
+ path = '/currencies'
30
52
  path += "/#{currency}" if currency
53
+ response = get(path: path, args: args)
54
+ handle_response(response)
55
+ end
56
+
57
+ ### System Timestamp
58
+ ### GET https://api.poloniex.com/timestamp
59
+ ### GET https://api.poloniex.com/timestamp
60
+ ### https://api-docs.poloniex.com/spot/api/public/reference-data#system-timestamp
61
+ def timestamp
62
+ response = get(path: '/timestamp')
63
+ handle_response(response)
64
+ end
65
+
66
+ ## Market Data
67
+
68
+ ### Prices
69
+ ### GET https://api.poloniex.com/markets/price
70
+ ### GET https://api.poloniex.com/markets/{symbol}/price
71
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#prices
72
+ def price(symbol = nil)
73
+ path = '/markets/price'
74
+ path = "/markets/#{symbol}/price" if symbol
31
75
  response = get(path: path)
32
76
  handle_response(response)
33
77
  end
34
78
 
35
- # Get mark prices for all symbols if no symbol specified.
36
- def mark_prices(symbol = nil)
79
+ ### Mark Price
80
+ ### GET https://api.poloniex.com/markets/markPrice
81
+ ### GET https://api.poloniex.com/markets/{symbol}/markPrice
82
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#mark-price
83
+ def mark_price(symbol = nil)
37
84
  path = '/markets/markPrice'
38
85
  path = "/markets/#{symbol}/markPrice" if symbol
39
86
  response = get(path: path)
40
87
  handle_response(response)
41
88
  end
42
89
 
43
- # Get mark price components for specific symbol
90
+ ### Mark Price Components
91
+ ### GET https://api.poloniex.com/markets/{symbol}/markPriceComponents
92
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#mark-price-components
44
93
  def mark_price_components(symbol)
45
94
  response = get(path: "/markets/#{symbol}/markPriceComponents")
46
95
  handle_response(response)
47
96
  end
48
97
 
49
- # Get all markets/symbols if no symbol specified.
50
- def markets(symbol = nil)
51
- path = "/markets"
52
- path += "/#{symbol}"if symbol
53
- response = get(path: path)
98
+ ### Order Book
99
+ ### GET https://api.poloniex.com/markets/{symbol}/orderBook
100
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#order-book
101
+ def order_book(symbol, scale: nil, limit: nil)
102
+ response = get(
103
+ path: "/markets/#{symbol}/orderBook",
104
+ args: {
105
+ scale: scale,
106
+ limit: limit
107
+ }
108
+ )
54
109
  handle_response(response)
55
110
  end
56
111
 
57
- # Get order book for specific symbol
58
- def order_book(symbol, scale: nil, limit: nil)
59
- args = {scale: scale, limit: limit}
60
- response = get(path: "/markets/#{symbol}/orderBook", args: args)
112
+ ### Candles
113
+ ### GET https://api.poloniex.com/markets/{symbol}/candles
114
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#candles
115
+ ### interval: The unit of time by which to aggregate data. Valid values are: MINUTE_1, MINUTE_5, MINUTE_10, MINUTE_15, MINUTE_30, HOUR_1, HOUR_2, HOUR_4, HOUR_6, HOUR_12, DAY_1, DAY_3, WEEK_1 and MONTH_1.
116
+ def candles(symbol, interval: 'DAY_1', start_time: nil, end_time: nil, limit: nil)
117
+ response = get(
118
+ path: "/markets/#{symbol}/candles",
119
+ args: {
120
+ interval: interval,
121
+ startTime: start_time,
122
+ endTime: end_time,
123
+ limit: limit
124
+ }
125
+ )
61
126
  handle_response(response)
62
127
  end
63
128
 
64
- # Get prices for all symbols if no symbol specified.
65
- def prices(symbol = nil)
66
- path = '/markets/price'
67
- path = "/markets/#{symbol}/price" if symbol
68
- response = get(path: path)
129
+ ### Trades
130
+ ### GET https://api.poloniex.com/markets/{symbol}/trades
131
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#trades
132
+ def trades(symbol, limit: nil)
133
+ response = get(
134
+ path: "/markets/#{symbol}/trades",
135
+ args: {limit: limit}
136
+ )
69
137
  handle_response(response)
70
138
  end
71
139
 
72
- # Get 24h ticker for all symbols if no symbol specified.
140
+ ### Ticker
141
+ ### GET https://api.poloniex.com/markets/ticker24h
142
+ ### GET https://api.poloniex.com/markets/{symbol}/ticker24h
143
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#ticker
73
144
  def ticker_24h(symbol = nil)
74
145
  path = '/markets/ticker24h'
75
146
  path = "/markets/#{symbol}/ticker24h" if symbol
@@ -77,16 +148,369 @@ module Poloniex
77
148
  handle_response(response)
78
149
  end
79
150
 
80
- # Get server timestamp
81
- def timestamp
82
- response = get(path: '/timestamp')
151
+ ## Margin
152
+
153
+ ### Collateral Info
154
+ ### GET https://api.poloniex.com/markets/collateralInfo
155
+ ### GET https://api.poloniex.com/markets/{currency}/collateralInfo
156
+ ### https://api-docs.poloniex.com/spot/api/public/margin#collateral-info
157
+ def collateral_info(currency = nil)
158
+ path = '/markets/collateralInfo'
159
+ path = "/markets/#{currency}/collateralInfo" if currency
160
+ response = get(path: path)
83
161
  handle_response(response)
84
162
  end
85
163
 
86
- # Get trades for specific symbol
87
- def trades(symbol, limit: nil)
88
- args = {limit: limit}
89
- response = get(path: "/markets/#{symbol}/trades", args: args)
164
+ ### Borrow Rates Info
165
+ ### GET https://api.poloniex.com/markets/borrowRatesInfo
166
+ ### https://api-docs.poloniex.com/spot/api/public/margin#borrow-rates-info
167
+ def borrow_rates_info
168
+ response = get(path: '/markets/borrowRatesInfo')
169
+ handle_response(response)
170
+ end
171
+
172
+ # Authenticated endpoints
173
+
174
+ ## Account
175
+
176
+ ### Account Information
177
+ ### GET https://api.poloniex.com/accounts
178
+ ### https://api-docs.poloniex.com/spot/api/private/account#account-activity
179
+ def accounts
180
+ response = get(path: '/accounts')
181
+ handle_response(response)
182
+ end
183
+
184
+ ### All Account Balances
185
+ ### GET https://api.poloniex.com/accounts/balances
186
+ ### GET https://api.poloniex.com/accounts/{id}/balances
187
+ ### https://api-docs.poloniex.com/spot/api/private/account#account-activity
188
+ def accounts_balances(account_id: nil, account_type: nil)
189
+ path = '/accounts/balances'
190
+ path = "/accounts/#{account_id}/balances" if account_id
191
+ response = get(path: path, args: {accountType: account_type})
192
+ handle_response(response)
193
+ end
194
+
195
+ ### Account Activity
196
+ ### GET https://api.poloniex.com/accounts/activity
197
+ ### https://api-docs.poloniex.com/spot/api/private/account#account-activity
198
+ def accounts_activity(start_time: nil, end_time: nil, acivity_type: nil, limit: nil, from: nil, direction: nil, currency: nil)
199
+ response = get(
200
+ path: '/accounts/activity',
201
+ args: {
202
+ startTime: start_time,
203
+ endTime: end_time,
204
+ acivityType: acivity_type,
205
+ limit: limit,
206
+ from: from,
207
+ direction: direction,
208
+ currency: currency
209
+ }
210
+ )
211
+ handle_response(response)
212
+ end
213
+
214
+ ### Accounts Transfer
215
+ ### POST https://api.poloniex.com/accounts/transfer
216
+ ### https://api-docs.poloniex.com/spot/api/private/account#accounts-transfer
217
+ def accounts_transfer(currency:, amount:, from_account:, to_account:)
218
+ response = post(
219
+ path: '/accounts/transfer',
220
+ args: {
221
+ currency: currency,
222
+ amount: amount,
223
+ fromAccount: from_account,
224
+ toAccount: to_account
225
+ }
226
+ )
227
+ handle_response(response)
228
+ end
229
+
230
+ ### Accounts Transfer Records
231
+ ### GET https://api.poloniex.com/accounts/transfer
232
+ ### GET https://api.poloniex.com/accounts/transfer/{id}
233
+ ### https://api-docs.poloniex.com/spot/api/private/account#accounts-transfer-records
234
+ def accounts_transfer_records(transfer_id: nil, limit: nil, from: nil, direction: nil, currency: nil, start_time: nil, end_time: nil)
235
+ path = '/accounts/transfer'
236
+ path += "/#{transfer_id}" if transfer_id
237
+ response = get(
238
+ path: path,
239
+ args: {
240
+ limit: limit,
241
+ from: from,
242
+ direction: direction,
243
+ currency: currency,
244
+ startTime: start_time,
245
+ endTime: end_time
246
+ }
247
+ )
248
+ handle_response(response)
249
+ end
250
+
251
+ ### Fee Info
252
+ ### GET https://api.poloniex.com/feeinfo
253
+ ### https://api-docs.poloniex.com/spot/api/private/account#fee-info
254
+ ### Should it be called accounts_fee_info?
255
+ def fee_info
256
+ response = get(path: '/feeinfo')
257
+ handle_response(response)
258
+ end
259
+
260
+ ### Interest History
261
+ ### GET https://api.poloniex.com/accounts/interest/history
262
+ ### https://api-docs.poloniex.com/spot/api/private/account#interest-history
263
+ def accounts_interest_history(limit: nil, from: nil, direction: nil, start_time: nil, end_time: nil)
264
+ response = get(
265
+ path: '/accounts/interest/history',
266
+ args: {
267
+ limit: limit,
268
+ from: from,
269
+ direction: direction,
270
+ startTime: start_time,
271
+ endTime: end_time
272
+ }
273
+ )
274
+ handle_response(response)
275
+ end
276
+
277
+ ## Wallet
278
+
279
+ ### Deposit Addresses
280
+ ### GET https://api.poloniex.com/wallets/addresses
281
+ ### https://api-docs.poloniex.com/spot/api/private/wallet#deposit-addresses
282
+ def deposit_addresses(currency = nil)
283
+ response = get(path: '/wallets/addresses', args: {currency: currency})
284
+ handle_response(response)
285
+ end
286
+
287
+ ### Wallets Activity Records
288
+ ### GET https://api.poloniex.com/wallets/activity
289
+ ### https://api-docs.poloniex.com/spot/api/private/wallet#wallets-activity-records
290
+ def wallets_activity(start:, finish:, activity_type: nil)
291
+ response = get(
292
+ path: '/wallets/activity',
293
+ args: {
294
+ start: start,
295
+ end: finish,
296
+ activityType: activity_type
297
+ }
298
+ )
299
+ handle_response(response)
300
+ end
301
+
302
+ ### New Currency Address
303
+ ### POST https://api.poloniex.com/wallets/address
304
+ ### https://api-docs.poloniex.com/spot/api/private/wallet#new-currency-address
305
+ def new_currency_address(currency)
306
+ response = post(path: '/wallets/address', args: {currency: currency})
307
+ handle_response(response)
308
+ end
309
+
310
+ ### Withdraw Currency
311
+ ### POST https://api.poloniex.com/wallets/withdraw
312
+ ### https://api-docs.poloniex.com/spot/api/private/wallet#withdraw-currency
313
+ def withdraw_currency(currency:, amount:, address:, payment_id: nil, allow_borrow: nil)
314
+ response = post(
315
+ path: '/wallets/withdraw',
316
+ args: {
317
+ currency: currency,
318
+ amount: amount,
319
+ address: address,
320
+ paymentId: payment_id,
321
+ allowBorrow: allow_borrow
322
+ }
323
+ )
324
+ handle_response(response)
325
+ end
326
+
327
+ ## Margin
328
+
329
+ ### Account Margin
330
+ ### GET https://api.poloniex.com/margin/accountMargin
331
+ ### https://api-docs.poloniex.com/spot/api/private/margin#account-margin
332
+ ### account_type: Currently only SPOT is supported.
333
+ def account_margin(account_type: 'SPOT')
334
+ response = get(path: '/margin/accountMargin', args: {accountType: account_type})
335
+ handle_response(response)
336
+ end
337
+
338
+ ### Borrow Status
339
+ ### GET https://api.poloniex.com/margin/borrowStatus
340
+ ### https://api-docs.poloniex.com/spot/api/private/margin#borrow-status
341
+ def borrow_status(currency: nil)
342
+ response = get(path: '/margin/borrowStatus', args: {currency: currency})
343
+ handle_response(response)
344
+ end
345
+
346
+ ### Maximum Buy/Sell Amount
347
+ ### GET https://api.poloniex.com/margin/maxSize
348
+ ### https://api-docs.poloniex.com/spot/api/private/margin#maximum-buysell-amount
349
+ def max_buy_sell_amount(symbol)
350
+ response = get(path: '/margin/maxSize', args: {symbol: symbol})
351
+ handle_response(response)
352
+ end
353
+
354
+ ## Orders
355
+
356
+ ### Create Order
357
+ ### POST https://api.poloniex.com/orders
358
+ ### https://api-docs.poloniex.com/spot/api/private/order#create-order
359
+ def create_order(
360
+ symbol:,
361
+ side:,
362
+ time_in_force: nil,
363
+ type: nil,
364
+ account_type: nil,
365
+ price: nil,
366
+ quantity: nil,
367
+ amount: nil,
368
+ client_order_id: nil,
369
+ allow_borrow: nil,
370
+ stp_mode: nil,
371
+ slippage_tolerance: nil
372
+ )
373
+ response = post(
374
+ path: '/orders',
375
+ args: {
376
+ symbol: symbol,
377
+ side: side,
378
+ timeInForce: time_in_force,
379
+ type: type,
380
+ accountType: account_type,
381
+ price: price,
382
+ quantity: quantity,
383
+ amount: amount,
384
+ clientOrderId: client_order_id,
385
+ allowBorrow: allow_borrow,
386
+ stpMode: stp_mode,
387
+ slippageTolerance: slippage_tolerance,
388
+ }
389
+ )
390
+ handle_response(response)
391
+ end
392
+
393
+ ### Create Multiple Orders
394
+ ### POST https://api.poloniex.com/orders/batch
395
+ ### https://api-docs.poloniex.com/spot/api/private/order#create-multiple-orders
396
+ def create_multiple_orders(orders)
397
+ response = post(path: '/orders/batch', args: orders)
398
+ handle_response(response)
399
+ end
400
+
401
+ ### Cancel Replace Order
402
+ ### PUT https://api.poloniex.com/orders/{id}
403
+ ### PUT https://api.poloniex.com/orders/cid:{clientOrderId}
404
+ ### https://api-docs.poloniex.com/spot/api/private/order#cancel-replace-order
405
+ def cancel_replace_order(
406
+ order_id: nil,
407
+ client_order_id: false,
408
+ price: nil,
409
+ quantity: nil,
410
+ amount: nil,
411
+ type: nil,
412
+ time_in_force: nil,
413
+ allow_borrow: nil,
414
+ proceed_on_failure: nil,
415
+ slippage_tolerance: nil
416
+ )
417
+ path = "/orders/#{order_id}"
418
+ path = "/orders/cid:#{order_id}" if client_order_id
419
+ args = {
420
+ price: price,
421
+ quantity: quantity,
422
+ amount: amount,
423
+ type: type,
424
+ timeInForce: time_in_force,
425
+ allowBorrow: allow_borrow,
426
+ proceedOnFailure: proceed_on_failure,
427
+ slippageTolerance: slippage_tolerance,
428
+ }
429
+ args.merge!(clientOrderId: order_id) if client_order_id
430
+ response = put(path: path, args: args)
431
+ handle_response(response)
432
+ end
433
+
434
+ ### Open Orders
435
+ ### GET https://api.poloniex.com/orders
436
+ ### https://api-docs.poloniex.com/spot/api/private/order#open-orders
437
+ def open_orders(symbol: nil, side: nil, from: nil, direction: nil, limit: nil)
438
+ response = get(
439
+ path: '/orders',
440
+ args: {
441
+ symbol: symbol,
442
+ side: side,
443
+ from: from,
444
+ direction: direction,
445
+ limit: limit
446
+ }
447
+ )
448
+ handle_response(response)
449
+ end
450
+
451
+ ### Order Details
452
+ ### GET https://api.poloniex.com/orders/{id}
453
+ ### GET https://api.poloniex.com/orders/cid:{clientOrderId}
454
+ ### https://api-docs.poloniex.com/spot/api/private/order#order-details
455
+ def order_details(order_id:, client_order_id: false)
456
+ path = "/orders/#{order_id}"
457
+ path = "/orders/cid:#{order_id}" if client_order_id
458
+ response = get(path: path, args: {id: order_id})
459
+ handle_response(response)
460
+ end
461
+
462
+ ### Cancel Order by Id
463
+ ### DELETE https://api.poloniex.com/orders/{id}
464
+ ### DELETE https://api.poloniex.com/orders/cid:{clientOrderId}
465
+ ### https://api-docs.poloniex.com/spot/api/private/order#cancel-order-by-id
466
+ def cancel_order(order_id:, client_order_id: false)
467
+ path = "/orders/#{order_id}"
468
+ path = "/orders/cid:#{order_id}" if client_order_id
469
+ response = delete(path: path, args: {id: order_id})
470
+ handle_response(response)
471
+ end
472
+
473
+ ### Cancel Multiple Orders by Ids
474
+ ### DELETE https://api.poloniex.com/orders/cancelByIds
475
+ ### https://api-docs.poloniex.com/spot/api/private/order#cancel-multiple-orders-by-ids
476
+ def cancel_multiple_orders(order_ids: nil, client_order_ids: nil)
477
+ response = delete(
478
+ path: '/orders/cancelByIds',
479
+ args: {
480
+ orderIds: order_ids,
481
+ clientOrderIds: client_order_ids
482
+ }
483
+ )
484
+ handle_response(response)
485
+ end
486
+
487
+ ### Cancel All Orders
488
+ ### DELETE https://api.poloniex.com/orders
489
+ ### https://api-docs.poloniex.com/spot/api/private/order#cancel-all-orders
490
+ def cancel_all_orders(symbols: nil, account_types: nil)
491
+ response = delete(
492
+ path: '/orders',
493
+ args: {
494
+ symbols: symbols,
495
+ accountTypes: account_types
496
+ }
497
+ )
498
+ handle_response(response)
499
+ end
500
+
501
+ ### Kill Switch
502
+ ### POST https://api.poloniex.com/orders/killSwitch
503
+ ### https://api-docs.poloniex.com/spot/api/private/order#kill-switch
504
+ def kill_switch(timeout:)
505
+ response = post(path: '/orders/killSwitch', args: {timeout: timeout})
506
+ handle_response(response)
507
+ end
508
+
509
+ ### Kill Switch Status
510
+ ### GET https://api.poloniex.com/orders/killSwitchStatus
511
+ ### https://api-docs.poloniex.com/spot/api/private/order#kill-switch-status
512
+ def kill_switch_status
513
+ response = get(path: '/orders/killSwitchStatus')
90
514
  handle_response(response)
91
515
  end
92
516
 
@@ -101,58 +525,109 @@ module Poloniex
101
525
  def initialize(api_key: nil, api_secret: nil, debug: nil, logger: nil, configuration: nil)
102
526
  @api_key = api_key || configuration&.api_key || Poloniex.configuration.api_key
103
527
  @api_secret = api_secret || configuration&.api_secret || Poloniex.configuration.api_secret
104
- @debug = debug || configuration&.debug || Poloniex.configuration.debug
528
+ @debug = !![debug, configuration&.debug, Poloniex.configuration.debug].compact.first
105
529
  @logger = logger || configuration&.logger || Poloniex.configuration.logger
106
530
  end
107
531
 
108
- def request_timestamp
109
- @request_timestamp ||= (Time.now.to_i * 1000).to_s
532
+ def get(path:, args: {})
533
+ do_request(verb: 'GET', path: path, args: args)
110
534
  end
111
535
 
112
- def message(verb:, path:, args:)
113
- case verb
114
- when 'GET'
115
- if args.empty?
116
- [request_timestamp, verb, path].join
536
+ def post(path:, args: {})
537
+ do_request(verb: 'POST', path: path, args: args)
538
+ end
539
+
540
+ def delete(path:, args: {})
541
+ do_request(verb: 'DELETE', path: path, args: args)
542
+ end
543
+
544
+ def put(path:, args: {})
545
+ do_request(verb: 'PUT', path: path, args: args)
546
+ end
547
+
548
+ def do_request(verb:, path:, args: {})
549
+ verb = verb.to_s.upcase
550
+ raise ArgumentError, "Unsupported HTTP method: #{verb}" unless verb.in?(Poloniex::V1::Client::ALLOWABLE_VERBS)
551
+ request_string = request_string(path:)
552
+ args = args(verb: verb, path: path, supplied_args: args)
553
+ headers = headers(verb: verb, path: path, args: args)
554
+ log_request(verb: verb, request_string: request_string, args: args, headers: headers) if use_logging?
555
+ response =
556
+ if verb.in?(%w{POST PUT}) && args.is_a?(Hash) && (args.key?(:body) || args.key?('body'))
557
+ raw_body = args[:body] || args['body']
558
+ HTTP.send(verb.downcase, request_string, raw_body, headers)
117
559
  else
118
- query_string = args.x_www_form_urlencode
119
- [request_timestamp, verb, path, '?', query_string].join
560
+ HTTP.send(verb.downcase, request_string, args, headers)
120
561
  end
562
+ @request_timestamp = nil
563
+ response
564
+ end
565
+
566
+ def request_string(path:)
567
+ "https://#{Poloniex::V1::Client::API_HOST}#{self.class.path_prefix}#{path}"
568
+ end
569
+
570
+ def args(verb:, path:, supplied_args:)
571
+ return supplied_args if supplied_args.is_a?(Array) # create_multiple_orders() supplies an array.
572
+ args = supplied_args.reject{|_, v| v.nil?}.transform_keys(&:to_s).transform_values(&:to_s)
573
+ if verb.in?(%w{POST PUT})
574
+ return { body: '' } if args.empty?
575
+ return { body: request_body(args) }
576
+ end
577
+ if verb.in?(%w{GET DELETE}) && use_auth?(path)
578
+ args.merge!("signTimestamp" => request_timestamp.to_s)
121
579
  end
580
+ args
122
581
  end
123
582
 
124
- def signature(message)
125
- # digest = OpenSSL::Digest.new('SHA256')
126
- # hmac = OpenSSL::HMAC.digest(digest, @api_secret, message)
127
- # Base64.strict_encode64(hmac)
128
- OpenSSL::HMAC.hexdigest(
129
- 'sha256',
130
- @api_secret,
131
- message
132
- )
583
+ def headers(verb:, path:, args:)
584
+ headers = {}
585
+ if use_auth?(path)
586
+ if verb.in?(%w{POST PUT})
587
+ if args.is_a?(Hash) && (args.key?(:body) || args.key?('body'))
588
+ body_str = args[:body] || args['body']
589
+ headers["Content-Type"] = "application/json; charset=UTF-8" unless body_str.to_s.empty?
590
+ else
591
+ headers["Content-Type"] = "application/json; charset=UTF-8"
592
+ end
593
+ end
594
+ headers.merge!(
595
+ 'key' => @api_key,
596
+ 'signature' => signature(verb: verb, path: path, args: args),
597
+ 'signTimestamp' => request_timestamp.to_s
598
+ )
599
+ end
600
+ headers
133
601
  end
134
602
 
135
- def request_string(path)
136
- "https://#{API_HOST}#{path}"
603
+ def request_timestamp
604
+ @request_timestamp ||= (Time.now.to_f * 1000).to_i
137
605
  end
138
606
 
139
- def headers(signature)
140
- {
141
- 'Content-Type' => 'application/json',
142
- 'key' => @api_key,
143
- 'signatureMethod' => 'HmacSHA256',
144
- 'signature' => signature,
145
- 'signatureVersion' => '2',
146
- 'timestamp' => request_timestamp
147
- }
607
+ def signature_message(verb:, path:, args:)
608
+ case verb
609
+ when 'GET', 'DELETE'
610
+ args_for_signing = args.merge("signTimestamp" => request_timestamp.to_s).transform_values(&:to_s)
611
+ signed_args = args_for_signing.sort_by{|k, _| k}.to_h.x_www_form_urlencode
612
+ when 'POST', 'PUT'
613
+ body_str = request_body(args)
614
+ signed_args = "requestBody=#{body_str}&signTimestamp=#{request_timestamp}"
615
+ end
616
+ "#{verb}\n#{path}\n#{signed_args}"
148
617
  end
149
618
 
150
- def use_logging?
151
- !@logger.nil?
619
+ def request_body(args)
620
+ return '' if args.nil?
621
+ if args.respond_to?(:keys) && (args.key?(:body) || args.key?('body'))
622
+ return args[:body] || args['body']
623
+ end
624
+ return '' if args.empty?
625
+ JSON.generate(args, separators: [',', ':'])
152
626
  end
153
627
 
154
- def log_args?(args)
155
- !args.values.all?(&:nil?)
628
+ def signature(verb:, path:, args:)
629
+ digest = OpenSSL::HMAC.digest("sha256", @api_secret, signature_message(verb: verb, path: path, args: args).encode("utf-8"))
630
+ Base64.strict_encode64(digest)
156
631
  end
157
632
 
158
633
  def log_request(verb:, request_string:, args:, headers:)
@@ -178,24 +653,15 @@ module Poloniex
178
653
  @logger.error(log_string)
179
654
  end
180
655
 
181
- def do_request(verb:, path:, args: {})
182
- sorted_args = args.reject{|_, v| v.nil?}.sort.to_h
183
- message = message(verb: verb, path: path, args: sorted_args)
184
- signature = signature(message)
185
- headers = headers(signature)
186
- log_request(verb: verb, request_string: request_string(path), args: sorted_args, headers: headers) if use_logging?
187
- @request_timestamp = nil
188
- HTTP.send(verb.to_s.downcase, request_string(path), sorted_args, headers)
189
- end
190
-
191
- def get(path:, args: {})
192
- do_request(verb: 'GET', path: path, args: args)
193
- end
194
-
195
656
  def handle_response(response)
196
657
  if response.success?
197
- parsed_body = JSON.parse(response.body)
198
- log_response(code: response.code, message: response.message, body: response.body) if use_logging?
658
+ body = response.body.to_s
659
+ if body.strip.empty?
660
+ log_response(code: response.code, message: response.message, body: body) if use_logging?
661
+ return {}
662
+ end
663
+ parsed_body = JSON.parse(body)
664
+ log_response(code: response.code, message: response.message, body: body) if use_logging?
199
665
  parsed_body
200
666
  else
201
667
  case response.code.to_i
@@ -204,32 +670,51 @@ module Poloniex
204
670
  raise Poloniex::InvalidRequestError.new(
205
671
  code: response.code,
206
672
  message: response.message,
207
- body: body
673
+ body: response.body
208
674
  )
209
675
  when 401
210
676
  log_error(code: response.code, message: response.message, body: response.body) if use_logging?
211
677
  raise Poloniex::AuthenticationError.new(
212
678
  code: response.code,
213
679
  message: response.message,
214
- body: body
680
+ body: response.body
215
681
  )
216
682
  when 429
217
683
  log_error(code: response.code, message: response.message, body: response.body) if use_logging?
218
684
  raise Poloniex::RateLimitError.new(
219
685
  code: response.code,
220
686
  message: response.message,
221
- body: body
687
+ body: response.body
222
688
  )
223
689
  else
224
690
  log_error(code: response.code, message: response.message, body: response.body) if use_logging?
225
691
  raise Poloniex::APIError.new(
226
692
  code: response.code,
227
693
  message: response.message,
228
- body: body
694
+ body: response.body
229
695
  )
230
696
  end
231
697
  end
232
698
  end
699
+
700
+ def log_args?(args)
701
+ return false if args.nil?
702
+ return !args.values.all?(&:nil?) if args.respond_to?(:values) # Hash-like
703
+ return !args.empty? if args.respond_to?(:empty?) # Array-like or others
704
+ true
705
+ end
706
+
707
+ def public_path?(path)
708
+ Poloniex::V1::PUBLIC_PATH_PREFIXES.any?{|prefix| path.start_with?(prefix)}
709
+ end
710
+
711
+ def use_auth?(path)
712
+ !public_path?(path)
713
+ end
714
+
715
+ def use_logging?
716
+ !@logger.nil?
717
+ end
233
718
  end
234
719
  end
235
720
  end