coinbase-sdk 0.0.13 → 0.0.16

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/lib/coinbase/address/external_address.rb +3 -166
  3. data/lib/coinbase/address/wallet_address.rb +51 -4
  4. data/lib/coinbase/address.rb +164 -0
  5. data/lib/coinbase/client/api/assets_api.rb +2 -2
  6. data/lib/coinbase/client/api/contract_events_api.rb +121 -0
  7. data/lib/coinbase/client/api/external_addresses_api.rb +85 -0
  8. data/lib/coinbase/client/api/networks_api.rb +85 -0
  9. data/lib/coinbase/client/api/stake_api.rb +361 -0
  10. data/lib/coinbase/client/api/webhooks_api.rb +286 -0
  11. data/lib/coinbase/client/models/address_historical_balance_list.rb +258 -0
  12. data/lib/coinbase/client/models/broadcast_staking_operation_request.rb +239 -0
  13. data/lib/coinbase/client/models/contract_event.rb +336 -0
  14. data/lib/coinbase/client/models/contract_event_list.rb +259 -0
  15. data/lib/coinbase/client/models/create_staking_operation_request.rb +274 -0
  16. data/lib/coinbase/client/models/create_transfer_request.rb +14 -4
  17. data/lib/coinbase/client/models/create_webhook_request.rb +282 -0
  18. data/lib/coinbase/client/models/ethereum_validator.rb +374 -0
  19. data/lib/coinbase/client/models/feature_set.rb +307 -0
  20. data/lib/coinbase/client/models/fetch_historical_staking_balances200_response.rb +258 -0
  21. data/lib/coinbase/client/models/get_validator200_response.rb +221 -0
  22. data/lib/coinbase/client/models/get_validator200_response_validator.rb +214 -0
  23. data/lib/coinbase/client/models/historical_balance.rb +273 -0
  24. data/lib/coinbase/client/models/network.rb +355 -0
  25. data/lib/coinbase/client/models/network_identifier.rb +44 -0
  26. data/lib/coinbase/client/models/sponsored_send.rb +338 -0
  27. data/lib/coinbase/client/models/staking_balance.rb +289 -0
  28. data/lib/coinbase/client/models/staking_context_context.rb +222 -74
  29. data/lib/coinbase/client/models/staking_operation.rb +15 -5
  30. data/lib/coinbase/client/models/staking_reward.rb +22 -6
  31. data/lib/coinbase/client/models/staking_reward_format.rb +2 -1
  32. data/lib/coinbase/client/models/staking_reward_usd_value.rb +257 -0
  33. data/lib/coinbase/client/models/transaction.rb +2 -2
  34. data/lib/coinbase/client/models/transaction_type.rb +2 -1
  35. data/lib/coinbase/client/models/transfer.rb +29 -24
  36. data/lib/coinbase/client/models/update_webhook_request.rb +289 -0
  37. data/lib/coinbase/client/models/validator_list_data.rb +216 -0
  38. data/lib/coinbase/client/models/wallet.rb +13 -16
  39. data/lib/coinbase/client/models/webhook.rb +299 -0
  40. data/lib/coinbase/client/models/webhook_event_filter.rb +236 -0
  41. data/lib/coinbase/client/models/webhook_event_type.rb +42 -0
  42. data/lib/coinbase/client/models/webhook_list.rb +244 -0
  43. data/lib/coinbase/client.rb +22 -3
  44. data/lib/coinbase/errors.rb +7 -0
  45. data/lib/coinbase/historical_balance.rb +53 -0
  46. data/lib/coinbase/middleware.rb +12 -0
  47. data/lib/coinbase/server_signer.rb +14 -3
  48. data/lib/coinbase/sponsored_send.rb +110 -0
  49. data/lib/coinbase/staking_balance.rb +86 -0
  50. data/lib/coinbase/staking_operation.rb +106 -5
  51. data/lib/coinbase/staking_reward.rb +18 -0
  52. data/lib/coinbase/trade.rb +1 -1
  53. data/lib/coinbase/transaction.rb +7 -3
  54. data/lib/coinbase/transfer.rb +56 -29
  55. data/lib/coinbase/wallet/data.rb +31 -0
  56. data/lib/coinbase/wallet.rb +91 -46
  57. data/lib/coinbase.rb +17 -4
  58. metadata +74 -2
@@ -9,6 +9,67 @@ module Coinbase
9
9
  class StakingOperation
10
10
  attr_reader :transactions
11
11
 
12
+ # Builds an ephemeral staking operation this is intended to be called via an Address or Wallet.
13
+ # @param amount [BigDecimal] The amount to stake, in the primary denomination of the asset
14
+ # @param network_id [Symbol] The Network ID
15
+ # @param asset_id [Symbol] The Asset ID
16
+ # @param address_id [String] The Address ID
17
+ # @param action [Symbol] The action to perform
18
+ # @param mode [Symbol] The staking mode
19
+ # @param options [Hash] Additional options
20
+ # @return [Coinbase::StakingOperation] The staking operation
21
+ def self.build(amount, network_id, asset_id, address_id, action, mode, options)
22
+ model = Coinbase.call_api do
23
+ asset = Coinbase::Asset.fetch(network_id, asset_id)
24
+ stake_api.build_staking_operation(
25
+ {
26
+ asset_id: asset.primary_denomination.to_s,
27
+ address_id: address_id,
28
+ action: action,
29
+ network_id: Coinbase.normalize_network(network_id),
30
+ options: {
31
+ amount: asset.to_atomic_amount(amount).to_i.to_s,
32
+ mode: mode
33
+ }.merge(options)
34
+ }
35
+ )
36
+ end
37
+
38
+ new(model)
39
+ end
40
+
41
+ # Creates a persisted staking operation this is intended to be called via an Address or Wallet.
42
+ # @param amount [BigDecimal] The amount to stake, in the primary denomination of the asset
43
+ # @param network_id [Symbol] The Network ID
44
+ # @param asset_id [Symbol] The Asset ID
45
+ # @param address_id [String] The Address ID
46
+ # @param wallet_id [String] The Wallet ID
47
+ # @param action [Symbol] The action to perform
48
+ # @param mode [Symbol] The staking mode
49
+ # @param options [Hash] Additional options
50
+ # @return [Coinbase::StakingOperation] The staking operation
51
+ def self.create(amount, network_id, asset_id, address_id, wallet_id, action, mode, options)
52
+ model = Coinbase.call_api do
53
+ asset = Coinbase::Asset.fetch(network_id, asset_id)
54
+ stake_api.create_staking_operation(
55
+ wallet_id,
56
+ address_id,
57
+ {
58
+ asset_id: asset.primary_denomination.to_s,
59
+ address_id: address_id,
60
+ action: action,
61
+ network_id: Coinbase.normalize_network(network_id),
62
+ options: {
63
+ amount: asset.to_atomic_amount(amount).to_i.to_s,
64
+ mode: mode
65
+ }.merge(options)
66
+ }
67
+ )
68
+ end
69
+
70
+ new(model)
71
+ end
72
+
12
73
  # Returns a new StakingOperation object.
13
74
  # @param model [Coinbase::Client::StakingOperation] The underlying StakingOperation object
14
75
  def initialize(model)
@@ -39,6 +100,12 @@ module Coinbase
39
100
  @model.status
40
101
  end
41
102
 
103
+ # Returns the Wallet ID of the Staking Operation.
104
+ # @return [String] The Wallet ID
105
+ def wallet_id
106
+ @model.wallet_id
107
+ end
108
+
42
109
  # Waits until the Staking Operation is completed or failed by polling its status at the given interval. Raises a
43
110
  # Timeout::Error if the Staking Operation takes longer than the given timeout.
44
111
  # @param interval_seconds [Integer] The interval at which to poll, in seconds
@@ -66,13 +133,18 @@ module Coinbase
66
133
  # @param network_id [Symbol] The Network ID
67
134
  # @param address_id [Symbol] The Address ID
68
135
  # @param id [String] The ID of the StakingOperation
136
+ # @param wallet_id [String] The optional Wallet ID
69
137
  # @return [Coinbase::StakingOperation] The staking operation
70
- def self.fetch(network_id, address_id, id)
138
+ def self.fetch(network_id, address_id, id, wallet_id: nil)
71
139
  staking_operation_model = Coinbase.call_api do
72
- stake_api.get_external_staking_operation(network_id, address_id, id)
140
+ if wallet_id.nil?
141
+ stake_api.get_external_staking_operation(network_id, address_id, id)
142
+ else
143
+ stake_api.get_staking_operation(wallet_id, address_id, id)
144
+ end
73
145
  end
74
146
 
75
- from_model(staking_operation_model)
147
+ new(staking_operation_model)
76
148
  end
77
149
 
78
150
  # Signs the Open Transactions with the provided key
@@ -87,7 +159,11 @@ module Coinbase
87
159
  # @return [Coinbase::StakingOperation] The updated staking operation
88
160
  def reload
89
161
  @model = Coinbase.call_api do
90
- stake_api.get_external_staking_operation(network_id, address_id, id)
162
+ if wallet_id.nil?
163
+ stake_api.get_external_staking_operation(network_id, address_id, id)
164
+ else
165
+ stake_api.get_staking_operation(wallet_id, address_id, id)
166
+ end
91
167
  end
92
168
 
93
169
  from_model(@model)
@@ -108,10 +184,33 @@ module Coinbase
108
184
  signed_voluntary_exit_messages
109
185
  end
110
186
 
187
+ # Broadcasts the Staking Operation transactions to the network
188
+ # @return [Coinbase::StakingOperation]
189
+ def broadcast!
190
+ transactions.each_with_index do |transaction, i|
191
+ raise TransactionNotSignedError unless transaction.signed?
192
+
193
+ Coinbase.call_api do
194
+ stake_api.broadcast_staking_operation(
195
+ wallet_id,
196
+ address_id,
197
+ id,
198
+ { signed_payload: transaction.raw.hex, transaction_index: i }
199
+ )
200
+ end
201
+ end
202
+
203
+ self
204
+ end
205
+
206
+ def self.stake_api
207
+ Coinbase::Client::StakeApi.new(Coinbase.configuration.api_client)
208
+ end
209
+
111
210
  private
112
211
 
113
212
  def stake_api
114
- @stake_api ||= Coinbase::Client::StakeApi.new(Coinbase.configuration.api_client)
213
+ @stake_api ||= self.class.stake_api
115
214
  end
116
215
 
117
216
  def from_model(model)
@@ -120,6 +219,8 @@ module Coinbase
120
219
  @transactions = model.transactions.map do |transaction_model|
121
220
  Transaction.new(transaction_model)
122
221
  end
222
+
223
+ self
123
224
  end
124
225
  end
125
226
  end
@@ -53,6 +53,24 @@ module Coinbase
53
53
  @model.address_id
54
54
  end
55
55
 
56
+ # Returns the USD value of the StakingReward.
57
+ # @return [BigDecimal] The USD value
58
+ def usd_value
59
+ BigDecimal(@model.usd_value.amount.to_i) / BigDecimal(100)
60
+ end
61
+
62
+ # Returns the USD conversion price of the StakingReward.
63
+ # @return [BigDecimal] The USD conversion price
64
+ def usd_conversion_price
65
+ BigDecimal(@model.usd_value.conversion_price.to_i) / BigDecimal(100)
66
+ end
67
+
68
+ # Returns the USD conversion time of the StakingReward.
69
+ # @return [Time] The USD conversion time
70
+ def usd_conversion_time
71
+ @model.usd_value.conversion_time
72
+ end
73
+
56
74
  # Returns a string representation of the StakingReward.
57
75
  # @return [String] a string representation of the StakingReward
58
76
  def to_s
@@ -147,7 +147,7 @@ module Coinbase
147
147
 
148
148
  payloads = { signed_payload: transaction.raw.hex }
149
149
 
150
- payloads[:approve_tx_signed_payload] = approve_transaction.raw.hex unless approve_transaction.nil?
150
+ payloads[:approve_transaction_signed_payload] = approve_transaction.raw.hex unless approve_transaction.nil?
151
151
 
152
152
  @model = Coinbase.call_api do
153
153
  trades_api.broadcast_trade(wallet_id, address_id, id, payloads)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'constants'
4
- require 'bigdecimal'
5
3
  require 'eth'
6
4
  require 'json'
7
5
 
@@ -113,12 +111,18 @@ module Coinbase
113
111
  @raw = Eth::Tx::Eip1559.new(Eth::Tx.validate_eip1559_params(params))
114
112
  end
115
113
 
114
+ # Returns the signature of the Transaction.
115
+ # @return [String] The hex-encode signature
116
+ def signature
117
+ raw.hex
118
+ end
119
+
116
120
  # Signs the Transaction with the provided key and returns the hex signing payload.
117
121
  # @return [String] The hex-encoded signed payload
118
122
  def sign(key)
119
123
  raw.sign(key)
120
124
 
121
- raw.hex
125
+ signature
122
126
  end
123
127
 
124
128
  # Returns whether the Transaction has been signed.
@@ -3,7 +3,6 @@
3
3
  require_relative 'constants'
4
4
  require 'bigdecimal'
5
5
  require 'eth'
6
- require 'json'
7
6
 
8
7
  module Coinbase
9
8
  # A representation of a Transfer, which moves an amount of an Asset from
@@ -25,7 +24,7 @@ module Coinbase
25
24
  # @param wallet_id [String] The Wallet ID of the sending Wallet
26
25
  # @return [Transfer] The new pending Transfer object
27
26
  # @raise [Coinbase::ApiError] If the Transfer fails
28
- def create(address_id:, amount:, asset_id:, destination:, network_id:, wallet_id:)
27
+ def create(address_id:, amount:, asset_id:, destination:, network_id:, wallet_id:, gasless: false)
29
28
  asset = Asset.fetch(network_id, asset_id)
30
29
 
31
30
  model = Coinbase.call_api do
@@ -36,7 +35,8 @@ module Coinbase
36
35
  amount: asset.to_atomic_amount(amount).to_i.to_s,
37
36
  asset_id: asset.primary_denomination.to_s,
38
37
  destination: Coinbase::Destination.new(destination, network_id: network_id).address_id,
39
- network_id: Coinbase.normalize_network(network_id)
38
+ network_id: Coinbase.normalize_network(network_id),
39
+ gasless: gasless
40
40
  }
41
41
  )
42
42
  end
@@ -122,41 +122,52 @@ module Coinbase
122
122
  BigDecimal(@model.amount) / BigDecimal(10).power(@model.asset.decimals)
123
123
  end
124
124
 
125
- # Returns the link to the transaction on the blockchain explorer.
126
- # @return [String] The link to the transaction on the blockchain explorer
127
- def transaction_link
128
- # TODO: Parameterize this by Network.
129
- "https://sepolia.basescan.org/tx/#{transaction_hash}"
130
- end
125
+ # Signs the Transfer with the given key. This is required before broadcasting the Transfer.
126
+ # @param key [Eth::Key] The key to sign the Transfer with
127
+ # @raise [RuntimeError] If the key is not an Eth::Key
128
+ # @return [Transfer] The Transfer object
129
+ def sign(key)
130
+ raise unless key.is_a?(Eth::Key)
131
131
 
132
- # Returns the Unsigned Payload of the Transfer.
133
- # @return [String] The Unsigned Payload
134
- def unsigned_payload
135
- @model.unsigned_payload
136
- end
132
+ unless sponsored_send.nil?
133
+ sponsored_send.sign(key)
134
+
135
+ return
136
+ end
137
137
 
138
- # Returns the Signed Payload of the Transfer.
139
- # @return [String] The Signed Payload
140
- def signed_payload
141
- @model.signed_payload
138
+ transaction.sign(key)
139
+
140
+ self
142
141
  end
143
142
 
144
143
  # Returns the Transfer transaction.
145
144
  # @return [Coinbase::Transaction] The Transfer transaction
146
145
  def transaction
147
- @transaction ||= Coinbase::Transaction.new(@model.transaction)
146
+ @transaction ||= @model.transaction.nil? ? nil : Coinbase::Transaction.new(@model.transaction)
148
147
  end
149
148
 
150
- # Returns the Transaction Hash of the Transfer.
151
- # @return [String] The Transaction Hash
152
- def transaction_hash
153
- @model.transaction_hash
149
+ # Returns the SponsoredSend of the Transfer, if the transfer is gasless.
150
+ # @return [Coinbase::SponsoredSend] The SponsoredSend object
151
+ def sponsored_send
152
+ @sponsored_send ||= @model.sponsored_send.nil? ? nil : Coinbase::SponsoredSend.new(@model.sponsored_send)
154
153
  end
155
154
 
156
155
  # Returns the status of the Transfer.
157
156
  # @return [Symbol] The status
158
157
  def status
159
- transaction.status
158
+ send_tx_delegate.status
159
+ end
160
+
161
+ # Returns the link to the transaction on the blockchain explorer.
162
+ # @return [String] The link to the transaction on the blockchain explorer
163
+ def transaction_link
164
+ send_tx_delegate.transaction_link
165
+ end
166
+
167
+ # Returns the Transaction Hash of the Transfer.
168
+ # @return [String] The Transaction Hash
169
+ def transaction_hash
170
+ send_tx_delegate.transaction_hash
160
171
  end
161
172
 
162
173
  # Broadcasts the Transfer to the Network.
@@ -164,18 +175,19 @@ module Coinbase
164
175
  # @raise [RuntimeError] If the Transfer is not signed
165
176
  # @return [Transfer] The Transfer object
166
177
  def broadcast!
167
- raise TransactionNotSignedError unless transaction.signed?
178
+ raise TransactionNotSignedError unless send_tx_delegate.signed?
168
179
 
169
180
  @model = Coinbase.call_api do
170
181
  transfers_api.broadcast_transfer(
171
182
  wallet_id,
172
183
  from_address_id,
173
184
  id,
174
- { signed_payload: transaction.raw.hex }
185
+ { signed_payload: send_tx_delegate.signature }
175
186
  )
176
187
  end
177
188
 
178
- update_transaction(@model)
189
+ update_transaction(@model) unless @model.transaction.nil?
190
+ update_sponsored_send(@model) unless @model.sponsored_send.nil?
179
191
 
180
192
  self
181
193
  end
@@ -187,7 +199,8 @@ module Coinbase
187
199
  transfers_api.get_transfer(wallet_id, from_address_id, id)
188
200
  end
189
201
 
190
- update_transaction(@model)
202
+ update_transaction(@model) unless @model.transaction.nil?
203
+ update_sponsored_send(@model) unless @model.sponsored_send.nil?
191
204
 
192
205
  self
193
206
  end
@@ -203,7 +216,7 @@ module Coinbase
203
216
  loop do
204
217
  reload
205
218
 
206
- return self if transaction.terminal_state?
219
+ return self if terminal_state?
207
220
 
208
221
  raise Timeout::Error, 'Transfer timed out' if Time.now - start_time > timeout_seconds
209
222
 
@@ -230,6 +243,16 @@ module Coinbase
230
243
 
231
244
  private
232
245
 
246
+ def terminal_state?
247
+ send_tx_delegate.terminal_state?
248
+ end
249
+
250
+ def send_tx_delegate
251
+ return sponsored_send unless sponsored_send.nil?
252
+
253
+ transaction
254
+ end
255
+
233
256
  def transfers_api
234
257
  @transfers_api ||= Coinbase::Client::TransfersApi.new(Coinbase.configuration.api_client)
235
258
  end
@@ -237,5 +260,9 @@ module Coinbase
237
260
  def update_transaction(model)
238
261
  @transaction = Coinbase::Transaction.new(model.transaction)
239
262
  end
263
+
264
+ def update_sponsored_send(model)
265
+ @sponsored_send = Coinbase::SponsoredSend.new(model.sponsored_send)
266
+ end
240
267
  end
241
268
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coinbase
4
+ class Wallet
5
+ # The data required to recreate a Wallet.
6
+ class Data
7
+ attr_reader :wallet_id, :seed
8
+
9
+ # Returns a new Data object.
10
+ # @param wallet_id [String] The ID of the Wallet
11
+ # @param seed [String] The seed of the Wallet
12
+ def initialize(wallet_id:, seed:)
13
+ @wallet_id = wallet_id
14
+ @seed = seed
15
+ end
16
+
17
+ # Converts the Data object to a Hash.
18
+ # @return [Hash] The Hash representation of the Data object
19
+ def to_hash
20
+ { wallet_id: wallet_id, seed: seed }
21
+ end
22
+
23
+ # Creates a Data object from the given Hash.
24
+ # @param data [Hash] The Hash to create the Data object from
25
+ # @return [Data] The new Data object
26
+ def self.from_hash(data)
27
+ Data.new(wallet_id: data['wallet_id'], seed: data['seed'])
28
+ end
29
+ end
30
+ end
31
+ end
@@ -5,6 +5,8 @@ require 'json'
5
5
  require 'money-tree'
6
6
  require 'securerandom'
7
7
 
8
+ require_relative 'wallet/data'
9
+
8
10
  module Coinbase
9
11
  # A representation of a Wallet. Wallets come with a single default Address, but can expand to have a set of Addresses,
10
12
  # each of which can hold a balance of one or more Assets. Wallets can create new Addresses, list their addresses,
@@ -13,17 +15,6 @@ module Coinbase
13
15
  # The maximum number of addresses in a Wallet.
14
16
  MAX_ADDRESSES = 20
15
17
 
16
- # A representation of ServerSigner status in a Wallet.
17
- module ServerSignerStatus
18
- # The Wallet is awaiting seed creation by the ServerSigner. At this point,
19
- # the Wallet cannot create addresses or sign transactions.
20
- PENDING = 'pending_seed_creation'
21
-
22
- # The Wallet has an associated seed created by the ServerSigner. It is ready
23
- # to create addresses and sign transactions.
24
- ACTIVE = 'active_seed'
25
- end
26
-
27
18
  class << self
28
19
  # Imports a Wallet from previously exported wallet data.
29
20
  # @param data [Coinbase::Wallet::Data] the Wallet data to import
@@ -43,7 +34,7 @@ module Coinbase
43
34
  # converted to an array, etc...
44
35
  # @return [Enumerable<Coinbase::Wallet>] Enumerator that returns wallets
45
36
  def list
46
- Coinbase::Pagination.enumerate(lambda(&method(:fetch_wallets_page))) do |wallet|
37
+ Coinbase::Pagination.enumerate(method(:fetch_wallets_page)) do |wallet|
47
38
  Coinbase::Wallet.new(wallet, seed: '')
48
39
  end
49
40
  end
@@ -107,7 +98,7 @@ module Coinbase
107
98
  wallets_api.get_wallet(wallet_id)
108
99
  end
109
100
 
110
- return self if model.server_signer_status == ServerSignerStatus::ACTIVE
101
+ return self if model.server_signer_status == ServerSigner::Status::ACTIVE
111
102
 
112
103
  if Time.now - start_time > timeout_seconds
113
104
  raise Timeout::Error, 'Wallet creation timed out. Check status of your Server-Signer'
@@ -278,9 +269,15 @@ module Coinbase
278
269
  # @param asset_id [Symbol] The ID of the Asset to send
279
270
  # @param destination [Wallet | Address | String] The destination of the transfer. If a Wallet, sends to the Wallet's
280
271
  # default address. If a String, interprets it as the address ID.
272
+ # @param gasless [Boolean] Whether gas fee for the transfer should be covered by Coinbase.
273
+ # Defaults to false. Check the API documentation for network and asset support.
281
274
  # @return [Coinbase::Transfer] The Transfer object.
282
- def transfer(amount, asset_id, destination)
283
- default_address.transfer(amount, asset_id, destination)
275
+ def transfer(amount, asset_id, destination, options = {})
276
+ # For ruby 2.7 compatibility we cannot pass in keyword args when the create wallet
277
+ # options is empty
278
+ return default_address.transfer(amount, asset_id, destination) if options.empty?
279
+
280
+ default_address.transfer(amount, asset_id, destination, **options)
284
281
  end
285
282
 
286
283
  # Trades the given amount of the given Asset for another Asset.
@@ -294,8 +291,84 @@ module Coinbase
294
291
  default_address.trade(amount, from_asset_id, to_asset_id)
295
292
  end
296
293
 
294
+ # Stakes the given amount of the given Asset.
295
+ # Currently only the default_address is used to source the Stake.
296
+ # @param amount [Integer, Float, BigDecimal] The amount of the Asset to stake.
297
+ # @param asset_id [Symbol] The ID of the Asset to stake.
298
+ # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
299
+ # @param options [Hash] (Optional) Additional options for the staking operation.
300
+ # @return [Coinbase::StakingOperation] The stake operation
301
+ def stake(amount, asset_id, mode: :default, options: {})
302
+ default_address.stake(amount, asset_id, mode: mode, options: options)
303
+ end
304
+
305
+ # Unstakes the given amount of the given Asset.
306
+ # Currently only the default_address is used to source the Unstake.
307
+ # @param amount [Integer, Float, BigDecimal] The amount of the Asset to unstake.
308
+ # @param asset_id [Symbol] The ID of the Asset to unstake.
309
+ # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
310
+ # @param options [Hash] (Optional) Additional options for the unstaking operation.
311
+ # @return [Coinbase::StakingOperation] The unstake operation
312
+ def unstake(amount, asset_id, mode: :default, options: {})
313
+ default_address.unstake(amount, asset_id, mode: mode, options: options)
314
+ end
315
+
316
+ # Claims stake of the given amount of the given Asset.
317
+ # Currently only the default_address is used as the source for claim_stake.
318
+ # @param amount [Integer, Float, BigDecimal] The amount of the Asset to claim_stake.
319
+ # @param asset_id [Symbol] The ID of the Asset to claim_stake.
320
+ # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
321
+ # @param options [Hash] (Optional) Additional options for the unstaking operation.
322
+ # @return [Coinbase::StakingOperation] The claim_stake operation
323
+ def claim_stake(amount, asset_id, mode: :default, options: {})
324
+ default_address.claim_stake(amount, asset_id, mode: mode, options: options)
325
+ end
326
+
327
+ # Retrieves the balances used for staking for the supplied asset.
328
+ # Currently only the default_address is used to source the staking balances.
329
+ # @param asset_id [Symbol] The asset to retrieve staking balances for
330
+ # @param mode [Symbol] The staking mode. Defaults to :default.
331
+ # @param options [Hash] Additional options for the staking operation
332
+ # @return [Hash] The staking balances
333
+ # @return [BigDecimal] :stakeable_balance The amount of the asset that can be staked
334
+ # @return [BigDecimal] :unstakeable_balance The amount of the asset that is currently staked and cannot be unstaked
335
+ # @return [BigDecimal] :claimable_balance The amount of the asset that can be claimed
336
+ def staking_balances(asset_id, mode: :default, options: {})
337
+ default_address.staking_balances(asset_id, mode: mode, options: options)
338
+ end
339
+
340
+ # Retrieves the stakeable balance for the supplied asset.
341
+ # Currently only the default_address is used to source the stakeable balance.
342
+ # @param asset_id [Symbol] The asset to retrieve the stakeable balance for
343
+ # @param mode [Symbol] The staking mode. Defaults to :default.
344
+ # @param options [Hash] Additional options for the staking operation
345
+ # @return [BigDecimal] The stakeable balance
346
+ def stakeable_balance(asset_id, mode: :default, options: {})
347
+ default_address.stakeable_balance(asset_id, mode: mode, options: options)
348
+ end
349
+
350
+ # Retrieves the unstakeable balance for the supplied asset.
351
+ # Currently only the default_address is used to source the unstakeable balance.
352
+ # @param asset_id [Symbol] The asset to retrieve the unstakeable balance for
353
+ # @param mode [Symbol] The staking mode. Defaults to :default.
354
+ # @param options [Hash] Additional options for the staking operation
355
+ # @return [BigDecimal] The unstakeable balance
356
+ def unstakeable_balance(asset_id, mode: :default, options: {})
357
+ default_address.unstakeable_balance(asset_id, mode: mode, options: options)
358
+ end
359
+
360
+ # Retrieves the claimable balance for the supplied asset.
361
+ # Currently only the default_address is used to source the claimable balance.
362
+ # @param asset_id [Symbol] The asset to retrieve the claimable balance for
363
+ # @param mode [Symbol] The staking mode. Defaults to :default.
364
+ # @param options [Hash] Additional options for the staking operation
365
+ # @return [BigDecimal] The claimable balance
366
+ def claimable_balance(asset_id, mode: :default, options: {})
367
+ default_address.claimable_balance(asset_id, mode: mode, options: options)
368
+ end
369
+
297
370
  # Exports the Wallet's data to a Data object.
298
- # @return [Data] The Wallet data
371
+ # @return [Coinbase::Wallet::Data] The Wallet data
299
372
  def export
300
373
  # TODO: Improve this check by relying on the backend data to decide whether a wallet is server-signer backed.
301
374
  raise 'Cannot export data for Server-Signer backed Wallet' if Coinbase.use_server_signer?
@@ -357,9 +430,7 @@ module Coinbase
357
430
  iv: iv
358
431
  }
359
432
 
360
- File.open(file_path, 'w') do |file|
361
- file.write(JSON.pretty_generate(existing_seeds_in_store))
362
- end
433
+ File.write(file_path, JSON.pretty_generate(existing_seeds_in_store))
363
434
 
364
435
  "Successfully saved seed for wallet #{id} to #{file_path}."
365
436
  end
@@ -416,32 +487,6 @@ module Coinbase
416
487
  to_s
417
488
  end
418
489
 
419
- # The data required to recreate a Wallet.
420
- class Data
421
- attr_reader :wallet_id, :seed
422
-
423
- # Returns a new Data object.
424
- # @param wallet_id [String] The ID of the Wallet
425
- # @param seed [String] The seed of the Wallet
426
- def initialize(wallet_id:, seed:)
427
- @wallet_id = wallet_id
428
- @seed = seed
429
- end
430
-
431
- # Converts the Data object to a Hash.
432
- # @return [Hash] The Hash representation of the Data object
433
- def to_hash
434
- { wallet_id: wallet_id, seed: seed }
435
- end
436
-
437
- # Creates a Data object from the given Hash.
438
- # @param data [Hash] The Hash to create the Data object from
439
- # @return [Data] The new Data object
440
- def self.from_hash(data)
441
- Data.new(wallet_id: data['wallet_id'], seed: data['seed'])
442
- end
443
- end
444
-
445
490
  private
446
491
 
447
492
  # Reloads the Wallet with the latest data.
@@ -464,7 +509,7 @@ module Coinbase
464
509
  def address_path_prefix
465
510
  # TODO: Push this logic to the backend.
466
511
  @address_path_prefix ||= case network_id.to_s.split('_').first
467
- when 'base', 'ethereum'
512
+ when 'base', 'ethereum', 'polygon'
468
513
  "m/44'/60'/0'/0"
469
514
  else
470
515
  raise ArgumentError, "Unsupported network ID: #{network_id}"
data/lib/coinbase.rb CHANGED
@@ -7,6 +7,7 @@ require_relative 'coinbase/asset'
7
7
  require_relative 'coinbase/authenticator'
8
8
  require_relative 'coinbase/balance'
9
9
  require_relative 'coinbase/balance_map'
10
+ require_relative 'coinbase/historical_balance'
10
11
  require_relative 'coinbase/client'
11
12
  require_relative 'coinbase/constants'
12
13
  require_relative 'coinbase/destination'
@@ -21,6 +22,8 @@ require_relative 'coinbase/transaction'
21
22
  require_relative 'coinbase/user'
22
23
  require_relative 'coinbase/wallet'
23
24
  require_relative 'coinbase/server_signer'
25
+ require_relative 'coinbase/sponsored_send'
26
+ require_relative 'coinbase/staking_balance'
24
27
  require_relative 'coinbase/staking_operation'
25
28
  require_relative 'coinbase/staking_reward'
26
29
  require_relative 'coinbase/validator'
@@ -62,13 +65,14 @@ module Coinbase
62
65
 
63
66
  # Configuration object for the Coinbase SDK.
64
67
  class Configuration
65
- attr_accessor :api_url, :api_key_name, :api_key_private_key, :debug_api, :use_server_signer
68
+ attr_accessor :api_url, :api_key_name, :api_key_private_key, :debug_api, :use_server_signer, :max_network_tries
66
69
 
67
70
  # Initializes the configuration object.
68
71
  def initialize
69
72
  @api_url = 'https://api.cdp.coinbase.com'
70
73
  @debug_api = false
71
74
  @use_server_signer = false
75
+ @max_network_tries = 3
72
76
  end
73
77
 
74
78
  # Sets configuration values based on the provided CDP API Key JSON file.
@@ -110,7 +114,7 @@ module Coinbase
110
114
  # @param network_sym [Symbol] the network symbol to convert
111
115
  # @return [String] the converted string
112
116
  def self.normalize_network(network_sym)
113
- network_sym.to_s.gsub(/_/, '-')
117
+ network_sym.to_s.gsub('_', '-')
114
118
  end
115
119
 
116
120
  # Loads the default user.
@@ -127,8 +131,17 @@ module Coinbase
127
131
  yield
128
132
  rescue Coinbase::Client::ApiError => e
129
133
  raise Coinbase::APIError.from_error(e), cause: nil
130
- rescue StandardError => e
131
- raise e
134
+ end
135
+
136
+ # Returns a pretty-printed object string that contains the object's class name and
137
+ # the details of the object, filtering out nil values.
138
+ # @param klass [Class] the class of the object
139
+ # @param details [Hash] the details of the object
140
+ # @return [String] the pretty-printed object string
141
+ def self.pretty_print_object(klass, **details)
142
+ filtered_details = details.filter { |_, v| !v.nil? }.map { |k, v| "#{k}: '#{v}'" }
143
+
144
+ "#{klass}{#{filtered_details.join(', ')}}"
132
145
  end
133
146
 
134
147
  # Returns whether to use a server signer to manage private keys.