coinbase-pro 0.4.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +21 -0
- data/Gemfile +4 -0
- data/LICENSE +201 -0
- data/README.md +462 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/coinbase-pro.gemspec +29 -0
- data/lib/coinbase/pro.rb +21 -0
- data/lib/coinbase/pro/adapters/em_http.rb +83 -0
- data/lib/coinbase/pro/adapters/net_http.rb +67 -0
- data/lib/coinbase/pro/api_client.rb +413 -0
- data/lib/coinbase/pro/api_object.rb +39 -0
- data/lib/coinbase/pro/api_response.rb +29 -0
- data/lib/coinbase/pro/ca-coinbase.crt +629 -0
- data/lib/coinbase/pro/client.rb +9 -0
- data/lib/coinbase/pro/errors.rb +42 -0
- data/lib/coinbase/pro/version.rb +6 -0
- data/lib/coinbase/pro/websocket.rb +119 -0
- metadata +176 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "coinbase/pro"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'coinbase/pro/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "coinbase-pro"
|
8
|
+
spec.version = Coinbase::Pro::VERSION
|
9
|
+
spec.authors = ["Vertbase"]
|
10
|
+
spec.email = ["dev@vertbase.com"]
|
11
|
+
|
12
|
+
spec.summary = "Client library for Coinbase Pro"
|
13
|
+
spec.homepage = "https://github.com/vertbase/coinbase-pro"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "bigdecimal"
|
21
|
+
spec.add_dependency "faye-websocket"
|
22
|
+
spec.add_dependency "em-http-request"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.8"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "webmock"
|
28
|
+
spec.add_development_dependency "pry"
|
29
|
+
end
|
data/lib/coinbase/pro.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "bigdecimal"
|
2
|
+
require "json"
|
3
|
+
require "uri"
|
4
|
+
require "net/http"
|
5
|
+
require "em-http-request"
|
6
|
+
require "faye/websocket"
|
7
|
+
|
8
|
+
require "coinbase/pro/errors"
|
9
|
+
require "coinbase/pro/api_object"
|
10
|
+
require "coinbase/pro/api_response"
|
11
|
+
require "coinbase/pro/api_client.rb"
|
12
|
+
require "coinbase/pro/adapters/net_http.rb"
|
13
|
+
require "coinbase/pro/adapters/em_http.rb"
|
14
|
+
require "coinbase/pro/client"
|
15
|
+
require "coinbase/pro/websocket"
|
16
|
+
|
17
|
+
module Coinbase
|
18
|
+
# Coinbase Pro module
|
19
|
+
module Pro
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Pro
|
3
|
+
# EM-Http Adapter
|
4
|
+
class EMHTTPClient < APIClient
|
5
|
+
def initialize(api_key = '', api_secret = '', api_pass = '', options = {})
|
6
|
+
super(api_key, api_secret, api_pass, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def http_verb(method, path, body = nil)
|
12
|
+
if !EventMachine.reactor_running?
|
13
|
+
EM.run do
|
14
|
+
# FIXME: This doesn't work with paginated endpoints
|
15
|
+
http_verb(method, path, body) do |resp|
|
16
|
+
yield(resp)
|
17
|
+
EM.stop
|
18
|
+
end
|
19
|
+
end
|
20
|
+
else
|
21
|
+
req_ts = Time.now.utc.to_i.to_s
|
22
|
+
signature = Base64.encode64(
|
23
|
+
OpenSSL::HMAC.digest('sha256', Base64.decode64(@api_secret).strip,
|
24
|
+
"#{req_ts}#{method}#{path}#{body}")).strip
|
25
|
+
headers = {}
|
26
|
+
headers['Content-Type'] = 'application/json'
|
27
|
+
headers['CB-ACCESS-TIMESTAMP'] = req_ts
|
28
|
+
headers['CB-ACCESS-PASSPHRASE'] = @api_pass
|
29
|
+
headers['CB-ACCESS-KEY'] = @api_key
|
30
|
+
headers['CB-ACCESS-SIGN'] = signature
|
31
|
+
|
32
|
+
# NOTE: This is documented but not implemented in em-http-request
|
33
|
+
# https://github.com/igrigorik/em-http-request/issues/182
|
34
|
+
# https://github.com/igrigorik/em-http-request/pull/179
|
35
|
+
ssl_opts = { cert_chain_file: File.expand_path(File.join(File.dirname(__FILE__), 'ca-coinbase.crt')),
|
36
|
+
verify_peer: true }
|
37
|
+
|
38
|
+
case method
|
39
|
+
when 'GET'
|
40
|
+
req = EM::HttpRequest.new(@api_uri).get(path: path, head: headers, body: body, ssl: ssl_opts)
|
41
|
+
when 'POST'
|
42
|
+
req = EM::HttpRequest.new(@api_uri).post(path: path, head: headers, body: body, ssl: ssl_opts)
|
43
|
+
when 'DELETE'
|
44
|
+
req = EM::HttpRequest.new(@api_uri).delete(path: path, head: headers, ssl: ssl_opts)
|
45
|
+
else fail
|
46
|
+
end
|
47
|
+
req.callback do |resp|
|
48
|
+
case resp.response_header.status
|
49
|
+
when 200 then yield(EMHTTPResponse.new(resp))
|
50
|
+
when 400 then fail BadRequestError, resp.response
|
51
|
+
when 401 then fail NotAuthorizedError, resp.response
|
52
|
+
when 403 then fail ForbiddenError, resp.response
|
53
|
+
when 404 then fail NotFoundError, resp.response
|
54
|
+
when 429 then fail RateLimitError, resp.response
|
55
|
+
when 500 then fail InternalServerError, resp.response
|
56
|
+
end
|
57
|
+
end
|
58
|
+
req.errback do |resp|
|
59
|
+
fail APIError, "#{method} #{@api_uri}#{path}: #{resp.error}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# EM-Http response object
|
66
|
+
class EMHTTPResponse < APIResponse
|
67
|
+
def body
|
68
|
+
@response.response
|
69
|
+
end
|
70
|
+
|
71
|
+
def headers
|
72
|
+
out = @response.response_header.map do |key, val|
|
73
|
+
[ key.upcase.gsub('_', '-'), val ]
|
74
|
+
end
|
75
|
+
out.to_h
|
76
|
+
end
|
77
|
+
|
78
|
+
def status
|
79
|
+
@response.response_header.status
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Pro
|
3
|
+
# Net-HTTP adapter
|
4
|
+
class NetHTTPClient < APIClient
|
5
|
+
def initialize(api_key = '', api_secret = '', api_pass = '', options = {})
|
6
|
+
super(api_key, api_secret, api_pass, options)
|
7
|
+
@conn = Net::HTTP.new(@api_uri.host, @api_uri.port)
|
8
|
+
@conn.use_ssl = true if @api_uri.scheme == 'https'
|
9
|
+
@conn.cert_store = self.class.whitelisted_certificates
|
10
|
+
@conn.ssl_version = :TLSv1
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def http_verb(method, path, body = nil)
|
16
|
+
case method
|
17
|
+
when 'GET' then req = Net::HTTP::Get.new(path)
|
18
|
+
when 'POST' then req = Net::HTTP::Post.new(path)
|
19
|
+
when 'DELETE' then req = Net::HTTP::Delete.new(path)
|
20
|
+
else fail
|
21
|
+
end
|
22
|
+
|
23
|
+
req.body = body
|
24
|
+
|
25
|
+
req_ts = Time.now.utc.to_i.to_s
|
26
|
+
signature = Base64.encode64(
|
27
|
+
OpenSSL::HMAC.digest('sha256', Base64.decode64(@api_secret).strip,
|
28
|
+
"#{req_ts}#{method}#{path}#{body}")).strip
|
29
|
+
req['Content-Type'] = 'application/json'
|
30
|
+
req['CB-ACCESS-TIMESTAMP'] = req_ts
|
31
|
+
req['CB-ACCESS-PASSPHRASE'] = @api_pass
|
32
|
+
req['CB-ACCESS-KEY'] = @api_key
|
33
|
+
req['CB-ACCESS-SIGN'] = signature
|
34
|
+
|
35
|
+
resp = @conn.request(req)
|
36
|
+
case resp.code
|
37
|
+
when "200" then yield(NetHTTPResponse.new(resp))
|
38
|
+
when "400" then fail BadRequestError, resp.body
|
39
|
+
when "401" then fail NotAuthorizedError, resp.body
|
40
|
+
when "403" then fail ForbiddenError, resp.body
|
41
|
+
when "404" then fail NotFoundError, resp.body
|
42
|
+
when "429" then fail RateLimitError, resp.body
|
43
|
+
when "500" then fail InternalServerError, resp.body
|
44
|
+
end
|
45
|
+
resp.body
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Net-Http response object
|
50
|
+
class NetHTTPResponse < APIResponse
|
51
|
+
def body
|
52
|
+
@response.body
|
53
|
+
end
|
54
|
+
|
55
|
+
def headers
|
56
|
+
out = @response.to_hash.map do |key, val|
|
57
|
+
[ key.upcase.gsub('_', '-'), val.count == 1 ? val.first : val ]
|
58
|
+
end
|
59
|
+
out.to_h
|
60
|
+
end
|
61
|
+
|
62
|
+
def status
|
63
|
+
@response.code.to_i
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,413 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Pro
|
3
|
+
# Net-http client for Coinbase Pro API
|
4
|
+
class APIClient
|
5
|
+
def initialize(api_key = '', api_secret = '', api_pass = '', options = {})
|
6
|
+
@api_uri = URI.parse(options[:api_url] || "https://api.pro.coinbase.com")
|
7
|
+
@api_pass = api_pass
|
8
|
+
@api_key = api_key
|
9
|
+
@api_secret = api_secret
|
10
|
+
@default_product = options[:product_id] || "BTC-USD"
|
11
|
+
end
|
12
|
+
|
13
|
+
def server_epoch(params = {})
|
14
|
+
get("/time", params) do |resp|
|
15
|
+
yield(resp) if block_given?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Market Data
|
21
|
+
#
|
22
|
+
def currencies(params = {})
|
23
|
+
out = nil
|
24
|
+
get("/currencies", params) do |resp|
|
25
|
+
out = response_collection(resp)
|
26
|
+
yield(out, resp) if block_given?
|
27
|
+
end
|
28
|
+
out
|
29
|
+
end
|
30
|
+
|
31
|
+
def products(params = {})
|
32
|
+
out = nil
|
33
|
+
get("/products", params) do |resp|
|
34
|
+
out = response_collection(resp)
|
35
|
+
yield(out, resp) if block_given?
|
36
|
+
end
|
37
|
+
out
|
38
|
+
end
|
39
|
+
|
40
|
+
def orderbook(params = {})
|
41
|
+
product = params[:product_id] || @default_product
|
42
|
+
|
43
|
+
out = nil
|
44
|
+
get("/products/#{product}/book", params) do |resp|
|
45
|
+
out = response_object(resp)
|
46
|
+
yield(out, resp) if block_given?
|
47
|
+
end
|
48
|
+
out
|
49
|
+
end
|
50
|
+
|
51
|
+
def last_trade(params = {})
|
52
|
+
product = params[:product_id] || @default_product
|
53
|
+
|
54
|
+
out = nil
|
55
|
+
get("/products/#{product}/ticker", params) do |resp|
|
56
|
+
out = response_object(resp)
|
57
|
+
yield(out, resp) if block_given?
|
58
|
+
end
|
59
|
+
out
|
60
|
+
end
|
61
|
+
|
62
|
+
def trade_history(params = {})
|
63
|
+
product = params[:product_id] || @default_product
|
64
|
+
|
65
|
+
out = nil
|
66
|
+
get("/products/#{product}/trades", params, paginate: true) do |resp|
|
67
|
+
out = response_collection(resp)
|
68
|
+
yield(out, resp) if block_given?
|
69
|
+
end
|
70
|
+
out
|
71
|
+
end
|
72
|
+
|
73
|
+
def price_history(params = {})
|
74
|
+
product = params[:product_id] || @default_product
|
75
|
+
|
76
|
+
out = nil
|
77
|
+
get("/products/#{product}/candles", params) do |resp|
|
78
|
+
out = response_collection(
|
79
|
+
resp.map do |item|
|
80
|
+
{ 'start' => Time.at(item[0]),
|
81
|
+
'low' => item[1],
|
82
|
+
'high' => item[2],
|
83
|
+
'open' => item[3],
|
84
|
+
'close' => item[4],
|
85
|
+
'volume' => item[5]
|
86
|
+
}
|
87
|
+
end
|
88
|
+
)
|
89
|
+
yield(out, resp) if block_given?
|
90
|
+
end
|
91
|
+
out
|
92
|
+
end
|
93
|
+
|
94
|
+
def daily_stats(params = {})
|
95
|
+
product = params[:product_id] || @default_product
|
96
|
+
|
97
|
+
out = nil
|
98
|
+
get("/products/#{product}/stats", params) do |resp|
|
99
|
+
resp["start"] = (Time.now - 24 * 60 * 60).to_s
|
100
|
+
out = response_object(resp)
|
101
|
+
yield(out, resp) if block_given?
|
102
|
+
end
|
103
|
+
out
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Accounts
|
108
|
+
#
|
109
|
+
def accounts(params = {})
|
110
|
+
out = nil
|
111
|
+
get("/accounts", params) do |resp|
|
112
|
+
out = response_collection(resp)
|
113
|
+
yield(out, resp) if block_given?
|
114
|
+
end
|
115
|
+
out
|
116
|
+
end
|
117
|
+
|
118
|
+
def account(id, params = {})
|
119
|
+
out = nil
|
120
|
+
get("/accounts/#{id}", params) do |resp|
|
121
|
+
out = response_object(resp)
|
122
|
+
yield(out, resp) if block_given?
|
123
|
+
end
|
124
|
+
out
|
125
|
+
end
|
126
|
+
|
127
|
+
def account_history(id, params = {})
|
128
|
+
out = nil
|
129
|
+
get("/accounts/#{id}/ledger", params, paginate: true) do |resp|
|
130
|
+
out = response_collection(resp)
|
131
|
+
yield(out, resp) if block_given?
|
132
|
+
end
|
133
|
+
out
|
134
|
+
end
|
135
|
+
|
136
|
+
def account_holds(id, params = {})
|
137
|
+
out = nil
|
138
|
+
get("/accounts/#{id}/holds", params, paginate: true) do |resp|
|
139
|
+
out = response_collection(resp)
|
140
|
+
yield(out, resp) if block_given?
|
141
|
+
end
|
142
|
+
out
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Orders
|
147
|
+
#
|
148
|
+
def bid(amt, price, params = {})
|
149
|
+
params[:product_id] ||= @default_product
|
150
|
+
params[:size] = amt
|
151
|
+
params[:price] = price
|
152
|
+
params[:side] = "buy"
|
153
|
+
|
154
|
+
out = nil
|
155
|
+
post("/orders", params) do |resp|
|
156
|
+
out = response_object(resp)
|
157
|
+
yield(out, resp) if block_given?
|
158
|
+
end
|
159
|
+
out
|
160
|
+
end
|
161
|
+
alias_method :buy, :bid
|
162
|
+
|
163
|
+
def ask(amt, price, params = {})
|
164
|
+
params[:product_id] ||= @default_product
|
165
|
+
params[:size] = amt
|
166
|
+
params[:price] = price
|
167
|
+
params[:side] = "sell"
|
168
|
+
|
169
|
+
out = nil
|
170
|
+
post("/orders", params) do |resp|
|
171
|
+
out = response_object(resp)
|
172
|
+
yield(out, resp) if block_given?
|
173
|
+
end
|
174
|
+
out
|
175
|
+
end
|
176
|
+
alias_method :sell, :ask
|
177
|
+
|
178
|
+
def cancel(id)
|
179
|
+
out = nil
|
180
|
+
delete("/orders/#{id}") do |resp|
|
181
|
+
out = response_object(resp)
|
182
|
+
yield(out, resp) if block_given?
|
183
|
+
end
|
184
|
+
out
|
185
|
+
end
|
186
|
+
|
187
|
+
def orders(params = {})
|
188
|
+
params[:status] ||= "all"
|
189
|
+
|
190
|
+
out = nil
|
191
|
+
get("/orders", params, paginate: true) do |resp|
|
192
|
+
out = response_collection(resp)
|
193
|
+
yield(out, resp) if block_given?
|
194
|
+
end
|
195
|
+
out
|
196
|
+
end
|
197
|
+
|
198
|
+
def order(id, params = {})
|
199
|
+
out = nil
|
200
|
+
get("/orders/#{id}", params) do |resp|
|
201
|
+
out = response_object(resp)
|
202
|
+
yield(out, resp) if block_given?
|
203
|
+
end
|
204
|
+
out
|
205
|
+
end
|
206
|
+
|
207
|
+
def fills(params = {})
|
208
|
+
out = nil
|
209
|
+
get("/fills", params, paginate: true) do |resp|
|
210
|
+
out = response_collection(resp)
|
211
|
+
yield(out, resp) if block_given?
|
212
|
+
end
|
213
|
+
out
|
214
|
+
end
|
215
|
+
|
216
|
+
#
|
217
|
+
# Transfers
|
218
|
+
#
|
219
|
+
def deposit(account_id, amt, params = {})
|
220
|
+
params[:type] = "deposit"
|
221
|
+
params[:coinbase_account_id] = account_id
|
222
|
+
params[:amount] = amt
|
223
|
+
|
224
|
+
out = nil
|
225
|
+
post("/transfers", params) do |resp|
|
226
|
+
out = response_object(resp)
|
227
|
+
yield(out, resp) if block_given?
|
228
|
+
end
|
229
|
+
out
|
230
|
+
end
|
231
|
+
|
232
|
+
def withdraw(account_id, amt, params = {})
|
233
|
+
params[:type] = "withdraw"
|
234
|
+
params[:coinbase_account_id] = account_id
|
235
|
+
params[:amount] = amt
|
236
|
+
|
237
|
+
out = nil
|
238
|
+
post("/transfers", params) do |resp|
|
239
|
+
out = response_object(resp)
|
240
|
+
yield(out, resp) if block_given?
|
241
|
+
end
|
242
|
+
out
|
243
|
+
end
|
244
|
+
|
245
|
+
def payment_method_withdrawal(amount, currency, payment_method_id, params = {})
|
246
|
+
params[:amount] = amount
|
247
|
+
params[:currency] = currency
|
248
|
+
params[:payment_method_id] = payment_method_id
|
249
|
+
out = nil
|
250
|
+
post("/withdrawals/payment-method", params) do |resp|
|
251
|
+
out = response_object(resp)
|
252
|
+
yield(out, resp) if block_given?
|
253
|
+
end
|
254
|
+
out
|
255
|
+
end
|
256
|
+
|
257
|
+
def coinbase_withdrawal(amount, currency, coinbase_account_id, params = {})
|
258
|
+
params[:amount] = amount
|
259
|
+
params[:currency] = currency
|
260
|
+
params[:coinbase_account_id] = coinbase_account_id
|
261
|
+
out = nil
|
262
|
+
post("/withdrawals/coinbase-account", params) do |resp|
|
263
|
+
out = response_object(resp)
|
264
|
+
yield(out, resp) if block_given?
|
265
|
+
end
|
266
|
+
out
|
267
|
+
end
|
268
|
+
|
269
|
+
def crypto_withdrawal(amount, currency, crypto_address, params = {})
|
270
|
+
params[:amount] = amount
|
271
|
+
params[:currency] = currency
|
272
|
+
params[:crypto_address] = crypto_address
|
273
|
+
out = nil
|
274
|
+
post("/withdrawals/crypto", params) do |resp|
|
275
|
+
out = response_object(resp)
|
276
|
+
yield(out, resp) if block_given?
|
277
|
+
end
|
278
|
+
out
|
279
|
+
end
|
280
|
+
|
281
|
+
def payment_methods(params = {})
|
282
|
+
out = nil
|
283
|
+
get("/payment-methods", params, paginate: true) do |resp|
|
284
|
+
out = response_collection(resp)
|
285
|
+
yield(out, resp) if block_given?
|
286
|
+
end
|
287
|
+
out
|
288
|
+
end
|
289
|
+
|
290
|
+
def coinbase_accounts(params = {})
|
291
|
+
out = nil
|
292
|
+
get("/coinbase-accounts", params, paginate: true) do |resp|
|
293
|
+
out = response_collection(resp)
|
294
|
+
yield(out, resp) if block_given?
|
295
|
+
end
|
296
|
+
out
|
297
|
+
end
|
298
|
+
|
299
|
+
private
|
300
|
+
|
301
|
+
def response_collection(resp)
|
302
|
+
out = resp.map { |item| APIObject.new(item) }
|
303
|
+
out.instance_eval { @response = resp }
|
304
|
+
add_metadata(out)
|
305
|
+
out
|
306
|
+
end
|
307
|
+
|
308
|
+
def response_object(resp)
|
309
|
+
out = APIObject.new(resp)
|
310
|
+
out.instance_eval { @response = resp.response }
|
311
|
+
add_metadata(out)
|
312
|
+
out
|
313
|
+
end
|
314
|
+
|
315
|
+
def add_metadata(resp)
|
316
|
+
resp.instance_eval do
|
317
|
+
def response
|
318
|
+
@response
|
319
|
+
end
|
320
|
+
|
321
|
+
def raw
|
322
|
+
@response.raw
|
323
|
+
end
|
324
|
+
|
325
|
+
def response_headers
|
326
|
+
@response.headers
|
327
|
+
end
|
328
|
+
|
329
|
+
def response_status
|
330
|
+
@response.status
|
331
|
+
end
|
332
|
+
end
|
333
|
+
resp
|
334
|
+
end
|
335
|
+
|
336
|
+
def get(path, params = {}, options = {})
|
337
|
+
params[:limit] ||= 100 if options[:paginate] == true
|
338
|
+
|
339
|
+
http_verb('GET', "#{path}?#{URI.encode_www_form(params)}") do |resp|
|
340
|
+
begin
|
341
|
+
out = JSON.parse(resp.body)
|
342
|
+
rescue JSON::ParserError
|
343
|
+
out = resp.body
|
344
|
+
end
|
345
|
+
out.instance_eval { @response = resp }
|
346
|
+
add_metadata(out)
|
347
|
+
|
348
|
+
if options[:paginate] && out.count == params[:limit]
|
349
|
+
params[:after] = resp.headers['CB-AFTER']
|
350
|
+
get(path, params, options) do |pages|
|
351
|
+
out += pages
|
352
|
+
add_metadata(out)
|
353
|
+
yield(out)
|
354
|
+
end
|
355
|
+
else
|
356
|
+
yield(out)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def post(path, params = {})
|
362
|
+
http_verb('POST', path, params.to_json) do |resp|
|
363
|
+
begin
|
364
|
+
out = JSON.parse(resp.body)
|
365
|
+
rescue JSON::ParserError
|
366
|
+
out = resp.body
|
367
|
+
end
|
368
|
+
out.instance_eval { @response = resp }
|
369
|
+
add_metadata(out)
|
370
|
+
yield(out)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def delete(path)
|
375
|
+
http_verb('DELETE', path) do |resp|
|
376
|
+
begin
|
377
|
+
out = JSON.parse(resp.body)
|
378
|
+
rescue
|
379
|
+
out = resp.body
|
380
|
+
end
|
381
|
+
out.instance_eval { @response = resp }
|
382
|
+
add_metadata(out)
|
383
|
+
yield(out)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def http_verb(_method, _path, _body)
|
388
|
+
fail NotImplementedError
|
389
|
+
end
|
390
|
+
|
391
|
+
def self.whitelisted_certificates
|
392
|
+
path = File.expand_path(File.join(File.dirname(__FILE__), 'ca-coinbase.crt'))
|
393
|
+
|
394
|
+
certs = [ [] ]
|
395
|
+
File.readlines(path).each do |line|
|
396
|
+
next if ["\n", "#"].include?(line[0])
|
397
|
+
certs.last << line
|
398
|
+
certs << [] if line == "-----END CERTIFICATE-----\n"
|
399
|
+
end
|
400
|
+
|
401
|
+
result = OpenSSL::X509::Store.new
|
402
|
+
|
403
|
+
certs.each do |lines|
|
404
|
+
next if lines.empty?
|
405
|
+
cert = OpenSSL::X509::Certificate.new(lines.join)
|
406
|
+
result.add_cert(cert)
|
407
|
+
end
|
408
|
+
|
409
|
+
result
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|