coinbase-sdk 0.0.14 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/coinbase/address/wallet_address.rb +31 -21
  3. data/lib/coinbase/address.rb +51 -17
  4. data/lib/coinbase/asset.rb +11 -8
  5. data/lib/coinbase/client/api/contract_events_api.rb +17 -9
  6. data/lib/coinbase/client/api/external_addresses_api.rb +85 -0
  7. data/lib/coinbase/client/api/networks_api.rb +85 -0
  8. data/lib/coinbase/client/api/stake_api.rb +74 -195
  9. data/lib/coinbase/client/api/validators_api.rb +2 -2
  10. data/lib/coinbase/client/api/wallet_stake_api.rb +263 -0
  11. data/lib/coinbase/client/models/address.rb +21 -4
  12. data/lib/coinbase/client/models/{native_eth_staking_context.rb → address_historical_balance_list.rb} +39 -35
  13. data/lib/coinbase/client/models/contract_event.rb +99 -8
  14. data/lib/coinbase/client/models/create_address_request.rb +14 -4
  15. data/lib/coinbase/client/models/create_transfer_request.rb +14 -4
  16. data/lib/coinbase/client/models/ethereum_validator_metadata.rb +11 -11
  17. data/lib/coinbase/client/models/feature.rb +2 -1
  18. data/lib/coinbase/client/models/feature_set.rb +307 -0
  19. data/lib/coinbase/client/models/{partial_eth_staking_context.rb → fetch_historical_staking_balances200_response.rb} +39 -35
  20. data/lib/coinbase/client/models/historical_balance.rb +273 -0
  21. data/lib/coinbase/client/models/network.rb +365 -0
  22. data/lib/coinbase/client/models/network_identifier.rb +44 -0
  23. data/lib/coinbase/client/models/sponsored_send.rb +338 -0
  24. data/lib/coinbase/client/models/staking_balance.rb +289 -0
  25. data/lib/coinbase/client/models/staking_context_context.rb +222 -74
  26. data/lib/coinbase/client/models/staking_operation.rb +2 -2
  27. data/lib/coinbase/client/models/staking_reward.rb +22 -6
  28. data/lib/coinbase/client/models/staking_reward_format.rb +2 -1
  29. data/lib/coinbase/client/models/staking_reward_usd_value.rb +257 -0
  30. data/lib/coinbase/client/models/transaction.rb +17 -7
  31. data/lib/coinbase/client/models/transaction_type.rb +2 -1
  32. data/lib/coinbase/client/models/transfer.rb +101 -8
  33. data/lib/coinbase/client/models/validator.rb +23 -2
  34. data/lib/coinbase/client/models/validator_status.rb +52 -0
  35. data/lib/coinbase/client/models/wallet.rb +13 -16
  36. data/lib/coinbase/client/models/webhook_event_type.rb +2 -1
  37. data/lib/coinbase/client.rb +12 -3
  38. data/lib/coinbase/constants.rb +0 -10
  39. data/lib/coinbase/contract_event.rb +104 -0
  40. data/lib/coinbase/correlation.rb +30 -0
  41. data/lib/coinbase/destination.rb +11 -9
  42. data/lib/coinbase/errors.rb +14 -0
  43. data/lib/coinbase/historical_balance.rb +53 -0
  44. data/lib/coinbase/middleware.rb +2 -0
  45. data/lib/coinbase/network.rb +103 -20
  46. data/lib/coinbase/server_signer.rb +14 -3
  47. data/lib/coinbase/smart_contract.rb +106 -0
  48. data/lib/coinbase/sponsored_send.rb +110 -0
  49. data/lib/coinbase/staking_balance.rb +92 -0
  50. data/lib/coinbase/staking_operation.rb +134 -36
  51. data/lib/coinbase/staking_reward.rb +18 -0
  52. data/lib/coinbase/trade.rb +10 -9
  53. data/lib/coinbase/transaction.rb +13 -3
  54. data/lib/coinbase/transfer.rb +65 -36
  55. data/lib/coinbase/validator.rb +18 -10
  56. data/lib/coinbase/version.rb +5 -0
  57. data/lib/coinbase/wallet/data.rb +31 -0
  58. data/lib/coinbase/wallet.rb +143 -182
  59. data/lib/coinbase/webhook.rb +181 -0
  60. data/lib/coinbase.rb +64 -21
  61. metadata +51 -5
  62. data/lib/coinbase/user.rb +0 -65
@@ -5,25 +5,18 @@ 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,
11
13
  # list their balances, and transfer Assets to other Addresses.
12
14
  class Wallet
15
+ extend Forwardable
16
+
13
17
  # The maximum number of addresses in a Wallet.
14
18
  MAX_ADDRESSES = 20
15
19
 
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
20
  class << self
28
21
  # Imports a Wallet from previously exported wallet data.
29
22
  # @param data [Coinbase::Wallet::Data] the Wallet data to import
@@ -43,7 +36,7 @@ module Coinbase
43
36
  # converted to an array, etc...
44
37
  # @return [Enumerable<Coinbase::Wallet>] Enumerator that returns wallets
45
38
  def list
46
- Coinbase::Pagination.enumerate(lambda(&method(:fetch_wallets_page))) do |wallet|
39
+ Coinbase::Pagination.enumerate(method(:fetch_wallets_page)) do |wallet|
47
40
  Coinbase::Wallet.new(wallet, seed: '')
48
41
  end
49
42
  end
@@ -63,18 +56,19 @@ module Coinbase
63
56
  end
64
57
 
65
58
  # Creates a new Wallet on the specified Network and generate a default address for it.
66
- # @param network_id [String] (Optional) the ID of the blockchain network. Defaults to 'base-sepolia'.
59
+ # @param network [Coinbase::Network, Symbol] (Optional) The network object or ID to create the
60
+ # Wallet on. When omitted this uses the SDK configured default network.
67
61
  # @param interval_seconds [Integer] The interval at which to poll the CDPService for the Wallet to
68
62
  # have an active seed, if using a ServerSigner, in seconds
69
63
  # @param timeout_seconds [Integer] The maximum amount of time to wait for the ServerSigner to
70
64
  # create a seed for the Wallet, in seconds
71
65
  # @return [Coinbase::Wallet] the new Wallet
72
- def create(network_id: 'base-sepolia', interval_seconds: 0.2, timeout_seconds: 20)
66
+ def create(network: Coinbase.default_network, interval_seconds: 0.2, timeout_seconds: 20)
73
67
  model = Coinbase.call_api do
74
68
  wallets_api.create_wallet(
75
69
  create_wallet_request: {
76
70
  wallet: {
77
- network_id: Coinbase.normalize_network(network_id),
71
+ network_id: Coinbase.normalize_network(network),
78
72
  use_server_signer: Coinbase.use_server_signer?
79
73
  }
80
74
  }
@@ -107,7 +101,7 @@ module Coinbase
107
101
  wallets_api.get_wallet(wallet_id)
108
102
  end
109
103
 
110
- return self if model.server_signer_status == ServerSignerStatus::ACTIVE
104
+ return self if model.server_signer_status == ServerSigner::Status::ACTIVE
111
105
 
112
106
  if Time.now - start_time > timeout_seconds
113
107
  raise Timeout::Error, 'Wallet creation timed out. Check status of your Server-Signer'
@@ -132,8 +126,7 @@ module Coinbase
132
126
  end
133
127
  end
134
128
 
135
- # Returns a new Wallet object. Do not use this method directly. Instead, use User#create_wallet or
136
- # User#import_wallet.
129
+ # Returns a new Wallet object. Do not use this method directly. Instead use Coinbase::Wallet.create.
137
130
  # @param model [Coinbase::Client::Wallet] The underlying Wallet object
138
131
  # @param seed [String] (Optional) The seed to use for the Wallet. Expects a 32-byte hexadecimal with no 0x prefix.
139
132
  # If nil, a new seed will be generated. If the empty string, no seed is generated, and the Wallet will be
@@ -149,19 +142,98 @@ module Coinbase
149
142
  @master = master_node(seed)
150
143
  end
151
144
 
145
+ # @!method transfer
146
+ # Transfers the amount of the Asset from the default address to the specified destination.
147
+ # @param amount [Integer, Float, BigDecimal] The amount of the Asset to send
148
+ # @param asset_id [Symbol] The ID of the Asset to send
149
+ # @param destination [Wallet | Address | String] The destination of the transfer.
150
+ # If a Wallet, sends to the Wallet's default address.
151
+ # If a String, interprets it as the address ID.
152
+ # @param gasless [Boolean] Whether the transfer should be gasless. Defaults to false.
153
+ # @return [Coinbase::Transfer] The Transfer object.
154
+ # (see Coinbase::Address::WalletAddress#transfer)
155
+
156
+ # @!method trade
157
+ # Trades the specified amount from one asset to another using the default address.
158
+ # @param amount [Integer, Float, BigDecimal] The amount of the Asset to send.
159
+ # @param from_asset_id [Symbol] The ID of the Asset to trade from.
160
+ # @param to_asset_id [Symbol] The ID of the Asset to trade to.
161
+ # default address. If a String, interprets it as the address ID.
162
+ # @return [Coinbase::Trade] The Trade object.
163
+
164
+ # @!method faucet
165
+ # Requests funds from the faucet for the Wallet's default address and returns the faucet transaction.
166
+ # This is only supported on testnet networks.
167
+ # @return [Coinbase::FaucetTransaction] The successful faucet transaction
168
+ # @raise [Coinbase::FaucetLimitReachedError] If the faucet limit has been reached for the address or user.
169
+ # @raise [Coinbase::Client::ApiError] If an unexpected error occurs while requesting faucet funds.
170
+
171
+ # @!method stake
172
+ # Stakes the given amount of the given Asset for the default address.
173
+ # @param amount [Integer, Float, BigDecimal] The amount of the Asset to stake.
174
+ # @param asset_id [Symbol] The ID of the Asset to stake.
175
+ # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
176
+ # @param options [Hash] (Optional) Additional options for the staking operation.
177
+ # @return [Coinbase::StakingOperation] The stake operation
178
+
179
+ # @!method unstake
180
+ # Unstakes the given amount of the given Asset on the default address.
181
+ # @param amount [Integer, Float, BigDecimal] The amount of the Asset to unstake.
182
+ # @param asset_id [Symbol] The ID of the Asset to unstake.
183
+ # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
184
+ # @param options [Hash] (Optional) Additional options for the unstaking operation.
185
+ # @return [Coinbase::StakingOperation] The unstake operation
186
+
187
+ # @!method claim_stake
188
+ # Claims stake of the given amount of the given Asset for the default address.
189
+ # @param amount [Integer, Float, BigDecimal] The amount of the Asset to claim_stake.
190
+ # @param asset_id [Symbol] The ID of the Asset to claim_stake.
191
+ # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
192
+ # @param options [Hash] (Optional) Additional options for the unstaking operation.
193
+ # @return [Coinbase::StakingOperation] The claim_stake operation
194
+
195
+ # @!method staking_balances
196
+ # Retrieves the balances used for staking for the supplied asset for the default address.
197
+ # @param asset_id [Symbol] The asset to retrieve staking balances for
198
+ # @param mode [Symbol] The staking mode. Defaults to :default.
199
+ # @param options [Hash] Additional options for the staking operation
200
+ # @return [Hash] The staking balances
201
+ # @return [BigDecimal] :stakeable_balance The amount of the asset that can be staked
202
+ # @return [BigDecimal] :unstakeable_balance The amount of the asset that is currently staked and cannot be unstaked
203
+ # @return [BigDecimal] :claimable_balance The amount of the asset that can be claimed
204
+
205
+ # @!method stakeable_balance
206
+ # Retrieves the stakeable balance of the supplied asset for the default address.
207
+ # @param asset_id [Symbol] The asset to retrieve the stakeable balance for
208
+ # @param mode [Symbol] The staking mode. Defaults to :default.
209
+ # @param options [Hash] Additional options for the staking operation
210
+ # @return [BigDecimal] The stakeable balance
211
+
212
+ # @!method unstakeable_balance
213
+ # Retrieves the unstakeable balance for the supplied asset.
214
+ # Currently only the default_address is used to source the unstakeable balance.
215
+ # @param asset_id [Symbol] The asset to retrieve the unstakeable balance for
216
+ # @param mode [Symbol] The staking mode. Defaults to :default.
217
+ # @param options [Hash] Additional options for the staking operation
218
+ # @return [BigDecimal] The unstakeable balance
219
+
220
+ # @!method claimable_balance
221
+ # Retrieves the claimable balance for the supplied asset.
222
+ # Currently only the default_address is used to source the claimable balance.
223
+ # @param asset_id [Symbol] The asset to retrieve the claimable balance for
224
+ # @param mode [Symbol] The staking mode. Defaults to :default.
225
+ # @param options [Hash] Additional options for the staking operation
226
+ # @return [BigDecimal] The claimable balance
227
+
228
+ def_delegators :default_address, :transfer, :trade, :faucet, :stake, :unstake, :claim_stake, :staking_balances,
229
+ :stakeable_balance, :unstakeable_balance, :claimable_balance
230
+
152
231
  # Returns the addresses belonging to the Wallet.
153
232
  # @return [Array<Coinbase::WalletAddress>] The addresses belonging to the Wallet
154
233
  def addresses
155
- @addresses ||= begin
156
- address_list = Coinbase.call_api do
157
- addresses_api.list_addresses(@model.id, { limit: MAX_ADDRESSES })
158
- end
234
+ return @addresses unless @addresses.nil?
159
235
 
160
- # Build the WalletAddress objects, injecting the key if available.
161
- address_list.data.each_with_index.map do |address_model, index|
162
- build_wallet_address(address_model, index)
163
- end
164
- end
236
+ set_addresses
165
237
  end
166
238
 
167
239
  # Returns the Wallet ID.
@@ -170,10 +242,10 @@ module Coinbase
170
242
  @model.id
171
243
  end
172
244
 
173
- # Returns the Network ID of the Wallet.
174
- # @return [Symbol] The Network ID
175
- def network_id
176
- Coinbase.to_sym(@model.network_id)
245
+ # Returns the Network of the Wallet.
246
+ # @return [Coinbase::Network] The Network of the Wallet
247
+ def network
248
+ @network ||= Coinbase::Network.from_id(@model.network_id)
177
249
  end
178
250
 
179
251
  # Returns the ServerSigner Status of the Wallet.
@@ -207,7 +279,12 @@ module Coinbase
207
279
  # Creates a new Address in the Wallet.
208
280
  # @return [Address] The new Address
209
281
  def create_address
210
- opts = { create_address_request: {} }
282
+ req = {}
283
+
284
+ # Ensure that the address cache is set before creating a new address.
285
+ # This ensures that for a server signer, the addresses have been loaded and we
286
+ # can create a new address and add it to a cache.
287
+ set_addresses if @addresses.nil?
211
288
 
212
289
  unless Coinbase.use_server_signer?
213
290
  # The index for the next address is the number of addresses already registered.
@@ -215,22 +292,25 @@ module Coinbase
215
292
 
216
293
  key = derive_key(private_key_index)
217
294
 
218
- opts = {
219
- create_address_request: {
220
- public_key: key.public_key.compressed.unpack1('H*'),
221
- attestation: create_attestation(key)
222
- }
295
+ req = {
296
+ public_key: key.public_key.compressed.unpack1('H*'),
297
+ attestation: create_attestation(key),
298
+ address_index: private_key_index
223
299
  }
224
300
  end
225
301
 
226
302
  address_model = Coinbase.call_api do
227
- addresses_api.create_address(id, opts)
303
+ addresses_api.create_address(id, { create_address_request: req })
228
304
  end
229
305
 
230
- # Auto-reload wallet to set default address on first address creation.
306
+ # Default address can be nil because either this is the first address being
307
+ # created for this wallet or the addresses cache has not yet been loaded.
308
+
309
+ # If the default address is nil, we must reload the wallet model after creating
310
+ # the address, in order for the default address to be set.
231
311
  reload if default_address.nil?
232
312
 
233
- # Cache the address in our memoized list
313
+ # The addreses cache is already created, so we can add the new address to the cache.
234
314
  address = WalletAddress.new(address_model, key)
235
315
  @addresses << address
236
316
  address
@@ -272,106 +352,8 @@ module Coinbase
272
352
  Coinbase::Balance.from_model_and_asset_id(response, asset_id).amount
273
353
  end
274
354
 
275
- # Transfers the given amount of the given Asset to the specified address or wallet.
276
- # Only same-network Transfers are supported. Currently only the default_address is used to source the Transfer.
277
- # @param amount [Integer, Float, BigDecimal] The amount of the Asset to send
278
- # @param asset_id [Symbol] The ID of the Asset to send
279
- # @param destination [Wallet | Address | String] The destination of the transfer. If a Wallet, sends to the Wallet's
280
- # default address. If a String, interprets it as the address ID.
281
- # @return [Coinbase::Transfer] The Transfer object.
282
- def transfer(amount, asset_id, destination)
283
- default_address.transfer(amount, asset_id, destination)
284
- end
285
-
286
- # Trades the given amount of the given Asset for another Asset.
287
- # Currently only the default_address is used to source the Trade
288
- # @param amount [Integer, Float, BigDecimal] The amount of the Asset to send.
289
- # @param from_asset_id [Symbol] The ID of the Asset to trade from. For Ether, :eth, :gwei, and :wei are supported.
290
- # @param to_asset_id [Symbol] The ID of the Asset to trade to. For Ether, :eth, :gwei, and :wei are supported.
291
- # default address. If a String, interprets it as the address ID.
292
- # @return [Coinbase::Trade] The Trade object.
293
- def trade(amount, from_asset_id, to_asset_id)
294
- default_address.trade(amount, from_asset_id, to_asset_id)
295
- end
296
-
297
- # Stakes the given amount of the given Asset.
298
- # Currently only the default_address is used to source the Stake.
299
- # @param amount [Integer, Float, BigDecimal] The amount of the Asset to stake.
300
- # @param asset_id [Symbol] The ID of the Asset to stake.
301
- # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
302
- # @param options [Hash] (Optional) Additional options for the staking operation.
303
- # @return [Coinbase::StakingOperation] The stake operation
304
- def stake(amount, asset_id, mode: :default, options: {})
305
- default_address.stake(amount, asset_id, mode: mode, options: options)
306
- end
307
-
308
- # Unstakes the given amount of the given Asset.
309
- # Currently only the default_address is used to source the Unstake.
310
- # @param amount [Integer, Float, BigDecimal] The amount of the Asset to unstake.
311
- # @param asset_id [Symbol] The ID of the Asset to unstake.
312
- # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
313
- # @param options [Hash] (Optional) Additional options for the unstaking operation.
314
- # @return [Coinbase::StakingOperation] The unstake operation
315
- def unstake(amount, asset_id, mode: :default, options: {})
316
- default_address.unstake(amount, asset_id, mode: mode, options: options)
317
- end
318
-
319
- # Claims stake of the given amount of the given Asset.
320
- # Currently only the default_address is used as the source for claim_stake.
321
- # @param amount [Integer, Float, BigDecimal] The amount of the Asset to claim_stake.
322
- # @param asset_id [Symbol] The ID of the Asset to claim_stake.
323
- # @param mode [Symbol] (Optional) The staking mode. Defaults to :default.
324
- # @param options [Hash] (Optional) Additional options for the unstaking operation.
325
- # @return [Coinbase::StakingOperation] The claim_stake operation
326
- def claim_stake(amount, asset_id, mode: :default, options: {})
327
- default_address.claim_stake(amount, asset_id, mode: mode, options: options)
328
- end
329
-
330
- # Retrieves the balances used for staking for the supplied asset.
331
- # Currently only the default_address is used to source the staking balances.
332
- # @param asset_id [Symbol] The asset to retrieve staking balances for
333
- # @param mode [Symbol] The staking mode. Defaults to :default.
334
- # @param options [Hash] Additional options for the staking operation
335
- # @return [Hash] The staking balances
336
- # @return [BigDecimal] :stakeable_balance The amount of the asset that can be staked
337
- # @return [BigDecimal] :unstakeable_balance The amount of the asset that is currently staked and cannot be unstaked
338
- # @return [BigDecimal] :claimable_balance The amount of the asset that can be claimed
339
- def staking_balances(asset_id, mode: :default, options: {})
340
- default_address.staking_balances(asset_id, mode: mode, options: options)
341
- end
342
-
343
- # Retrieves the stakeable balance for the supplied asset.
344
- # Currently only the default_address is used to source the stakeable balance.
345
- # @param asset_id [Symbol] The asset to retrieve the stakeable balance for
346
- # @param mode [Symbol] The staking mode. Defaults to :default.
347
- # @param options [Hash] Additional options for the staking operation
348
- # @return [BigDecimal] The stakeable balance
349
- def stakeable_balance(asset_id, mode: :default, options: {})
350
- default_address.stakeable_balance(asset_id, mode: mode, options: options)
351
- end
352
-
353
- # Retrieves the unstakeable balance for the supplied asset.
354
- # Currently only the default_address is used to source the unstakeable balance.
355
- # @param asset_id [Symbol] The asset to retrieve the unstakeable balance for
356
- # @param mode [Symbol] The staking mode. Defaults to :default.
357
- # @param options [Hash] Additional options for the staking operation
358
- # @return [BigDecimal] The unstakeable balance
359
- def unstakeable_balance(asset_id, mode: :default, options: {})
360
- default_address.unstakeable_balance(asset_id, mode: mode, options: options)
361
- end
362
-
363
- # Retrieves the claimable balance for the supplied asset.
364
- # Currently only the default_address is used to source the claimable balance.
365
- # @param asset_id [Symbol] The asset to retrieve the claimable balance for
366
- # @param mode [Symbol] The staking mode. Defaults to :default.
367
- # @param options [Hash] Additional options for the staking operation
368
- # @return [BigDecimal] The claimable balance
369
- def claimable_balance(asset_id, mode: :default, options: {})
370
- default_address.claimable_balance(asset_id, mode: mode, options: options)
371
- end
372
-
373
355
  # Exports the Wallet's data to a Data object.
374
- # @return [Data] The Wallet data
356
+ # @return [Coinbase::Wallet::Data] The Wallet data
375
357
  def export
376
358
  # TODO: Improve this check by relying on the backend data to decide whether a wallet is server-signer backed.
377
359
  raise 'Cannot export data for Server-Signer backed Wallet' if Coinbase.use_server_signer?
@@ -381,11 +363,6 @@ module Coinbase
381
363
  Data.new(wallet_id: id, seed: @master.seed_hex)
382
364
  end
383
365
 
384
- # Requests funds from the faucet for the Wallet's default address and returns the faucet transaction.
385
- # This is only supported on testnet networks.
386
- # @return [Coinbase::FaucetTransaction] The successful faucet transaction
387
- # @raise [Coinbase::FaucetLimitReachedError] If the faucet limit has been reached for the address or user.
388
- # @raise [Coinbase::Client::ApiError] If an unexpected error occurs while requesting faucet funds.
389
366
  def faucet
390
367
  Coinbase.call_api do
391
368
  Coinbase::FaucetTransaction.new(addresses_api.request_faucet_funds(id, default_address.id))
@@ -433,9 +410,7 @@ module Coinbase
433
410
  iv: iv
434
411
  }
435
412
 
436
- File.open(file_path, 'w') do |file|
437
- file.write(JSON.pretty_generate(existing_seeds_in_store))
438
- end
413
+ File.write(file_path, JSON.pretty_generate(existing_seeds_in_store))
439
414
 
440
415
  "Successfully saved seed for wallet #{id} to #{file_path}."
441
416
  end
@@ -482,8 +457,12 @@ module Coinbase
482
457
  # Returns a String representation of the Wallet.
483
458
  # @return [String] a String representation of the Wallet
484
459
  def to_s
485
- "Coinbase::Wallet{wallet_id: '#{id}', network_id: '#{network_id}', " \
486
- "default_address: '#{@model.default_address&.address_id}'}"
460
+ Coinbase.pretty_print_object(
461
+ self.class,
462
+ id: id,
463
+ network_id: network.id,
464
+ default_address: @model.default_address&.address_id
465
+ )
487
466
  end
488
467
 
489
468
  # Same as to_s.
@@ -492,32 +471,6 @@ module Coinbase
492
471
  to_s
493
472
  end
494
473
 
495
- # The data required to recreate a Wallet.
496
- class Data
497
- attr_reader :wallet_id, :seed
498
-
499
- # Returns a new Data object.
500
- # @param wallet_id [String] The ID of the Wallet
501
- # @param seed [String] The seed of the Wallet
502
- def initialize(wallet_id:, seed:)
503
- @wallet_id = wallet_id
504
- @seed = seed
505
- end
506
-
507
- # Converts the Data object to a Hash.
508
- # @return [Hash] The Hash representation of the Data object
509
- def to_hash
510
- { wallet_id: wallet_id, seed: seed }
511
- end
512
-
513
- # Creates a Data object from the given Hash.
514
- # @param data [Hash] The Hash to create the Data object from
515
- # @return [Data] The new Data object
516
- def self.from_hash(data)
517
- Data.new(wallet_id: data['wallet_id'], seed: data['seed'])
518
- end
519
- end
520
-
521
474
  private
522
475
 
523
476
  # Reloads the Wallet with the latest data.
@@ -538,13 +491,11 @@ module Coinbase
538
491
  end
539
492
 
540
493
  def address_path_prefix
541
- # TODO: Push this logic to the backend.
542
- @address_path_prefix ||= case network_id.to_s.split('_').first
543
- when 'base', 'ethereum'
544
- "m/44'/60'/0'/0"
545
- else
546
- raise ArgumentError, "Unsupported network ID: #{network_id}"
547
- end
494
+ if network.address_path_prefix.nil? || network.address_path_prefix.empty?
495
+ raise ArgumentError, "Cannot create address for network #{network.id}"
496
+ end
497
+
498
+ network.address_path_prefix
548
499
  end
549
500
 
550
501
  # Derives a key for the given address index.
@@ -626,5 +577,15 @@ module Coinbase
626
577
  def wallets_api
627
578
  @wallets_api ||= Coinbase::Client::WalletsApi.new(Coinbase.configuration.api_client)
628
579
  end
580
+
581
+ def set_addresses
582
+ address_list = Coinbase.call_api do
583
+ addresses_api.list_addresses(@model.id, { limit: MAX_ADDRESSES })
584
+ end
585
+
586
+ @addresses = address_list.data.each_with_index.map do |address_model, index|
587
+ build_wallet_address(address_model, index)
588
+ end
589
+ end
629
590
  end
630
591
  end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coinbase
4
+ # A representation of a Webhook.
5
+ # This class provides methods to create, list, update, and delete webhooks
6
+ # that are used to receive notifications of specific events.
7
+ class Webhook
8
+ # Event type for ERC20 transfer
9
+ ERC20_TRANSFER_EVENT = 'erc20_transfer'
10
+
11
+ # Event type for ERC721 transfer
12
+ ERC721_TRANSFER_EVENT = 'erc721_transfer'
13
+
14
+ class << self
15
+ # Creates a new webhook for a specified network.
16
+ #
17
+ # @param network_id [String] The network ID for which the webhook is created.
18
+ # @param notification_uri [String] The URI where notifications should be sent.
19
+ # @param event_type [String] The type of event for the webhook. Must be one of the following:
20
+ # - `Coinbase::Webhook::ERC20_TRANSFER_EVENT`
21
+ # - `Coinbase::Webhook::ERC721_TRANSFER_EVENT`
22
+ # @param event_filters [Array<Hash>] Filters applied to the events that determine
23
+ # which specific events trigger the webhook. Each filter should be a hash that
24
+ # can include keys like `contract_address`, `from_address`, or `to_address`.
25
+ # @return [Coinbase::Webhook] A new instance of Webhook.
26
+ #
27
+ # @example Create a new webhook
28
+ # webhook = Coinbase::Webhook.create(
29
+ # network_id: :ethereum_mainnet,
30
+ # notification_uri: 'https://example.com/callback',
31
+ # event_type: 'transaction',
32
+ # event_filters: [{ 'contract_address' => '0x...', 'from_address' => '0x...', 'to_address' => '0x...' }]
33
+ # )
34
+ def create(network_id:, notification_uri:, event_type:, event_filters:)
35
+ model = Coinbase.call_api do
36
+ webhooks_api.create_webhook(
37
+ create_webhook_request: {
38
+ network_id: Coinbase.normalize_network(network_id),
39
+ notification_uri: notification_uri,
40
+ event_type: event_type,
41
+ event_filters: event_filters
42
+ }
43
+ )
44
+ end
45
+
46
+ new(model)
47
+ end
48
+
49
+ # Enumerates the webhooks.
50
+ # The result is an enumerator that lazily fetches from the server, and can be iterated over,
51
+ # converted to an array, etc...
52
+ # @return [Enumerable<Coinbase::Webhook>] Enumerator that returns webhooks
53
+ def list
54
+ Coinbase::Pagination.enumerate(method(:fetch_webhooks_page).to_proc) do |webhook|
55
+ Coinbase::Webhook.new(webhook)
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def webhooks_api
62
+ Coinbase::Client::WebhooksApi.new(Coinbase.configuration.api_client)
63
+ end
64
+
65
+ def fetch_webhooks_page(page)
66
+ webhooks_api.list_webhooks({ limit: DEFAULT_PAGE_LIMIT, page: page })
67
+ end
68
+ end
69
+
70
+ # Initializes a new Webhook object.
71
+ #
72
+ # @param model [Coinbase::Client::Webhook] The underlying Webhook object.
73
+ # @raise [ArgumentError] If the model is not a Coinbase::Client::Webhook.
74
+ def initialize(model)
75
+ raise ArgumentError, 'model must be a Webhook' unless model.is_a?(Coinbase::Client::Webhook)
76
+
77
+ @model = model
78
+ end
79
+
80
+ # Returns the ID of the webhook.
81
+ #
82
+ # @return [String] The ID of the webhook.
83
+ def id
84
+ @model.id
85
+ end
86
+
87
+ # Returns the network ID associated with the webhook.
88
+ #
89
+ # @return [Symbol] The network ID of the webhook.
90
+ def network_id
91
+ Coinbase.to_sym(@model.network_id)
92
+ end
93
+
94
+ # Returns the notification URI of the webhook.
95
+ #
96
+ # @return [String] The URI where notifications are sent.
97
+ def notification_uri
98
+ @model.notification_uri
99
+ end
100
+
101
+ # Returns the event type of the webhook.
102
+ #
103
+ # @return [String] The type of event the webhook listens for.
104
+ def event_type
105
+ @model.event_type
106
+ end
107
+
108
+ # Returns the event filters applied to the webhook.
109
+ #
110
+ # @return [Array<Coinbase::Client::WebhookEventFilter>] An array of event filters used by the webhook.
111
+ def event_filters
112
+ @model.event_filters
113
+ end
114
+
115
+ # Updates the webhook with a new notification URI.
116
+ #
117
+ # @param notification_uri [String] The new URI for webhook notifications.
118
+ # @return [self] Returns the updated Webhook object.
119
+ #
120
+ # @example Update the notification URI of a webhook
121
+ # webhook.update(notification_uri: 'https://new-url.com/callback')
122
+ def update(notification_uri:)
123
+ model = Coinbase.call_api do
124
+ webhooks_api.update_webhook(
125
+ id,
126
+ update_webhook_request: {
127
+ network_id: network_id,
128
+ notification_uri: notification_uri,
129
+ event_type: event_type,
130
+ event_filters: event_filters.map(&:to_hash)
131
+ }
132
+ )
133
+ end
134
+
135
+ @model = model
136
+
137
+ self
138
+ end
139
+
140
+ # Deletes the webhook.
141
+ #
142
+ # @return [self] Returns the Webhook object with nil attributes.
143
+ #
144
+ # @example Delete a webhook
145
+ # webhook.delete
146
+ def delete
147
+ Coinbase.call_api do
148
+ webhooks_api.delete_webhook(id)
149
+ end
150
+
151
+ @model = nil
152
+
153
+ self
154
+ end
155
+
156
+ # Returns a String representation of the Webhook.
157
+ # @return [String] a String representation of the Webhook
158
+ def to_s
159
+ Coinbase.pretty_print_object(
160
+ self.class,
161
+ id: @model.id,
162
+ network_id: @model.network_id,
163
+ event_type: @model.event_type,
164
+ notification_uri: @model.notification_uri,
165
+ event_filters: @model.event_filters.map(&:to_hash).to_json
166
+ )
167
+ end
168
+
169
+ # Same as to_s.
170
+ # @return [String] a String representation of the Webhook
171
+ def inspect
172
+ to_s
173
+ end
174
+
175
+ private
176
+
177
+ def webhooks_api
178
+ @webhooks_api ||= Coinbase::Client::WebhooksApi.new(Coinbase.configuration.api_client)
179
+ end
180
+ end
181
+ end