coinbase-sdk 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|