coinbase-sdk 0.0.8 → 0.0.9
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.
- checksums.yaml +4 -4
- data/lib/coinbase/address/external_address.rb +173 -0
- data/lib/coinbase/address/wallet_address.rb +219 -0
- data/lib/coinbase/address.rb +32 -219
- data/lib/coinbase/asset.rb +1 -1
- data/lib/coinbase/authenticator.rb +2 -0
- data/lib/coinbase/client/api/addresses_api.rb +1 -1
- data/lib/coinbase/client/api/assets_api.rb +1 -1
- data/lib/coinbase/client/api/external_addresses_api.rb +1 -1
- data/lib/coinbase/client/api/server_signers_api.rb +1 -1
- data/lib/coinbase/client/api/stake_api.rb +1 -1
- data/lib/coinbase/client/api/trades_api.rb +1 -1
- data/lib/coinbase/client/api/transfers_api.rb +1 -1
- data/lib/coinbase/client/api/users_api.rb +1 -1
- data/lib/coinbase/client/api/wallets_api.rb +1 -1
- data/lib/coinbase/client/api_client.rb +3 -3
- data/lib/coinbase/client/api_error.rb +1 -1
- data/lib/coinbase/client/configuration.rb +1 -1
- data/lib/coinbase/client/models/address.rb +1 -1
- data/lib/coinbase/client/models/address_balance_list.rb +1 -1
- data/lib/coinbase/client/models/address_list.rb +1 -1
- data/lib/coinbase/client/models/asset.rb +1 -1
- data/lib/coinbase/client/models/balance.rb +1 -1
- data/lib/coinbase/client/models/broadcast_trade_request.rb +1 -1
- data/lib/coinbase/client/models/broadcast_transfer_request.rb +1 -1
- data/lib/coinbase/client/models/build_staking_operation_request.rb +1 -1
- data/lib/coinbase/client/models/create_address_request.rb +1 -1
- data/lib/coinbase/client/models/create_server_signer_request.rb +22 -5
- data/lib/coinbase/client/models/create_trade_request.rb +1 -1
- data/lib/coinbase/client/models/create_transfer_request.rb +1 -1
- data/lib/coinbase/client/models/create_wallet_request.rb +1 -1
- data/lib/coinbase/client/models/create_wallet_request_wallet.rb +1 -1
- data/lib/coinbase/client/models/error.rb +1 -1
- data/lib/coinbase/client/models/faucet_transaction.rb +23 -5
- data/lib/coinbase/client/models/feature.rb +1 -1
- data/lib/coinbase/client/models/fetch_staking_rewards200_response.rb +1 -1
- data/lib/coinbase/client/models/fetch_staking_rewards_request.rb +2 -15
- data/lib/coinbase/client/models/get_staking_context_request.rb +1 -1
- data/lib/coinbase/client/models/partial_eth_staking_context.rb +4 -7
- data/lib/coinbase/client/models/seed_creation_event.rb +1 -1
- data/lib/coinbase/client/models/seed_creation_event_result.rb +1 -1
- data/lib/coinbase/client/models/server_signer.rb +22 -5
- data/lib/coinbase/client/models/server_signer_event.rb +1 -1
- data/lib/coinbase/client/models/server_signer_event_event.rb +1 -1
- data/lib/coinbase/client/models/server_signer_event_list.rb +1 -1
- data/lib/coinbase/client/models/server_signer_list.rb +1 -1
- data/lib/coinbase/client/models/signature_creation_event.rb +1 -1
- data/lib/coinbase/client/models/signature_creation_event_result.rb +1 -1
- data/lib/coinbase/client/models/staking_context.rb +1 -1
- data/lib/coinbase/client/models/staking_context_context.rb +1 -1
- data/lib/coinbase/client/models/staking_operation.rb +15 -12
- data/lib/coinbase/client/models/staking_reward.rb +21 -5
- data/lib/coinbase/client/models/staking_reward_format.rb +40 -0
- data/lib/coinbase/client/models/trade.rb +1 -1
- data/lib/coinbase/client/models/trade_list.rb +1 -1
- data/lib/coinbase/client/models/transaction.rb +1 -1
- data/lib/coinbase/client/models/transaction_type.rb +1 -1
- data/lib/coinbase/client/models/transfer.rb +1 -1
- data/lib/coinbase/client/models/transfer_list.rb +1 -1
- data/lib/coinbase/client/models/user.rb +1 -1
- data/lib/coinbase/client/models/wallet.rb +1 -1
- data/lib/coinbase/client/models/wallet_list.rb +1 -1
- data/lib/coinbase/client/version.rb +1 -1
- data/lib/coinbase/client.rb +2 -1
- data/lib/coinbase/errors.rb +7 -0
- data/lib/coinbase/faucet_transaction.rb +5 -4
- data/lib/coinbase/pagination.rb +26 -0
- data/lib/coinbase/staking_operation.rb +29 -0
- data/lib/coinbase/staking_reward.rb +79 -0
- data/lib/coinbase/transaction.rb +6 -0
- data/lib/coinbase/user.rb +5 -51
- data/lib/coinbase/wallet.rb +95 -100
- data/lib/coinbase.rb +11 -0
- metadata +8 -19
- data/lib/coinbase/client/api/balances_api.rb +0 -97
- data/lib/coinbase/client/api/transfer_api.rb +0 -114
- data/lib/coinbase/client/models/request_faucet_funds200_response.rb +0 -222
data/lib/coinbase/client.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
The version of the OpenAPI document: 0.0.1-alpha
|
7
7
|
Contact: yuga.cohler@coinbase.com
|
8
8
|
Generated by: https://openapi-generator.tech
|
9
|
-
Generator version: 7.
|
9
|
+
Generator version: 7.6.0
|
10
10
|
|
11
11
|
=end
|
12
12
|
|
@@ -51,6 +51,7 @@ Coinbase::Client.autoload :StakingContext, 'coinbase/client/models/staking_conte
|
|
51
51
|
Coinbase::Client.autoload :StakingContextContext, 'coinbase/client/models/staking_context_context'
|
52
52
|
Coinbase::Client.autoload :StakingOperation, 'coinbase/client/models/staking_operation'
|
53
53
|
Coinbase::Client.autoload :StakingReward, 'coinbase/client/models/staking_reward'
|
54
|
+
Coinbase::Client.autoload :StakingRewardFormat, 'coinbase/client/models/staking_reward_format'
|
54
55
|
Coinbase::Client.autoload :Trade, 'coinbase/client/models/trade'
|
55
56
|
Coinbase::Client.autoload :TradeList, 'coinbase/client/models/trade_list'
|
56
57
|
Coinbase::Client.autoload :Transaction, 'coinbase/client/models/transaction'
|
data/lib/coinbase/errors.rb
CHANGED
@@ -56,6 +56,13 @@ module Coinbase
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
# An error raised when an operation is attempted with insufficient funds.
|
60
|
+
class InsufficientFundsError < StandardError
|
61
|
+
def initialize(expected, exact, msg = 'Insufficient funds')
|
62
|
+
super("#{msg}: have #{exact}, need #{expected}.")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
59
66
|
class UnimplementedError < APIError; end
|
60
67
|
class UnauthorizedError < APIError; end
|
61
68
|
class InternalError < APIError; end
|
@@ -12,8 +12,6 @@ module Coinbase
|
|
12
12
|
@model = model
|
13
13
|
end
|
14
14
|
|
15
|
-
attr_reader :model
|
16
|
-
|
17
15
|
# Returns the transaction hash.
|
18
16
|
# @return [String] The onchain transaction hash
|
19
17
|
def transaction_hash
|
@@ -23,8 +21,7 @@ module Coinbase
|
|
23
21
|
# Returns the link to the transaction on the blockchain explorer.
|
24
22
|
# @return [String] The link to the transaction on the blockchain explorer
|
25
23
|
def transaction_link
|
26
|
-
|
27
|
-
"https://sepolia.basescan.org/tx/#{transaction_hash}"
|
24
|
+
model.transaction_link
|
28
25
|
end
|
29
26
|
|
30
27
|
# Returns a String representation of the FaucetTransaction.
|
@@ -38,5 +35,9 @@ module Coinbase
|
|
38
35
|
def inspect
|
39
36
|
to_s
|
40
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :model
|
41
42
|
end
|
42
43
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Coinbase
|
4
|
+
# A module of helper methods for paginating through resources.
|
5
|
+
module Pagination
|
6
|
+
def self.enumerate(fetcher, &build_resource)
|
7
|
+
Enumerator.new do |yielder|
|
8
|
+
page = nil
|
9
|
+
|
10
|
+
loop do
|
11
|
+
response = Coinbase.call_api { fetcher.call(page) }
|
12
|
+
|
13
|
+
break if response.data.empty?
|
14
|
+
|
15
|
+
response.data.each do |model|
|
16
|
+
yielder << build_resource.call(model)
|
17
|
+
end
|
18
|
+
|
19
|
+
break unless response.has_more
|
20
|
+
|
21
|
+
page = response.next_page
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Coinbase
|
4
|
+
# A representation of a staking operation (stake, unstake, claim rewards, etc). It
|
5
|
+
# may have multiple steps with some being transactions to sign, and others to wait.
|
6
|
+
# @attr_reader [Array<Coinbase::Transaction>] transactions The list of current
|
7
|
+
# transactions associated with the operation
|
8
|
+
class StakingOperation
|
9
|
+
attr_reader :transactions
|
10
|
+
|
11
|
+
# Returns a new StakingOperation object.
|
12
|
+
# @param model [Coinbase::Client::StakingOperation] The underlying StakingOperation object
|
13
|
+
def initialize(model)
|
14
|
+
@model = model
|
15
|
+
|
16
|
+
@transactions = model.transactions.map do |transaction_model|
|
17
|
+
Transaction.new(transaction_model)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Signs the Open Transactions with the provided key
|
22
|
+
# @param key [Eth::Key] The key to sign the transactions with
|
23
|
+
def sign(key)
|
24
|
+
transactions.each do |transaction|
|
25
|
+
transaction.sign(key) unless transaction.signed?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Coinbase
|
6
|
+
# A representation of a staking reward earned on a network for a given asset.
|
7
|
+
class StakingReward
|
8
|
+
# Returns a list of StakingRewards for the provided network, asset, and addresses.
|
9
|
+
# @param network_id [Symbol] The network ID
|
10
|
+
# @param asset_id [Symbol] The asset ID
|
11
|
+
# @param address_ids [Array<String>] The address IDs
|
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
|
+
# @param format [Symbol] The format to return the rewards in. (:usd, :native) Defaults to :usd.
|
15
|
+
# @return [Enumerable<Coinbase::StakingReward>] The staking rewards
|
16
|
+
def self.list(network_id, asset_id, address_ids, start_time: DateTime.now.prev_month(1), end_time: DateTime.now,
|
17
|
+
format: :usd)
|
18
|
+
asset = Coinbase.call_api do
|
19
|
+
Asset.fetch(network_id, asset_id)
|
20
|
+
end
|
21
|
+
Coinbase::Pagination.enumerate(
|
22
|
+
->(page) { list_page(network_id, asset_id, address_ids, start_time, end_time, page, format) }
|
23
|
+
) do |staking_reward|
|
24
|
+
new(staking_reward, asset, format)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a new StakingReward object.
|
29
|
+
# @param model [Coinbase::Client::StakingReward] The underlying StakingReward object
|
30
|
+
def initialize(model, asset, format)
|
31
|
+
@model = model
|
32
|
+
@asset = asset
|
33
|
+
@format = format
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the amount of the StakingReward.
|
37
|
+
# @return [BigDecimal] The amount
|
38
|
+
def amount
|
39
|
+
return BigDecimal(@model.amount.to_i) / BigDecimal(100) if @format == :usd
|
40
|
+
|
41
|
+
@asset.from_atomic_amount(@model.amount.to_i)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the date of the StakingReward.
|
45
|
+
# @return [Time] The date
|
46
|
+
def date
|
47
|
+
@model.date
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a string representation of the StakingReward.
|
51
|
+
# @return [String] a string representation of the StakingReward
|
52
|
+
def to_s
|
53
|
+
"Coinbase::StakingReward{amount: '#{amount}'}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Same as to_s.
|
57
|
+
# @return [String] a string representation of the StakingReward
|
58
|
+
def inspect
|
59
|
+
to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.stake_api
|
63
|
+
Coinbase::Client::StakeApi.new(Coinbase.configuration.api_client)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.list_page(network_id, asset_id, address_ids, start_time, end_time, page, format)
|
67
|
+
req = {
|
68
|
+
network_id: Coinbase.normalize_network(network_id),
|
69
|
+
asset_id: asset_id,
|
70
|
+
address_ids: address_ids,
|
71
|
+
start_time: start_time.iso8601,
|
72
|
+
end_time: end_time.iso8601,
|
73
|
+
format: format,
|
74
|
+
next_page: page
|
75
|
+
}
|
76
|
+
stake_api.fetch_staking_rewards(req)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/coinbase/transaction.rb
CHANGED
@@ -110,6 +110,12 @@ module Coinbase
|
|
110
110
|
raw.hex
|
111
111
|
end
|
112
112
|
|
113
|
+
# Returns whether the Transaction has been signed.
|
114
|
+
# @return [Boolean] Whether the Transaction has been signed
|
115
|
+
def signed?
|
116
|
+
Eth::Tx.signed?(raw)
|
117
|
+
end
|
118
|
+
|
113
119
|
# Returns a String representation of the Transaction.
|
114
120
|
# @return [String] a String representation of the Transaction
|
115
121
|
def to_s
|
data/lib/coinbase/user.rb
CHANGED
@@ -37,53 +37,17 @@ module Coinbase
|
|
37
37
|
Wallet.import(data)
|
38
38
|
end
|
39
39
|
|
40
|
-
#
|
41
|
-
# @
|
42
|
-
|
43
|
-
|
44
|
-
# any.
|
45
|
-
def wallets(page_size: 10, next_page_token: nil)
|
46
|
-
opts = {
|
47
|
-
limit: page_size
|
48
|
-
}
|
49
|
-
|
50
|
-
opts[:page] = next_page_token unless next_page_token.nil?
|
51
|
-
|
52
|
-
wallet_list = Coinbase.call_api do
|
53
|
-
wallets_api.list_wallets(opts)
|
54
|
-
end
|
55
|
-
|
56
|
-
# A map from wallet_id to address models.
|
57
|
-
address_model_map = {}
|
58
|
-
|
59
|
-
wallet_list.data.each do |wallet_model|
|
60
|
-
addresses_list = Coinbase.call_api do
|
61
|
-
addresses_api.list_addresses(wallet_model.id, { limit: Coinbase::Wallet::MAX_ADDRESSES })
|
62
|
-
end
|
63
|
-
|
64
|
-
address_model_map[wallet_model.id] = addresses_list.data
|
65
|
-
end
|
66
|
-
|
67
|
-
wallets = wallet_list.data.map do |wallet_model|
|
68
|
-
Wallet.new(wallet_model, seed: '', address_models: address_model_map[wallet_model.id])
|
69
|
-
end
|
70
|
-
|
71
|
-
[wallets, wallet_list.next_page]
|
40
|
+
# Enumerates the Wallets belonging to the User.
|
41
|
+
# @return [Enumerator<Coinbase::Wallet>] the Wallets belonging to the User
|
42
|
+
def wallets
|
43
|
+
Wallet.list
|
72
44
|
end
|
73
45
|
|
74
46
|
# Returns the Wallet with the given ID.
|
75
47
|
# @param wallet_id [String] the ID of the Wallet
|
76
48
|
# @return [Coinbase::Wallet] the unhydrated Wallet
|
77
49
|
def wallet(wallet_id)
|
78
|
-
|
79
|
-
wallets_api.get_wallet(wallet_id)
|
80
|
-
end
|
81
|
-
|
82
|
-
addresses_list = Coinbase.call_api do
|
83
|
-
addresses_api.list_addresses(wallet_model.id, { limit: Coinbase::Wallet::MAX_ADDRESSES })
|
84
|
-
end
|
85
|
-
|
86
|
-
Wallet.new(wallet_model, seed: '', address_models: addresses_list.data)
|
50
|
+
Wallet.fetch(wallet_id)
|
87
51
|
end
|
88
52
|
|
89
53
|
# Returns a string representation of the User.
|
@@ -97,15 +61,5 @@ module Coinbase
|
|
97
61
|
def inspect
|
98
62
|
to_s
|
99
63
|
end
|
100
|
-
|
101
|
-
private
|
102
|
-
|
103
|
-
def addresses_api
|
104
|
-
@addresses_api ||= Coinbase::Client::AddressesApi.new(Coinbase.configuration.api_client)
|
105
|
-
end
|
106
|
-
|
107
|
-
def wallets_api
|
108
|
-
@wallets_api ||= Coinbase::Client::WalletsApi.new(Coinbase.configuration.api_client)
|
109
|
-
end
|
110
64
|
end
|
111
65
|
end
|
data/lib/coinbase/wallet.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'digest'
|
4
|
-
require 'jimson'
|
5
4
|
require 'json'
|
6
5
|
require 'money-tree'
|
7
6
|
require 'securerandom'
|
@@ -9,14 +8,14 @@ require 'securerandom'
|
|
9
8
|
module Coinbase
|
10
9
|
# A representation of a Wallet. Wallets come with a single default Address, but can expand to have a set of Addresses,
|
11
10
|
# each of which can hold a balance of one or more Assets. Wallets can create new Addresses, list their addresses,
|
12
|
-
# list their balances, and transfer Assets to other Addresses.
|
13
|
-
# User#import_wallet.
|
11
|
+
# list their balances, and transfer Assets to other Addresses.
|
14
12
|
class Wallet
|
15
|
-
attr_reader :addresses, :model
|
16
|
-
|
17
13
|
# The maximum number of addresses in a Wallet.
|
18
14
|
MAX_ADDRESSES = 20
|
19
15
|
|
16
|
+
# The maximum number of wallets to fetch in a single page.
|
17
|
+
PAGE_LIMIT = 100
|
18
|
+
|
20
19
|
# A representation of ServerSigner status in a Wallet.
|
21
20
|
module ServerSignerStatus
|
22
21
|
# The Wallet is awaiting seed creation by the ServerSigner. At this point,
|
@@ -39,11 +38,31 @@ module Coinbase
|
|
39
38
|
wallets_api.get_wallet(data.wallet_id)
|
40
39
|
end
|
41
40
|
|
42
|
-
|
43
|
-
|
41
|
+
new(model, seed: data.seed)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Enumerates the wallets for the requesting user.
|
45
|
+
# The result is an enumerator that lazily fetches from the server, and can be iterated over,
|
46
|
+
# converted to an array, etc...
|
47
|
+
# @return [Enumerable<Coinbase::Wallet>] Enumerator that returns wallets
|
48
|
+
def list
|
49
|
+
Coinbase::Pagination.enumerate(lambda(&method(:fetch_wallets_page))) do |wallet|
|
50
|
+
Coinbase::Wallet.new(wallet, seed: '')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Fetches a Wallet by its ID.
|
55
|
+
# The returned wallet can be immediately used for signing operations if backed by a server signer.
|
56
|
+
# If the wallet is not backed by a server signer, the wallet's seed will need to be set before
|
57
|
+
# it can be used for signing operations.
|
58
|
+
# @param wallet_id [String] The ID of the Wallet to fetch
|
59
|
+
# @return [Coinbase::Wallet] The fetched Wallet
|
60
|
+
def fetch(wallet_id)
|
61
|
+
model = Coinbase.call_api do
|
62
|
+
wallets_api.get_wallet(wallet_id)
|
44
63
|
end
|
45
64
|
|
46
|
-
new(model, seed:
|
65
|
+
new(model, seed: '')
|
47
66
|
end
|
48
67
|
|
49
68
|
# Creates a new Wallet on the specified Network and generate a default address for it.
|
@@ -58,7 +77,7 @@ module Coinbase
|
|
58
77
|
wallets_api.create_wallet(
|
59
78
|
create_wallet_request: {
|
60
79
|
wallet: {
|
61
|
-
network_id: network_id,
|
80
|
+
network_id: Coinbase.normalize_network(network_id),
|
62
81
|
use_server_signer: Coinbase.use_server_signer?
|
63
82
|
}
|
64
83
|
}
|
@@ -103,7 +122,6 @@ module Coinbase
|
|
103
122
|
self
|
104
123
|
end
|
105
124
|
|
106
|
-
# TODO: Memoize these objects in a thread-safe way at the top-level.
|
107
125
|
def addresses_api
|
108
126
|
Coinbase::Client::AddressesApi.new(Coinbase.configuration.api_client)
|
109
127
|
end
|
@@ -111,6 +129,10 @@ module Coinbase
|
|
111
129
|
def wallets_api
|
112
130
|
Coinbase::Client::WalletsApi.new(Coinbase.configuration.api_client)
|
113
131
|
end
|
132
|
+
|
133
|
+
def fetch_wallets_page(page)
|
134
|
+
wallets_api.list_wallets({ limit: PAGE_LIMIT, page: page })
|
135
|
+
end
|
114
136
|
end
|
115
137
|
|
116
138
|
# Returns a new Wallet object. Do not use this method directly. Instead, use User#create_wallet or
|
@@ -119,21 +141,30 @@ module Coinbase
|
|
119
141
|
# @param seed [String] (Optional) The seed to use for the Wallet. Expects a 32-byte hexadecimal with no 0x prefix.
|
120
142
|
# If nil, a new seed will be generated. If the empty string, no seed is generated, and the Wallet will be
|
121
143
|
# instantiated without a seed and its corresponding private keys.
|
122
|
-
# @param address_models [Array<Coinbase::Client::Address>] (Optional) The models of the addresses already registered
|
123
144
|
# with the Wallet. If not provided, the Wallet will derive the first default address.
|
124
|
-
|
125
|
-
|
126
|
-
validate_seed_and_address_models(seed, address_models) unless Coinbase.use_server_signer?
|
145
|
+
def initialize(model, seed: nil)
|
146
|
+
raise ArgumentError, 'model must be a Wallet' unless model.is_a?(Coinbase::Client::Wallet)
|
127
147
|
|
128
148
|
@model = model
|
129
|
-
@addresses = []
|
130
149
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
150
|
+
return if Coinbase.use_server_signer?
|
151
|
+
|
152
|
+
@master = master_node(seed)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns the addresses belonging to the Wallet.
|
156
|
+
# @return [Array<Coinbase::WalletAddress>] The addresses belonging to the Wallet
|
157
|
+
def addresses
|
158
|
+
@addresses ||= begin
|
159
|
+
address_list = Coinbase.call_api do
|
160
|
+
addresses_api.list_addresses(@model.id, { limit: MAX_ADDRESSES })
|
161
|
+
end
|
135
162
|
|
136
|
-
|
163
|
+
# Build the WalletAddress objects, injecting the key if available.
|
164
|
+
address_list.data.each_with_index.map do |address_model, index|
|
165
|
+
build_wallet_address(address_model, index)
|
166
|
+
end
|
167
|
+
end
|
137
168
|
end
|
138
169
|
|
139
170
|
# Returns the Wallet ID.
|
@@ -157,18 +188,22 @@ module Coinbase
|
|
157
188
|
# Sets the seed of the Wallet. This seed is used to derive keys and sign transactions.
|
158
189
|
# @param seed [String] The seed to set. Expects a 32-byte hexadecimal with no 0x prefix.
|
159
190
|
def seed=(seed)
|
160
|
-
raise ArgumentError, 'Seed must be
|
161
|
-
raise 'Seed is already set' unless @master.nil?
|
162
|
-
|
191
|
+
raise ArgumentError, 'Seed must not be empty' if seed.nil? || seed.empty?
|
192
|
+
raise StandardError, 'Seed is already set' unless @master.nil?
|
193
|
+
|
194
|
+
@master = master_node(seed)
|
163
195
|
|
164
|
-
|
196
|
+
# If the addresses are not loaded the keys will be set on them whenever they are loaded.
|
197
|
+
return if @addresses.nil?
|
165
198
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
raise "Seed does not match wallet; cannot find address #{key.address}" if a.nil?
|
199
|
+
# If addresses are already loaded, set the keys on each address.
|
200
|
+
addresses.each_with_index.each do |address, index|
|
201
|
+
key = derive_key(index)
|
170
202
|
|
171
|
-
a
|
203
|
+
# If we derive a key the derived address must match the address from the API.
|
204
|
+
raise StandardError, 'Seed does not match wallet' unless address.id == key.address.to_s
|
205
|
+
|
206
|
+
address.key = key
|
172
207
|
end
|
173
208
|
end
|
174
209
|
|
@@ -178,7 +213,10 @@ module Coinbase
|
|
178
213
|
opts = { create_address_request: {} }
|
179
214
|
|
180
215
|
unless Coinbase.use_server_signer?
|
181
|
-
|
216
|
+
# The index for the next address is the number of addresses already registered.
|
217
|
+
private_key_index = addresses.count
|
218
|
+
|
219
|
+
key = derive_key(private_key_index)
|
182
220
|
|
183
221
|
opts = {
|
184
222
|
create_address_request: {
|
@@ -193,9 +231,12 @@ module Coinbase
|
|
193
231
|
end
|
194
232
|
|
195
233
|
# Auto-reload wallet to set default address on first address creation.
|
196
|
-
reload if
|
234
|
+
reload if default_address.nil?
|
197
235
|
|
198
|
-
|
236
|
+
# Cache the address in our memoized list
|
237
|
+
address = WalletAddress.new(address_model, key)
|
238
|
+
@addresses << address
|
239
|
+
address
|
199
240
|
end
|
200
241
|
|
201
242
|
# Returns the default address of the Wallet.
|
@@ -208,7 +249,7 @@ module Coinbase
|
|
208
249
|
# @param address_id [String] The ID of the Address to retrieve
|
209
250
|
# @return [Address] The Address
|
210
251
|
def address(address_id)
|
211
|
-
|
252
|
+
addresses.find { |address| address.id == address_id }
|
212
253
|
end
|
213
254
|
|
214
255
|
# Returns the list of balances of this Wallet. Balances are aggregated across all Addresses in the Wallet.
|
@@ -413,10 +454,13 @@ module Coinbase
|
|
413
454
|
end
|
414
455
|
end
|
415
456
|
|
457
|
+
# Returns the master node for the given seed.
|
416
458
|
def master_node(seed)
|
417
459
|
return MoneyTree::Master.new if seed.nil?
|
418
460
|
return nil if seed.empty?
|
419
461
|
|
462
|
+
validate_seed(seed)
|
463
|
+
|
420
464
|
MoneyTree::Master.new(seed_hex: seed)
|
421
465
|
end
|
422
466
|
|
@@ -430,68 +474,15 @@ module Coinbase
|
|
430
474
|
end
|
431
475
|
end
|
432
476
|
|
433
|
-
# Derives
|
434
|
-
# @param address_models [Array<Coinbase::Client::Address>] The models of the addresses already registered with the
|
435
|
-
# Wallet
|
436
|
-
def derive_addresses(address_models)
|
437
|
-
return unless address_models.any?
|
438
|
-
|
439
|
-
# Create a map tracking which addresses are already registered with the Wallet.
|
440
|
-
address_map = build_address_map(address_models)
|
441
|
-
|
442
|
-
address_models.each do |address_model|
|
443
|
-
# Derive the addresses using the provided models.
|
444
|
-
derive_address(address_map, address_model)
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
# Derives an already registered Address in the Wallet.
|
449
|
-
# @param address_map [Hash<String, Boolean>] The map of registered Address IDs
|
450
|
-
# @param address_model [Coinbase::Client::Address] The Address model
|
451
|
-
# @return [Address] The new Address
|
452
|
-
def derive_address(address_map, address_model)
|
453
|
-
key = @master.nil? ? nil : derive_key
|
454
|
-
|
455
|
-
unless key.nil?
|
456
|
-
address_from_key = key.address.to_s
|
457
|
-
raise 'Invalid address' if address_map[address_from_key].nil?
|
458
|
-
end
|
459
|
-
|
460
|
-
cache_address(address_model, key)
|
461
|
-
end
|
462
|
-
|
463
|
-
# Derives a key for an already registered Address in the Wallet.
|
477
|
+
# Derives a key for the given address index.
|
464
478
|
# @return [Eth::Key] The new key
|
465
|
-
def derive_key
|
479
|
+
def derive_key(index)
|
466
480
|
raise 'Cannot derive key for Wallet without seed loaded' if @master.nil?
|
467
481
|
|
468
|
-
path = "#{address_path_prefix}/#{
|
482
|
+
path = "#{address_path_prefix}/#{index}"
|
469
483
|
private_key = @master.node_for_path(path).private_key.to_hex
|
470
|
-
@private_key_index += 1
|
471
|
-
Eth::Key.new(priv: private_key)
|
472
|
-
end
|
473
|
-
|
474
|
-
# Caches an Address on the client-side and increments the address index.
|
475
|
-
# @param address_model [Coinbase::Client::Address] The Address model
|
476
|
-
# @param key [Eth::Key] The private key of the Address
|
477
|
-
# @return [Address] The new Address
|
478
|
-
def cache_address(address_model, key)
|
479
|
-
address = Address.new(address_model, key)
|
480
|
-
@addresses << address
|
481
|
-
address
|
482
|
-
end
|
483
484
|
|
484
|
-
|
485
|
-
# @param address_models [Array<Coinbase::Client::Address>] The models of the addresses already registered with the
|
486
|
-
# Wallet
|
487
|
-
# @return [Hash<String, Boolean>] The Hash of registered Addresses
|
488
|
-
def build_address_map(address_models)
|
489
|
-
address_map = {}
|
490
|
-
address_models.each do |address_model|
|
491
|
-
address_map[address_model.address_id] = true
|
492
|
-
end
|
493
|
-
|
494
|
-
address_map
|
485
|
+
Eth::Key.new(priv: private_key)
|
495
486
|
end
|
496
487
|
|
497
488
|
# Creates an attestation for the Address currently being created.
|
@@ -520,16 +511,9 @@ module Coinbase
|
|
520
511
|
|
521
512
|
# Validates the seed and address models passed to the constructor.
|
522
513
|
# @param seed [String] The seed to use for the Wallet
|
523
|
-
# @
|
524
|
-
|
525
|
-
|
526
|
-
raise ArgumentError, 'Seed must be 32 bytes' if !seed.nil? && !seed.empty? && seed.length != 64
|
527
|
-
|
528
|
-
raise ArgumentError, 'Seed must be present if address_models are provided' if seed.nil? && address_models.any?
|
529
|
-
|
530
|
-
return unless !seed.nil? && seed.empty? && address_models.empty?
|
531
|
-
|
532
|
-
raise ArgumentError, 'Seed must be empty if address_models are not provided'
|
514
|
+
# @raise [ArgumentError] If the seed is invalid
|
515
|
+
def validate_seed(seed)
|
516
|
+
raise ArgumentError, 'Seed must be 32 bytes' unless seed.length == 64
|
533
517
|
end
|
534
518
|
|
535
519
|
# Loads the Hash of Wallet seeds from the given file.
|
@@ -551,6 +535,17 @@ module Coinbase
|
|
551
535
|
pk.dh_compute_key(public_key)
|
552
536
|
end
|
553
537
|
|
538
|
+
def build_wallet_address(address_model, index)
|
539
|
+
# Return an unhydrated wallet address is no master seed is set.
|
540
|
+
return WalletAddress.new(address_model, nil) if @master.nil?
|
541
|
+
|
542
|
+
key = derive_key(index)
|
543
|
+
|
544
|
+
raise StandardError, 'Seed does not match wallet' unless address_model.address_id == key.address.to_s
|
545
|
+
|
546
|
+
WalletAddress.new(address_model, key)
|
547
|
+
end
|
548
|
+
|
554
549
|
def addresses_api
|
555
550
|
@addresses_api ||= Coinbase::Client::AddressesApi.new(Coinbase.configuration.api_client)
|
556
551
|
end
|
data/lib/coinbase.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'coinbase/address'
|
4
|
+
require_relative 'coinbase/address/wallet_address'
|
5
|
+
require_relative 'coinbase/address/external_address'
|
4
6
|
require_relative 'coinbase/asset'
|
5
7
|
require_relative 'coinbase/authenticator'
|
6
8
|
require_relative 'coinbase/balance'
|
@@ -11,12 +13,15 @@ require_relative 'coinbase/errors'
|
|
11
13
|
require_relative 'coinbase/faucet_transaction'
|
12
14
|
require_relative 'coinbase/middleware'
|
13
15
|
require_relative 'coinbase/network'
|
16
|
+
require_relative 'coinbase/pagination'
|
14
17
|
require_relative 'coinbase/trade'
|
15
18
|
require_relative 'coinbase/transfer'
|
16
19
|
require_relative 'coinbase/transaction'
|
17
20
|
require_relative 'coinbase/user'
|
18
21
|
require_relative 'coinbase/wallet'
|
19
22
|
require_relative 'coinbase/server_signer'
|
23
|
+
require_relative 'coinbase/staking_operation'
|
24
|
+
require_relative 'coinbase/staking_reward'
|
20
25
|
require 'json'
|
21
26
|
|
22
27
|
# The Coinbase SDK.
|
@@ -129,4 +134,10 @@ module Coinbase
|
|
129
134
|
def self.use_server_signer?
|
130
135
|
Coinbase.configuration.use_server_signer
|
131
136
|
end
|
137
|
+
|
138
|
+
# Returns whether the SDK is configured.
|
139
|
+
# @return [bool] whether the SDK is configured
|
140
|
+
def self.configured?
|
141
|
+
!Coinbase.configuration.api_key_name.nil? && !Coinbase.configuration.api_key_private_key.nil?
|
142
|
+
end
|
132
143
|
end
|