coinbase-sdk 0.0.14 → 0.1.0

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