coinbase-sdk 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/coinbase/address.rb +113 -40
  3. data/lib/coinbase/asset.rb +18 -4
  4. data/lib/coinbase/authenticator.rb +1 -1
  5. data/lib/coinbase/client/api/server_signers_api.rb +429 -0
  6. data/lib/coinbase/client/api/stake_api.rb +86 -0
  7. data/lib/coinbase/client/api/trades_api.rb +342 -0
  8. data/lib/coinbase/client/models/broadcast_trade_request.rb +232 -0
  9. data/lib/coinbase/client/models/build_staking_operation_request.rb +291 -0
  10. data/lib/coinbase/client/models/create_address_request.rb +0 -14
  11. data/lib/coinbase/client/models/create_server_signer_request.rb +239 -0
  12. data/lib/coinbase/client/models/create_trade_request.rb +256 -0
  13. data/lib/coinbase/client/models/create_wallet_request.rb +1 -1
  14. data/lib/coinbase/client/models/create_wallet_request_wallet.rb +233 -0
  15. data/lib/coinbase/client/models/feature.rb +42 -0
  16. data/lib/coinbase/client/models/request_faucet_funds200_response.rb +222 -0
  17. data/lib/coinbase/client/models/seed_creation_event.rb +240 -0
  18. data/lib/coinbase/client/models/seed_creation_event_result.rb +274 -0
  19. data/lib/coinbase/client/models/server_signer.rb +235 -0
  20. data/lib/coinbase/client/models/server_signer_event.rb +239 -0
  21. data/lib/coinbase/client/models/server_signer_event_event.rb +105 -0
  22. data/lib/coinbase/client/models/server_signer_event_list.rb +275 -0
  23. data/lib/coinbase/client/models/server_signer_list.rb +275 -0
  24. data/lib/coinbase/client/models/signature_creation_event.rb +363 -0
  25. data/lib/coinbase/client/models/signature_creation_event_result.rb +329 -0
  26. data/lib/coinbase/client/models/staking_operation.rb +222 -0
  27. data/lib/coinbase/client/models/trade.rb +365 -0
  28. data/lib/coinbase/client/models/trade_list.rb +275 -0
  29. data/lib/coinbase/client/models/transaction.rb +338 -0
  30. data/lib/coinbase/client/models/transaction_type.rb +39 -0
  31. data/lib/coinbase/client/models/transfer.rb +33 -1
  32. data/lib/coinbase/client/models/wallet.rb +74 -4
  33. data/lib/coinbase/client.rb +23 -0
  34. data/lib/coinbase/errors.rb +3 -0
  35. data/lib/coinbase/server_signer.rb +57 -0
  36. data/lib/coinbase/trade.rb +147 -0
  37. data/lib/coinbase/transaction.rb +125 -0
  38. data/lib/coinbase/transfer.rb +38 -71
  39. data/lib/coinbase/user.rb +14 -89
  40. data/lib/coinbase/wallet.rb +188 -27
  41. data/lib/coinbase.rb +19 -4
  42. metadata +43 -2
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'client'
4
+
5
+ module Coinbase
6
+ # A representation of a Server-Signer. Server-Signers are assigned to sign transactions for a Wallet.
7
+ class ServerSigner
8
+ # Returns a new Server-Signer object. Do not use this method directly. Instead, use ServerSigner.default.
9
+ def initialize(model)
10
+ @model = model
11
+ end
12
+
13
+ class << self
14
+ # Returns the default ServerSigner for the CDP Project.
15
+ # @return [Coinbase::ServerSigner] the default Server-Signer
16
+ def default
17
+ response = Coinbase.call_api do
18
+ server_signers_api.list_server_signers
19
+ end
20
+
21
+ raise 'No Server-Signer is associated with the project' if response.data.empty?
22
+
23
+ new(response.data.first)
24
+ end
25
+
26
+ private
27
+
28
+ def server_signers_api
29
+ Coinbase::Client::ServerSignersApi.new(Coinbase.configuration.api_client)
30
+ end
31
+ end
32
+
33
+ # Returns the Server-Signer ID.
34
+ # @return [String] the Server-Signer ID
35
+ def id
36
+ @model.server_signer_id
37
+ end
38
+
39
+ # Returns the IDs of the Wallet's the Server-Signer can sign for.
40
+ # @return [Array<String>] the wallet IDs
41
+ def wallets
42
+ @model.wallets
43
+ end
44
+
45
+ # Returns a string representation of the Server-Signer.
46
+ # @return [String] a string representation of the Server-Signer
47
+ def to_s
48
+ "Coinbase::ServerSigner{server_signer_id: '#{id}', wallets: [#{wallets.join(', ')}]}"
49
+ end
50
+
51
+ # Same as to_s.
52
+ # @return [String] a string representation of the Server-Signer
53
+ def inspect
54
+ to_s
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require 'bigdecimal'
5
+ require 'eth'
6
+
7
+ module Coinbase
8
+ # A representation of a Trade, which trades an amount of an Asset to another Asset on a Network.
9
+ # The fee is assumed to be paid in the native Asset of the Network.
10
+ # Trades should be created through Wallet#trade or # Address#trade.
11
+ class Trade
12
+ # Returns a new Trade object. Do not use this method directly. Instead, use Wallet#trade or
13
+ # Address#trade.
14
+ # @param model [Coinbase::Client::Trade] The underlying Trade object
15
+ def initialize(model)
16
+ raise unless model.is_a?(Coinbase::Client::Trade)
17
+
18
+ @model = model
19
+ end
20
+
21
+ # Returns the Trade ID.
22
+ # @return [String] The Trade ID
23
+ def id
24
+ @model.trade_id
25
+ end
26
+
27
+ # Returns the Network ID of the Trade.
28
+ # @return [Symbol] The Network ID
29
+ def network_id
30
+ Coinbase.to_sym(@model.network_id)
31
+ end
32
+
33
+ # Returns the Wallet ID of the Trade.
34
+ # @return [String] The Wallet ID
35
+ def wallet_id
36
+ @model.wallet_id
37
+ end
38
+
39
+ # Returns the Address ID of the Trade.
40
+ # @return [String] The Address ID
41
+ def address_id
42
+ @model.address_id
43
+ end
44
+
45
+ # Returns the From Asset ID of the Trade.
46
+ # @return [Symbol] The From Asset ID
47
+ def from_asset_id
48
+ @model.from_asset.asset_id.to_sym
49
+ end
50
+
51
+ # Returns the amount of the from asset for the Trade.
52
+ # @return [BigDecimal] The amount of the from asset
53
+ def from_amount
54
+ BigDecimal(@model.from_amount) / BigDecimal(10).power(@model.from_asset.decimals)
55
+ end
56
+
57
+ # Returns the To Asset ID of the Trade.
58
+ # @return [Symbol] The To Asset ID
59
+ def to_asset_id
60
+ @model.to_asset.asset_id.to_sym
61
+ end
62
+
63
+ # Returns the amount of the to asset for the Trade.
64
+ # @return [BigDecimal] The amount of the to asset
65
+ def to_amount
66
+ BigDecimal(@model.to_amount) / BigDecimal(10).power(@model.to_asset.decimals)
67
+ end
68
+
69
+ # Returns the Trade transaction.
70
+ # @return [Coinbase::Transaction] The Trade transaction
71
+ def transaction
72
+ @transaction ||= Coinbase::Transaction.new(@model.transaction)
73
+ end
74
+
75
+ def approve_transaction
76
+ @approve_transaction ||= @model.approve_transaction ? Coinbase::Transaction.new(@model.approve_transaction) : nil
77
+ end
78
+
79
+ # Returns the status of the Trade.
80
+ # @return [Symbol] The status
81
+ def status
82
+ transaction.status
83
+ end
84
+
85
+ # Waits until the Trade is completed or failed by polling the Network at the given interval. Raises a
86
+ # Timeout::Error if the Trade takes longer than the given timeout.
87
+ # @param interval_seconds [Integer] The interval at which to poll the Network, in seconds
88
+ # @param timeout_seconds [Integer] The maximum amount of time to wait for the Trade to complete, in seconds
89
+ # @return [Trade] The completed Trade object
90
+ def wait!(interval_seconds = 0.2, timeout_seconds = 10)
91
+ start_time = Time.now
92
+
93
+ loop do
94
+ reload
95
+
96
+ # Wait for the trade transaction to be in a terminal state.
97
+ # The approve transaction is optional and must last first, so we don't need to wait for it.
98
+ # We may want to handle a situation where the approve transaction fails and the
99
+ # trade transaction does not ever get broadcast.
100
+ break if transaction.terminal_state?
101
+
102
+ raise Timeout::Error, 'Trade timed out' if Time.now - start_time > timeout_seconds
103
+
104
+ self.sleep interval_seconds
105
+ end
106
+
107
+ self
108
+ end
109
+
110
+ # Reloads the Trade model with the latest version from the server side.
111
+ # @return [Trade] The most recent version of Trade from the server.
112
+ def reload
113
+ @model = Coinbase.call_api do
114
+ trades_api.get_trade(wallet_id, address_id, id)
115
+ end
116
+
117
+ # Update the memoized transaction.
118
+ @transaction = Coinbase::Transaction.new(@model.transaction)
119
+
120
+ # Update the memoized approve transaction if it exists.
121
+ @approve_transaction = @model.approve_transaction ? Coinbase::Transaction.new(@model.approve_transaction) : nil
122
+
123
+ self
124
+ end
125
+
126
+ # Returns a String representation of the Trade.
127
+ # @return [String] a String representation of the Trade
128
+ def to_s
129
+ "Coinbase::Trade{transfer_id: '#{id}', network_id: '#{network_id}', " \
130
+ "address_id: '#{address_id}', from_asset_id: '#{from_asset_id}', " \
131
+ "to_asset_id: '#{to_asset_id}', from_amount: '#{from_amount}', " \
132
+ "to_amount: '#{to_amount}' status: '#{status}'}"
133
+ end
134
+
135
+ # Same as to_s.
136
+ # @return [String] a String representation of the Trade
137
+ def inspect
138
+ to_s
139
+ end
140
+
141
+ private
142
+
143
+ def trades_api
144
+ @trades_api ||= Coinbase::Client::TradesApi.new(Coinbase.configuration.api_client)
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require 'bigdecimal'
5
+ require 'eth'
6
+ require 'json'
7
+
8
+ module Coinbase
9
+ # A representation of an onchain Transaction.
10
+ # Transactions should be constructed via higher level abstractions like Trade or Transfer.
11
+ class Transaction
12
+ # A representation of a Transaction status.
13
+ module Status
14
+ # The Transaction is awaiting being broadcast to the Network.
15
+ # At this point, transaction hashes may not yet be assigned.
16
+ PENDING = 'pending'
17
+
18
+ # The Transaction has been broadcast to the Network.
19
+ # At this point, at least the transaction hash should be assigned.
20
+ BROADCAST = 'broadcast'
21
+
22
+ # The Transaction is complete and has confirmed on the Network.
23
+ COMPLETE = 'complete'
24
+
25
+ # The Transaction has failed for some reason.
26
+ FAILED = 'failed'
27
+
28
+ # The states that are considered terminal on-chain.
29
+ TERMINAL_STATES = [COMPLETE, FAILED].freeze
30
+ end
31
+
32
+ # Returns a new Transaction object. Do not use this method directly.
33
+ # @param model [Coinbase::Client::Transaction] The underlying Transaction object
34
+ def initialize(model)
35
+ raise unless model.is_a?(Coinbase::Client::Transaction)
36
+
37
+ @model = model
38
+ end
39
+
40
+ # Returns the Unsigned Payload of the Transaction.
41
+ # @return [String] The Unsigned Payload
42
+ def unsigned_payload
43
+ @model.unsigned_payload
44
+ end
45
+
46
+ # Returns the Signed Payload of the Transaction.
47
+ # @return [String] The Signed Payload
48
+ def signed_payload
49
+ @model.signed_payload
50
+ end
51
+
52
+ # Returns the Transaction Hash of the Transaction.
53
+ # @return [String] The Transaction Hash
54
+ def transaction_hash
55
+ @model.transaction_hash
56
+ end
57
+
58
+ # Returns the status of the Transaction.
59
+ # @return [Symbol] The status
60
+ def status
61
+ @model.status
62
+ end
63
+
64
+ # Returns the from address for the Transaction.
65
+ # @return [String] The from address
66
+ def from_address_id
67
+ @model.from_address_id
68
+ end
69
+
70
+ # Returns whether the Transaction is in a terminal state.
71
+ # @return [Boolean] Whether the Transaction is in a terminal state
72
+ def terminal_state?
73
+ Status::TERMINAL_STATES.include?(status)
74
+ end
75
+
76
+ # Returns the link to the transaction on the blockchain explorer.
77
+ # @return [String] The link to the transaction on the blockchain explorer
78
+ def transaction_link
79
+ @model.transaction_link
80
+ end
81
+
82
+ # Returns the underlying raw transaction.
83
+ # @return [Eth::Tx::Eip1559] The raw transaction
84
+ def raw
85
+ return @raw unless @raw.nil?
86
+
87
+ raw_payload = [unsigned_payload].pack('H*')
88
+ parsed_payload = JSON.parse(raw_payload)
89
+
90
+ params = {
91
+ chain_id: parsed_payload['chainId'].to_i(16),
92
+ nonce: parsed_payload['nonce'].to_i(16),
93
+ priority_fee: parsed_payload['maxPriorityFeePerGas'].to_i(16),
94
+ max_gas_fee: parsed_payload['maxFeePerGas'].to_i(16),
95
+ gas_limit: parsed_payload['gas'].to_i(16), # TODO: Handle multiple currencies.
96
+ from: Eth::Address.new(from_address_id),
97
+ to: Eth::Address.new(parsed_payload['to']),
98
+ value: parsed_payload['value'].to_i(16),
99
+ data: parsed_payload['input'] || ''
100
+ }
101
+
102
+ @raw = Eth::Tx::Eip1559.new(Eth::Tx.validate_eip1559_params(params))
103
+ end
104
+
105
+ # Signs the Transaction with the provided key and returns the hex signing payload.
106
+ # @return [String] The hex-encoded signed payload
107
+ def sign(key)
108
+ raw.sign(key)
109
+
110
+ raw.hex
111
+ end
112
+
113
+ # Returns a String representation of the Transaction.
114
+ # @return [String] a String representation of the Transaction
115
+ def to_s
116
+ "Coinbase::Transaction{transaction_hash: '#{transaction_hash}', status: '#{status}'}"
117
+ end
118
+
119
+ # Same as to_s.
120
+ # @return [String] a String representation of the Transaction
121
+ def inspect
122
+ to_s
123
+ end
124
+ end
125
+ end
@@ -11,27 +11,12 @@ module Coinbase
11
11
  # in the native Asset of the Network. Transfers should be created through Wallet#transfer or
12
12
  # Address#transfer.
13
13
  class Transfer
14
- # A representation of a Transfer status.
15
- module Status
16
- # The Transfer is awaiting being broadcast to the Network. At this point, transaction
17
- # hashes may not yet be assigned.
18
- PENDING = :pending
19
-
20
- # The Transfer has been broadcast to the Network. At this point, at least the transaction hash
21
- # should be assigned.
22
- BROADCAST = :broadcast
23
-
24
- # The Transfer is complete, and has confirmed on the Network.
25
- COMPLETE = :complete
26
-
27
- # The Transfer has failed for some reason.
28
- FAILED = :failed
29
- end
30
-
31
14
  # Returns a new Transfer object. Do not use this method directly. Instead, use Wallet#transfer or
32
15
  # Address#transfer.
33
16
  # @param model [Coinbase::Client::Transfer] The underlying Transfer object
34
17
  def initialize(model)
18
+ raise unless model.is_a?(Coinbase::Client::Transfer)
19
+
35
20
  @model = model
36
21
  end
37
22
 
@@ -65,6 +50,10 @@ module Coinbase
65
50
  @model.destination
66
51
  end
67
52
 
53
+ def asset
54
+ @asset ||= Coinbase::Asset.from_model(@model.asset)
55
+ end
56
+
68
57
  # Returns the Asset ID of the Transfer.
69
58
  # @return [Symbol] The Asset ID
70
59
  def asset_id
@@ -74,12 +63,7 @@ module Coinbase
74
63
  # Returns the amount of the asset for the Transfer.
75
64
  # @return [BigDecimal] The amount of the asset
76
65
  def amount
77
- case asset_id
78
- when :eth
79
- BigDecimal(@model.amount) / BigDecimal(Coinbase::WEI_PER_ETHER.to_s)
80
- else
81
- BigDecimal(@model.amount)
82
- end
66
+ BigDecimal(@model.amount) / BigDecimal(10).power(@model.asset.decimals)
83
67
  end
84
68
 
85
69
  # Returns the link to the transaction on the blockchain explorer.
@@ -101,60 +85,35 @@ module Coinbase
101
85
  @model.signed_payload
102
86
  end
103
87
 
88
+ # Returns the Transfer transaction.
89
+ # @return [Coinbase::Transaction] The Transfer transaction
90
+ def transaction
91
+ @transaction ||= Coinbase::Transaction.new(@model.transaction)
92
+ end
93
+
104
94
  # Returns the Transaction Hash of the Transfer.
105
95
  # @return [String] The Transaction Hash
106
96
  def transaction_hash
107
97
  @model.transaction_hash
108
98
  end
109
99
 
110
- # Returns the underlying Transfer transaction, creating it if it has not been yet.
111
- # @return [Eth::Tx::Eip1559] The Transfer transaction
112
- def transaction
113
- return @transaction unless @transaction.nil?
114
-
115
- raw_payload = [unsigned_payload].pack('H*')
116
- parsed_payload = JSON.parse(raw_payload)
117
-
118
- params = {
119
- chain_id: parsed_payload['chainId'].to_i(16),
120
- nonce: parsed_payload['nonce'].to_i(16),
121
- priority_fee: parsed_payload['maxPriorityFeePerGas'].to_i(16),
122
- max_gas_fee: parsed_payload['maxFeePerGas'].to_i(16),
123
- gas_limit: parsed_payload['gas'].to_i(16), # TODO: Handle multiple currencies.
124
- from: Eth::Address.new(from_address_id),
125
- to: Eth::Address.new(parsed_payload['to']),
126
- value: parsed_payload['value'].to_i(16),
127
- data: parsed_payload['input'] || ''
128
- }
129
-
130
- @transaction = Eth::Tx::Eip1559.new(Eth::Tx.validate_eip1559_params(params))
131
- @transaction
132
- end
133
-
134
100
  # Returns the status of the Transfer.
135
101
  # @return [Symbol] The status
136
102
  def status
137
- # Check if the transfer has been signed yet.
138
- return Status::PENDING if transaction_hash.nil?
139
-
140
- onchain_transaction = Coinbase.configuration.base_sepolia_client.eth_getTransactionByHash(transaction_hash)
141
-
142
- if onchain_transaction.nil?
143
- # If the transaction has not been broadcast, it is still pending.
144
- Status::PENDING
145
- elsif onchain_transaction['blockHash'].nil?
146
- # If the transaction has been broadcast but hasn't been included in a block, it is
147
- # broadcast.
148
- Status::BROADCAST
149
- else
150
- transaction_receipt = Coinbase.configuration.base_sepolia_client.eth_getTransactionReceipt(transaction_hash)
151
-
152
- if transaction_receipt['status'].to_i(16) == 1
153
- Status::COMPLETE
154
- else
155
- Status::FAILED
156
- end
103
+ transaction.status
104
+ end
105
+
106
+ # Reload reloads the Transfer model with the latest version from the server side.
107
+ # @return [Transfer] The most recent version of Transfer from the server.
108
+ def reload
109
+ @model = Coinbase.call_api do
110
+ transfers_api.get_transfer(wallet_id, from_address_id, id)
157
111
  end
112
+
113
+ # Update memoized transaction.
114
+ @transaction = Coinbase::Transaction.new(@model.transaction)
115
+
116
+ self
158
117
  end
159
118
 
160
119
  # Waits until the Transfer is completed or failed by polling the Network at the given interval. Raises a
@@ -162,11 +121,13 @@ module Coinbase
162
121
  # @param interval_seconds [Integer] The interval at which to poll the Network, in seconds
163
122
  # @param timeout_seconds [Integer] The maximum amount of time to wait for the Transfer to complete, in seconds
164
123
  # @return [Transfer] The completed Transfer object
165
- def wait!(interval_seconds = 0.2, timeout_seconds = 10)
124
+ def wait!(interval_seconds = 0.2, timeout_seconds = 20)
166
125
  start_time = Time.now
167
126
 
168
127
  loop do
169
- return self if status == Status::COMPLETE || status == Status::FAILED
128
+ reload
129
+
130
+ return self if transaction.terminal_state?
170
131
 
171
132
  raise Timeout::Error, 'Transfer timed out' if Time.now - start_time > timeout_seconds
172
133
 
@@ -181,8 +142,8 @@ module Coinbase
181
142
  def to_s
182
143
  "Coinbase::Transfer{transfer_id: '#{id}', network_id: '#{network_id}', " \
183
144
  "from_address_id: '#{from_address_id}', destination_address_id: '#{destination_address_id}', " \
184
- "asset_id: '#{asset_id}', amount: '#{amount}', transaction_hash: '#{transaction_hash}', " \
185
- "transaction_link: '#{transaction_link}', status: '#{status}'}"
145
+ "asset_id: '#{asset_id}', amount: '#{amount}', transaction_link: '#{transaction_link}', " \
146
+ "status: '#{status}'}"
186
147
  end
187
148
 
188
149
  # Same as to_s.
@@ -190,5 +151,11 @@ module Coinbase
190
151
  def inspect
191
152
  to_s
192
153
  end
154
+
155
+ private
156
+
157
+ def transfers_api
158
+ @transfers_api ||= Coinbase::Client::TransfersApi.new(Coinbase.configuration.api_client)
159
+ end
193
160
  end
194
161
  end
data/lib/coinbase/user.rb CHANGED
@@ -40,7 +40,8 @@ module Coinbase
40
40
  # Lists the Wallets belonging to the User.
41
41
  # @param page_size [Integer] (Optional) the number of Wallets to return per page. Defaults to 10
42
42
  # @param next_page_token [String] (Optional) the token for the next page of Wallets
43
- # @return [Coinbase::Wallet] the Wallets belonging to the User
43
+ # @return [Array<Coinbase::Wallet, String>] the Wallets belonging to the User and the pagination token, if
44
+ # any.
44
45
  def wallets(page_size: 10, next_page_token: nil)
45
46
  opts = {
46
47
  limit: page_size
@@ -63,85 +64,26 @@ module Coinbase
63
64
  address_model_map[wallet_model.id] = addresses_list.data
64
65
  end
65
66
 
66
- wallet_list.data.map do |wallet_model|
67
+ wallets = wallet_list.data.map do |wallet_model|
67
68
  Wallet.new(wallet_model, seed: '', address_models: address_model_map[wallet_model.id])
68
69
  end
70
+
71
+ [wallets, wallet_list.next_page]
69
72
  end
70
73
 
71
- # Saves a wallet to local file system. Wallet saved this way can be re-instantiated with load_wallets_from_local
72
- # function, provided the backup_file is available. This is an insecure method of storing wallet seeds and should
73
- # only be used for development purposes. If you call save_wallet_locally! twice with wallets containing the same
74
- # wallet_id, the backup will be overwritten during the second attempt.
75
- # The default backup_file is `seeds.json` in the root folder. It can be configured by changing
76
- # Coinbase.configuration.backup_file_path.
77
- #
78
- # @param wallet [Coinbase::Wallet] The wallet model to save.
79
- # @param encrypt [bool] (Optional) Boolean representing whether the backup persisted to local file system should be
80
- # encrypted or not. Data is unencrypted by default.
81
- # @return [Coinbase::Wallet] the saved wallet.
82
- def save_wallet_locally!(wallet, encrypt: false)
83
- existing_seeds_in_store = existing_seeds
84
- data = wallet.export
85
- seed_to_store = data.seed
86
- auth_tag = ''
87
- iv = ''
88
- if encrypt
89
- shared_secret = store_encryption_key
90
- cipher = OpenSSL::Cipher.new('aes-256-gcm').encrypt
91
- cipher.key = OpenSSL::Digest.digest('SHA256', shared_secret)
92
- iv = cipher.random_iv
93
- cipher.iv = iv
94
- cipher.auth_data = ''
95
- encrypted_data = cipher.update(data.seed) + cipher.final
96
- auth_tag = cipher.auth_tag.unpack1('H*')
97
- iv = iv.unpack1('H*')
98
- seed_to_store = encrypted_data.unpack1('H*')
74
+ # Returns the Wallet with the given ID.
75
+ # @param wallet_id [String] the ID of the Wallet
76
+ # @return [Coinbase::Wallet] the unhydrated Wallet
77
+ def wallet(wallet_id)
78
+ wallet_model = Coinbase.call_api do
79
+ wallets_api.get_wallet(wallet_id)
99
80
  end
100
81
 
101
- existing_seeds_in_store[data.wallet_id] = {
102
- seed: seed_to_store,
103
- encrypted: encrypt,
104
- auth_tag: auth_tag,
105
- iv: iv
106
- }
107
-
108
- File.open(Coinbase.configuration.backup_file_path, 'w') do |file|
109
- file.write(JSON.pretty_generate(existing_seeds_in_store))
82
+ addresses_list = Coinbase.call_api do
83
+ addresses_api.list_addresses(wallet_model.id, { limit: Coinbase::Wallet::MAX_ADDRESSES })
110
84
  end
111
- wallet
112
- end
113
85
 
114
- # Loads all wallets belonging to the User with backup persisted to the local file system.
115
- # @return [Map<String>Coinbase::Wallet] the map of wallet_ids to the wallets.
116
- def load_wallets_from_local
117
- existing_seeds_in_store = existing_seeds
118
- raise ArgumentError, 'Backup file not found' if existing_seeds_in_store == {}
119
-
120
- wallets = {}
121
- existing_seeds_in_store.each do |wallet_id, seed_data|
122
- seed = seed_data['seed']
123
- raise ArgumentError, 'Malformed backup data' if seed.nil? || seed == ''
124
-
125
- if seed_data['encrypted']
126
- shared_secret = store_encryption_key
127
- raise ArgumentError, 'Malformed encrypted seed data' if seed_data['iv'] == '' ||
128
- seed_data['auth_tag'] == ''
129
-
130
- cipher = OpenSSL::Cipher.new('aes-256-gcm').decrypt
131
- cipher.key = OpenSSL::Digest.digest('SHA256', shared_secret)
132
- iv = [seed_data['iv']].pack('H*')
133
- cipher.iv = iv
134
- auth_tag = [seed_data['auth_tag']].pack('H*')
135
- cipher.auth_tag = auth_tag
136
- cipher.auth_data = ''
137
- hex_decoded_data = [seed_data['seed']].pack('H*')
138
- seed = cipher.update(hex_decoded_data) + cipher.final
139
- end
140
-
141
- data = Coinbase::Wallet::Data.new(wallet_id: wallet_id, seed: seed)
142
- wallets[wallet_id] = import_wallet(data)
143
- end
144
- wallets
86
+ Wallet.new(wallet_model, seed: '', address_models: addresses_list.data)
145
87
  end
146
88
 
147
89
  # Returns a string representation of the User.
@@ -165,22 +107,5 @@ module Coinbase
165
107
  def wallets_api
166
108
  @wallets_api ||= Coinbase::Client::WalletsApi.new(Coinbase.configuration.api_client)
167
109
  end
168
-
169
- def existing_seeds
170
- existing_seed_data = '{}'
171
- file_path = Coinbase.configuration.backup_file_path
172
- existing_seed_data = File.read(file_path) if File.exist?(file_path)
173
- output = JSON.parse(existing_seed_data)
174
-
175
- raise ArgumentError, 'Malformed backup data' unless output.is_a?(Hash)
176
-
177
- output
178
- end
179
-
180
- def store_encryption_key
181
- pk = OpenSSL::PKey.read(Coinbase.configuration.api_key_private_key)
182
- public_key = pk.public_key # use own public key to generate the shared secret.
183
- pk.dh_compute_key(public_key)
184
- end
185
110
  end
186
111
  end