coinbase-sdk 0.0.1 → 0.0.3
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.rb +152 -51
- data/lib/coinbase/asset.rb +2 -1
- data/lib/coinbase/authenticator.rb +52 -0
- data/lib/coinbase/balance_map.rb +2 -2
- data/lib/coinbase/client/api/addresses_api.rb +454 -0
- data/lib/coinbase/client/api/transfers_api.rb +342 -0
- data/lib/coinbase/client/api/users_api.rb +79 -0
- data/lib/coinbase/client/api/wallets_api.rb +348 -0
- data/lib/coinbase/client/api_client.rb +431 -0
- data/lib/coinbase/client/api_error.rb +58 -0
- data/lib/coinbase/client/configuration.rb +375 -0
- data/lib/coinbase/client/models/address.rb +273 -0
- data/lib/coinbase/client/models/address_balance_list.rb +275 -0
- data/lib/coinbase/client/models/address_list.rb +275 -0
- data/lib/coinbase/client/models/asset.rb +260 -0
- data/lib/coinbase/client/models/balance.rb +239 -0
- data/lib/coinbase/client/models/broadcast_transfer_request.rb +222 -0
- data/lib/coinbase/client/models/create_address_request.rb +239 -0
- data/lib/coinbase/client/models/create_transfer_request.rb +273 -0
- data/lib/coinbase/client/models/create_wallet_request.rb +221 -0
- data/lib/coinbase/client/models/error.rb +278 -0
- data/lib/coinbase/client/models/faucet_transaction.rb +222 -0
- data/lib/coinbase/client/models/transfer.rb +413 -0
- data/lib/coinbase/client/models/transfer_list.rb +275 -0
- data/lib/coinbase/client/models/user.rb +231 -0
- data/lib/coinbase/client/models/wallet.rb +241 -0
- data/lib/coinbase/client/models/wallet_list.rb +275 -0
- data/lib/coinbase/client/version.rb +15 -0
- data/lib/coinbase/client.rb +59 -0
- data/lib/coinbase/constants.rb +8 -2
- data/lib/coinbase/errors.rb +120 -0
- data/lib/coinbase/faucet_transaction.rb +42 -0
- data/lib/coinbase/middleware.rb +21 -0
- data/lib/coinbase/network.rb +2 -2
- data/lib/coinbase/transfer.rb +106 -65
- data/lib/coinbase/user.rb +180 -0
- data/lib/coinbase/wallet.rb +168 -52
- data/lib/coinbase.rb +127 -9
- metadata +92 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cac6b46a83009db9f165f0a2c4396122a2bf4bfa5bf895867b135695c346465e
|
4
|
+
data.tar.gz: 0b8cfea979b389b9890a09104b8c7533e334873a789b19d7f704789cdcf6a70d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bccd3af15fec48789603988eadf3c67ad4ec67e5b3081fd2f06c7877d6ac35c6d2eda16db533ff149cf2f9f38d59ba38527284a0205529c84da6ac781dad8e3a
|
7
|
+
data.tar.gz: 072d5372f38da2b9a3eb97a024f4a289ac7ef1906df8cf1da22de0b37c77e18b9a724db7771019fc9c6eb3834720f48010edb869f310dcc114429cb0e0bd1403
|
data/lib/coinbase/address.rb
CHANGED
@@ -2,65 +2,77 @@
|
|
2
2
|
|
3
3
|
require_relative 'balance_map'
|
4
4
|
require_relative 'constants'
|
5
|
+
require_relative 'wallet'
|
5
6
|
require 'bigdecimal'
|
6
7
|
require 'eth'
|
7
8
|
require 'jimson'
|
8
9
|
|
9
10
|
module Coinbase
|
10
11
|
# A representation of a blockchain Address, which is a user-controlled account on a Network. Addresses are used to
|
11
|
-
# send and receive Assets, and should be created using
|
12
|
-
#
|
12
|
+
# send and receive Assets, and should be created using Wallet#create_address. Addresses require an
|
13
|
+
# Eth::Key to sign transaction data.
|
13
14
|
class Address
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
# @param network_id [Symbol] The ID of the Network on which the Address exists
|
18
|
-
# @param address_id [String] The ID of the Address. On EVM Networks, for example, this is a hash of the public key.
|
19
|
-
# @param wallet_id [String] The ID of the Wallet to which the Address belongs
|
15
|
+
# Returns a new Address object. Do not use this method directly. Instead, use Wallet#create_address, or use
|
16
|
+
# the Wallet's default_address.
|
17
|
+
# @param model [Coinbase::Client::Address] The underlying Address object
|
20
18
|
# @param key [Eth::Key] The key backing the Address
|
21
|
-
|
22
|
-
|
23
|
-
client: Jimson::Client.new(Coinbase.base_sepolia_rpc_url))
|
24
|
-
# TODO: Don't require key.
|
25
|
-
@network_id = network_id
|
26
|
-
@address_id = address_id
|
27
|
-
@wallet_id = wallet_id
|
19
|
+
def initialize(model, key)
|
20
|
+
@model = model
|
28
21
|
@key = key
|
29
|
-
@client = client
|
30
22
|
end
|
31
23
|
|
32
|
-
# Returns the
|
24
|
+
# Returns the Network ID of the Address.
|
25
|
+
# @return [Symbol] The Network ID
|
26
|
+
def network_id
|
27
|
+
Coinbase.to_sym(@model.network_id)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the Wallet ID of the Address.
|
31
|
+
# @return [String] The Wallet ID
|
32
|
+
def wallet_id
|
33
|
+
@model.wallet_id
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the Address ID.
|
37
|
+
# @return [String] The Address ID
|
38
|
+
def address_id
|
39
|
+
@model.address_id
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the balances of the Address.
|
33
43
|
# @return [BalanceMap] The balances of the Address, keyed by asset ID. Ether balances are denominated
|
34
44
|
# in ETH.
|
35
45
|
def list_balances
|
36
|
-
|
37
|
-
|
38
|
-
|
46
|
+
response = Coinbase.call_api do
|
47
|
+
addresses_api.list_address_balances(wallet_id, address_id)
|
48
|
+
end
|
39
49
|
|
40
|
-
|
50
|
+
Coinbase.to_balance_map(response)
|
41
51
|
end
|
42
52
|
|
43
|
-
# Returns the balance of the provided Asset.
|
53
|
+
# Returns the balance of the provided Asset.
|
44
54
|
# @param asset_id [Symbol] The Asset to retrieve the balance for
|
45
55
|
# @return [BigDecimal] The balance of the Asset
|
46
56
|
def get_balance(asset_id)
|
47
|
-
normalized_asset_id =
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
57
|
+
normalized_asset_id = normalize_asset_id(asset_id)
|
58
|
+
|
59
|
+
response = Coinbase.call_api do
|
60
|
+
addresses_api.get_address_balance(wallet_id, address_id, normalized_asset_id.to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
return BigDecimal('0') if response.nil?
|
52
64
|
|
53
|
-
|
65
|
+
amount = BigDecimal(response.amount)
|
54
66
|
|
55
67
|
case asset_id
|
56
68
|
when :eth
|
57
|
-
|
69
|
+
amount / BigDecimal(Coinbase::WEI_PER_ETHER.to_s)
|
58
70
|
when :gwei
|
59
|
-
|
60
|
-
when :
|
61
|
-
|
71
|
+
amount / BigDecimal(Coinbase::GWEI_PER_ETHER.to_s)
|
72
|
+
when :usdc
|
73
|
+
amount / BigDecimal(Coinbase::ATOMIC_UNITS_PER_USDC.to_s)
|
62
74
|
else
|
63
|
-
|
75
|
+
amount
|
64
76
|
end
|
65
77
|
end
|
66
78
|
|
@@ -71,15 +83,14 @@ module Coinbase
|
|
71
83
|
# default address. If a String, interprets it as the address ID.
|
72
84
|
# @return [String] The hash of the Transfer transaction.
|
73
85
|
def transfer(amount, asset_id, destination)
|
74
|
-
# TODO: Handle multiple currencies.
|
75
86
|
raise ArgumentError, "Unsupported asset: #{asset_id}" unless Coinbase::SUPPORTED_ASSET_IDS[asset_id]
|
76
87
|
|
77
88
|
if destination.is_a?(Wallet)
|
78
|
-
raise ArgumentError, 'Transfer must be on the same Network' if destination.network_id !=
|
89
|
+
raise ArgumentError, 'Transfer must be on the same Network' if destination.network_id != network_id
|
79
90
|
|
80
91
|
destination = destination.default_address.address_id
|
81
92
|
elsif destination.is_a?(Address)
|
82
|
-
raise ArgumentError, 'Transfer must be on the same Network' if destination.network_id !=
|
93
|
+
raise ArgumentError, 'Transfer must be on the same Network' if destination.network_id != network_id
|
83
94
|
|
84
95
|
destination = destination.address_id
|
85
96
|
end
|
@@ -89,39 +100,129 @@ module Coinbase
|
|
89
100
|
raise ArgumentError, "Insufficient funds: #{amount} requested, but only #{current_balance} available"
|
90
101
|
end
|
91
102
|
|
92
|
-
|
93
|
-
|
103
|
+
normalized_amount = normalize_asset_amount(amount, asset_id)
|
104
|
+
|
105
|
+
normalized_asset_id = normalize_asset_id(asset_id)
|
106
|
+
|
107
|
+
create_transfer_request = {
|
108
|
+
amount: normalized_amount.to_i.to_s,
|
109
|
+
network_id: network_id,
|
110
|
+
asset_id: normalized_asset_id.to_s,
|
111
|
+
destination: destination
|
112
|
+
}
|
113
|
+
|
114
|
+
transfer_model = Coinbase.call_api do
|
115
|
+
transfers_api.create_transfer(wallet_id, address_id, create_transfer_request)
|
116
|
+
end
|
117
|
+
|
118
|
+
transfer = Coinbase::Transfer.new(transfer_model)
|
94
119
|
|
95
120
|
transaction = transfer.transaction
|
96
121
|
transaction.sign(@key)
|
97
|
-
@client.eth_sendRawTransaction("0x#{transaction.hex}")
|
98
122
|
|
99
|
-
|
123
|
+
signed_payload = transaction.hex
|
124
|
+
|
125
|
+
broadcast_transfer_request = {
|
126
|
+
signed_payload: signed_payload
|
127
|
+
}
|
128
|
+
|
129
|
+
transfer_model = Coinbase.call_api do
|
130
|
+
transfers_api.broadcast_transfer(wallet_id, address_id, transfer.transfer_id, broadcast_transfer_request)
|
131
|
+
end
|
132
|
+
|
133
|
+
Coinbase::Transfer.new(transfer_model)
|
100
134
|
end
|
101
135
|
|
102
|
-
# Returns
|
103
|
-
# @return [String]
|
136
|
+
# Returns a String representation of the Address.
|
137
|
+
# @return [String] a String representation of the Address
|
104
138
|
def to_s
|
105
|
-
|
139
|
+
"Coinbase::Address{address_id: '#{address_id}', network_id: '#{network_id}', wallet_id: '#{wallet_id}'}"
|
140
|
+
end
|
141
|
+
|
142
|
+
# Same as to_s.
|
143
|
+
# @return [String] a String representation of the Address
|
144
|
+
def inspect
|
145
|
+
to_s
|
146
|
+
end
|
147
|
+
|
148
|
+
# Requests funds for the address from the faucet and returns the faucet transaction.
|
149
|
+
# This is only supported on testnet networks.
|
150
|
+
# @return [Coinbase::FaucetTransaction] The successful faucet transaction
|
151
|
+
# @raise [Coinbase::FaucetLimitReached] If the faucet limit has been reached for the address or user.
|
152
|
+
# @raise [Coinbase::Client::ApiError] If an unexpected error occurs while requesting faucet funds.
|
153
|
+
def faucet
|
154
|
+
Coinbase.call_api do
|
155
|
+
Coinbase::FaucetTransaction.new(addresses_api.request_faucet_funds(wallet_id, address_id))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Exports the Address's private key to a hex string.
|
160
|
+
# @return [String] The Address's private key as a hex String
|
161
|
+
def export
|
162
|
+
@key.private_hex
|
163
|
+
end
|
164
|
+
|
165
|
+
# Lists the IDs of all Transfers associated with the given Wallet and Address.
|
166
|
+
# @return [Array<String>] The IDs of all Transfers belonging to the Wallet and Address
|
167
|
+
def list_transfer_ids
|
168
|
+
transfer_ids = []
|
169
|
+
page = nil
|
170
|
+
|
171
|
+
loop do
|
172
|
+
response = Coinbase.call_api do
|
173
|
+
transfers_api.list_transfers(wallet_id, address_id, { limit: 100, page: page })
|
174
|
+
end
|
175
|
+
|
176
|
+
transfer_ids.concat(response.data.map(&:transfer_id)) if response.data
|
177
|
+
|
178
|
+
break unless response.has_more
|
179
|
+
|
180
|
+
page = response.next_page
|
181
|
+
end
|
182
|
+
|
183
|
+
transfer_ids
|
106
184
|
end
|
107
185
|
|
108
186
|
private
|
109
187
|
|
110
|
-
# Normalizes the amount of
|
188
|
+
# Normalizes the amount of the Asset to send to the atomic unit.
|
111
189
|
# @param amount [Integer, Float, BigDecimal] The amount to normalize
|
112
190
|
# @param asset_id [Symbol] The ID of the Asset being transferred
|
113
|
-
# @return [BigDecimal] The normalized amount in units
|
114
|
-
def
|
191
|
+
# @return [BigDecimal] The normalized amount in atomic units
|
192
|
+
def normalize_asset_amount(amount, asset_id)
|
193
|
+
big_amount = BigDecimal(amount.to_s)
|
194
|
+
|
115
195
|
case asset_id
|
116
196
|
when :eth
|
117
|
-
|
197
|
+
big_amount * Coinbase::WEI_PER_ETHER
|
118
198
|
when :gwei
|
119
|
-
|
120
|
-
when :
|
121
|
-
|
199
|
+
big_amount * Coinbase::WEI_PER_GWEI
|
200
|
+
when :usdc
|
201
|
+
big_amount * Coinbase::ATOMIC_UNITS_PER_USDC
|
202
|
+
when :weth
|
203
|
+
big_amount * Coinbase::WEI_PER_ETHER
|
122
204
|
else
|
123
|
-
|
205
|
+
big_amount
|
124
206
|
end
|
125
207
|
end
|
208
|
+
|
209
|
+
# Normalizes the asset ID to use during requests.
|
210
|
+
# @param asset_id [Symbol] The asset ID to normalize
|
211
|
+
# @return [Symbol] The normalized asset ID
|
212
|
+
def normalize_asset_id(asset_id)
|
213
|
+
if %i[wei gwei].include?(asset_id)
|
214
|
+
:eth
|
215
|
+
else
|
216
|
+
asset_id
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def addresses_api
|
221
|
+
@addresses_api ||= Coinbase::Client::AddressesApi.new(Coinbase.configuration.api_client)
|
222
|
+
end
|
223
|
+
|
224
|
+
def transfers_api
|
225
|
+
@transfers_api ||= Coinbase::Client::TransfersApi.new(Coinbase.configuration.api_client)
|
226
|
+
end
|
126
227
|
end
|
127
228
|
end
|
data/lib/coinbase/asset.rb
CHANGED
@@ -5,7 +5,8 @@ module Coinbase
|
|
5
5
|
class Asset
|
6
6
|
attr_reader :network_id, :asset_id, :display_name, :address_id
|
7
7
|
|
8
|
-
# Returns a new Asset object.
|
8
|
+
# Returns a new Asset object. Do not use this method. Instead, use the Asset constants defined in
|
9
|
+
# the Coinbase module.
|
9
10
|
# @param network_id [Symbol] The ID of the Network to which the Asset belongs
|
10
11
|
# @param asset_id [Symbol] The Asset ID
|
11
12
|
# @param display_name [String] The Asset's display name
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'jwt'
|
5
|
+
require 'openssl'
|
6
|
+
require 'securerandom'
|
7
|
+
|
8
|
+
module Coinbase
|
9
|
+
# A class that builds JWTs for authenticating with the Coinbase Platform APIs.
|
10
|
+
class Authenticator < Faraday::Middleware
|
11
|
+
# Initializes the Authenticator.
|
12
|
+
# @param app [Faraday::Connection] The Faraday connection
|
13
|
+
def initialize(app)
|
14
|
+
super(app)
|
15
|
+
@app = app
|
16
|
+
end
|
17
|
+
|
18
|
+
# Processes the request by adding the JWT to the Authorization header.
|
19
|
+
# @param env [Faraday::Env] The Faraday request environment
|
20
|
+
def call(env)
|
21
|
+
method = env.method.downcase.to_sym
|
22
|
+
uri = env.url.to_s
|
23
|
+
uri_without_protocol = URI(uri).host
|
24
|
+
token = build_jwt("#{method.upcase} #{uri_without_protocol}#{env.url.path}")
|
25
|
+
env.request_headers['Authorization'] = "Bearer #{token}"
|
26
|
+
@app.call(env)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Builds the JWT for the given API endpoint URI. The JWT is signed with the API key's private key.
|
30
|
+
# @param uri [String] The API endpoint URI
|
31
|
+
# @return [String] The JWT
|
32
|
+
def build_jwt(uri)
|
33
|
+
header = {
|
34
|
+
typ: 'JWT',
|
35
|
+
kid: Coinbase.configuration.api_key_name,
|
36
|
+
nonce: SecureRandom.hex(16)
|
37
|
+
}
|
38
|
+
|
39
|
+
claims = {
|
40
|
+
sub: Coinbase.configuration.api_key_name,
|
41
|
+
iss: 'coinbase-cloud',
|
42
|
+
aud: ['cdp_service'],
|
43
|
+
nbf: Time.now.to_i,
|
44
|
+
exp: Time.now.to_i + 60, # Expiration time: 1 minute from now.
|
45
|
+
uris: [uri]
|
46
|
+
}
|
47
|
+
|
48
|
+
private_key = OpenSSL::PKey.read(Coinbase.configuration.api_key_private_key)
|
49
|
+
JWT.encode(claims, private_key, 'ES256', header)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/coinbase/balance_map.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'bigdecimal'
|
4
4
|
|
5
5
|
module Coinbase
|
6
|
-
# A convenience class for printing out
|
6
|
+
# A convenience class for printing out Asset balances in a human-readable format.
|
7
7
|
class BalanceMap < Hash
|
8
8
|
# Returns a new BalanceMap object.
|
9
9
|
# @param hash [Map<Symbol, BigDecimal>] The hash to initialize with
|
@@ -42,7 +42,7 @@ module Coinbase
|
|
42
42
|
result[asset_id] = str
|
43
43
|
end
|
44
44
|
|
45
|
-
result
|
45
|
+
result.to_s
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|