coinbase 0.0.1 → 4.0.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.

Potentially problematic release.


This version of coinbase might be problematic. Click here for more details.

Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +67 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +578 -0
  7. data/Rakefile +7 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +7 -0
  10. data/coinbase.gemspec +30 -0
  11. data/lib/coinbase/wallet/adapters/em_http.rb +64 -0
  12. data/lib/coinbase/wallet/adapters/net_http.rb +62 -0
  13. data/lib/coinbase/wallet/api_client.rb +659 -0
  14. data/lib/coinbase/wallet/api_errors.rb +112 -0
  15. data/lib/coinbase/wallet/api_response.rb +37 -0
  16. data/lib/coinbase/wallet/client.rb +98 -0
  17. data/lib/coinbase/wallet/models/account.rb +187 -0
  18. data/lib/coinbase/wallet/models/api_object.rb +46 -0
  19. data/lib/coinbase/wallet/models/checkout.rb +19 -0
  20. data/lib/coinbase/wallet/models/order.rb +12 -0
  21. data/lib/coinbase/wallet/models/transaction.rb +21 -0
  22. data/lib/coinbase/wallet/models/transfer.rb +13 -0
  23. data/lib/coinbase/wallet/models/user.rb +15 -0
  24. data/lib/coinbase/wallet/version.rb +5 -0
  25. data/lib/coinbase/wallet.rb +23 -156
  26. data/spec/account_spec.rb +193 -0
  27. data/spec/clients/client_spec.rb +34 -0
  28. data/spec/clients/oauth_client_spec.rb +56 -0
  29. data/spec/endpoints_spec.rb +346 -0
  30. data/spec/error_spec.rb +130 -0
  31. data/spec/models/api_object_spec.rb +63 -0
  32. data/spec/models/checkout_spec.rb +52 -0
  33. data/spec/models/current_user_spec.rb +29 -0
  34. data/spec/models/order_spec.rb +52 -0
  35. data/spec/models/request_spec.rb +47 -0
  36. data/spec/models/transfer_spec.rb +64 -0
  37. data/spec/models/user_spec.rb +24 -0
  38. data/spec/spec_helper.rb +13 -0
  39. metadata +72 -82
  40. data/lib/coinbase/address.rb +0 -127
  41. data/lib/coinbase/asset.rb +0 -20
  42. data/lib/coinbase/balance_map.rb +0 -48
  43. data/lib/coinbase/constants.rb +0 -38
  44. data/lib/coinbase/network.rb +0 -55
  45. data/lib/coinbase/transfer.rb +0 -153
  46. data/lib/coinbase.rb +0 -18
@@ -0,0 +1,112 @@
1
+ module Coinbase
2
+ module Wallet
3
+ def self.format_error(resp)
4
+ error = resp.body && (resp.body['errors'] || resp.body['warnings']).first
5
+ return resp.body unless error
6
+ message = error['message']
7
+ message += " (#{error['url']})" if error["url"]
8
+ message
9
+ end
10
+
11
+ def self.check_response_status(resp)
12
+ (resp.body['warnings'] || []).each do |warning|
13
+ message = "WARNING: #{warning['message']}"
14
+ message += " (#{warning['url']})" if warning["url"]
15
+ $stderr.puts message
16
+ end
17
+
18
+ case resp.status
19
+ when 400
20
+ case resp.body['errors'].first['id']
21
+ when 'param_required' then raise ParamRequiredError, format_error(resp)
22
+ when 'invalid_request' then raise InvalidRequestError, format_error(resp)
23
+ when 'personal_details_required' then raise PersonalDetailsRequiredError, format_error(resp)
24
+ end
25
+ raise BadRequestError, format_error(resp)
26
+ when 401
27
+ case resp.body['errors'].first['id']
28
+ when 'authentication_error' then raise AuthenticationError, format_error(resp)
29
+ when 'unverified_email' then raise UnverifiedEmailError, format_error(resp)
30
+ when 'invalid_token' then raise InvalidTokenError, format_error(resp)
31
+ when 'revoked_token' then raise RevokedTokenError, format_error(resp)
32
+ when 'expired_token' then raise ExpiredTokenError, format_error(resp)
33
+ end
34
+ raise AuthenticationError, format_error(resp)
35
+ when 402 then raise TwoFactorRequiredError, format_error(resp)
36
+ when 403 then raise InvalidScopeError, format_error(resp)
37
+ when 404 then raise NotFoundError, format_error(resp)
38
+ when 422 then raise ValidationError, format_error(resp)
39
+ when 429 then raise RateLimitError, format_error(resp)
40
+ when 500 then raise InternalServerError, format_error(resp)
41
+ when 503 then raise ServiceUnavailableError, format_error(resp)
42
+ end
43
+
44
+ if resp.status > 400
45
+ raise APIError, resp.body
46
+ end
47
+ end
48
+
49
+ #
50
+ # Rest API Errors
51
+ #
52
+ class APIError < RuntimeError
53
+ end
54
+
55
+ # Status 400
56
+ class BadRequestError < APIError
57
+ end
58
+
59
+ class ParamRequiredError < APIError
60
+ end
61
+
62
+ class InvalidRequestError < APIError
63
+ end
64
+
65
+ class PersonalDetailsRequiredError < APIError
66
+ end
67
+
68
+ # Status 401
69
+ class AuthenticationError < APIError
70
+ end
71
+
72
+ class UnverifiedEmailError < APIError
73
+ end
74
+
75
+ class InvalidTokenError < APIError
76
+ end
77
+
78
+ class RevokedTokenError < APIError
79
+ end
80
+
81
+ class ExpiredTokenError < APIError
82
+ end
83
+
84
+ # Status 402
85
+ class TwoFactorRequiredError < APIError
86
+ end
87
+
88
+ # Status 403
89
+ class InvalidScopeError < APIError
90
+ end
91
+
92
+ # Status 404
93
+ class NotFoundError < APIError
94
+ end
95
+
96
+ # Status 422
97
+ class ValidationError < APIError
98
+ end
99
+
100
+ # Status 429
101
+ class RateLimitError < APIError
102
+ end
103
+
104
+ # Status 500
105
+ class InternalServerError < APIError
106
+ end
107
+
108
+ # Status 503
109
+ class ServiceUnavailableError < APIError
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,37 @@
1
+ module Coinbase
2
+ module Wallet
3
+ # Encapsulate data for an API response
4
+ class APIResponse
5
+ attr_reader :received_at
6
+ attr_accessor :client
7
+ attr_accessor :method
8
+ attr_accessor :params
9
+
10
+ def initialize(resp)
11
+ @received_at = Time.now
12
+ @response = resp
13
+ end
14
+
15
+ def raw
16
+ @response
17
+ end
18
+
19
+ def body
20
+ raise NotImplementedError
21
+ end
22
+ alias_method :data, :body
23
+
24
+ def headers
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def status
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def has_more?
33
+ body.has_key?('pagination') && body['pagination']['next_uri'] != nil
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,98 @@
1
+ module Coinbase
2
+ module Wallet
3
+ BASE_API_URL = "https://api.coinbase.com"
4
+ API_VERSION = '2015-06-16'
5
+
6
+ class Client < NetHTTPClient
7
+ def initialize(options={})
8
+ [ :api_key, :api_secret ].each do |opt|
9
+ raise unless options.has_key? opt
10
+ end
11
+ @api_key = options[:api_key]
12
+ @api_secret = options[:api_secret]
13
+ @api_uri = URI.parse(options[:api_url] || BASE_API_URL)
14
+ super(@api_uri, options)
15
+ end
16
+
17
+ def auth_headers(method, path, body)
18
+ ts = Time.now.to_i.to_s
19
+ signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
20
+ @api_secret,
21
+ ts + method + path + body.to_s)
22
+ { 'CB-ACCESS-KEY' => @api_key,
23
+ 'CB-ACCESS-SIGN' => signature,
24
+ 'CB-ACCESS-TIMESTAMP' => ts,
25
+ 'CB-VERSION' => API_VERSION }
26
+ end
27
+ end
28
+
29
+ class OAuthClient < NetHTTPClient
30
+ attr_accessor :access_token, :refresh_token
31
+
32
+ def initialize(options={})
33
+ raise unless options.has_key? :access_token
34
+ @access_token = options[:access_token]
35
+ @refresh_token = options[:refresh_token]
36
+ @oauth_uri = URI.parse(options[:api_url] || BASE_API_URL)
37
+ super(@oauth_uri, options)
38
+ end
39
+
40
+ def auth_headers(method, path, body)
41
+ { 'Authorization' => "Bearer #{@access_token}",
42
+ 'CB-VERSION' => API_VERSION }
43
+ end
44
+
45
+ def authorize!(redirect_url, params = {})
46
+ raise NotImplementedError
47
+ end
48
+
49
+ def revoke!(params = {})
50
+ params[:token] ||= @access_token
51
+
52
+ out = nil
53
+ post("/oauth/revoke", params) do |data, resp|
54
+ out = APIObject.new(self, resp.body)
55
+ yield(out, resp) if block_given?
56
+ end
57
+ out
58
+ end
59
+
60
+ def refresh!(params = {})
61
+ params[:grant_type] = 'refresh_token'
62
+ params[:refresh_token] ||= @refresh_token
63
+
64
+ raise "Missing Parameter: refresh_token" unless params.has_key?(:refresh_token)
65
+
66
+ out = nil
67
+ post("/oauth/token", params) do |data, resp|
68
+ out = APIObject.new(self, resp.body)
69
+ @access_token = out.access_token
70
+ yield(out, resp) if block_given?
71
+ end
72
+ out
73
+ end
74
+ end
75
+
76
+ class AsyncClient < EMHTTPClient
77
+ def initialize(options={})
78
+ [ :api_key, :api_secret ].each do |opt|
79
+ raise unless options.has_key? opt
80
+ end
81
+ @api_key = options[:api_key]
82
+ @api_secret = options[:api_secret]
83
+ @api_uri = URI.parse(options[:api_url] || BASE_API_URL)
84
+ end
85
+
86
+ def auth_headers(method, path, body)
87
+ ts = Time.now.to_i.to_s
88
+ signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
89
+ @api_secret,
90
+ ts + method + path + body.to_s)
91
+ { 'CB-ACCESS-KEY' => @api_key,
92
+ 'CB-ACCESS-SIGN' => signature,
93
+ 'CB-ACCESS-TIMESTAMP' => ts,
94
+ 'CB-VERSION' => API_VERSION }
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,187 @@
1
+ module Coinbase
2
+ module Wallet
3
+ class Account < APIObject
4
+ def update!(params = {})
5
+ @client.update_account(self['id'], params) do |data, resp|
6
+ update(data)
7
+ yield(data, resp) if block_given?
8
+ end
9
+ end
10
+
11
+ def make_primary!(params = {})
12
+ @client.set_primary_account(self['id'], params) do |data, resp|
13
+ update(data)
14
+ yield(data, resp) if block_given?
15
+ end
16
+ end
17
+
18
+ def delete!(params = {})
19
+ @client.delete_account(self['id'], params) do |data, resp|
20
+ yield(data, resp) if block_given?
21
+ end
22
+ end
23
+
24
+ #
25
+ # Addresses
26
+ #
27
+ def addresses(params = {})
28
+ @client.addresses(self['id'], params) do |data, resp|
29
+ yield(data, resp) if block_given?
30
+ end
31
+ end
32
+
33
+ def address(address_id, params = {})
34
+ @client.address(self['id'], address_id, params) do |data, resp|
35
+ yield(data, resp) if block_given?
36
+ end
37
+ end
38
+
39
+ def create_address(params = {})
40
+ @client.create_address(self['id']) do |data, resp|
41
+ yield(data, resp) if block_given?
42
+ end
43
+ end
44
+
45
+ #
46
+ # Transactions
47
+ #
48
+ def transactions(params = {})
49
+ @client.transactions(self['id'], params) do |data, resp|
50
+ yield(data, resp) if block_given?
51
+ end
52
+ end
53
+
54
+ def transaction(transaction_id, params = {})
55
+ @client.transaction(self['id'], transaction_id, params) do |data, resp|
56
+ yield(data, resp) if block_given?
57
+ end
58
+ end
59
+
60
+ def send(params = {})
61
+ @client.send(self['id'], params) do |data, resp|
62
+ yield(data, resp) if block_given?
63
+ end
64
+ end
65
+
66
+ def transfer(params = {})
67
+ @client.transfer(self['id'], params) do |data, resp|
68
+ yield(data, resp) if block_given?
69
+ end
70
+ end
71
+
72
+ def request(params = {})
73
+ @client.request(self['id'], params) do |data, resp|
74
+ yield(data, resp) if block_given?
75
+ end
76
+ end
77
+
78
+ #
79
+ # Buys
80
+ #
81
+ def list_buys(params = {})
82
+ @client.list_buys(self['id'], params) do |data, resp|
83
+ yield(data, resp) if block_given?
84
+ end
85
+ end
86
+
87
+ def list_buy(transaction_id, params = {})
88
+ @client.list_buy(self['id'], transaction_id, params) do |data, resp|
89
+ yield(data, resp) if block_given?
90
+ end
91
+ end
92
+
93
+ def buy(params = {})
94
+ @client.buy(self['id'], params) do |data, resp|
95
+ yield(data, resp) if block_given?
96
+ end
97
+ end
98
+
99
+ def commit_buy(transaction_id, params = {})
100
+ @client.commit_buy(self['id'], transaction_id, params) do |data, resp|
101
+ yield(data, resp) if block_given?
102
+ end
103
+ end
104
+
105
+ #
106
+ # Sells
107
+ #
108
+ def list_sells(params = {})
109
+ @client.list_sells(self['id'], params) do |data, resp|
110
+ yield(data, resp) if block_given?
111
+ end
112
+ end
113
+
114
+ def list_sell(transaction_id, params = {})
115
+ @client.list_sell(self['id'], transaction_id, params) do |data, resp|
116
+ yield(data, resp) if block_given?
117
+ end
118
+ end
119
+
120
+ def sell(params = {})
121
+ @client.sell(self['id'], params) do |data, resp|
122
+ yield(data, resp) if block_given?
123
+ end
124
+ end
125
+
126
+ def commit_sell(transaction_id, params = {})
127
+ @client.commit_sell(self['id'], transaction_id, params) do |data, resp|
128
+ yield(data, resp) if block_given?
129
+ end
130
+ end
131
+
132
+ #
133
+ # Deposit
134
+ #
135
+ def list_deposits(params = {})
136
+ @client.list_deposits(self['id'], params) do |data, resp|
137
+ yield(data, resp) if block_given?
138
+ end
139
+ end
140
+
141
+ def list_deposit(transaction_id, params = {})
142
+ @client.list_deposit(self['id'], transaction_id, params) do |data, resp|
143
+ yield(data, resp) if block_given?
144
+ end
145
+ end
146
+
147
+ def deposit(params = {})
148
+ @client.deposit(self['id'], params) do |data, resp|
149
+ yield(data, resp) if block_given?
150
+ end
151
+ end
152
+
153
+ def commit_deposit(transaction_id, params = {})
154
+ @client.commit_deposit(self['id'], transaction_id, params) do |data, resp|
155
+ yield(data, resp) if block_given?
156
+ end
157
+ end
158
+
159
+ #
160
+ # Withdrawals
161
+ #
162
+ def list_withdrawals(params = {})
163
+ @client.list_withdrawals(self['id'], params) do |data, resp|
164
+ yield(data, resp) if block_given?
165
+ end
166
+ end
167
+
168
+ def list_withdrawal(transaction_id, params = {})
169
+ @client.list_withdrawal(self['id'], transaction_id, params) do |data, resp|
170
+ yield(data, resp) if block_given?
171
+ end
172
+ end
173
+
174
+ def withdraw(params = {})
175
+ @client.withdraw(self['id'], params) do |data, resp|
176
+ yield(data, resp) if block_given?
177
+ end
178
+ end
179
+
180
+ def commit_withdrawal(transaction_id, params = {})
181
+ @client.commit_withdrawal(self['id'], transaction_id, params) do |data, resp|
182
+ yield(data, resp) if block_given?
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,46 @@
1
+ module Coinbase
2
+ module Wallet
3
+ # Response item abstract model
4
+ class APIObject < Hash
5
+ def initialize(client, data)
6
+ super()
7
+ update(data)
8
+ @client = client
9
+ end
10
+
11
+ def refresh!(params = {})
12
+ @client.get(self['resource_path'], params) do |data, resp|
13
+ update(data)
14
+ yield(data, resp) if block_given?
15
+ end
16
+ end
17
+
18
+ def update(data)
19
+ return if data.nil?
20
+ data.each { |key, val| self[key] = val } if data.is_a?(Hash)
21
+ end
22
+
23
+ def format(key, val)
24
+ return if val.nil?
25
+ # Looks like a number or currency
26
+ if val.class == Hash
27
+ APIObject.new(@client, val)
28
+ elsif key =~ /_at$/ && (Time.iso8601(val) rescue nil)
29
+ Time.parse(val)
30
+ elsif key == "amount" && val =~ /^.{0,1}\s*[0-9,]*\.{0,1}[0-9]*$/
31
+ BigDecimal(val.gsub(/[^0-9\.]/, ''))
32
+ else
33
+ val
34
+ end
35
+ end
36
+
37
+ def method_missing(method, *args, &blk)
38
+ format(method.to_s, self[method.to_s]) || super
39
+ end
40
+
41
+ def respond_to_missing?(method, include_all = false)
42
+ self.key?(method.to_s) || super
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ module Coinbase
2
+ module Wallet
3
+ class Checkout < APIObject
4
+ def orders(params = {})
5
+ @client.get("#{self['resource_path']}/orders", params) do |data, resp|
6
+ out = resp.data.map { |item| Order.new(self, item) }
7
+ yield(out, resp) if block_given?
8
+ end
9
+ end
10
+
11
+ def create_order(params = {})
12
+ @client.post("#{self['resource_path']}/orders", params) do |data, resp|
13
+ out = Order.new(self, resp.data)
14
+ yield(out, resp) if block_given?
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ module Coinbase
2
+ module Wallet
3
+ class Order < APIObject
4
+ def refund!(params = {})
5
+ @client.post("#{self['resource_path']}/refund", params) do |data, resp|
6
+ update(data)
7
+ yield(data, resp) if block_given?
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ module Coinbase
2
+ module Wallet
3
+ class Transaction < APIObject
4
+ end
5
+
6
+ class Request < Transaction
7
+ def resend!(params = {})
8
+ @client.post("#{self['resource_path']}/resend", params) do |data, resp|
9
+ yield(data, resp) if block_given?
10
+ end
11
+ end
12
+
13
+ def cancel!(params = {})
14
+ @client.delete("#{self['resource_path']}", params) do |data, resp|
15
+ yield(data, resp) if block_given?
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,13 @@
1
+ module Coinbase
2
+ module Wallet
3
+ class Transfer < APIObject
4
+ def commit!(params = {})
5
+ @client.post("#{self['resource_path']}/commit", params) do |data, resp|
6
+ update(data)
7
+ yield(data, resp) if block_given?
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,15 @@
1
+ module Coinbase
2
+ module Wallet
3
+ class User < APIObject
4
+ end
5
+
6
+ class CurrentUser < User
7
+ def update!(params = {})
8
+ @client.update_current_user(params) do |data, resp|
9
+ update(data)
10
+ yield(data, resp) if block_given?
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Coinbase
2
+ module Wallet
3
+ VERSION = "4.0.0"
4
+ end
5
+ end