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
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coinbase
4
+ # A representation of a SmartContract on the blockchain.
5
+ class SmartContract
6
+ # Returns a list of ContractEvents for the provided network, contract, and event details.
7
+ # @param network_id [Symbol] The network ID
8
+ # @param protocol_name [String] The protocol name
9
+ # @param contract_address [String] The contract address
10
+ # @param contract_name [String] The contract name
11
+ # @param event_name [String] The event name
12
+ # @param from_block_height [Integer] The start block height
13
+ # @param to_block_height [Integer] The end block height
14
+ # @return [Enumerable<Coinbase::ContractEvent>] The contract events
15
+ def self.list_events(
16
+ network_id:,
17
+ protocol_name:,
18
+ contract_address:,
19
+ contract_name:,
20
+ event_name:,
21
+ from_block_height:,
22
+ to_block_height:
23
+ )
24
+ Coinbase::Pagination.enumerate(
25
+ lambda { |page|
26
+ list_events_page(network_id, protocol_name, contract_address, contract_name, event_name, from_block_height,
27
+ to_block_height, page)
28
+ }
29
+ ) do |contract_event|
30
+ Coinbase::ContractEvent.new(contract_event)
31
+ end
32
+ end
33
+
34
+ # Returns a new SmartContract object.
35
+ # @param model [Coinbase::Client::SmartContract] The underlying SmartContract object
36
+ def initialize(model)
37
+ @model = model
38
+ end
39
+
40
+ # Returns the network ID of the SmartContract.
41
+ # @return [String] The network ID
42
+ def network_id
43
+ Coinbase.to_sym(@model.network_id)
44
+ end
45
+
46
+ # Returns the protocol name of the SmartContract.
47
+ # @return [String] The protocol name
48
+ def protocol_name
49
+ @model.protocol_name
50
+ end
51
+
52
+ # Returns the contract name of the SmartContract.
53
+ # @return [String] The contract name
54
+ def contract_name
55
+ @model.contract_name
56
+ end
57
+
58
+ # Returns the address of the SmartContract.
59
+ # @return [String] The contract address
60
+ def address
61
+ @model.address
62
+ end
63
+
64
+ # Returns a string representation of the SmartContract.
65
+ # @return [String] a string representation of the SmartContract
66
+ def to_s
67
+ "Coinbase::SmartContract{
68
+ network_id: '#{network_id}',
69
+ protocol_name: '#{protocol_name}',
70
+ contract_name: '#{contract_name}',
71
+ address: '#{address}'}"
72
+ end
73
+
74
+ # Same as to_s.
75
+ # @return [String] a string representation of the SmartContract
76
+ def inspect
77
+ to_s
78
+ end
79
+
80
+ def self.contract_events_api
81
+ Coinbase::Client::ContractEventsApi.new(Coinbase.configuration.api_client)
82
+ end
83
+
84
+ def self.list_events_page(
85
+ network_id,
86
+ protocol_name,
87
+ contract_address,
88
+ contract_name,
89
+ event_name,
90
+ from_block_height,
91
+ to_block_height,
92
+ page
93
+ )
94
+ contract_events_api.list_contract_events(
95
+ Coinbase.normalize_network(network_id),
96
+ protocol_name,
97
+ contract_address,
98
+ contract_name,
99
+ event_name,
100
+ from_block_height,
101
+ to_block_height,
102
+ { next_page: page }
103
+ )
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eth'
4
+
5
+ module Coinbase
6
+ # A representation of an onchain Sponsored Send.
7
+ # Sponsored Sends should be constructed via higher level abstractions like Transfer.
8
+ class SponsoredSend
9
+ # A representation of a Transaction status.
10
+ module Status
11
+ # The SponsoredSend is awaiting being signed.
12
+ PENDING = 'pending'
13
+
14
+ # The Sponsored Send has been signed, but has not been submitted to be
15
+ # built into a transaction yet.
16
+ SIGNED = 'signed'
17
+
18
+ # The Sponsored Send has been submitted to be built into a transaction,
19
+ # that the sponsor will sign and submit to the network.
20
+ # At this point, transaction hashes may not yet be assigned.
21
+ SUBMITTED = 'submitted'
22
+
23
+ # The Sponsored Send's corresponding transaction is complete and has
24
+ # confirmed on the Network.
25
+ COMPLETE = 'complete'
26
+
27
+ # The Sponsored Send has failed for some reason.
28
+ FAILED = 'failed'
29
+
30
+ # The states that are considered terminal on-chain.
31
+ TERMINAL_STATES = [COMPLETE, FAILED].freeze
32
+ end
33
+
34
+ # Returns a new SponsoredSend object. Do not use this method directly.
35
+ # @param model [Coinbase::Client::SponsoredSend] The underlying SponsoredSend object
36
+ def initialize(model)
37
+ raise unless model.is_a?(Coinbase::Client::SponsoredSend)
38
+
39
+ @model = model
40
+ end
41
+
42
+ # Returns the Keccak256 hash of the typed data. This payload must be signed
43
+ # by the sender to be used as an approval in the EIP-3009 transaction.
44
+ # @return [String] The Keccak256 hash of the typed data
45
+ def typed_data_hash
46
+ @model.typed_data_hash
47
+ end
48
+
49
+ # Returns the signature of the typed data.
50
+ def signature
51
+ @signature ||= @model.signature
52
+ end
53
+
54
+ # Signs the Transaction with the provided key and returns the hex signing payload.
55
+ # @return [String] The hex-encoded signed payload
56
+ def sign(key)
57
+ raise unless key.is_a?(Eth::Key)
58
+ raise Coinbase::AlreadySignedError if signed?
59
+
60
+ @signature = Eth::Util.prefix_hex(key.sign(Eth::Util.hex_to_bin(typed_data_hash)))
61
+ end
62
+
63
+ # Returns whether the Transaction has been signed.
64
+ # @return [Boolean] Whether the Transaction has been signed
65
+ def signed?
66
+ !signature.nil?
67
+ end
68
+
69
+ # Returns the status of the Transaction.
70
+ # @return [Symbol] The status
71
+ def status
72
+ @model.status
73
+ end
74
+
75
+ # Returns whether the Sponsored Send is in a terminal state.
76
+ # @return [Boolean] Whether the Transaction is in a terminal state
77
+ def terminal_state?
78
+ Status::TERMINAL_STATES.include?(status)
79
+ end
80
+
81
+ # Returns the Transaction Hash of the Transaction.
82
+ # @return [String] The Transaction Hash
83
+ def transaction_hash
84
+ @model.transaction_hash
85
+ end
86
+
87
+ # Returns the link to the transaction on the blockchain explorer.
88
+ # @return [String] The link to the transaction on the blockchain explorer
89
+ def transaction_link
90
+ @model.transaction_link
91
+ end
92
+
93
+ # Returns a String representation of the SponsoredSend.
94
+ # @return [String] a String representation of the SponsoredSend
95
+ def to_s
96
+ Coinbase.pretty_print_object(
97
+ self.class,
98
+ status: status,
99
+ transaction_hash: transaction_hash,
100
+ transaction_link: transaction_link
101
+ )
102
+ end
103
+
104
+ # Same as to_s.
105
+ # @return [String] a String representation of the SponsoredSend
106
+ def inspect
107
+ to_s
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module Coinbase
6
+ # A representation of a staking balance on a network for a given asset.
7
+ class StakingBalance
8
+ # Returns a list of StakingBalance for the provided network, asset, and addresses.
9
+ # @param network [Coinbase::Network, Symbol] The Network or Network ID
10
+ # @param asset_id [Symbol] The asset ID
11
+ # @param address_id [String] The address ID
12
+ # @param start_time [Time] The start time. Defaults to one month ago.
13
+ # @param end_time [Time] The end time. Defaults to the current time.
14
+ # @return [Enumerable<Coinbase::StakingBalance>] The staking balances
15
+ def self.list(network, asset_id, address_id, start_time: DateTime.now.prev_month(1), end_time: DateTime.now)
16
+ network = Coinbase::Network.from_id(network)
17
+
18
+ Coinbase::Pagination.enumerate(
19
+ ->(page) { list_page(network, asset_id, address_id, start_time, end_time, page) }
20
+ ) do |staking_balance|
21
+ new(staking_balance)
22
+ end
23
+ end
24
+
25
+ # Returns a new StakingBalance object.
26
+ # @param model [Coinbase::Client::StakingBalance] The underlying StakingBalance object
27
+ def initialize(model)
28
+ @model = model
29
+ end
30
+
31
+ # Returns the date of the StakingBalance.
32
+ # @return [Time] The date
33
+ def date
34
+ @model.date
35
+ end
36
+
37
+ # Returns the onchain address of the StakingBalance.
38
+ # @return [Time] The onchain address
39
+ def address
40
+ @model.address
41
+ end
42
+
43
+ # Returns the bonded stake as a Balance
44
+ # @return [Balance] The bonded stake
45
+ def bonded_stake
46
+ @bonded_stake ||= Balance.from_model(@model.bonded_stake)
47
+ end
48
+
49
+ # Returns the unbonded balance as a Balance
50
+ # @return [Balance] The unbonded balance
51
+ def unbonded_balance
52
+ @unbonded_balance ||= Balance.from_model(@model.unbonded_balance)
53
+ end
54
+
55
+ # Returns the participant type of the StakingBalance.
56
+ # @return [String] The participant type
57
+ def participant_type
58
+ @model.participant_type
59
+ end
60
+
61
+ # Returns a string representation of the StakingBalance.
62
+ # @return [String] a string representation of the StakingBalance
63
+ def to_s
64
+ "Coinbase::StakingBalance{date: '#{date}' address: '#{address}'}"
65
+ end
66
+
67
+ # Same as to_s.
68
+ # @return [String] a string representation of the StakingBalance
69
+ def inspect
70
+ to_s
71
+ end
72
+
73
+ def self.stake_api
74
+ Coinbase::Client::StakeApi.new(Coinbase.configuration.api_client)
75
+ end
76
+
77
+ private_class_method :stake_api
78
+
79
+ def self.list_page(network, asset_id, address_id, start_time, end_time, page)
80
+ stake_api.fetch_historical_staking_balances(
81
+ network.normalized_id,
82
+ asset_id,
83
+ address_id,
84
+ start_time.iso8601,
85
+ end_time.iso8601,
86
+ { next_page: page }
87
+ )
88
+ end
89
+
90
+ private_class_method :list_page
91
+ end
92
+ end
@@ -11,22 +11,24 @@ module Coinbase
11
11
 
12
12
  # Builds an ephemeral staking operation this is intended to be called via an Address or Wallet.
13
13
  # @param amount [BigDecimal] The amount to stake, in the primary denomination of the asset
14
- # @param network_id [Symbol] The Network ID
14
+ # @param network [Coinbase::Network, Symbol] The Network or Network ID
15
15
  # @param asset_id [Symbol] The Asset ID
16
16
  # @param address_id [String] The Address ID
17
17
  # @param action [Symbol] The action to perform
18
18
  # @param mode [Symbol] The staking mode
19
19
  # @param options [Hash] Additional options
20
20
  # @return [Coinbase::StakingOperation] The staking operation
21
- def self.build(amount, network_id, asset_id, address_id, action, mode, options)
21
+ def self.build(amount, network, asset_id, address_id, action, mode, options)
22
+ network = Coinbase::Network.from_id(network)
23
+ asset = network.get_asset(asset_id)
24
+
22
25
  model = Coinbase.call_api do
23
- asset = Coinbase::Asset.fetch(network_id, asset_id)
24
26
  stake_api.build_staking_operation(
25
27
  {
26
28
  asset_id: asset.primary_denomination.to_s,
27
29
  address_id: address_id,
28
30
  action: action,
29
- network_id: Coinbase.normalize_network(network_id),
31
+ network_id: Coinbase.normalize_network(network),
30
32
  options: {
31
33
  amount: asset.to_atomic_amount(amount).to_i.to_s,
32
34
  mode: mode
@@ -40,7 +42,7 @@ module Coinbase
40
42
 
41
43
  # Creates a persisted staking operation this is intended to be called via an Address or Wallet.
42
44
  # @param amount [BigDecimal] The amount to stake, in the primary denomination of the asset
43
- # @param network_id [Symbol] The Network ID
45
+ # @param network [Coinbase::Network, Symbol] The Network or Network ID
44
46
  # @param asset_id [Symbol] The Asset ID
45
47
  # @param address_id [String] The Address ID
46
48
  # @param wallet_id [String] The Wallet ID
@@ -48,17 +50,19 @@ module Coinbase
48
50
  # @param mode [Symbol] The staking mode
49
51
  # @param options [Hash] Additional options
50
52
  # @return [Coinbase::StakingOperation] The staking operation
51
- def self.create(amount, network_id, asset_id, address_id, wallet_id, action, mode, options)
53
+ def self.create(amount, network, asset_id, address_id, wallet_id, action, mode, options)
54
+ network = Coinbase::Network.from_id(network)
55
+ asset = network.get_asset(asset_id)
56
+
52
57
  model = Coinbase.call_api do
53
- asset = Coinbase::Asset.fetch(network_id, asset_id)
54
- stake_api.create_staking_operation(
58
+ wallet_stake_api.create_staking_operation(
55
59
  wallet_id,
56
60
  address_id,
57
61
  {
58
62
  asset_id: asset.primary_denomination.to_s,
59
63
  address_id: address_id,
60
64
  action: action,
61
- network_id: Coinbase.normalize_network(network_id),
65
+ network_id: Coinbase.normalize_network(network),
62
66
  options: {
63
67
  amount: asset.to_atomic_amount(amount).to_i.to_s,
64
68
  mode: mode
@@ -70,10 +74,24 @@ module Coinbase
70
74
  new(model)
71
75
  end
72
76
 
77
+ def self.stake_api
78
+ Coinbase::Client::StakeApi.new(Coinbase.configuration.api_client)
79
+ end
80
+
81
+ private_class_method :stake_api
82
+
83
+ def self.wallet_stake_api
84
+ Coinbase::Client::WalletStakeApi.new(Coinbase.configuration.api_client)
85
+ end
86
+
87
+ private_class_method :wallet_stake_api
88
+
73
89
  # Returns a new StakingOperation object.
74
90
  # @param model [Coinbase::Client::StakingOperation] The underlying StakingOperation object
75
91
  def initialize(model)
76
- from_model(model)
92
+ @model = model
93
+ @transactions ||= []
94
+ update_transactions(model.transactions)
77
95
  end
78
96
 
79
97
  # Returns the Staking Operation ID.
@@ -82,10 +100,10 @@ module Coinbase
82
100
  @model.id
83
101
  end
84
102
 
85
- # Returns the Network ID of the Staking Operation.
86
- # @return [Symbol] The Network ID
87
- def network_id
88
- Coinbase.to_sym(@model.network_id)
103
+ # Returns the Network of the Staking Operation.
104
+ # @return [Coinbase::Network] The Network
105
+ def network
106
+ @network ||= Coinbase::Network.from_id(@model.network_id)
89
107
  end
90
108
 
91
109
  # Returns the Address ID of the Staking Operation.
@@ -100,18 +118,48 @@ module Coinbase
100
118
  @model.status
101
119
  end
102
120
 
121
+ # Returns whether the Staking Operation is in a terminal state.
122
+ # @return [Boolean] Whether the Staking Operation is in a terminal state
123
+ def terminal_state?
124
+ failed? || completed?
125
+ end
126
+
127
+ # Returns whether the Staking Operation is in a failed state.
128
+ # @return [Boolean] Whether the Staking Operation is in a failed state
129
+ def failed?
130
+ status == 'failed'
131
+ end
132
+
133
+ # Returns whether the Staking Operation is in a complete state.
134
+ # @return [Boolean] Whether the Staking Operation is in a complete state
135
+ def completed?
136
+ status == 'complete'
137
+ end
138
+
139
+ # Returns a String representation of the Staking Operation.
140
+ # @return [String] a String representation of the Staking Operation
141
+ def to_s
142
+ Coinbase.pretty_print_object(
143
+ self.class,
144
+ id: id,
145
+ status: status,
146
+ network_id: network.id,
147
+ address_id: address_id
148
+ )
149
+ end
150
+
103
151
  # Returns the Wallet ID of the Staking Operation.
104
152
  # @return [String] The Wallet ID
105
153
  def wallet_id
106
154
  @model.wallet_id
107
155
  end
108
156
 
109
- # Waits until the Staking Operation is completed or failed by polling its status at the given interval. Raises a
110
- # Timeout::Error if the Staking Operation takes longer than the given timeout.
157
+ # Waits until the Staking Operation is completed or failed by polling its status at the given interval.
111
158
  # @param interval_seconds [Integer] The interval at which to poll, in seconds
112
159
  # @param timeout_seconds [Integer] The maximum amount of time
113
160
  # to wait for the StakingOperation to complete, in seconds
114
161
  # @return [StakingOperation] The completed StakingOperation object
162
+ # @raise [Timeout::Error] if the Staking Operation takes longer than the given timeout.
115
163
  def wait!(interval_seconds = 5, timeout_seconds = 3600)
116
164
  start_time = Time.now
117
165
 
@@ -119,7 +167,7 @@ module Coinbase
119
167
  reload
120
168
 
121
169
  # Wait for the Staking Operation to be in a terminal state.
122
- break if status == 'complete'
170
+ break if terminal_state?
123
171
 
124
172
  raise Timeout::Error, 'Staking Operation timed out' if Time.now - start_time > timeout_seconds
125
173
 
@@ -129,18 +177,58 @@ module Coinbase
129
177
  self
130
178
  end
131
179
 
180
+ # Complete helps the Staking Operation reach complete state, by polling its status at the given interval, signing
181
+ # and broadcasting any available transaction.
182
+ # @param key [Eth::Key] The key to sign the Staking Operation transactions with
183
+ # @param interval_seconds [Integer] The interval at which to poll, in seconds
184
+ # @param timeout_seconds [Integer] The maximum amount of time
185
+ # to wait for the StakingOperation to complete, in seconds
186
+ # @return [StakingOperation] The completed StakingOperation object
187
+ # @raise [Timeout::Error] if the Staking Operation takes longer than the given timeout.
188
+ def complete(key, interval_seconds: 5, timeout_seconds: 600)
189
+ start_time = Time.now
190
+
191
+ loop do
192
+ @transactions.each_with_index do |transaction, i|
193
+ next if transaction.signed?
194
+
195
+ transaction.sign(key)
196
+ @model = Coinbase.call_api do
197
+ stake_api.broadcast_staking_operation(
198
+ wallet_id,
199
+ address_id,
200
+ id,
201
+ { signed_payload: transaction.raw.hex, transaction_index: i }
202
+ )
203
+ end
204
+ end
205
+
206
+ return self if terminal_state?
207
+
208
+ reload
209
+
210
+ raise Timeout::Error, 'Staking Operation timed out' if Time.now - start_time > timeout_seconds
211
+
212
+ sleep interval_seconds
213
+ end
214
+
215
+ self
216
+ end
217
+
132
218
  # Fetch the StakingOperation with the provided network, address and staking operation ID.
133
- # @param network_id [Symbol] The Network ID
219
+ # @param network [Coinbase::Network, Symbol] The Network or Network ID
134
220
  # @param address_id [Symbol] The Address ID
135
221
  # @param id [String] The ID of the StakingOperation
136
222
  # @param wallet_id [String] The optional Wallet ID
137
223
  # @return [Coinbase::StakingOperation] The staking operation
138
- def self.fetch(network_id, address_id, id, wallet_id: nil)
224
+ def self.fetch(network, address_id, id, wallet_id: nil)
225
+ network = Coinbase::Network.from_id(network)
226
+
139
227
  staking_operation_model = Coinbase.call_api do
140
228
  if wallet_id.nil?
141
- stake_api.get_external_staking_operation(network_id, address_id, id)
229
+ stake_api.get_external_staking_operation(network.id, address_id, id)
142
230
  else
143
- stake_api.get_staking_operation(wallet_id, address_id, id)
231
+ wallet_stake_api.get_staking_operation(wallet_id, address_id, id)
144
232
  end
145
233
  end
146
234
 
@@ -160,13 +248,15 @@ module Coinbase
160
248
  def reload
161
249
  @model = Coinbase.call_api do
162
250
  if wallet_id.nil?
163
- stake_api.get_external_staking_operation(network_id, address_id, id)
251
+ stake_api.get_external_staking_operation(network.id, address_id, id)
164
252
  else
165
- stake_api.get_staking_operation(wallet_id, address_id, id)
253
+ wallet_stake_api.get_staking_operation(wallet_id, address_id, id)
166
254
  end
167
255
  end
168
256
 
169
- from_model(@model)
257
+ update_transactions(@model.transactions)
258
+
259
+ self
170
260
  end
171
261
 
172
262
  # Fetches the presigned_voluntary exit messages for the staking operation
@@ -191,7 +281,7 @@ module Coinbase
191
281
  raise TransactionNotSignedError unless transaction.signed?
192
282
 
193
283
  Coinbase.call_api do
194
- stake_api.broadcast_staking_operation(
284
+ wallet_stake_api.broadcast_staking_operation(
195
285
  wallet_id,
196
286
  address_id,
197
287
  id,
@@ -203,24 +293,32 @@ module Coinbase
203
293
  self
204
294
  end
205
295
 
206
- def self.stake_api
207
- Coinbase::Client::StakeApi.new(Coinbase.configuration.api_client)
208
- end
209
-
210
296
  private
211
297
 
212
298
  def stake_api
213
- @stake_api ||= self.class.stake_api
299
+ @stake_api ||= Coinbase::Client::StakeApi.new(Coinbase.configuration.api_client)
214
300
  end
215
301
 
216
- def from_model(model)
217
- @model = model
218
- @status = model.status
219
- @transactions = model.transactions.map do |transaction_model|
220
- Transaction.new(transaction_model)
302
+ def wallet_stake_api
303
+ @wallet_stake_api ||= Coinbase::Client::WalletStakeApi.new(Coinbase.configuration.api_client)
304
+ end
305
+
306
+ def update_transactions(transactions)
307
+ # Only overwrite the transactions if the response is populated.
308
+ return unless transactions && !transactions.empty?
309
+
310
+ # Create a set of existing unsigned payloads to avoid duplicates.
311
+ existing_unsigned_payloads = Set.new
312
+ @transactions.each do |transaction|
313
+ existing_unsigned_payloads.add(transaction.unsigned_payload)
221
314
  end
222
315
 
223
- self
316
+ # Add transactions that are not already in the transactions array.
317
+ transactions.each do |transaction_model|
318
+ unless existing_unsigned_payloads.include?(transaction_model.unsigned_payload)
319
+ @transactions << Transaction.new(transaction_model)
320
+ end
321
+ end
224
322
  end
225
323
  end
226
324
  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)
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
@@ -15,12 +15,13 @@ module Coinbase
15
15
  # @param from_asset_id [Symbol] The Asset ID of the Asset to trade from
16
16
  # @param to_asset_id [Symbol] The Asset ID of the Asset to trade to
17
17
  # @param amount [BigDecimal] The amount of the Asset to send
18
- # @param network_id [Symbol] The Network ID of the Asset
18
+ # @param network [Coinbase::Network, Symbol] The Network or Network ID of the Asset
19
19
  # @param wallet_id [String] The Wallet ID of the sending Wallet
20
20
  # @return [Send] The new pending Send object
21
- def create(address_id:, from_asset_id:, to_asset_id:, amount:, network_id:, wallet_id:)
22
- from_asset = Asset.fetch(network_id, from_asset_id)
23
- to_asset = Asset.fetch(network_id, to_asset_id)
21
+ def create(address_id:, from_asset_id:, to_asset_id:, amount:, network:, wallet_id:)
22
+ network = Coinbase::Network.from_id(network)
23
+ from_asset = network.get_asset(from_asset_id)
24
+ to_asset = network.get_asset(to_asset_id)
24
25
 
25
26
  model = Coinbase.call_api do
26
27
  trades_api.create_trade(
@@ -75,10 +76,10 @@ module Coinbase
75
76
  @model.trade_id
76
77
  end
77
78
 
78
- # Returns the Network ID of the Trade.
79
- # @return [Symbol] The Network ID
80
- def network_id
81
- Coinbase.to_sym(@model.network_id)
79
+ # Returns the Network of the Trade.
80
+ # @return [Coinbase::Network] The Network the Trade is on
81
+ def network
82
+ @network ||= Coinbase::Network.from_id(@model.network_id)
82
83
  end
83
84
 
84
85
  # Returns the Wallet ID of the Trade.
@@ -198,7 +199,7 @@ module Coinbase
198
199
  # Returns a String representation of the Trade.
199
200
  # @return [String] a String representation of the Trade
200
201
  def to_s
201
- "Coinbase::Trade{transfer_id: '#{id}', network_id: '#{network_id}', " \
202
+ "Coinbase::Trade{transfer_id: '#{id}', network_id: '#{network.id}', " \
202
203
  "address_id: '#{address_id}', from_asset_id: '#{from_asset_id}', " \
203
204
  "to_asset_id: '#{to_asset_id}', from_amount: '#{from_amount}', " \
204
205
  "to_amount: '#{to_amount}' status: '#{status}'}"