coinsync 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE.txt +21 -0
- data/README.md +255 -0
- data/bin/coinsync +78 -0
- data/doc/importers.md +201 -0
- data/lib/coinsync.rb +18 -0
- data/lib/coinsync/balance.rb +19 -0
- data/lib/coinsync/balance_task.rb +65 -0
- data/lib/coinsync/build_task.rb +43 -0
- data/lib/coinsync/builder.rb +32 -0
- data/lib/coinsync/config.rb +94 -0
- data/lib/coinsync/crypto_classifier.rb +27 -0
- data/lib/coinsync/currencies.rb +35 -0
- data/lib/coinsync/currency_converter.rb +65 -0
- data/lib/coinsync/currency_converters/all.rb +1 -0
- data/lib/coinsync/currency_converters/base.rb +46 -0
- data/lib/coinsync/currency_converters/cache.rb +34 -0
- data/lib/coinsync/currency_converters/fixer.rb +36 -0
- data/lib/coinsync/currency_converters/nbp.rb +38 -0
- data/lib/coinsync/formatter.rb +44 -0
- data/lib/coinsync/import_task.rb +37 -0
- data/lib/coinsync/importers/all.rb +1 -0
- data/lib/coinsync/importers/ark_voting.rb +121 -0
- data/lib/coinsync/importers/base.rb +35 -0
- data/lib/coinsync/importers/binance_api.rb +210 -0
- data/lib/coinsync/importers/bitbay20.rb +144 -0
- data/lib/coinsync/importers/bitbay_api.rb +224 -0
- data/lib/coinsync/importers/bitcurex.rb +71 -0
- data/lib/coinsync/importers/bittrex_api.rb +81 -0
- data/lib/coinsync/importers/bittrex_csv.rb +75 -0
- data/lib/coinsync/importers/changelly.rb +57 -0
- data/lib/coinsync/importers/circle.rb +58 -0
- data/lib/coinsync/importers/default.rb +90 -0
- data/lib/coinsync/importers/etherdelta.rb +93 -0
- data/lib/coinsync/importers/kraken_api.rb +134 -0
- data/lib/coinsync/importers/kraken_common.rb +137 -0
- data/lib/coinsync/importers/kraken_csv.rb +28 -0
- data/lib/coinsync/importers/kucoin_api.rb +172 -0
- data/lib/coinsync/importers/lisk_voting.rb +110 -0
- data/lib/coinsync/outputs/all.rb +1 -0
- data/lib/coinsync/outputs/base.rb +32 -0
- data/lib/coinsync/outputs/list.rb +123 -0
- data/lib/coinsync/outputs/raw.rb +45 -0
- data/lib/coinsync/outputs/summary.rb +48 -0
- data/lib/coinsync/request.rb +31 -0
- data/lib/coinsync/run_command_task.rb +20 -0
- data/lib/coinsync/source.rb +43 -0
- data/lib/coinsync/source_filter.rb +10 -0
- data/lib/coinsync/table_printer.rb +29 -0
- data/lib/coinsync/transaction.rb +125 -0
- data/lib/coinsync/version.rb +3 -0
- metadata +95 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'csv'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
require_relative 'base'
|
6
|
+
require_relative '../currencies'
|
7
|
+
require_relative '../transaction'
|
8
|
+
|
9
|
+
module CoinSync
|
10
|
+
module Importers
|
11
|
+
class BittrexCSV < Base
|
12
|
+
register_importer :bittrex_csv
|
13
|
+
|
14
|
+
class HistoryEntry
|
15
|
+
TIME_FORMAT = '%m/%d/%Y %H:%M:%S %p %z'
|
16
|
+
|
17
|
+
attr_accessor :uuid, :currency, :asset, :type, :quantity, :limit, :commission, :price,
|
18
|
+
:time_opened, :time_closed
|
19
|
+
|
20
|
+
def initialize(line)
|
21
|
+
@uuid = line[0]
|
22
|
+
@currency, @asset = line[1].split('-').map { |c| CryptoCurrency.new(c) }
|
23
|
+
@type = line[2]
|
24
|
+
@quantity = BigDecimal.new(line[3])
|
25
|
+
@limit = BigDecimal.new(line[4])
|
26
|
+
@commission = BigDecimal.new(line[5])
|
27
|
+
@price = BigDecimal.new(line[6])
|
28
|
+
@time_opened = Time.strptime(line[7] + ' +0000', TIME_FORMAT)
|
29
|
+
@time_closed = Time.strptime(line[8] + ' +0000', TIME_FORMAT)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def read_transaction_list(source)
|
34
|
+
contents = source.read.gsub("\u0000", '').gsub("\r", '')
|
35
|
+
entries = []
|
36
|
+
transactions = []
|
37
|
+
|
38
|
+
CSV.parse(contents, col_sep: ',') do |line|
|
39
|
+
next if line[0] == 'OrderUuid'
|
40
|
+
|
41
|
+
entries << HistoryEntry.new(line)
|
42
|
+
end
|
43
|
+
|
44
|
+
entries.sort_by! { |e| [e.time_closed, e.uuid] }
|
45
|
+
|
46
|
+
entries.each do |entry|
|
47
|
+
case entry.type
|
48
|
+
when 'LIMIT_BUY', 'MARKET_BUY' then
|
49
|
+
transactions << Transaction.new(
|
50
|
+
exchange: 'Bittrex',
|
51
|
+
time: entry.time_closed,
|
52
|
+
bought_amount: entry.quantity,
|
53
|
+
bought_currency: entry.asset,
|
54
|
+
sold_amount: entry.price + entry.commission,
|
55
|
+
sold_currency: entry.currency
|
56
|
+
)
|
57
|
+
when 'LIMIT_SELL', 'MARKET_SELL' then
|
58
|
+
transactions << Transaction.new(
|
59
|
+
exchange: 'Bittrex',
|
60
|
+
time: entry.time_closed,
|
61
|
+
bought_amount: entry.price - entry.commission, # TODO check this
|
62
|
+
bought_currency: entry.currency,
|
63
|
+
sold_amount: entry.quantity,
|
64
|
+
sold_currency: entry.asset
|
65
|
+
)
|
66
|
+
else
|
67
|
+
raise "Bittrex importer error: unexpected entry type '#{entry.type}'"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
transactions
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'csv'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
require_relative 'base'
|
6
|
+
require_relative '../currencies'
|
7
|
+
require_relative '../transaction'
|
8
|
+
|
9
|
+
module CoinSync
|
10
|
+
module Importers
|
11
|
+
class Changelly < Base
|
12
|
+
register_importer :changelly
|
13
|
+
|
14
|
+
class HistoryEntry
|
15
|
+
attr_accessor :status, :date, :exchanged_currency, :exchanged_amount, :received_currency, :received_amount
|
16
|
+
|
17
|
+
def initialize(line)
|
18
|
+
@status = line[0]
|
19
|
+
@date = Time.parse(line[1] + ' +0000')
|
20
|
+
|
21
|
+
amount, name = line[2].gsub(',', '').split(/\s+/)
|
22
|
+
@exchanged_currency = CryptoCurrency.new(name)
|
23
|
+
@exchanged_amount = BigDecimal.new(amount)
|
24
|
+
|
25
|
+
amount, name = line[6].gsub(',', '').split(/\s+/)
|
26
|
+
@received_currency = CryptoCurrency.new(name)
|
27
|
+
@received_amount = BigDecimal.new(amount)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_transaction_list(source)
|
32
|
+
csv = CSV.new(source, col_sep: ',')
|
33
|
+
|
34
|
+
transactions = []
|
35
|
+
|
36
|
+
csv.each do |line|
|
37
|
+
next if line[0] == 'Status'
|
38
|
+
|
39
|
+
entry = HistoryEntry.new(line)
|
40
|
+
|
41
|
+
next if entry.status != 'finished'
|
42
|
+
|
43
|
+
transactions << Transaction.new(
|
44
|
+
exchange: 'Changelly',
|
45
|
+
time: entry.date,
|
46
|
+
bought_amount: entry.received_amount,
|
47
|
+
bought_currency: entry.received_currency,
|
48
|
+
sold_amount: entry.exchanged_amount,
|
49
|
+
sold_currency: entry.exchanged_currency
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
transactions.reverse
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'csv'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
require_relative 'base'
|
6
|
+
require_relative '../currencies'
|
7
|
+
require_relative '../transaction'
|
8
|
+
|
9
|
+
module CoinSync
|
10
|
+
module Importers
|
11
|
+
class Circle < Base
|
12
|
+
register_importer :circle
|
13
|
+
|
14
|
+
class HistoryEntry
|
15
|
+
attr_accessor :date, :id, :type, :from_account, :to_account, :from_amount, :from_currency,
|
16
|
+
:to_amount, :to_currency, :status
|
17
|
+
|
18
|
+
def initialize(line)
|
19
|
+
@date = Time.strptime(line[0], '%a %b %d %Y %H:%M:%S GMT+0000 (%Z)')
|
20
|
+
@id = line[1]
|
21
|
+
@type = line[2]
|
22
|
+
@from_account = line[3]
|
23
|
+
@to_account = line[4]
|
24
|
+
@from_amount = BigDecimal.new(line[5].gsub(/[^\d\.]+/, ''))
|
25
|
+
@from_currency = FiatCurrency.new(line[6])
|
26
|
+
@to_amount = BigDecimal.new(line[7].gsub(/[^\d\.]+/, ''))
|
27
|
+
@to_currency = CryptoCurrency.new(line[8])
|
28
|
+
@status = line[9]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_transaction_list(source)
|
33
|
+
csv = CSV.new(source, col_sep: ',')
|
34
|
+
|
35
|
+
transactions = []
|
36
|
+
|
37
|
+
csv.each do |line|
|
38
|
+
next if line[0] == 'Date'
|
39
|
+
|
40
|
+
entry = HistoryEntry.new(line)
|
41
|
+
|
42
|
+
next if entry.type != 'deposit'
|
43
|
+
|
44
|
+
transactions << Transaction.new(
|
45
|
+
exchange: 'Circle',
|
46
|
+
bought_currency: entry.to_currency,
|
47
|
+
sold_currency: entry.from_currency,
|
48
|
+
time: entry.date,
|
49
|
+
bought_amount: entry.to_amount,
|
50
|
+
sold_amount: entry.from_amount
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
transactions
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'csv'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
require_relative 'base'
|
6
|
+
require_relative '../currencies'
|
7
|
+
require_relative '../formatter'
|
8
|
+
require_relative '../transaction'
|
9
|
+
|
10
|
+
module CoinSync
|
11
|
+
module Importers
|
12
|
+
class Default < Base
|
13
|
+
register_importer :default
|
14
|
+
|
15
|
+
class HistoryEntry
|
16
|
+
attr_accessor :lp, :exchange, :type, :date, :amount, :asset, :total, :currency
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(config, params = {})
|
20
|
+
super
|
21
|
+
@decimal_separator = config.custom_decimal_separator
|
22
|
+
@formatter = Formatter.new(config)
|
23
|
+
end
|
24
|
+
|
25
|
+
def read_transaction_list(source)
|
26
|
+
csv = CSV.new(source, col_sep: @config.column_separator)
|
27
|
+
|
28
|
+
transactions = []
|
29
|
+
|
30
|
+
csv.each do |line|
|
31
|
+
next if line.empty?
|
32
|
+
next if line.all? { |f| f.to_s.strip == '' }
|
33
|
+
next if line[0] == 'Lp'
|
34
|
+
|
35
|
+
entry = parse_line(line)
|
36
|
+
|
37
|
+
if entry.type.downcase == Transaction::TYPE_PURCHASE.to_s
|
38
|
+
transactions << Transaction.new(
|
39
|
+
exchange: entry.exchange,
|
40
|
+
bought_currency: entry.asset,
|
41
|
+
sold_currency: entry.currency,
|
42
|
+
time: entry.date,
|
43
|
+
bought_amount: entry.amount,
|
44
|
+
sold_amount: entry.total
|
45
|
+
)
|
46
|
+
elsif entry.type.downcase == Transaction::TYPE_SALE.to_s
|
47
|
+
transactions << Transaction.new(
|
48
|
+
exchange: entry.exchange,
|
49
|
+
bought_currency: entry.currency,
|
50
|
+
sold_currency: entry.asset,
|
51
|
+
time: entry.date,
|
52
|
+
bought_amount: entry.total,
|
53
|
+
sold_amount: entry.amount
|
54
|
+
)
|
55
|
+
else
|
56
|
+
raise "Default importer error: unexpected entry type '#{entry.type}'"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
transactions
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def parse_line(line)
|
66
|
+
entry = HistoryEntry.new
|
67
|
+
|
68
|
+
entry.lp = line[0].to_i
|
69
|
+
entry.exchange = line[1]
|
70
|
+
entry.type = line[2]
|
71
|
+
entry.date = Time.parse(line[3])
|
72
|
+
entry.amount = @formatter.parse_decimal(line[4])
|
73
|
+
entry.asset = CryptoCurrency.new(line[5])
|
74
|
+
entry.total = @formatter.parse_decimal(line[6])
|
75
|
+
|
76
|
+
entry.currency = if line[7].to_s.start_with?('$')
|
77
|
+
CryptoCurrency.new(line[7][1..-1])
|
78
|
+
else
|
79
|
+
FiatCurrency.new(line[7])
|
80
|
+
end
|
81
|
+
|
82
|
+
if entry.currency.code.nil? && entry.total > 0
|
83
|
+
raise "Default importer error: Currency must be specified if total value is non-zero"
|
84
|
+
end
|
85
|
+
|
86
|
+
entry
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'csv'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
require_relative 'base'
|
6
|
+
require_relative '../currencies'
|
7
|
+
require_relative '../transaction'
|
8
|
+
|
9
|
+
module CoinSync
|
10
|
+
module Importers
|
11
|
+
# Look up your transactions using DeltaBalances at https://deltabalances.github.io/history.html,
|
12
|
+
# specifying the time range you need, and then download the "Default" CSV in the top-right section
|
13
|
+
|
14
|
+
class EtherDelta < Base
|
15
|
+
register_importer :etherdelta
|
16
|
+
|
17
|
+
ETH = CryptoCurrency.new('ETH')
|
18
|
+
|
19
|
+
class HistoryEntry
|
20
|
+
attr_accessor :type, :trade, :token, :amount, :price, :total, :date, :fee, :fee_token
|
21
|
+
|
22
|
+
def initialize(line)
|
23
|
+
@type = line[0]
|
24
|
+
|
25
|
+
if !['Maker', 'Taker'].include?(@type)
|
26
|
+
raise "EtherDelta importer: incorrect csv format - unexpected '#{@type}' in the first column"
|
27
|
+
end
|
28
|
+
|
29
|
+
@trade = line[1]
|
30
|
+
|
31
|
+
if !['Buy', 'Sell'].include?(@trade)
|
32
|
+
raise "EtherDelta importer: incorrect csv format - unexpected '#{@trade}' in the second column"
|
33
|
+
end
|
34
|
+
|
35
|
+
@token = CryptoCurrency.new(line[2])
|
36
|
+
|
37
|
+
@amount = BigDecimal.new(line[3])
|
38
|
+
@price = BigDecimal.new(line[4])
|
39
|
+
@total = BigDecimal.new(line[5])
|
40
|
+
|
41
|
+
@date = Time.parse(line[6])
|
42
|
+
|
43
|
+
@fee = BigDecimal.new(line[11])
|
44
|
+
@fee_token = CryptoCurrency.new(line[12])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def read_transaction_list(source)
|
49
|
+
csv = CSV.new(source, col_sep: ',')
|
50
|
+
|
51
|
+
transactions = []
|
52
|
+
|
53
|
+
csv.each do |line|
|
54
|
+
next if line[0] == 'Type'
|
55
|
+
|
56
|
+
entry = HistoryEntry.new(line)
|
57
|
+
|
58
|
+
next if entry.amount.round(8) == 0 || entry.total.round(8) == 0
|
59
|
+
|
60
|
+
if entry.trade == 'Buy'
|
61
|
+
if entry.fee_token != ETH
|
62
|
+
raise "EtherDelta importer: Unexpected fee currency: #{entry.fee_token.code}"
|
63
|
+
end
|
64
|
+
|
65
|
+
transactions << Transaction.new(
|
66
|
+
exchange: 'EtherDelta',
|
67
|
+
time: entry.date,
|
68
|
+
bought_amount: entry.amount,
|
69
|
+
bought_currency: entry.token,
|
70
|
+
sold_amount: entry.total + entry.fee,
|
71
|
+
sold_currency: ETH
|
72
|
+
)
|
73
|
+
else
|
74
|
+
if entry.fee_token != entry.token
|
75
|
+
raise "EtherDelta importer: Unexpected fee currency: #{entry.fee_token.code}"
|
76
|
+
end
|
77
|
+
|
78
|
+
transactions << Transaction.new(
|
79
|
+
exchange: 'EtherDelta',
|
80
|
+
time: entry.date,
|
81
|
+
bought_amount: entry.total,
|
82
|
+
bought_currency: ETH,
|
83
|
+
sold_amount: entry.amount + entry.fee,
|
84
|
+
sold_currency: entry.token
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
transactions.sort_by(&:time)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'bigdecimal'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'openssl'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
require_relative 'base'
|
9
|
+
require_relative 'kraken_common'
|
10
|
+
require_relative '../balance'
|
11
|
+
require_relative '../currencies'
|
12
|
+
require_relative '../request'
|
13
|
+
|
14
|
+
module CoinSync
|
15
|
+
module Importers
|
16
|
+
class KrakenAPI < Base
|
17
|
+
register_importer :kraken_api
|
18
|
+
|
19
|
+
include Kraken::Common
|
20
|
+
|
21
|
+
BASE_URL = "https://api.kraken.com"
|
22
|
+
API_RENEWAL_INTERVAL = 3.0
|
23
|
+
|
24
|
+
def initialize(config, params = {})
|
25
|
+
super
|
26
|
+
@api_key = params['api_key']
|
27
|
+
@secret_api_key = params['private_key']
|
28
|
+
@decoded_secret = Base64.decode64(@secret_api_key) if @secret_api_key
|
29
|
+
end
|
30
|
+
|
31
|
+
def can_import?(type)
|
32
|
+
@api_key && @secret_api_key && [:balances, :transactions].include?(type)
|
33
|
+
end
|
34
|
+
|
35
|
+
def import_transactions(filename)
|
36
|
+
offset = 0
|
37
|
+
entries = []
|
38
|
+
slowdown = false
|
39
|
+
|
40
|
+
loop do
|
41
|
+
response = make_request('/0/private/Ledgers', ofs: offset)
|
42
|
+
print slowdown ? '-' : '.'
|
43
|
+
sleep(2 * API_RENEWAL_INTERVAL) if slowdown # rate limiting
|
44
|
+
|
45
|
+
case response
|
46
|
+
when Net::HTTPSuccess
|
47
|
+
json = JSON.parse(response.body)
|
48
|
+
|
49
|
+
if json['result'].nil? || json['error'].length > 0
|
50
|
+
if json['error'].first == 'EAPI:Rate limit exceeded'
|
51
|
+
slowdown = true
|
52
|
+
print '!'
|
53
|
+
sleep(4 * API_RENEWAL_INTERVAL)
|
54
|
+
next
|
55
|
+
else
|
56
|
+
raise "Kraken importer: Invalid response: #{response.body}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
data = json['result']
|
61
|
+
list = data && data['ledger']
|
62
|
+
|
63
|
+
if !list
|
64
|
+
raise "Kraken importer: No data returned: #{response.body}"
|
65
|
+
end
|
66
|
+
|
67
|
+
break if list.empty?
|
68
|
+
|
69
|
+
entries.concat(list.values)
|
70
|
+
offset += list.length
|
71
|
+
when Net::HTTPBadRequest
|
72
|
+
raise "Kraken importer: Bad request: #{response.body}"
|
73
|
+
else
|
74
|
+
raise "Kraken importer: Bad response: #{response.body}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
File.write(filename, JSON.pretty_generate(entries) + "\n")
|
79
|
+
end
|
80
|
+
|
81
|
+
def import_balances
|
82
|
+
response = make_request('/0/private/Balance')
|
83
|
+
|
84
|
+
case response
|
85
|
+
when Net::HTTPSuccess
|
86
|
+
json = JSON.parse(response.body)
|
87
|
+
|
88
|
+
if !json['error'].empty? || !json['result']
|
89
|
+
raise "Kraken importer: Invalid response: #{response.body}"
|
90
|
+
end
|
91
|
+
|
92
|
+
return json['result'].map { |k, v|
|
93
|
+
[Kraken::LedgerEntry.parse_currency(k), BigDecimal.new(v)]
|
94
|
+
}.select { |currency, amount|
|
95
|
+
amount > 0 && currency.crypto?
|
96
|
+
}.map { |currency, amount|
|
97
|
+
Balance.new(currency, available: amount)
|
98
|
+
}
|
99
|
+
when Net::HTTPBadRequest
|
100
|
+
raise "Kraken importer: Bad request: #{response.body}"
|
101
|
+
else
|
102
|
+
raise "Kraken importer: Bad response: #{response.body}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def read_transaction_list(source)
|
107
|
+
json = JSON.parse(source.read)
|
108
|
+
|
109
|
+
build_transaction_list(json.map { |hash| Kraken::LedgerEntry.from_json(hash) })
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def make_request(path, params = {})
|
115
|
+
(@api_key && @secret_api_key) or raise "Public and secret API keys must be provided"
|
116
|
+
|
117
|
+
nonce = (Time.now.to_f * 1000).to_i
|
118
|
+
|
119
|
+
url = URI(BASE_URL + path)
|
120
|
+
params['nonce'] = nonce
|
121
|
+
|
122
|
+
post_data = URI.encode_www_form(params)
|
123
|
+
string_to_hash = path + OpenSSL::Digest.new('sha256', nonce.to_s + post_data).digest
|
124
|
+
hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('sha512', @decoded_secret, string_to_hash))
|
125
|
+
|
126
|
+
Request.post(url) do |request|
|
127
|
+
request.body = post_data
|
128
|
+
request['API-Key'] = @api_key
|
129
|
+
request['API-Sign'] = hmac
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|