coinbase-sdk 0.0.5 → 0.0.7

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.
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