coinbase 0.0.1 → 4.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +7 -0
  4. data/CONTRIBUTING.md +53 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +201 -0
  7. data/README.md +621 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +7 -0
  11. data/coinbase.gemspec +26 -0
  12. data/lib/coinbase/util.rb +16 -0
  13. data/lib/coinbase/wallet/adapters/em_http.rb +78 -0
  14. data/lib/coinbase/wallet/adapters/net_http.rb +68 -0
  15. data/lib/coinbase/wallet/api_client.rb +755 -0
  16. data/lib/coinbase/wallet/api_errors.rb +120 -0
  17. data/lib/coinbase/wallet/api_response.rb +41 -0
  18. data/lib/coinbase/wallet/ca-coinbase.crt +101 -0
  19. data/lib/coinbase/wallet/client.rb +101 -0
  20. data/lib/coinbase/wallet/coinbase-callback.pub +14 -0
  21. data/lib/coinbase/wallet/models/account.rb +193 -0
  22. data/lib/coinbase/wallet/models/address.rb +12 -0
  23. data/lib/coinbase/wallet/models/api_object.rb +46 -0
  24. data/lib/coinbase/wallet/models/checkout.rb +19 -0
  25. data/lib/coinbase/wallet/models/order.rb +12 -0
  26. data/lib/coinbase/wallet/models/transaction.rb +21 -0
  27. data/lib/coinbase/wallet/models/transfer.rb +13 -0
  28. data/lib/coinbase/wallet/models/user.rb +15 -0
  29. data/lib/coinbase/wallet/version.rb +5 -0
  30. data/lib/coinbase/wallet.rb +24 -156
  31. data/spec/account_spec.rb +199 -0
  32. data/spec/callback_signature_verification_spec.rb +16 -0
  33. data/spec/clients/client_spec.rb +34 -0
  34. data/spec/clients/oauth_client_spec.rb +56 -0
  35. data/spec/endpoints_spec.rb +352 -0
  36. data/spec/error_spec.rb +137 -0
  37. data/spec/models/address_spec.rb +26 -0
  38. data/spec/models/api_object_spec.rb +70 -0
  39. data/spec/models/checkout_spec.rb +52 -0
  40. data/spec/models/current_user_spec.rb +29 -0
  41. data/spec/models/order_spec.rb +52 -0
  42. data/spec/models/request_spec.rb +47 -0
  43. data/spec/models/transfer_spec.rb +64 -0
  44. data/spec/models/user_spec.rb +24 -0
  45. data/spec/spec_helper.rb +13 -0
  46. metadata +67 -112
  47. data/lib/coinbase/address.rb +0 -127
  48. data/lib/coinbase/asset.rb +0 -20
  49. data/lib/coinbase/balance_map.rb +0 -48
  50. data/lib/coinbase/constants.rb +0 -38
  51. data/lib/coinbase/network.rb +0 -55
  52. data/lib/coinbase/transfer.rb +0 -153
  53. data/lib/coinbase.rb +0 -18
@@ -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 |resp|
6
+ update(resp.data)
7
+ yield(resp.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(resp.data)
10
+ yield(resp.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.2.3"
4
+ end
5
+ end
@@ -1,160 +1,28 @@
1
- # frozen_string_literal: true
2
-
3
- require 'jimson'
4
- require 'money-tree'
5
- require 'securerandom'
1
+ require "coinbase/wallet/version"
2
+
3
+ require "base64"
4
+ require "bigdecimal"
5
+ require "json"
6
+ require "uri"
7
+ require "net/https"
8
+
9
+ require "coinbase/wallet/api_errors"
10
+ require "coinbase/wallet/api_response"
11
+ require "coinbase/wallet/api_client"
12
+ require "coinbase/wallet/adapters/net_http.rb"
13
+ require "coinbase/wallet/adapters/em_http.rb"
14
+ require "coinbase/wallet/models/api_object"
15
+ require "coinbase/wallet/models/account"
16
+ require "coinbase/wallet/models/address"
17
+ require "coinbase/wallet/models/user"
18
+ require "coinbase/wallet/models/transaction"
19
+ require "coinbase/wallet/models/transfer"
20
+ require "coinbase/wallet/models/order"
21
+ require "coinbase/wallet/models/checkout"
22
+ require "coinbase/wallet/client"
23
+ require "coinbase/util"
6
24
 
7
25
  module Coinbase
8
- # A representation of a Wallet. Wallets come with a single default Address, but can expand to have a set of Addresses,
9
- # each of which can hold a balance of one or more Assets. Wallets can create new Addresses, list their addresses,
10
- # list their balances, and transfer Assets to other Addresses.
11
- class Wallet
12
- attr_reader :wallet_id, :network_id
13
-
14
- # Returns a new Wallet object.
15
- # @param seed [Integer] (Optional) The seed to use for the Wallet. Expects a 32-byte hexadecimal. If not provided,
16
- # a new seed will be generated.
17
- # @param address_count [Integer] (Optional) The number of addresses to generate for the Wallet. If not provided,
18
- # a single address will be generated.
19
- # @param client [Jimson::Client] (Optional) The JSON RPC client to use for interacting with the Network
20
- def initialize(seed: nil, address_count: 1, client: Jimson::Client.new(ENV.fetch('BASE_SEPOLIA_RPC_URL', nil)))
21
- raise ArgumentError, 'Seed must be 32 bytes' if !seed.nil? && seed.length != 64
22
- raise ArgumentError, 'Address count must be positive' if address_count < 1
23
-
24
- @master = seed.nil? ? MoneyTree::Master.new : MoneyTree::Master.new(seed_hex: seed)
25
-
26
- @wallet_id = SecureRandom.uuid
27
- # TODO: Make Network an argument to the constructor.
28
- @network_id = :base_sepolia
29
- @addresses = []
30
-
31
- # TODO: Adjust derivation path prefix based on network protocol.
32
- @address_path_prefix = "m/44'/60'/0'/0"
33
- @address_index = 0
34
-
35
- @client = client
36
-
37
- address_count.times { create_address }
38
- end
39
-
40
- # Creates a new Address in the Wallet.
41
- # @return [Address] The new Address
42
- def create_address
43
- # TODO: Register with server.
44
- path = "#{@address_path_prefix}/#{@address_index}"
45
- private_key = @master.node_for_path(path).private_key.to_hex
46
- key = Eth::Key.new(priv: private_key)
47
- address = Address.new(@network_id, key.address.address, @wallet_id, key, client: @client)
48
- @addresses << address
49
- @address_index += 1
50
- address
51
- end
52
-
53
- # Returns the default address of the Wallet.
54
- # @return [Address] The default address
55
- def default_address
56
- @addresses.first
57
- end
58
-
59
- # Returns the Address with the given ID.
60
- # @param address_id [String] The ID of the Address to retrieve
61
- # @return [Address] The Address
62
- def get_address(address_id)
63
- @addresses.find { |address| address.address_id == address_id }
64
- end
65
-
66
- # Returns the list of Addresses in the Wallet.
67
- # @return [Array<Address>] The list of Addresses
68
- def list_addresses
69
- # TODO: Register with server.
70
- @addresses
71
- end
72
-
73
- # Returns the list of balances of this Wallet. Balances are aggregated across all Addresses in the Wallet.
74
- # @return [BalanceMap] The list of balances. The key is the Asset ID, and the value is the balance.
75
- def list_balances
76
- balance_map = BalanceMap.new
77
-
78
- @addresses.each do |address|
79
- address.list_balances.each do |asset_id, balance|
80
- balance_map[asset_id] ||= BigDecimal(0)
81
- current_balance = balance_map[asset_id]
82
- new_balance = balance + current_balance
83
- balance_map[asset_id] = new_balance
84
- end
85
- end
86
-
87
- balance_map
88
- end
89
-
90
- # Returns the balance of the provided Asset. Balances are aggregated across all Addresses in the Wallet.
91
- # @param asset_id [Symbol] The ID of the Asset to retrieve the balance for
92
- # @return [BigDecimal] The balance of the Asset
93
- def get_balance(asset_id)
94
- normalized_asset_id = if %i[wei gwei].include?(asset_id)
95
- :eth
96
- else
97
- asset_id
98
- end
99
-
100
- eth_balance = list_balances[normalized_asset_id] || BigDecimal(0)
101
-
102
- case asset_id
103
- when :eth
104
- eth_balance
105
- when :gwei
106
- eth_balance * Coinbase::GWEI_PER_ETHER
107
- when :wei
108
- eth_balance * Coinbase::WEI_PER_ETHER
109
- else
110
- BigDecimal(0)
111
- end
112
- end
113
-
114
- # Transfers the given amount of the given Asset to the given address. Only same-Network Transfers are supported.
115
- # Currently only the default_address is used to source the Transfer.
116
- # @param amount [Integer, Float, BigDecimal] The amount of the Asset to send
117
- # @param asset_id [Symbol] The ID of the Asset to send
118
- # @param destination [Wallet | Address | String] The destination of the transfer. If a Wallet, sends to the Wallet's
119
- # default address. If a String, interprets it as the address ID.
120
- # @return [Transfer] The hash of the Transfer transaction.
121
- def transfer(amount, asset_id, destination)
122
- if destination.is_a?(Wallet)
123
- raise ArgumentError, 'Transfer must be on the same Network' if destination.network_id != @network_id
124
-
125
- destination = destination.default_address.address_id
126
- elsif destination.is_a?(Address)
127
- raise ArgumentError, 'Transfer must be on the same Network' if destination.network_id != @network_id
128
-
129
- destination = destination.address_id
130
- end
131
-
132
- default_address.transfer(amount, asset_id, destination)
133
- end
134
-
135
- # Exports the Wallet's data to a WalletData object.
136
- # @return [WalletData] The Wallet data
137
- def export
138
- WalletData.new(@master.seed_hex, @addresses.length)
139
- end
140
-
141
- # The data required to recreate a Wallet.
142
- class WalletData
143
- attr_reader :seed, :address_count
144
-
145
- # Returns a new WalletData object.
146
- # @param seed [String] The seed of the Wallet
147
- # @param address_count [Integer] The number of addresses in the Wallet
148
- def initialize(seed, address_count)
149
- @seed = seed
150
- @address_count = address_count
151
- end
152
- end
153
-
154
- # Returns the data required to recreate the Wallet.
155
- # @return [WalletData] The Wallet data
156
- def to_data
157
- WalletData.new(@master.seed_hex, @addresses.length)
158
- end
26
+ module Wallet
159
27
  end
160
28
  end
@@ -0,0 +1,199 @@
1
+ require 'spec_helper'
2
+
3
+ describe Coinbase::Wallet::Account do
4
+ before :all do
5
+ @object_data = {
6
+ 'id' => '2bbf394c-193b-5b2a-9155-3b4732659ede',
7
+ 'name' => 'My Wallet',
8
+ 'primary' => true,
9
+ 'type' => 'wallet',
10
+ 'currency' => 'BTC',
11
+ 'balance' => {
12
+ 'amount' => '39.59000000',
13
+ 'currency' => 'BTC'
14
+ },
15
+ 'native_balance' => {
16
+ 'amount' => '395.90',
17
+ 'currency' => 'USD'
18
+ },
19
+ 'created_at' => '2015-01-31T20:49:02Z',
20
+ 'updated_at' => '2015-01-31T20:49:02Z',
21
+ 'resource' => 'account',
22
+ 'resource_path' => '/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede'
23
+ }
24
+
25
+ @client = Coinbase::Wallet::Client.new(api_key: 'api_key', api_secret: 'api_secret')
26
+ @object = Coinbase::Wallet::Account.new(@client, @object_data)
27
+ end
28
+
29
+ it "updates itself" do
30
+ ret = @object_data.clone
31
+ ret['name'] = 'new name'
32
+ stub_request(:put, /#{@object.resource_path}/)
33
+ .to_return(body: { data: ret }.to_json)
34
+ @object.update!(name: "new name")
35
+ expect(@object.name).to eq "new name"
36
+ end
37
+
38
+ it "makes itself primary" do
39
+ stub_request(:post, /#{@object.resource_path}\/primary/)
40
+ .to_return(body: { data: mock_item }.to_json)
41
+ expect(@object.make_primary!).to eq mock_item
42
+ end
43
+
44
+ it "deletes itself" do
45
+ stub_request(:delete, /#{@object.resource_path}/)
46
+ .to_return(body: { data: mock_item }.to_json)
47
+ expect(@object.delete!).to eq mock_item
48
+ end
49
+
50
+ it "gets addresses" do
51
+ stub_request(:get, /#{@object.resource_path}\/addresses/)
52
+ .to_return(body: { data: mock_collection }.to_json)
53
+ expect(@object.addresses).to eq mock_collection
54
+ end
55
+
56
+ it "gets address" do
57
+ stub_request(:get, /#{@object.resource_path}\/addresses\/test/)
58
+ .to_return(body: { data: mock_item }.to_json)
59
+ expect(@object.address("test")).to eq mock_item
60
+ end
61
+
62
+ it "gets address transactions" do
63
+ stub_request(:get, /#{@object.resource_path}\/addresses\/test\/transactions/)
64
+ .to_return(body: { data: mock_collection }.to_json)
65
+ expect(@object.address_transactions("test")).to eq mock_collection
66
+ end
67
+
68
+ it "creates address" do
69
+ stub_request(:post, /#{@object.resource_path}\/addresses/)
70
+ .to_return(body: { data: mock_item }.to_json)
71
+ expect(@object.create_address).to eq mock_item
72
+ end
73
+
74
+ it "gets transactions" do
75
+ stub_request(:get, /#{@object.resource_path}\/transactions/)
76
+ .to_return(body: { data: mock_collection }.to_json)
77
+ expect(@object.transactions).to eq mock_collection
78
+ end
79
+
80
+ it "gets transaction" do
81
+ stub_request(:get, /#{@object.resource_path}\/transactions\/test/)
82
+ .to_return(body: { data: mock_item }.to_json)
83
+ expect(@object.transaction('test')).to eq mock_item
84
+ end
85
+
86
+ it "sends money" do
87
+ stub_request(:post, /#{@object.resource_path}\/transactions/)
88
+ .to_return(body: { data: mock_item }.to_json)
89
+ expect(@object.send(amount: 10, to: 'example@coinbase.com')).to eq mock_item
90
+ end
91
+
92
+ it "transfers money" do
93
+ stub_request(:post, /#{@object.resource_path}\/transactions/)
94
+ .to_return(body: { data: mock_item }.to_json)
95
+ expect(@object.transfer(amount: 10, to: 'example@coinbase.com')).to eq mock_item
96
+ end
97
+
98
+ it "requests money" do
99
+ stub_request(:post, /#{@object.resource_path}\/transactions/)
100
+ .to_return(body: { data: mock_item }.to_json)
101
+ expect(@object.request(amount: 10, currency: "BTC", to: 'example@coinbase.com')).to eq mock_item
102
+ end
103
+
104
+ it "gets buys" do
105
+ stub_request(:get, /#{@object.resource_path}\/buys/)
106
+ .to_return(body: { data: mock_collection }.to_json)
107
+ expect(@object.list_buys).to eq mock_collection
108
+ end
109
+
110
+ it "gets buy" do
111
+ stub_request(:get, /#{@object.resource_path}\/buys\/test/)
112
+ .to_return(body: { data: mock_item }.to_json)
113
+ expect(@object.list_buy('test')).to eq mock_item
114
+ end
115
+
116
+ it "buys bitcoin" do
117
+ stub_request(:post, /#{@object.resource_path}\/buys/)
118
+ .to_return(body: { data: mock_item }.to_json)
119
+ expect(@object.buy(amount: 10, currency: 'BTC')).to eq mock_item
120
+ end
121
+
122
+ it "commits buy" do
123
+ stub_request(:post, /#{@object.resource_path}\/buys\/test\/commit/)
124
+ .to_return(body: { data: mock_item }.to_json)
125
+ expect(@object.commit_buy("test")).to eq mock_item
126
+ end
127
+
128
+ it "gets sells" do
129
+ stub_request(:get, /#{@object.resource_path}\/sells/)
130
+ .to_return(body: { data: mock_collection }.to_json)
131
+ expect(@object.list_sells).to eq mock_collection
132
+ end
133
+
134
+ it "gets sell" do
135
+ stub_request(:get, /#{@object.resource_path}\/sells\/test/)
136
+ .to_return(body: { data: mock_item }.to_json)
137
+ expect(@object.list_sell('test')).to eq mock_item
138
+ end
139
+
140
+ it "sells bitcoin" do
141
+ stub_request(:post, /#{@object.resource_path}\/sells/)
142
+ .to_return(body: { data: mock_item }.to_json)
143
+ expect(@object.sell(amount: 10, currency: 'BTC')).to eq mock_item
144
+ end
145
+
146
+ it "commits sell" do
147
+ stub_request(:post, /#{@object.resource_path}\/sells\/test\/commit/)
148
+ .to_return(body: { data: mock_item }.to_json)
149
+ expect(@object.commit_sell("test")).to eq mock_item
150
+ end
151
+
152
+ it "gets deposits" do
153
+ stub_request(:get, /#{@object.resource_path}\/deposits/)
154
+ .to_return(body: { data: mock_collection }.to_json)
155
+ expect(@object.list_deposits).to eq mock_collection
156
+ end
157
+
158
+ it "gets deposit" do
159
+ stub_request(:get, /#{@object.resource_path}\/deposits\/test/)
160
+ .to_return(body: { data: mock_item }.to_json)
161
+ expect(@object.list_deposit('test')).to eq mock_item
162
+ end
163
+
164
+ it "deposits bitcoin" do
165
+ stub_request(:post, /#{@object.resource_path}\/deposits/)
166
+ .to_return(body: { data: mock_item }.to_json)
167
+ expect(@object.deposit(amount: 10, currency: 'BTC')).to eq mock_item
168
+ end
169
+
170
+ it "commits deposit" do
171
+ stub_request(:post, /#{@object.resource_path}\/deposits\/test\/commit/)
172
+ .to_return(body: { data: mock_item }.to_json)
173
+ expect(@object.commit_deposit("test")).to eq mock_item
174
+ end
175
+
176
+ it "gets withdrawals" do
177
+ stub_request(:get, /#{@object.resource_path}\/withdrawals/)
178
+ .to_return(body: { data: mock_collection }.to_json)
179
+ expect(@object.list_withdrawals).to eq mock_collection
180
+ end
181
+
182
+ it "gets withdrawal" do
183
+ stub_request(:get, /#{@object.resource_path}\/withdrawals\/test/)
184
+ .to_return(body: { data: mock_item }.to_json)
185
+ expect(@object.list_withdrawal('test')).to eq mock_item
186
+ end
187
+
188
+ it "withdrawals bitcoin" do
189
+ stub_request(:post, /#{@object.resource_path}\/withdrawals/)
190
+ .to_return(body: { data: mock_item }.to_json)
191
+ expect(@object.withdraw(amount: 10, currency: 'BTC')).to eq mock_item
192
+ end
193
+
194
+ it "commits withdrawal" do
195
+ stub_request(:post, /#{@object.resource_path}\/withdrawals\/test\/commit/)
196
+ .to_return(body: { data: mock_item }.to_json)
197
+ expect(@object.commit_withdrawal("test")).to eq mock_item
198
+ end
199
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Coinbase::Wallet::APIClient do
4
+ let(:client) { Coinbase::Wallet::Client.new(api_key: 'api_key', api_secret: 'api_secret') }
5
+ let(:signature) { "6yQRl17CNj5YSHSpF+tLjb0vVsNVEv021Tyy1bTVEQ69SWlmhwmJYuMc7jiDyeW9TLy4vRqSh4g4YEyN8eoQIM57pMoNw6Lw6Oudubqwp+E3cKtLFxW0l18db3Z/vhxn5BScAutHWwT/XrmkCNaHyCsvOOGMekwrNO7mxX9QIx21FBaEejJeviSYrF8bG6MbmFEs2VGKSybf9YrElR8BxxNe/uNfCXN3P5tO8MgR5wlL3Kr4yq8e6i4WWJgD08IVTnrSnoZR6v8JkPA+fn7I0M6cy0Xzw3BRMJAvdQB97wkobu97gFqJFKsOH2u/JR1S/UNP26vL0mzuAVuKAUwlRn0SUhWEAgcM3X0UCtWLYfCIb5QqrSHwlp7lwOkVnFt329Mrpjy+jAfYYSRqzIsw4ZsRRVauy/v3CvmjPI9sUKiJ5l1FSgkpK2lkjhFgKB3WaYZWy9ZfIAI9bDyG8vSTT7IDurlUhyTweDqVNlYUsO6jaUa4KmSpg1o9eIeHxm0XBQ2c0Lv/T39KNc/VOAi1LBfPiQYMXD1e/8VuPPBTDGgzOMD3i334ppSr36+8YtApAn3D36Hr9jqAfFrugM7uPecjCGuleWsHFyNnJErT0/amIt24Nh1GoiESEq42o7Co4wZieKZ+/yeAlIUErJzK41ACVGmTnGoDUwEBXxADOdA=" }
6
+
7
+ it 'correctly verifies valid callback' do
8
+ body = %[{"order":{"id":null,"created_at":null,"status":"completed","event":null,"total_btc":{"cents":100000000,"currency_iso":"BTC"},"total_native":{"cents":1000,"currency_iso":"USD"},"total_payout":{"cents":1000,"currency_iso":"USD"},"custom":"123456789","receive_address":"mzVoQenSY6RTBgBUcpSBTBAvUMNgGWxgJn","button":{"type":"buy_now","name":"Test Item","description":null,"id":null},"transaction":{"id":"53bdfe4d091c0d74a7000003","hash":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","confirmations":0}}}]
9
+ expect(client.verify_callback(body, signature)).to be true
10
+ end
11
+
12
+ it 'correctly rejects callback that has been tampered with' do
13
+ body = %[{"order":{"id":null,"created_at":null,"status":"mispaid","event":null,"total_btc":{"cents":100000000,"currency_iso":"BTC"},"total_native":{"cents":1000,"currency_iso":"USD"},"total_payout":{"cents":1000,"currency_iso":"USD"},"custom":"123456789","receive_address":"mzVoQenSY6RTBgBUcpSBTBAvUMNgGWxgJn","button":{"type":"buy_now","name":"Test Item","description":null,"id":null},"transaction":{"id":"53bdfe4d091c0d74a7000003","hash":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","confirmations":0}}}]
14
+ expect(client.verify_callback(body, signature)).to be false
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'timecop'
3
+
4
+ describe Coinbase::Wallet::Client do
5
+ let(:client) { Coinbase::Wallet::Client.new(api_key: 'api_key', api_secret: 'api_secret') }
6
+
7
+ it 'supplies correct headers' do
8
+ time = Time.utc(2015, 7, 1, 0, 0, 0)
9
+ timestamp = 1435708800
10
+
11
+ stub_request(:get, 'https://api.coinbase.com/v2/user')
12
+ .with('headers' => {
13
+ 'CB-ACCESS-KEY' => 'api_key',
14
+ 'CB-ACCESS-SIGN' => '9a413abc5a25a949932cd2b9963906d543f2df935c3a56159e24edb2095d78ee',
15
+ 'CB-ACCESS-TIMESTAMP' => timestamp.to_s,
16
+ 'CB-VERSION' => Coinbase::Wallet::API_VERSION,
17
+ })
18
+ .to_return(body: { data: mock_item }.to_json)
19
+
20
+ stub_request(:post, 'https://api.coinbase.com/v2/accounts')
21
+ .with('headers' => {
22
+ 'CB-ACCESS-KEY' => 'api_key',
23
+ 'CB-ACCESS-SIGN' => '3d4a73da32fc7fa55862865efd60c0cc994e5616d138a3c3b605a3ed504b235c',
24
+ 'CB-ACCESS-TIMESTAMP' => timestamp,
25
+ 'CB-VERSION' => Coinbase::Wallet::API_VERSION,
26
+ })
27
+ .to_return(body: { data: mock_item }.to_json)
28
+
29
+ Timecop.freeze(time) do
30
+ expect { client.current_user }.to_not raise_error
31
+ expect { client.create_account(name: "new wallet") }.to_not raise_error
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Coinbase::Wallet::OAuthClient do
4
+ let(:client) { Coinbase::Wallet::OAuthClient.new(access_token: 'access_token', refresh_token: 'refresh_token') }
5
+
6
+ it 'handles init with access token and refresh token' do
7
+ stub_request(:get, /.*/)
8
+ .with('headers' => {
9
+ 'Authorization' => 'Bearer access_token',
10
+ 'CB-VERSION' => Coinbase::Wallet::API_VERSION,
11
+ })
12
+ .to_return(body: { data: { id: 'id', resource: 'user',
13
+ resource_path: '/v2/user' } }.to_json,
14
+ status: 200)
15
+
16
+ expect { client.current_user }.to_not raise_error
17
+ end
18
+
19
+ it 'handles init with access token' do
20
+ stub_request(:get, /.*/)
21
+ .to_return(body: { data: { id: 'id', resource: 'user',
22
+ resource_path: '/v2/user' } }.to_json,
23
+ status: 200)
24
+
25
+ client = Coinbase::Wallet::OAuthClient.new(access_token: 'access_token')
26
+ expect { client.current_user }.to_not raise_error
27
+ end
28
+
29
+ it '#refresh!' do
30
+ body = {
31
+ 'access_token' => 'new_access_token',
32
+ 'token_type'=> 'bearer',
33
+ 'expires_in'=> 7200,
34
+ 'refresh_token'=> 'new_refresh_token',
35
+ 'scope'=> 'wallet:user:read'
36
+ }
37
+
38
+ stub_request(:post, 'https://api.coinbase.com/oauth/token')
39
+ .with(body: {
40
+ grant_type: 'refresh_token',
41
+ refresh_token: 'refresh_token'
42
+ })
43
+ .to_return(body: body.to_json, status: 200)
44
+
45
+ expect(client.refresh!).to eq body
46
+ expect(client.access_token).to eq 'new_access_token'
47
+ expect(client.refresh_token).to eq 'new_refresh_token'
48
+ end
49
+
50
+ it '#revoke!' do
51
+ stub_request(:post, "https://api.coinbase.com/oauth/revoke")
52
+ .with(body: { token: 'access_token' })
53
+ .to_return(body: mock_item.to_json)
54
+ expect(client.revoke!).to eq mock_item
55
+ end
56
+ end