Class: Coinbase::Wallet
- Inherits:
-
Object
- Object
- Coinbase::Wallet
- Extended by:
- Forwardable
- Defined in:
- lib/coinbase/wallet.rb,
lib/coinbase/wallet/data.rb
Overview
A representation of a Wallet. Wallets come with a single default Address, but can expand to have a set of Addresses, each of which can hold a balance of one or more Assets. Wallets can create new Addresses, list their addresses, list their balances, and transfer Assets to other Addresses.
Defined Under Namespace
Classes: Data
Constant Summary collapse
- MAX_ADDRESSES =
The maximum number of addresses in a Wallet.
20
Class Method Summary collapse
-
.create(network: Coinbase.default_network, interval_seconds: 0.2, timeout_seconds: 20) ⇒ Coinbase::Wallet
Creates a new Wallet on the specified Network and generate a default address for it.
-
.fetch(wallet_id) ⇒ Coinbase::Wallet
Fetches a Wallet by its ID.
-
.import(data) ⇒ Coinbase::Wallet
Imports a Wallet from previously exported wallet data.
-
.list ⇒ Enumerable<Coinbase::Wallet>
Enumerates the wallets for the requesting user.
Instance Method Summary collapse
-
#address(address_id) ⇒ Address
Returns the Address with the given ID.
-
#addresses ⇒ Array<Coinbase::WalletAddress>
Returns the addresses belonging to the Wallet.
-
#balance(asset_id) ⇒ BigDecimal
Returns the balance of the provided Asset.
-
#balances ⇒ BalanceMap
Returns the list of balances of this Wallet.
-
#can_sign? ⇒ Boolean
Returns whether the Wallet has a seed with which to derive keys and sign transactions.
-
#claim_stake ⇒ Coinbase::StakingOperation
Claims stake of the given amount of the given Asset for the default address.
-
#claimable_balance ⇒ BigDecimal
Retrieves the claimable balance for the supplied asset.
-
#create_address ⇒ Address
Creates a new Address in the Wallet.
-
#create_webhook(notification_uri:) ⇒ Coinbase::Client::Webhook
Creates a new webhook on the current wallet for tracking wallet activity events.
-
#default_address ⇒ Address
Returns the default address of the Wallet.
-
#deploy_multi_token ⇒ Coinbase::SmartContract
Deploys a new ERC1155 multi-token contract with the given URI.
-
#deploy_nft ⇒ Coinbase::SmartContract
Deploys a new ERC721 NFT contract with the given name, symbol, and base URI.
-
#deploy_token ⇒ Coinbase::SmartContract
Deploys a new ERC20 token contract with the given name, symbol, and total supply.
-
#export ⇒ Coinbase::Wallet::Data
Exports the Wallet's data to a Data object.
-
#faucet ⇒ Coinbase::FaucetTransaction
Requests funds from the faucet for the Wallet's default address and returns the faucet transaction.
-
#id ⇒ String
Returns the Wallet ID.
-
#initialize(model, seed: nil) ⇒ Wallet
constructor
Returns a new Wallet object.
-
#inspect ⇒ String
Same as to_s.
-
#invoke_contract ⇒ Object
Invokes a contract with the given ABI, method, and arguments.
-
#load_seed(file_path) ⇒ String
Loads the seed of the Wallet from the given file.
-
#network ⇒ Coinbase::Network
Returns the Network of the Wallet.
-
#save_seed!(file_path, encrypt: false) ⇒ String
Saves the seed of the Wallet to the given file.
-
#seed=(seed) ⇒ Object
Sets the seed of the Wallet.
-
#server_signer_status ⇒ Symbol
Returns the ServerSigner Status of the Wallet.
-
#sign_payload ⇒ Coinbase::PayloadSignature
Signs the given unsigned payload.
-
#stake ⇒ Coinbase::StakingOperation
Stakes the given amount of the given Asset for the default address.
-
#stakeable_balance ⇒ BigDecimal
Retrieves the stakeable balance of the supplied asset for the default address.
-
#staking_balances ⇒ Hash, BigDecimal
Retrieves the balances used for staking for the supplied asset for the default address.
-
#to_s ⇒ String
Returns a String representation of the Wallet.
-
#trade ⇒ Coinbase::Trade
Trades the specified amount from one asset to another using the default address.
-
#transfer ⇒ Coinbase::Transfer
Transfers the amount of the Asset from the default address to the specified destination.
-
#unstake ⇒ Coinbase::StakingOperation
Unstakes the given amount of the given Asset on the default address.
-
#unstakeable_balance ⇒ BigDecimal
Retrieves the unstakeable balance for the supplied asset.
Constructor Details
#initialize(model, seed: nil) ⇒ Wallet
Returns a new Wallet object. Do not use this method directly. Instead use Coinbase::Wallet.create.
135 136 137 138 139 140 141 142 143 |
# File 'lib/coinbase/wallet.rb', line 135 def initialize(model, seed: nil) raise ArgumentError, 'model must be a Wallet' unless model.is_a?(Coinbase::Client::Wallet) @model = model return if Coinbase.use_server_signer? @master = master_node(seed) end |
Class Method Details
.create(network: Coinbase.default_network, interval_seconds: 0.2, timeout_seconds: 20) ⇒ Coinbase::Wallet
Creates a new Wallet on the specified Network and generate a default address for it. have an active seed, if using a ServerSigner, in seconds create a seed for the Wallet, in seconds
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/coinbase/wallet.rb', line 66 def create(network: Coinbase.default_network, interval_seconds: 0.2, timeout_seconds: 20) model = Coinbase.call_api do wallets_api.create_wallet( create_wallet_request: { wallet: { network_id: Coinbase.normalize_network(network), use_server_signer: Coinbase.use_server_signer? } } ) end wallet = new(model) # When used with a ServerSigner, the Signer must first register # with the Wallet before addresses can be created. wait_for_signer(wallet.id, interval_seconds, timeout_seconds) if Coinbase.use_server_signer? wallet.create_address wallet end |
.fetch(wallet_id) ⇒ Coinbase::Wallet
Fetches a Wallet by its ID. The returned wallet can be immediately used for signing operations if backed by a server signer. If the wallet is not backed by a server signer, the wallet's seed will need to be set before it can be used for signing operations.
50 51 52 53 54 55 56 |
# File 'lib/coinbase/wallet.rb', line 50 def fetch(wallet_id) model = Coinbase.call_api do wallets_api.get_wallet(wallet_id) end new(model, seed: '') end |
.import(data) ⇒ Coinbase::Wallet
Imports a Wallet from previously exported wallet data.
24 25 26 27 28 29 30 31 32 |
# File 'lib/coinbase/wallet.rb', line 24 def import(data) raise ArgumentError, 'data must be a Coinbase::Wallet::Data object' unless data.is_a?(Data) model = Coinbase.call_api do wallets_api.get_wallet(data.wallet_id) end new(model, seed: data.seed) end |
.list ⇒ Enumerable<Coinbase::Wallet>
Enumerates the wallets for the requesting user. The result is an enumerator that lazily fetches from the server, and can be iterated over, converted to an array, etc…
38 39 40 41 42 |
# File 'lib/coinbase/wallet.rb', line 38 def list Coinbase::Pagination.enumerate(method(:fetch_wallets_page)) do |wallet| Coinbase::Wallet.new(wallet, seed: '') end end |
Instance Method Details
#address(address_id) ⇒ Address
Returns the Address with the given ID.
362 363 364 |
# File 'lib/coinbase/wallet.rb', line 362 def address(address_id) addresses.find { |address| address.id == address_id } end |
#addresses ⇒ Array<Coinbase::WalletAddress>
Returns the addresses belonging to the Wallet.
267 268 269 270 271 |
# File 'lib/coinbase/wallet.rb', line 267 def addresses return @addresses unless @addresses.nil? set_addresses end |
#balance(asset_id) ⇒ BigDecimal
Returns the balance of the provided Asset. Balances are aggregated across all Addresses in the Wallet.
379 380 381 382 383 384 385 386 387 |
# File 'lib/coinbase/wallet.rb', line 379 def balance(asset_id) response = Coinbase.call_api do wallets_api.get_wallet_balance(id, Coinbase::Asset.primary_denomination(asset_id).to_s) end return BigDecimal('0') if response.nil? Coinbase::Balance.from_model_and_asset_id(response, asset_id).amount end |
#balances ⇒ BalanceMap
Returns the list of balances of this Wallet. Balances are aggregated across all Addresses in the Wallet.
368 369 370 371 372 373 374 |
# File 'lib/coinbase/wallet.rb', line 368 def balances response = Coinbase.call_api do wallets_api.list_wallet_balances(id) end Coinbase::BalanceMap.from_balances(response.data) end |
#can_sign? ⇒ Boolean
Returns whether the Wallet has a seed with which to derive keys and sign transactions.
402 403 404 |
# File 'lib/coinbase/wallet.rb', line 402 def can_sign? !@master.nil? end |
#claim_stake ⇒ Coinbase::StakingOperation
Claims stake of the given amount of the given Asset for the default address.
|
# File 'lib/coinbase/wallet.rb', line 188
|
#claimable_balance ⇒ BigDecimal
Retrieves the claimable balance for the supplied asset. Currently only the default_address is used to source the claimable balance.
|
# File 'lib/coinbase/wallet.rb', line 221
|
#create_address ⇒ Address
Creates a new Address in the Wallet.
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/coinbase/wallet.rb', line 315 def create_address req = {} # Ensure that the address cache is set before creating a new address. # This ensures that for a server signer, the addresses have been loaded and we # can create a new address and add it to a cache. set_addresses if @addresses.nil? unless Coinbase.use_server_signer? # The index for the next address is the number of addresses already registered. private_key_index = addresses.count key = derive_key(private_key_index) req = { public_key: key.public_key.compressed.unpack1('H*'), attestation: create_attestation(key), address_index: private_key_index } end address_model = Coinbase.call_api do addresses_api.create_address(id, { create_address_request: req }) end # Default address can be nil because either this is the first address being # created for this wallet or the addresses cache has not yet been loaded. # If the default address is nil, we must reload the wallet model after creating # the address, in order for the default address to be set. reload if default_address.nil? # The addreses cache is already created, so we can add the new address to the cache. address = WalletAddress.new(address_model, key) @addresses << address address end |
#create_webhook(notification_uri:) ⇒ Coinbase::Client::Webhook
Creates a new webhook on the current wallet for tracking wallet activity events.
490 491 492 493 494 495 496 497 498 499 |
# File 'lib/coinbase/wallet.rb', line 490 def create_webhook(notification_uri:) Coinbase.call_api do webhooks_api.create_wallet_webhook( id, create_wallet_webhook_request: { notification_uri: notification_uri } ) end end |
#default_address ⇒ Address
Returns the default address of the Wallet.
355 356 357 |
# File 'lib/coinbase/wallet.rb', line 355 def default_address address(@model.default_address&.address_id) end |
#deploy_multi_token ⇒ Coinbase::SmartContract
Deploys a new ERC1155 multi-token contract with the given URI.
261 262 263 |
# File 'lib/coinbase/wallet.rb', line 261 def_delegators :default_address, :transfer, :trade, :faucet, :stake, :unstake, :claim_stake, :staking_balances, :stakeable_balance, :unstakeable_balance, :claimable_balance, :sign_payload, :invoke_contract, :deploy_token, :deploy_nft, :deploy_multi_token |
#deploy_nft ⇒ Coinbase::SmartContract
Deploys a new ERC721 NFT contract with the given name, symbol, and base URI.
|
# File 'lib/coinbase/wallet.rb', line 247
|
#deploy_token ⇒ Coinbase::SmartContract
Deploys a new ERC20 token contract with the given name, symbol, and total supply. whole units.
|
# File 'lib/coinbase/wallet.rb', line 238
|
#export ⇒ Coinbase::Wallet::Data
Exports the Wallet's data to a Data object.
391 392 393 394 395 396 397 398 |
# File 'lib/coinbase/wallet.rb', line 391 def export # TODO: Improve this check by relying on the backend data to decide whether a wallet is server-signer backed. raise 'Cannot export data for Server-Signer backed Wallet' if Coinbase.use_server_signer? raise 'Cannot export Wallet without loaded seed' if @master.nil? Data.new(wallet_id: id, seed: @master.seed_hex) end |
#faucet ⇒ Coinbase::FaucetTransaction
Requests funds from the faucet for the Wallet's default address and returns the faucet transaction. This is only supported on testnet networks.
|
# File 'lib/coinbase/wallet.rb', line 164
|
#id ⇒ String
Returns the Wallet ID.
275 276 277 |
# File 'lib/coinbase/wallet.rb', line 275 def id @model.id end |
#inspect ⇒ String
Same as to_s.
514 515 516 |
# File 'lib/coinbase/wallet.rb', line 514 def inspect to_s end |
#invoke_contract ⇒ Object
Invokes a contract with the given ABI, method, and arguments.
|
# File 'lib/coinbase/wallet.rb', line 234
|
#load_seed(file_path) ⇒ String
Loads the seed of the Wallet from the given file.
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/coinbase/wallet.rb', line 449 def load_seed(file_path) raise 'Wallet already has seed loaded' unless @master.nil? existing_seeds_in_store = existing_seeds(file_path) raise ArgumentError, "File #{file_path} does not contain seed data" if existing_seeds_in_store == {} if existing_seeds_in_store[id].nil? raise ArgumentError, "File #{file_path} does not contain seed data for wallet #{id}" end seed_data = existing_seeds_in_store[id] local_seed = seed_data['seed'] raise ArgumentError, 'Seed data is malformed' if local_seed.nil? || local_seed == '' if seed_data['encrypted'] raise ArgumentError, 'Encrypted seed data is malformed' if seed_data['iv'] == '' || seed_data['auth_tag'] == '' cipher = OpenSSL::Cipher.new('aes-256-gcm').decrypt cipher.key = OpenSSL::Digest.digest('SHA256', encryption_key) iv = [seed_data['iv']].pack('H*') cipher.iv = iv auth_tag = [seed_data['auth_tag']].pack('H*') cipher.auth_tag = auth_tag cipher.auth_data = '' hex_decoded_data = [seed_data['seed']].pack('H*') local_seed = cipher.update(hex_decoded_data) + cipher.final end self.seed = local_seed "Successfully loaded seed for wallet #{id} from #{file_path}." end |
#network ⇒ Coinbase::Network
Returns the Network of the Wallet.
281 282 283 |
# File 'lib/coinbase/wallet.rb', line 281 def network @network ||= Coinbase::Network.from_id(@model.network_id) end |
#save_seed!(file_path, encrypt: false) ⇒ String
Saves the seed of the Wallet to the given file. Wallets whose seeds are saved this way can be rehydrated using load_seed. A single file can be used for multiple Wallet seeds. This is an insecure method of storing Wallet seeds and should only be used for development purposes.
encrypted or not. Data is unencrypted by default.
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/coinbase/wallet.rb', line 414 def save_seed!(file_path, encrypt: false) raise 'Wallet does not have seed loaded' if @master.nil? existing_seeds_in_store = existing_seeds(file_path) seed_to_store = @master.seed_hex auth_tag = '' iv = '' if encrypt cipher = OpenSSL::Cipher.new('aes-256-gcm').encrypt cipher.key = OpenSSL::Digest.digest('SHA256', encryption_key) iv = cipher.random_iv cipher.iv = iv cipher.auth_data = '' encrypted_data = cipher.update(@master.seed_hex) + cipher.final auth_tag = cipher.auth_tag.unpack1('H*') iv = iv.unpack1('H*') seed_to_store = encrypted_data.unpack1('H*') end existing_seeds_in_store[id] = { seed: seed_to_store, encrypted: encrypt, auth_tag: auth_tag, iv: iv } File.write(file_path, JSON.pretty_generate(existing_seeds_in_store)) "Successfully saved seed for wallet #{id} to #{file_path}." end |
#seed=(seed) ⇒ Object
Sets the seed of the Wallet. This seed is used to derive keys and sign transactions.
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/coinbase/wallet.rb', line 293 def seed=(seed) raise ArgumentError, 'Seed must not be empty' if seed.nil? || seed.empty? raise StandardError, 'Seed is already set' unless @master.nil? @master = master_node(seed) # If the addresses are not loaded the keys will be set on them whenever they are loaded. return if @addresses.nil? # If addresses are already loaded, set the keys on each address. addresses.each_with_index.each do |address, index| key = derive_key(index) # If we derive a key the derived address must match the address from the API. raise StandardError, 'Seed does not match wallet' unless address.id == key.address.to_s address.key = key end end |
#server_signer_status ⇒ Symbol
Returns the ServerSigner Status of the Wallet.
287 288 289 |
# File 'lib/coinbase/wallet.rb', line 287 def server_signer_status Coinbase.to_sym(@model.server_signer_status) end |
#sign_payload ⇒ Coinbase::PayloadSignature
Signs the given unsigned payload.
|
# File 'lib/coinbase/wallet.rb', line 229
|
#stake ⇒ Coinbase::StakingOperation
Stakes the given amount of the given Asset for the default address.
|
# File 'lib/coinbase/wallet.rb', line 172
|
#stakeable_balance ⇒ BigDecimal
Retrieves the stakeable balance of the supplied asset for the default address.
|
# File 'lib/coinbase/wallet.rb', line 206
|
#staking_balances ⇒ Hash, BigDecimal
Retrieves the balances used for staking for the supplied asset for the default address.
|
# File 'lib/coinbase/wallet.rb', line 196
|
#to_s ⇒ String
Returns a String representation of the Wallet.
503 504 505 506 507 508 509 510 |
# File 'lib/coinbase/wallet.rb', line 503 def to_s Coinbase.pretty_print_object( self.class, id: id, network_id: network.id, default_address: @model.default_address&.address_id ) end |
#trade ⇒ Coinbase::Trade
Trades the specified amount from one asset to another using the default address.
|
# File 'lib/coinbase/wallet.rb', line 156
|
#transfer ⇒ Coinbase::Transfer
Transfers the amount of the Asset from the default address to the specified destination. (see Coinbase::Address::WalletAddress#transfer)
|
# File 'lib/coinbase/wallet.rb', line 145
|
#unstake ⇒ Coinbase::StakingOperation
Unstakes the given amount of the given Asset on the default address.
|
# File 'lib/coinbase/wallet.rb', line 180
|
#unstakeable_balance ⇒ BigDecimal
Retrieves the unstakeable balance for the supplied asset. Currently only the default_address is used to source the unstakeable balance.
|
# File 'lib/coinbase/wallet.rb', line 213
|