coinbase 0.0.1 → 4.0.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of coinbase might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +611 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/coinbase.gemspec +27 -0
- data/lib/coinbase/wallet/adapters/em_http.rb +78 -0
- data/lib/coinbase/wallet/adapters/net_http.rb +68 -0
- data/lib/coinbase/wallet/api_client.rb +692 -0
- data/lib/coinbase/wallet/api_errors.rb +120 -0
- data/lib/coinbase/wallet/api_response.rb +41 -0
- data/lib/coinbase/wallet/ca-coinbase.crt +629 -0
- data/lib/coinbase/wallet/client.rb +101 -0
- data/lib/coinbase/wallet/models/account.rb +187 -0
- data/lib/coinbase/wallet/models/address.rb +12 -0
- data/lib/coinbase/wallet/models/api_object.rb +46 -0
- data/lib/coinbase/wallet/models/checkout.rb +19 -0
- data/lib/coinbase/wallet/models/order.rb +12 -0
- data/lib/coinbase/wallet/models/transaction.rb +21 -0
- data/lib/coinbase/wallet/models/transfer.rb +13 -0
- data/lib/coinbase/wallet/models/user.rb +15 -0
- data/lib/coinbase/wallet/version.rb +5 -0
- data/lib/coinbase/wallet.rb +23 -156
- data/spec/account_spec.rb +193 -0
- data/spec/clients/client_spec.rb +34 -0
- data/spec/clients/oauth_client_spec.rb +56 -0
- data/spec/endpoints_spec.rb +352 -0
- data/spec/error_spec.rb +137 -0
- data/spec/models/address_spec.rb +26 -0
- data/spec/models/api_object_spec.rb +63 -0
- data/spec/models/checkout_spec.rb +52 -0
- data/spec/models/current_user_spec.rb +29 -0
- data/spec/models/order_spec.rb +52 -0
- data/spec/models/request_spec.rb +47 -0
- data/spec/models/transfer_spec.rb +64 -0
- data/spec/models/user_spec.rb +24 -0
- data/spec/spec_helper.rb +13 -0
- metadata +62 -98
- data/lib/coinbase/address.rb +0 -127
- data/lib/coinbase/asset.rb +0 -20
- data/lib/coinbase/balance_map.rb +0 -48
- data/lib/coinbase/constants.rb +0 -38
- data/lib/coinbase/network.rb +0 -55
- data/lib/coinbase/transfer.rb +0 -153
- data/lib/coinbase.rb +0 -18
@@ -0,0 +1,101 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Wallet
|
3
|
+
BASE_API_URL = "https://api.coinbase.com"
|
4
|
+
API_VERSION = '2015-06-16'
|
5
|
+
|
6
|
+
class Client < NetHTTPClient
|
7
|
+
def initialize(options={})
|
8
|
+
[ :api_key, :api_secret ].each do |opt|
|
9
|
+
raise unless options.has_key? opt
|
10
|
+
end
|
11
|
+
@api_key = options[:api_key]
|
12
|
+
@api_secret = options[:api_secret]
|
13
|
+
@api_uri = URI.parse(options[:api_url] || BASE_API_URL)
|
14
|
+
super(@api_uri, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def auth_headers(method, path, body)
|
18
|
+
ts = Time.now.to_i.to_s
|
19
|
+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
|
20
|
+
@api_secret,
|
21
|
+
ts + method + path + body.to_s)
|
22
|
+
{ 'CB-ACCESS-KEY' => @api_key,
|
23
|
+
'CB-ACCESS-SIGN' => signature,
|
24
|
+
'CB-ACCESS-TIMESTAMP' => ts,
|
25
|
+
'CB-VERSION' => API_VERSION }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class OAuthClient < NetHTTPClient
|
30
|
+
attr_accessor :access_token, :refresh_token
|
31
|
+
|
32
|
+
def initialize(options={})
|
33
|
+
raise unless options.has_key? :access_token
|
34
|
+
@access_token = options[:access_token]
|
35
|
+
@refresh_token = options[:refresh_token]
|
36
|
+
@oauth_uri = URI.parse(options[:api_url] || BASE_API_URL)
|
37
|
+
super(@oauth_uri, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def auth_headers(method, path, body)
|
41
|
+
{ 'Authorization' => "Bearer #{@access_token}",
|
42
|
+
'CB-VERSION' => API_VERSION }
|
43
|
+
end
|
44
|
+
|
45
|
+
def authorize!(redirect_url, params = {})
|
46
|
+
raise NotImplementedError
|
47
|
+
end
|
48
|
+
|
49
|
+
def revoke!(params = {})
|
50
|
+
params[:token] ||= @access_token
|
51
|
+
|
52
|
+
out = nil
|
53
|
+
post("/oauth/revoke", params) do |resp|
|
54
|
+
out = APIObject.new(self, resp.body)
|
55
|
+
yield(out, resp) if block_given?
|
56
|
+
end
|
57
|
+
out
|
58
|
+
end
|
59
|
+
|
60
|
+
def refresh!(params = {})
|
61
|
+
params[:grant_type] = 'refresh_token'
|
62
|
+
params[:refresh_token] ||= @refresh_token
|
63
|
+
|
64
|
+
raise "Missing Parameter: refresh_token" unless params.has_key?(:refresh_token)
|
65
|
+
|
66
|
+
out = nil
|
67
|
+
post("/oauth/token", params) do |resp|
|
68
|
+
out = APIObject.new(self, resp.body)
|
69
|
+
# Update tokens to current instance
|
70
|
+
# Developer should always persist them
|
71
|
+
@access_token = out.access_token
|
72
|
+
@refresh_token = out.refresh_token
|
73
|
+
yield(out, resp) if block_given?
|
74
|
+
end
|
75
|
+
out
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class AsyncClient < EMHTTPClient
|
80
|
+
def initialize(options={})
|
81
|
+
[ :api_key, :api_secret ].each do |opt|
|
82
|
+
raise unless options.has_key? opt
|
83
|
+
end
|
84
|
+
@api_key = options[:api_key]
|
85
|
+
@api_secret = options[:api_secret]
|
86
|
+
@api_uri = URI.parse(options[:api_url] || BASE_API_URL)
|
87
|
+
end
|
88
|
+
|
89
|
+
def auth_headers(method, path, body)
|
90
|
+
ts = Time.now.to_i.to_s
|
91
|
+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
|
92
|
+
@api_secret,
|
93
|
+
ts + method + path + body.to_s)
|
94
|
+
{ 'CB-ACCESS-KEY' => @api_key,
|
95
|
+
'CB-ACCESS-SIGN' => signature,
|
96
|
+
'CB-ACCESS-TIMESTAMP' => ts,
|
97
|
+
'CB-VERSION' => API_VERSION }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Wallet
|
3
|
+
class Account < APIObject
|
4
|
+
def update!(params = {})
|
5
|
+
@client.update_account(self['id'], params) do |data, resp|
|
6
|
+
update(data)
|
7
|
+
yield(data, resp) if block_given?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def make_primary!(params = {})
|
12
|
+
@client.set_primary_account(self['id'], params) do |data, resp|
|
13
|
+
update(data)
|
14
|
+
yield(data, resp) if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete!(params = {})
|
19
|
+
@client.delete_account(self['id'], params) do |data, resp|
|
20
|
+
yield(data, resp) if block_given?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Addresses
|
26
|
+
#
|
27
|
+
def addresses(params = {})
|
28
|
+
@client.addresses(self['id'], params) do |data, resp|
|
29
|
+
yield(data, resp) if block_given?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def address(address_id, params = {})
|
34
|
+
@client.address(self['id'], address_id, params) do |data, resp|
|
35
|
+
yield(data, resp) if block_given?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_address(params = {})
|
40
|
+
@client.create_address(self['id']) do |data, resp|
|
41
|
+
yield(data, resp) if block_given?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Transactions
|
47
|
+
#
|
48
|
+
def transactions(params = {})
|
49
|
+
@client.transactions(self['id'], params) do |data, resp|
|
50
|
+
yield(data, resp) if block_given?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def transaction(transaction_id, params = {})
|
55
|
+
@client.transaction(self['id'], transaction_id, params) do |data, resp|
|
56
|
+
yield(data, resp) if block_given?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def send(params = {})
|
61
|
+
@client.send(self['id'], params) do |data, resp|
|
62
|
+
yield(data, resp) if block_given?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def transfer(params = {})
|
67
|
+
@client.transfer(self['id'], params) do |data, resp|
|
68
|
+
yield(data, resp) if block_given?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def request(params = {})
|
73
|
+
@client.request(self['id'], params) do |data, resp|
|
74
|
+
yield(data, resp) if block_given?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Buys
|
80
|
+
#
|
81
|
+
def list_buys(params = {})
|
82
|
+
@client.list_buys(self['id'], params) do |data, resp|
|
83
|
+
yield(data, resp) if block_given?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def list_buy(transaction_id, params = {})
|
88
|
+
@client.list_buy(self['id'], transaction_id, params) do |data, resp|
|
89
|
+
yield(data, resp) if block_given?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def buy(params = {})
|
94
|
+
@client.buy(self['id'], params) do |data, resp|
|
95
|
+
yield(data, resp) if block_given?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def commit_buy(transaction_id, params = {})
|
100
|
+
@client.commit_buy(self['id'], transaction_id, params) do |data, resp|
|
101
|
+
yield(data, resp) if block_given?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Sells
|
107
|
+
#
|
108
|
+
def list_sells(params = {})
|
109
|
+
@client.list_sells(self['id'], params) do |data, resp|
|
110
|
+
yield(data, resp) if block_given?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def list_sell(transaction_id, params = {})
|
115
|
+
@client.list_sell(self['id'], transaction_id, params) do |data, resp|
|
116
|
+
yield(data, resp) if block_given?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def sell(params = {})
|
121
|
+
@client.sell(self['id'], params) do |data, resp|
|
122
|
+
yield(data, resp) if block_given?
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def commit_sell(transaction_id, params = {})
|
127
|
+
@client.commit_sell(self['id'], transaction_id, params) do |data, resp|
|
128
|
+
yield(data, resp) if block_given?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Deposit
|
134
|
+
#
|
135
|
+
def list_deposits(params = {})
|
136
|
+
@client.list_deposits(self['id'], params) do |data, resp|
|
137
|
+
yield(data, resp) if block_given?
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def list_deposit(transaction_id, params = {})
|
142
|
+
@client.list_deposit(self['id'], transaction_id, params) do |data, resp|
|
143
|
+
yield(data, resp) if block_given?
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def deposit(params = {})
|
148
|
+
@client.deposit(self['id'], params) do |data, resp|
|
149
|
+
yield(data, resp) if block_given?
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def commit_deposit(transaction_id, params = {})
|
154
|
+
@client.commit_deposit(self['id'], transaction_id, params) do |data, resp|
|
155
|
+
yield(data, resp) if block_given?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Withdrawals
|
161
|
+
#
|
162
|
+
def list_withdrawals(params = {})
|
163
|
+
@client.list_withdrawals(self['id'], params) do |data, resp|
|
164
|
+
yield(data, resp) if block_given?
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def list_withdrawal(transaction_id, params = {})
|
169
|
+
@client.list_withdrawal(self['id'], transaction_id, params) do |data, resp|
|
170
|
+
yield(data, resp) if block_given?
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def withdraw(params = {})
|
175
|
+
@client.withdraw(self['id'], params) do |data, resp|
|
176
|
+
yield(data, resp) if block_given?
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def commit_withdrawal(transaction_id, params = {})
|
181
|
+
@client.commit_withdrawal(self['id'], transaction_id, params) do |data, resp|
|
182
|
+
yield(data, resp) if block_given?
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Wallet
|
3
|
+
class Address < APIObject
|
4
|
+
def transactions(params = {})
|
5
|
+
@client.get("#{self['resource_path']}/transactions", params) do |resp|
|
6
|
+
out = resp.data.map { |item| Transaction.new(self, item) }
|
7
|
+
yield(out, resp) if block_given?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Wallet
|
3
|
+
# Response item abstract model
|
4
|
+
class APIObject < Hash
|
5
|
+
def initialize(client, data)
|
6
|
+
super()
|
7
|
+
update(data)
|
8
|
+
@client = client
|
9
|
+
end
|
10
|
+
|
11
|
+
def refresh!(params = {})
|
12
|
+
@client.get(self['resource_path'], params) do |resp|
|
13
|
+
update(resp.data)
|
14
|
+
yield(resp.data, resp) if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def update(data)
|
19
|
+
return if data.nil?
|
20
|
+
data.each { |key, val| self[key] = val } if data.is_a?(Hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
def format(key, val)
|
24
|
+
return if val.nil?
|
25
|
+
# Looks like a number or currency
|
26
|
+
if val.class == Hash
|
27
|
+
APIObject.new(@client, val)
|
28
|
+
elsif key =~ /_at$/ && (Time.iso8601(val) rescue nil)
|
29
|
+
Time.parse(val)
|
30
|
+
elsif key == "amount" && val =~ /^.{0,1}\s*[0-9,]*\.{0,1}[0-9]*$/
|
31
|
+
BigDecimal(val.gsub(/[^0-9\.]/, ''))
|
32
|
+
else
|
33
|
+
val
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_missing(method, *args, &blk)
|
38
|
+
format(method.to_s, self[method.to_s]) || super
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_to_missing?(method, include_all = false)
|
42
|
+
self.key?(method.to_s) || super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Wallet
|
3
|
+
class Checkout < APIObject
|
4
|
+
def orders(params = {})
|
5
|
+
@client.get("#{self['resource_path']}/orders", params) do |resp|
|
6
|
+
out = resp.data.map { |item| Order.new(self, item) }
|
7
|
+
yield(out, resp) if block_given?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_order(params = {})
|
12
|
+
@client.post("#{self['resource_path']}/orders", params) do |resp|
|
13
|
+
out = Order.new(self, resp.data)
|
14
|
+
yield(out, resp) if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Wallet
|
3
|
+
class Transaction < APIObject
|
4
|
+
end
|
5
|
+
|
6
|
+
class Request < Transaction
|
7
|
+
def resend!(params = {})
|
8
|
+
@client.post("#{self['resource_path']}/resend", params) do |resp|
|
9
|
+
yield(resp.data, resp) if block_given?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def cancel!(params = {})
|
14
|
+
@client.delete("#{self['resource_path']}", params) do |resp|
|
15
|
+
yield(resp.data, resp) if block_given?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Coinbase
|
2
|
+
module Wallet
|
3
|
+
class User < APIObject
|
4
|
+
end
|
5
|
+
|
6
|
+
class CurrentUser < User
|
7
|
+
def update!(params = {})
|
8
|
+
@client.update_current_user(params) do |data, resp|
|
9
|
+
update(resp.data)
|
10
|
+
yield(resp.data, resp) if block_given?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/coinbase/wallet.rb
CHANGED
@@ -1,160 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require "coinbase/wallet/version"
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "bigdecimal"
|
5
|
+
require "json"
|
6
|
+
require "uri"
|
7
|
+
require "net/https"
|
8
|
+
|
9
|
+
require "coinbase/wallet/api_errors"
|
10
|
+
require "coinbase/wallet/api_response"
|
11
|
+
require "coinbase/wallet/api_client"
|
12
|
+
require "coinbase/wallet/adapters/net_http.rb"
|
13
|
+
require "coinbase/wallet/adapters/em_http.rb"
|
14
|
+
require "coinbase/wallet/models/api_object"
|
15
|
+
require "coinbase/wallet/models/account"
|
16
|
+
require "coinbase/wallet/models/address"
|
17
|
+
require "coinbase/wallet/models/user"
|
18
|
+
require "coinbase/wallet/models/transaction"
|
19
|
+
require "coinbase/wallet/models/transfer"
|
20
|
+
require "coinbase/wallet/models/order"
|
21
|
+
require "coinbase/wallet/models/checkout"
|
22
|
+
require "coinbase/wallet/client"
|
6
23
|
|
7
24
|
module Coinbase
|
8
|
-
|
9
|
-
# each of which can hold a balance of one or more Assets. Wallets can create new Addresses, list their addresses,
|
10
|
-
# list their balances, and transfer Assets to other Addresses.
|
11
|
-
class Wallet
|
12
|
-
attr_reader :wallet_id, :network_id
|
13
|
-
|
14
|
-
# Returns a new Wallet object.
|
15
|
-
# @param seed [Integer] (Optional) The seed to use for the Wallet. Expects a 32-byte hexadecimal. If not provided,
|
16
|
-
# a new seed will be generated.
|
17
|
-
# @param address_count [Integer] (Optional) The number of addresses to generate for the Wallet. If not provided,
|
18
|
-
# a single address will be generated.
|
19
|
-
# @param client [Jimson::Client] (Optional) The JSON RPC client to use for interacting with the Network
|
20
|
-
def initialize(seed: nil, address_count: 1, client: Jimson::Client.new(ENV.fetch('BASE_SEPOLIA_RPC_URL', nil)))
|
21
|
-
raise ArgumentError, 'Seed must be 32 bytes' if !seed.nil? && seed.length != 64
|
22
|
-
raise ArgumentError, 'Address count must be positive' if address_count < 1
|
23
|
-
|
24
|
-
@master = seed.nil? ? MoneyTree::Master.new : MoneyTree::Master.new(seed_hex: seed)
|
25
|
-
|
26
|
-
@wallet_id = SecureRandom.uuid
|
27
|
-
# TODO: Make Network an argument to the constructor.
|
28
|
-
@network_id = :base_sepolia
|
29
|
-
@addresses = []
|
30
|
-
|
31
|
-
# TODO: Adjust derivation path prefix based on network protocol.
|
32
|
-
@address_path_prefix = "m/44'/60'/0'/0"
|
33
|
-
@address_index = 0
|
34
|
-
|
35
|
-
@client = client
|
36
|
-
|
37
|
-
address_count.times { create_address }
|
38
|
-
end
|
39
|
-
|
40
|
-
# Creates a new Address in the Wallet.
|
41
|
-
# @return [Address] The new Address
|
42
|
-
def create_address
|
43
|
-
# TODO: Register with server.
|
44
|
-
path = "#{@address_path_prefix}/#{@address_index}"
|
45
|
-
private_key = @master.node_for_path(path).private_key.to_hex
|
46
|
-
key = Eth::Key.new(priv: private_key)
|
47
|
-
address = Address.new(@network_id, key.address.address, @wallet_id, key, client: @client)
|
48
|
-
@addresses << address
|
49
|
-
@address_index += 1
|
50
|
-
address
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns the default address of the Wallet.
|
54
|
-
# @return [Address] The default address
|
55
|
-
def default_address
|
56
|
-
@addresses.first
|
57
|
-
end
|
58
|
-
|
59
|
-
# Returns the Address with the given ID.
|
60
|
-
# @param address_id [String] The ID of the Address to retrieve
|
61
|
-
# @return [Address] The Address
|
62
|
-
def get_address(address_id)
|
63
|
-
@addresses.find { |address| address.address_id == address_id }
|
64
|
-
end
|
65
|
-
|
66
|
-
# Returns the list of Addresses in the Wallet.
|
67
|
-
# @return [Array<Address>] The list of Addresses
|
68
|
-
def list_addresses
|
69
|
-
# TODO: Register with server.
|
70
|
-
@addresses
|
71
|
-
end
|
72
|
-
|
73
|
-
# Returns the list of balances of this Wallet. Balances are aggregated across all Addresses in the Wallet.
|
74
|
-
# @return [BalanceMap] The list of balances. The key is the Asset ID, and the value is the balance.
|
75
|
-
def list_balances
|
76
|
-
balance_map = BalanceMap.new
|
77
|
-
|
78
|
-
@addresses.each do |address|
|
79
|
-
address.list_balances.each do |asset_id, balance|
|
80
|
-
balance_map[asset_id] ||= BigDecimal(0)
|
81
|
-
current_balance = balance_map[asset_id]
|
82
|
-
new_balance = balance + current_balance
|
83
|
-
balance_map[asset_id] = new_balance
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
balance_map
|
88
|
-
end
|
89
|
-
|
90
|
-
# Returns the balance of the provided Asset. Balances are aggregated across all Addresses in the Wallet.
|
91
|
-
# @param asset_id [Symbol] The ID of the Asset to retrieve the balance for
|
92
|
-
# @return [BigDecimal] The balance of the Asset
|
93
|
-
def get_balance(asset_id)
|
94
|
-
normalized_asset_id = if %i[wei gwei].include?(asset_id)
|
95
|
-
:eth
|
96
|
-
else
|
97
|
-
asset_id
|
98
|
-
end
|
99
|
-
|
100
|
-
eth_balance = list_balances[normalized_asset_id] || BigDecimal(0)
|
101
|
-
|
102
|
-
case asset_id
|
103
|
-
when :eth
|
104
|
-
eth_balance
|
105
|
-
when :gwei
|
106
|
-
eth_balance * Coinbase::GWEI_PER_ETHER
|
107
|
-
when :wei
|
108
|
-
eth_balance * Coinbase::WEI_PER_ETHER
|
109
|
-
else
|
110
|
-
BigDecimal(0)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# Transfers the given amount of the given Asset to the given address. Only same-Network Transfers are supported.
|
115
|
-
# Currently only the default_address is used to source the Transfer.
|
116
|
-
# @param amount [Integer, Float, BigDecimal] The amount of the Asset to send
|
117
|
-
# @param asset_id [Symbol] The ID of the Asset to send
|
118
|
-
# @param destination [Wallet | Address | String] The destination of the transfer. If a Wallet, sends to the Wallet's
|
119
|
-
# default address. If a String, interprets it as the address ID.
|
120
|
-
# @return [Transfer] The hash of the Transfer transaction.
|
121
|
-
def transfer(amount, asset_id, destination)
|
122
|
-
if destination.is_a?(Wallet)
|
123
|
-
raise ArgumentError, 'Transfer must be on the same Network' if destination.network_id != @network_id
|
124
|
-
|
125
|
-
destination = destination.default_address.address_id
|
126
|
-
elsif destination.is_a?(Address)
|
127
|
-
raise ArgumentError, 'Transfer must be on the same Network' if destination.network_id != @network_id
|
128
|
-
|
129
|
-
destination = destination.address_id
|
130
|
-
end
|
131
|
-
|
132
|
-
default_address.transfer(amount, asset_id, destination)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Exports the Wallet's data to a WalletData object.
|
136
|
-
# @return [WalletData] The Wallet data
|
137
|
-
def export
|
138
|
-
WalletData.new(@master.seed_hex, @addresses.length)
|
139
|
-
end
|
140
|
-
|
141
|
-
# The data required to recreate a Wallet.
|
142
|
-
class WalletData
|
143
|
-
attr_reader :seed, :address_count
|
144
|
-
|
145
|
-
# Returns a new WalletData object.
|
146
|
-
# @param seed [String] The seed of the Wallet
|
147
|
-
# @param address_count [Integer] The number of addresses in the Wallet
|
148
|
-
def initialize(seed, address_count)
|
149
|
-
@seed = seed
|
150
|
-
@address_count = address_count
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
# Returns the data required to recreate the Wallet.
|
155
|
-
# @return [WalletData] The Wallet data
|
156
|
-
def to_data
|
157
|
-
WalletData.new(@master.seed_hex, @addresses.length)
|
158
|
-
end
|
25
|
+
module Wallet
|
159
26
|
end
|
160
27
|
end
|