Class: CoinapultClient

Inherits:
Object
  • Object
show all
Defined in:
lib/coinapult.rb

Instance Method Summary collapse

Constructor Details

#initialize(credentials: nil, baseURL: 'https://api.coinapult.com', ecc: nil, authmethod: nil) ⇒ CoinapultClient

Returns a new instance of CoinapultClient.



21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/coinapult.rb', line 21

def initialize(credentials: nil, baseURL: 'https://api.coinapult.com',
               ecc: nil, authmethod: nil)
  @key = ''
  @secret = ''
  @baseURL = baseURL
  @authmethod = authmethod

  if credentials
    @key = credentials[:key]
    @secret = credentials[:secret]
  end
  _setup_ECC_pair([ecc[:privkey], ecc[:pubkey]]) if ecc
end

Instance Method Details

#_format_response(response) ⇒ Object



83
84
85
86
87
# File 'lib/coinapult.rb', line 83

def _format_response(response)
  resp = JSON.parse(response)
  fail CoinapultError, resp['error'] if resp['error']
  resp
end

#_receive_ECC(resp) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/coinapult.rb', line 114

def _receive_ECC(resp)
  if resp['sign'].nil? || resp['data'].nil?
    fail CoinapultErrorECC, 'Invalid ECC message'
  end
  # Check signature.
  unless verify_ECC_sign(resp['sign'], resp['data'], ECC_COINAPULT_PUBKEY)
    fail CoinapultErrorECC, 'Invalid ECC signature'
  end

  JSON.parse(Base64.urlsafe_decode64(resp['data']))
end

#_send(method, url, headers, payload: nil) ⇒ Object



89
90
91
92
93
# File 'lib/coinapult.rb', line 89

def _send(method, url, headers, payload: nil)
   RestClient::Request.execute(method: method, url: url,
                               headers: headers, payload: payload,
                               ssl_version: SSL_VERSION)
end

#_send_ECC(url, values, new_account: false, sign: true) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/coinapult.rb', line 95

def _send_ECC(url, values, new_account: false, sign: true)
  headers = {}

  if !
    values['nonce'] = SecureRandom.hex(10)
    values['endpoint'] = url[4..-1]
    headers['cpt-ecc-pub'] = @ecc_pub_hash
  else
    headers['cpt-ecc-new'] = Base64.urlsafe_encode64(@ecc_pub_pem)
  end
  values['timestamp'] = Time.now.to_i

  data = Base64.urlsafe_encode64(JSON.generate(values))
  headers['cpt-ecc-sign'] = generate_ECC_sign(data, @ecc[:privkey])
  response = _send(:post, "#{@baseURL}#{url}", headers,
                   payload: { data: data })
  _format_response(response)
end

#_send_request(url, values, sign: false, post: true) ⇒ Object

Send a generic request to Coinapult.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/coinapult.rb', line 59

def _send_request(url, values, sign: false, post: true)
  headers = {}

  if sign
    values['timestamp'] = Time.now.to_i
    values['nonce'] = SecureRandom.hex(10)
    values['endpoint'] = url[4..-1]
    headers['cpt-key'] = @key
    signdata = Base64.urlsafe_encode64(JSON.generate(values))
    headers['cpt-hmac'] = OpenSSL::HMAC.hexdigest('sha512', @secret, signdata)
    data = { data: signdata }
  else
    data = values
  end
  if post
    response = _send(:post, "#{@baseURL}#{url}", headers, payload: data)
  else
    url = "#{@baseURL}#{url}"
    url += "?#{URI.encode_www_form(data)}" if data.length > 0
    response = _send(:get, url, headers)
  end
  _format_response(response)
end

#_setup_ECC_pair(keypair) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/coinapult.rb', line 40

def _setup_ECC_pair(keypair)
  if keypair.nil?
    privkey = OpenSSL::PKey::EC.new(ECC_CURVE)
    privkey.generate_key
    pubkey = OpenSSL::PKey::EC.new(privkey.group)
    pubkey.public_key = privkey.public_key
    @ecc = { privkey: privkey, pubkey: pubkey }
  else
    privkey, pubkey = keypair
    @ecc = {
      privkey: OpenSSL::PKey.read(privkey),
      pubkey: OpenSSL::PKey.read(pubkey)
    }
  end
  @ecc_pub_pem = @ecc[:pubkey].to_pem.strip
  @ecc_pub_hash = OpenSSL::Digest::SHA256.hexdigest(@ecc_pub_pem)
end

#account_address(address) ⇒ Object

Check if an address belongs to your account



332
333
334
335
336
337
338
339
# File 'lib/coinapult.rb', line 332

def (address)
  url = '/api/accountInfo/address'

  values = {}
  values['address'] = address

  send_to_coinapult(url, values, sign: true)
end

#account_info(balance_type: 'all', locks_as_BTC: false) ⇒ Object

Display basic account information



321
322
323
324
325
326
327
328
329
# File 'lib/coinapult.rb', line 321

def (balance_type: 'all', locks_as_BTC: false)
  url = '/api/accountInfo'

  values = {}
  values['balanceType'] = balance_type
  values['locksAsBTC'] = locks_as_BTC

  send_to_coinapult(url, values, sign: true)
end

#activate_account(agree, pubhash: nil) ⇒ Object



156
157
158
159
160
161
162
# File 'lib/coinapult.rb', line 156

def (agree, pubhash: nil)
  url = '/api/account/activate'

  pubhash = @ecc_pub_hash if pubhash.nil?
  values = { agree: agree, hash: pubhash }
  _receive_ECC(_send_ECC(url, values, new_account: true))
end

#authenticate_callback(recv_key, recv_sign, recv_data) ⇒ Object

Utility for authenticating callbacks.



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/coinapult.rb', line 342

def authenticate_callback(recv_key, recv_sign, recv_data)
  if recv_key.nil?
    # ECC
    return verify_ECC_sign(recv_sign, recv_data, ECC_COINAPULT_PUBKEY)
  end

  # HMAC
  if recv_key != @key
    # Unexpected API key received.
    return false
  end
  test_HMAC = OpenSSL::HMAC.hexdigest('sha512', @secret, recv_data)
  if test_HMAC != recv_sign
    # Signature does not match.
    return false
  end
  true
end

#convert(amount, in_currency: 'USD', out_currency: 'BTC') ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/coinapult.rb', line 215

def convert(amount, in_currency: 'USD', out_currency: 'BTC')
  url = '/api/t/convert/'

  if amount <= 0
    fail ArgumentError, 'invalid amount'
  elsif in_currency == out_currency
    fail ArgumentError, 'cannot convert currency to itself'
  end

  values = {}
  values['amount'] = amount
  values['inCurrency'] = in_currency
  values['outCurrency'] = out_currency

  send_to_coinapult(url, values, sign: true)
end

#create_account(create_local_keys: true, change_authmethod: true, **kwargs) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/coinapult.rb', line 136

def (create_local_keys: true, change_authmethod: true,
                   **kwargs)
  url = '/api/account/create'

  _setup_ECC_pair(nil) if create_local_keys

  pub_pem = @ecc_pub_pem
  result = _receive_ECC(_send_ECC(url, kwargs, new_account: true))
  unless result['success'].nil?
    if result['success'] != OpenSSL::Digest::SHA256.hexdigest(pub_pem)
      fail CoinapultErrorECC, 'Unexpected public key'
    end
    @authmethod = 'ecc' if change_authmethod
    puts "Please read the terms of service in TERMS.txt before \
proceeding with the account creation. #{result['info']}"
  end

  result
end

#export_ECCObject



35
36
37
38
# File 'lib/coinapult.rb', line 35

def export_ECC
  [@ecc[:privkey].to_pem,
   @ecc[:pubkey].to_pem]
end

#lock(amount, out_amount: 0, currency: 'USD', callback: nil) ⇒ Object



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/coinapult.rb', line 252

def lock(amount, out_amount: 0, currency: 'USD', callback: nil)
  url = '/api/t/lock/'

  if amount > 0 && out_amount > 0
    fail ArgumentError, 'specify either the input amount or the output amount'
  end

  values = {}
  if amount > 0
    values['amount'] = amount
  elsif out_amount > 0
    values['outAmount'] = out_amount
  else
    fail ArgumentError, 'no amount specified'
  end
  values['callback'] = callback if callback
  values['currency'] = currency

  send_to_coinapult(url, values, sign: true)
end

#new_bitcoin_addressObject

Get a new bitcoin address



316
317
318
# File 'lib/coinapult.rb', line 316

def new_bitcoin_address
  send_to_coinapult('/api/getBitcoinAddress', {}, sign: true)
end

#receive(amount: 0, out_amount: 0, currency: 'BTC', out_currency: nil, external_id: nil, callback: '') ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/coinapult.rb', line 164

def receive(amount: 0, out_amount: 0, currency: 'BTC', out_currency: nil,
            external_id: nil, callback: '')
  url = '/api/t/receive/'

  if amount > 0 && out_amount > 0
    fail ArgumentError, 'specify either the input amount or the output amount'
  end

  values = {}
  if amount > 0
    values['amount'] = amount
  elsif out_amount > 0
    values['outAmount'] = out_amount
  else
    fail ArgumentError, 'no amount specified'
  end

  out_currency = currency if out_currency.nil?

  values['currency'] = currency
  values['outCurrency'] = out_currency
  values['extOID'] = external_id unless external_id.nil?
  values['callback'] = callback

  send_to_coinapult(url, values, sign: true)
end

#search(transaction_id: nil, typ: nil, currency: nil, to: nil, fro: nil, external_id: nil, txhash: nil, many: false, page: nil) ⇒ Object



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/coinapult.rb', line 232

def search(transaction_id: nil, typ: nil, currency: nil,
           to: nil, fro: nil, external_id: nil, txhash: nil,
           many: false, page: nil)
  url = '/api/t/search/'

  values = {}
  values['transaction_id'] = transaction_id unless transaction_id.nil?
  values['type'] = typ unless typ.nil?
  values['currency'] = currency unless currency.nil
  values['to'] = to unless to.nil?
  values['from'] = fro unless fro.nil?
  values['extOID'] = external_id unless external_id.nil?
  values['txhash'] = txhash unless txhash.nil?
  fail ArgumentError, 'no search parameters provided' if values.length == 0
  values['many'] = '1' if many
  values['page'] = page unless page.nil?

  send_to_coinapult(url, values, sign: true)
end

#send(amount, address, out_amount: 0, currency: 'BTC', typ: 'bitcoin', callback: '') ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/coinapult.rb', line 191

def send(amount, address, out_amount: 0, currency: 'BTC', typ: 'bitcoin',
         callback: '')
  url = '/api/t/send/'

  if amount > 0 && out_amount > 0
    fail ArgumentError, 'specify either the input amount or the output amount'
  end

  values = {}
  if amount > 0
    values['amount'] = amount
  elsif out_amount > 0
    values['outAmount'] = out_amount
  else
    fail ArgumentError, 'no amount specified'
  end
  values['currency'] = currency
  values['address'] = address
  values['type'] = typ
  values['callback'] = callback

  send_to_coinapult(url, values, sign: true)
end

#send_to_coinapult(endpoint, values, sign: false, **kwargs) ⇒ Object



126
127
128
129
130
131
132
133
134
# File 'lib/coinapult.rb', line 126

def send_to_coinapult(endpoint, values, sign: false, **kwargs)
  if sign && @authmethod == 'ecc'
    method = self.method(:_send_ECC)
  else
    method = self.method(:_send_request)
  end

  method.call(endpoint, values, sign: sign, **kwargs)
end

#ticker(begint: nil, endt: nil, market: nil, filter: nil) ⇒ Object

Get the ticker



305
306
307
308
309
310
311
312
313
# File 'lib/coinapult.rb', line 305

def ticker(begint: nil, endt: nil, market: nil, filter: nil)
  data = {}
  data['begin'] = begint unless begint.nil?
  data['end'] = endt unless endt.nil?
  data['market'] = market unless market.nil?
  data['filter'] = filter unless filter.nil?

  send_to_coinapult('/api/ticker', data, sign: false, post: false)
end

#unlock(amount, address, out_amount: 0, currency: 'USD', callback: nil) ⇒ Object



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/coinapult.rb', line 273

def unlock(amount, address, out_amount: 0, currency: 'USD', callback: nil)
  url = '/api/t/unlock/'

  if amount > 0 && out_amount > 0
    fail ArgumentError, 'specify either the input amount or the output amount'
  end

  values = {}
  if amount > 0
    values['amount'] = amount
  elsif out_amount > 0
    values['outAmount'] = out_amount
  else
    fail ArgumentError, 'no amount specified'
  end
  values['callback'] = callback if callback
  values['currency'] = currency
  values['address'] = address

  send_to_coinapult(url, values, sign: true)
end

#unlock_confirm(transaction_id) ⇒ Object



295
296
297
298
299
300
301
302
# File 'lib/coinapult.rb', line 295

def unlock_confirm(transaction_id)
  url = '/api/t/unlock/confirm'

  values = {}
  values['transaction_id'] = transaction_id

  send_to_coinapult(url, values, sign: true)
end