Class: Nanook::Wallet
Overview
The Nanook::Wallet
class lets you manage your nano wallets. Your node will need the enable_control
setting enabled.
Wallet seeds vs ids
Your wallets each have an id as well as a seed. Both are 32-byte uppercase hex strings that look like this:
000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F
This class uses wallet ids to identify your wallet. A wallet id only exists locally on the nano node that it was created on. The person who knows this id can only perform all read and write actions against the wallet and all accounts inside the wallet from the same nano node that it was created on. This makes wallet ids fairly safe to use as a person needs to know your wallet id as well as have access to run RPC commands against your nano node to be able to control your accounts.
A seed on the other hand can be used to link any wallet to another wallet's accounts, from anywhere in the nano network. This happens by setting a wallet's seed to be the same as a previous wallet's seed. When a wallet has the same seed as another wallet, any accounts created in the second wallet will be the same accounts as those that were created in the previous wallet, and the new wallet's owner will also gain ownership of the previous wallet's accounts. Note, that the two wallets will have different ids, but the same seed.
Nanook is based on the Nano RPC, which uses wallet ids and not seeds. The RPC and therefore Nanook cannot tell you what a wallet's seed is, only its id. Knowing a wallet's seed is very useful for if you ever want to restore the wallet anywhere else on the nano network besides the node you originally created it on. The nano command line interface (CLI) is the only method for discovering a wallet's seed. See the https://docs.nano.org/commands/command-line-interface/#-wallet_decrypt_unsafe-walletwallet-passwordpassword.
Initializing
Initialize this class through the convenient #wallet method:
nanook = Nanook.new
wallet = nanook.wallet(wallet_id)
Or compose the longhand way like this:
rpc_conn = Nanook::Rpc.new
wallet = Nanook::Wallet.new(rpc_conn, wallet_id)
Constant Summary
Constants included from Util
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
True if wallets are equal.
-
#account(account = nil) ⇒ Nanook::WalletAccount
Returns the given account in the wallet as a WalletAccount instance to let you start working with it.
-
#accounts ⇒ Array<Nanook::WalletAccount>
Array of WalletAccount instances of accounts in the wallet.
-
#balance(account_break_down: false, unit: Nanook.default_unit) ⇒ Hash{Symbol=>Integer|Float|Hash}
Balance of all accounts in the wallet, optionally breaking the balances down by account.
-
#change_default_representative(representative) ⇒ Nanook::Account
(also: #change_representative)
Sets the default representative for the wallet.
-
#change_password(password) ⇒ Boolean
Changes the password for a wallet.
-
#change_seed(seed) ⇒ Boolean
Changes a wallet's seed.
-
#contains?(account) ⇒ Boolean
Will return
true
if the account exists in the wallet. -
#create ⇒ Nanook::Wallet
Creates a new wallet.
-
#default_representative ⇒ Nanook::Account
(also: #representative)
The default representative account id for the wallet.
-
#destroy ⇒ Boolean
Destroys the wallet.
-
#exists? ⇒ Boolean
Returns true if wallet exists on the node.
-
#export ⇒ String
Generates a String containing a JSON representation of your wallet.
-
#hash ⇒ Integer
The hash value is used along with #eql? by the Hash class to determine if two objects reference the same hash key.
-
#history(unit: Nanook.default_unit) ⇒ Array<Hash{Symbol=>String|Nanook::Account|Nanook::WalletAccount|Nanook::Block|Integer|Float|Time}>
Reports send/receive information for accounts in wallet.
-
#id ⇒ String
The wallet id.
-
#info(unit: Nanook.default_unit) ⇒ Hash{Symbol=>Integer|Float}
Information about this wallet.
-
#initialize(rpc, wallet = nil) ⇒ Wallet
constructor
A new instance of Wallet.
-
#ledger(unit: Nanook.default_unit) ⇒ Hash{Nanook::Account=>Hash{Symbol=>Nanook::Block|Integer|Float|Time}}
Information ledger information about this wallet's accounts.
-
#lock ⇒ Boolean
Locks the wallet.
-
#locked? ⇒ Boolean
Returns
true
if the wallet is locked. - #move_accounts(wallet, accounts) ⇒ Boolean
-
#pay(from:, to:, amount:, id:, unit: Nanook.default_unit) ⇒ Nanook::Block
Makes a payment from an account in your wallet to another account on the nano network.
-
#pending(limit: 1000, detailed: false, allow_unconfirmed: false, unit: Nanook.default_unit) ⇒ Object
Information about pending blocks (payments) that are waiting to be received by accounts in this wallet.
-
#receive(block = nil, into:) ⇒ Nanook::Block, false
Receives a pending payment into an account in the wallet.
- #remove_account(account) ⇒ Boolean
-
#republish_blocks(limit: 1000) ⇒ Array<Nanook::Block>
Rebroadcast blocks for accounts from wallet starting at frontier down to count to the network.
-
#restore(seed, accounts: 0) ⇒ Nanook::Wallet
Restores a previously created wallet by its seed.
-
#search_pending ⇒ Boolean
Tells the node to look for pending blocks for any account in the wallet.
- #to_s ⇒ String (also: #inspect)
-
#unlock(password = nil) ⇒ Boolean
Unlocks a previously locked wallet.
-
#work ⇒ Boolean
Returns a list of pairs of WalletAccount and work for wallet.
Constructor Details
#initialize(rpc, wallet = nil) ⇒ Wallet
Returns a new instance of Wallet.
55 56 57 58 |
# File 'lib/nanook/wallet.rb', line 55 def initialize(rpc, wallet = nil) @rpc = rpc @wallet = wallet.to_s if wallet end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Returns true if wallets are equal.
67 68 69 70 |
# File 'lib/nanook/wallet.rb', line 67 def ==(other) other.class == self.class && other.id == id end |
#account(account = nil) ⇒ Nanook::WalletAccount
Returns the given account in the wallet as a Nanook::WalletAccount instance to let you start working with it.
Call with no account
argument if you wish to create a new account in the wallet, like this:
wallet.account.create # => Nanook::WalletAccount
See Nanook::WalletAccount for all the methods you can call on the account object returned.
Examples:
wallet.account("nano_...") # => Nanook::WalletAccount
wallet.account.create # => Nanook::WalletAccount
103 104 105 106 107 108 |
# File 'lib/nanook/wallet.rb', line 103 def account(account = nil) check_wallet_required! # We `allow_blank` in order to support `WalletAccount#create`. as_wallet_account(account, allow_blank: true) end |
#accounts ⇒ Array<Nanook::WalletAccount>
Array of Nanook::WalletAccount instances of accounts in the wallet.
See Nanook::WalletAccount for all the methods you can call on the account objects returned.
Example:
wallet.accounts # => [Nanook::WalletAccount, Nanook::WalletAccount...]
120 121 122 123 124 |
# File 'lib/nanook/wallet.rb', line 120 def accounts rpc(:account_list, _access: :accounts, _coerce: Array).map do |account| as_wallet_account(account) end end |
#balance(account_break_down: false, unit: Nanook.default_unit) ⇒ Hash{Symbol=>Integer|Float|Hash}
Balance of all accounts in the wallet, optionally breaking the balances down by account.
Examples:
wallet.balance
Example response:
{
"balance"=>5,
"pending"=>0.001
}
Asking for the balances to be returned in raw instead of NANO.
wallet.balance(unit: :raw)
Example response:
{
"balance"=>5000000000000000000000000000000,
"pending"=>1000000000000000000000000000
}
Asking for totals to be broken down by account:
wallet.balance(account_break_down: true)
Example response:
{
"nano_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"=>{
"balance"=>2.5,
"pending"=>1
},
"nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"=>{
"balance"=>51.4,
"pending"=>0
},
}
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/nanook/wallet.rb', line 194 def balance(account_break_down: false, unit: Nanook.default_unit) validate_unit!(unit) if account_break_down return rpc(:wallet_balances, _access: :balances, _coerce: Hash).tap do |r| if unit == :nano r.each do |account, _balances| r[account][:balance] = raw_to_NANO(r[account][:balance]) r[account][:pending] = raw_to_NANO(r[account][:pending]) end end end end response = rpc(:wallet_info, _coerce: Hash).slice(:balance, :pending) return response unless unit == :nano { balance: raw_to_NANO(response[:balance]), pending: raw_to_NANO(response[:pending]) } end |
#change_default_representative(representative) ⇒ Nanook::Account Also known as: change_representative
Sets the default representative for the wallet. A wallet's default representative is the representative all new accounts created in the wallet will have. Changing the default representative for a wallet does not change the representatives for existing accounts in the wallet.
Example:
wallet.change_default_representative("nano_...") # => "nano_..."
508 509 510 511 512 513 514 515 516 517 |
# File 'lib/nanook/wallet.rb', line 508 def change_default_representative(representative) unless as_account(representative).exists? raise Nanook::Error, "Representative account does not exist: #{representative}" end raise Nanook::Error, 'Setting the representative failed' \ unless rpc(:wallet_representative_set, representative: representative, _access: :set) == 1 as_account(representative) end |
#change_password(password) ⇒ Boolean
Changes the password for a wallet.
Example:
wallet.change_password("new_pass") #=> true
706 707 708 |
# File 'lib/nanook/wallet.rb', line 706 def change_password(password) rpc(:password_change, password: password, _access: :changed) == 1 end |
#change_seed(seed) ⇒ Boolean
Changes a wallet's seed.
It's recommended to only change the seed of a wallet that contains no accounts. This will clear all deterministic accounts in the wallet. To restore accounts after changing the seed, see Nanook::WalletAccount#create.
Example:
wallet.change_seed("000D1BA...") # => true
wallet.account.create(5) # Restores first 5 accounts for wallet with new seed
230 231 232 |
# File 'lib/nanook/wallet.rb', line 230 def change_seed(seed) rpc(:wallet_change_seed, seed: seed).key?(:success) end |
#contains?(account) ⇒ Boolean
Will return true
if the account exists in the wallet.
Example:
wallet.contains?("nano_...") # => true
298 299 300 |
# File 'lib/nanook/wallet.rb', line 298 def contains?(account) rpc(:wallet_contains, account: account, _access: :exists) == 1 end |
#create ⇒ Nanook::Wallet
Creates a new wallet.
The wallet will be created only on this node. It's important that if you intend to add funds to accounts in this wallet that you backup the wallet seed in order to restore the wallet in future. The nano command line interface (CLI) is the only method for backing up a wallet's seed. See the –wallet_decrypt_unsafe CLI command.
Example:
Nanook.new.wallet.create # => Nanook::Wallet
248 249 250 251 252 |
# File 'lib/nanook/wallet.rb', line 248 def create skip_wallet_required! @wallet = rpc(:wallet_create, _access: :wallet) self end |
#default_representative ⇒ Nanook::Account Also known as: representative
The default representative account id for the wallet. This is the representative that all new accounts created in this wallet will have.
Changing the default representative for a wallet does not change the representatives for any accounts that have been created.
Example:
wallet.default_representative # => "nano_3pc..."
488 489 490 491 |
# File 'lib/nanook/wallet.rb', line 488 def default_representative representative = rpc(:wallet_representative, _access: :representative) as_account(representative) end |
#destroy ⇒ Boolean
Destroys the wallet.
Example:
wallet.destroy # => true
261 262 263 |
# File 'lib/nanook/wallet.rb', line 261 def destroy rpc(:wallet_destroy, _access: :destroyed) == 1 end |
#exists? ⇒ Boolean
Returns true if wallet exists on the node.
Example:
wallet.exists? # => true
284 285 286 287 288 289 |
# File 'lib/nanook/wallet.rb', line 284 def exists? export true rescue Nanook::NodeRpcError false end |
#export ⇒ String
Generates a String containing a JSON representation of your wallet.
Example:
wallet.export
# => "{\n \"0000000000000000000000000000000000000000000000000000000000000000\": \"0000000000000000000000000000000000000000000000000000000000000003\",\n \"0000000000000000000000000000000000000000000000000000000000000001\": \"C3A176FC3B90113277BFC91F55128FC9A1F1B6166A73E7446927CFFCA4C2C9D9\",\n \"0000000000000000000000000000000000000000000000000000000000000002\": \"3E58EC805B99C52B4715598BD332C234A1FBF1780577137E18F53B9B7F85F04B\",\n \"0000000000000000000000000000000000000000000000000000000000000003\": \"5FF8021122F3DEE0E4EC4241D35A3F41DEF63CCF6ADA66AF235DE857718498CD\",\n \"0000000000000000000000000000000000000000000000000000000000000004\": \"A30E0A32ED41C8607AA9212843392E853FCBCB4E7CB194E35C94F07F91DE59EF\",\n \"0000000000000000000000000000000000000000000000000000000000000005\": \"E707002E84143AA5F030A6DB8DD0C0480F2FFA75AB1FFD657EC22B5AA8E395D5\",\n \"0000000000000000000000000000000000000000000000000000000000000006\": \"0000000000000000000000000000000000000000000000000000000000000001\",\n \"8646C0423160DEAEAA64034F9C6858F7A5C8A329E73E825A5B16814F6CCAFFE3\": \"0000000000000000000000000000000000000000000000000000000100000000\"\n}\n"
273 274 275 |
# File 'lib/nanook/wallet.rb', line 273 def export rpc(:wallet_export, _access: :json) end |
#hash ⇒ Integer
The hash value is used along with #eql? by the Hash class to determine if two objects reference the same hash key.
77 78 79 |
# File 'lib/nanook/wallet.rb', line 77 def hash id.hash end |
#history(unit: Nanook.default_unit) ⇒ Array<Hash{Symbol=>String|Nanook::Account|Nanook::WalletAccount|Nanook::Block|Integer|Float|Time}>
Reports send/receive information for accounts in wallet. Change blocks are skipped, open blocks will appear as receive. Response will start with most recent blocks according to local ledger.
Example:
wallet.history
Example response:
[
{
"type": "send",
"account": Nanook::Account,
"amount": 3.2,
"block_account": Nanook::Account,
"hash": Nanook::Block,
"local_timestamp": Time
},
{
...
}
]
654 655 656 657 658 659 660 661 662 663 664 665 |
# File 'lib/nanook/wallet.rb', line 654 def history(unit: Nanook.default_unit) validate_unit!(unit) rpc(:wallet_history, _access: :history, _coerce: Array).map do |h| h[:account] = account(h[:account]) h[:block_account] = as_account(h[:block_account]) h[:amount] = raw_to_NANO(h[:amount]) if unit == :nano h[:block] = as_block(h.delete(:hash)) h[:local_timestamp] = as_time(h[:local_timestamp]) h end end |
#id ⇒ String
Returns the wallet id.
61 62 63 |
# File 'lib/nanook/wallet.rb', line 61 def id @wallet end |
#info(unit: Nanook.default_unit) ⇒ Hash{Symbol=>Integer|Float}
Information about this wallet.
This call may return results that include unconfirmed blocks, so it should not be used in any processes or integrations requiring only details from blocks confirmed by the network.
Examples:
wallet.info
Example response:
{
balance: 1.0,
pending: 2.3
accounts_count: 3,
adhoc_count: 1,
deterministic_count: 2,
deterministic_index: 2
}
614 615 616 617 618 619 620 621 622 623 624 625 |
# File 'lib/nanook/wallet.rb', line 614 def info(unit: Nanook.default_unit) validate_unit!(unit) response = rpc(:wallet_info, _coerce: Hash) if unit == :nano response[:balance] = raw_to_NANO(response[:balance]) response[:pending] = raw_to_NANO(response[:pending]) end response end |
#ledger(unit: Nanook.default_unit) ⇒ Hash{Nanook::Account=>Hash{Symbol=>Nanook::Block|Integer|Float|Time}}
Information ledger information about this wallet's accounts.
This call may return results that include unconfirmed blocks, so it should not be used in any processes or integrations requiring only details from blocks confirmed by the network.
Examples:
wallet.ledger
Example response:
{
Nanook::Account => {
frontier: "E71AF3E9DD86BBD8B4620EFA63E065B34D358CFC091ACB4E103B965F95783321",
open_block: "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
representative_block: "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
balance: 1.45,
modified_timestamp: 1511476234,
block_count: 2
},
Nanook::Account => { ... }
}
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
# File 'lib/nanook/wallet.rb', line 572 def ledger(unit: Nanook.default_unit) validate_unit!(unit) response = rpc(:wallet_ledger, _access: :accounts, _coerce: Hash) accounts = response.map do |account_id, data| data[:frontier] = as_block(data[:frontier]) data[:open_block] = as_block(data[:open_block]) data[:representative_block] = as_block(data[:representative_block]) data[:balance] = raw_to_NANO(data[:balance]) if unit == :nano data[:last_modified_at] = as_time(data.delete(:modified_timestamp)) [as_account(account_id), data] end Hash[accounts] end |
#lock ⇒ Boolean
Locks the wallet. A locked wallet cannot pocket pending transactions or make payments. See #unlock.
Example:
wallet.lock #=> true
674 675 676 |
# File 'lib/nanook/wallet.rb', line 674 def lock rpc(:wallet_lock, _access: :locked) == 1 end |
#locked? ⇒ Boolean
Returns true
if the wallet is locked.
Example:
wallet.locked? #=> false
685 686 687 |
# File 'lib/nanook/wallet.rb', line 685 def locked? rpc(:wallet_locked, _access: :locked) == 1 end |
#move_accounts(wallet, accounts) ⇒ Boolean
Move accounts from another Nanook::Wallet on the node to this Nanook::Wallet.
Example:
wallet.move_accounts("0023200...", ["nano_3e3j5...", "nano_5f2a1..."]) # => true
133 134 135 |
# File 'lib/nanook/wallet.rb', line 133 def move_accounts(wallet, accounts) rpc(:account_move, source: wallet, accounts: accounts, _access: :moved) == 1 end |
#pay(from:, to:, amount:, id:, unit: Nanook.default_unit) ⇒ Nanook::Block
Makes a payment from an account in your wallet to another account on the nano network.
Note, there may be a delay in receiving a response due to Proof of Work being done. From the Nano RPC:
Proof of Work is precomputed for one transaction in the background. If it has been a while since your last transaction it will send instantly, the next one will need to wait for Proof of Work to be generated.
Examples:
wallet.pay(from: "nano_...", to: "nano_...", amount: 1.1, id: "myUniqueId123") # => "9AE2311..."
wallet.pay(from: "nano_...", to: "nano_...", amount: 54000000000000, unit: :raw, id: "myUniqueId123")
# => "9AE2311..."
332 333 334 335 |
# File 'lib/nanook/wallet.rb', line 332 def pay(from:, to:, amount:, id:, unit: Nanook.default_unit) validate_wallet_contains_account!(from) account(from).pay(to: to, amount: amount, unit: unit, id: id) end |
#pending(limit: 1000, detailed: false, allow_unconfirmed: false, unit: Nanook.default_unit) ⇒ Object
Information about pending blocks (payments) that are waiting to be received by accounts in this wallet.
See also the #receive method of this class for how to receive a pending payment.
Examples:
wallet.pending
Example response:
{
Nanook::Account=>[
Nanook::Block,
Nanook::Block"
],
Nanook::Account=>[
Nanook::Block
]
}
Asking for more information:
wallet.pending(detailed: true)
Example response:
{
Nanook::Account=>[
{
:amount=>6.0,
:source=>Nanook::Account,
:block=>Nanook::Block
},
{
:amount=>12.0,
:source=>Nanook::Account,
:block=>Nanook::Block
}
],
Nanook::Account=>[
{
:amount=>106.370018,
:source=>Nanook::Account,
:block=>Nanook::Block
}
]
}
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/nanook/wallet.rb', line 393 def pending(limit: 1000, detailed: false, allow_unconfirmed: false, unit: Nanook.default_unit) validate_unit!(unit) params = { count: limit, include_only_confirmed: !allow_unconfirmed, _access: :blocks, _coerce: Hash } params[:source] = true if detailed response = rpc(:wallet_pending, params) unless detailed x = response.map do |account, block_ids| blocks = block_ids.map { |block_id| as_block(block_id) } [as_account(account), blocks] end return Hash[x] end # Map the RPC response, which is: # account=>block=>[amount|source] into # account=>[block|amount|source] x = response.map do |account, data| new_data = data.map do |block, amount_and_source| d = { block: as_block(block), source: as_account(amount_and_source[:source]), amount: amount_and_source[:amount] } d[:amount] = raw_to_NANO(d[:amount]) if unit == :nano d end [as_account(account), new_data] end Hash[x] end |
#receive(block = nil, into:) ⇒ Nanook::Block, false
Receives a pending payment into an account in the wallet.
When called with no block
argument, the latest pending payment for the account will be received.
Returns a receive block if a receive was successful, or false
if there were no pending payments to receive.
You can receive a specific pending block if you know it by passing the block has in as an argument.
Examples:
wallet.receive(into: "xrb...") # => Nanook::Block
wallet.receive("718CC21...", into: "xrb...") # => Nanook::Block
457 458 459 460 |
# File 'lib/nanook/wallet.rb', line 457 def receive(block = nil, into:) validate_wallet_contains_account!(into) account(into).receive(block) end |
#remove_account(account) ⇒ Boolean
Remove an Account from this Nanook::Wallet.
Example:
wallet.remove_account("nano_3e3j5...") # => true
144 145 146 |
# File 'lib/nanook/wallet.rb', line 144 def remove_account(account) rpc(:account_remove, account: account, _access: :removed) == 1 end |
#republish_blocks(limit: 1000) ⇒ Array<Nanook::Block>
Rebroadcast blocks for accounts from wallet starting at frontier down to count to the network.
Examples:
wallet.republish_blocks # => [Nanook::Block, ...]
wallet.republish_blocks(limit: 10) # => [Nanook::Block, ...
471 472 473 474 475 |
# File 'lib/nanook/wallet.rb', line 471 def republish_blocks(limit: 1000) rpc(:wallet_republish, count: limit, _access: :blocks, _coerce: Array).map do |block| as_block(block) end end |
#restore(seed, accounts: 0) ⇒ Nanook::Wallet
533 534 535 536 537 538 539 540 541 542 543 |
# File 'lib/nanook/wallet.rb', line 533 def restore(seed, accounts: 0) skip_wallet_required! create raise Nanook::Error, 'Unable to set seed for wallet' unless change_seed(seed) account.create(accounts) if accounts.positive? self end |
#search_pending ⇒ Boolean
Tells the node to look for pending blocks for any account in the wallet.
Example:
wallet.search_pending #=> true
716 717 718 |
# File 'lib/nanook/wallet.rb', line 716 def search_pending rpc(:search_pending, _access: :started) == 1 end |
#to_s ⇒ String Also known as: inspect
303 304 305 |
# File 'lib/nanook/wallet.rb', line 303 def to_s "#{self.class.name}(id: \"#{short_id}\")" end |
#unlock(password = nil) ⇒ Boolean
Unlocks a previously locked wallet.
Example:
wallet.unlock("new_pass") #=> true
696 697 698 |
# File 'lib/nanook/wallet.rb', line 696 def unlock(password = nil) rpc(:password_enter, password: password, _access: :valid) == 1 end |
#work ⇒ Boolean
Returns a list of pairs of Nanook::WalletAccount and work for wallet.
Example:
wallet.work
Example response:
{
Nanook::WalletAccount: "432e5cf728c90f4f",
Nanook::WalletAccount: "4efec5f63fc902cf"
}
733 734 735 736 737 738 739 |
# File 'lib/nanook/wallet.rb', line 733 def work hash = rpc(:wallet_work_get, _access: :works, _coerce: Hash).map do |account_id, work| [as_wallet_account(account_id), work] end Hash[hash] end |