lib/nanook/wallet.rb in nanook-2.5.1 vs lib/nanook/wallet.rb in nanook-3.0.0
- old
+ new
@@ -1,9 +1,12 @@
-class Nanook
+# frozen_string_literal: true
- # The <tt>Nanook::Wallet</tt> class lets you manage your nano wallets,
- # as well as some account-specific things like making and receiving payments.
+require_relative 'util'
+
+class Nanook
+ # The <tt>Nanook::Wallet</tt> class lets you manage your nano wallets.
+ # Your node will need the <tt>enable_control</tt> 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:
@@ -45,16 +48,38 @@
# Or compose the longhand way like this:
#
# rpc_conn = Nanook::Rpc.new
# wallet = Nanook::Wallet.new(rpc_conn, wallet_id)
class Wallet
+ include Nanook::Util
- def initialize(rpc, wallet)
+ def initialize(rpc, wallet = nil)
@rpc = rpc
- @wallet = wallet
+ @wallet = wallet.to_s if wallet
end
+ # @return [String] the wallet id
+ def id
+ @wallet
+ end
+
+ # @param other [Nanook::Wallet] wallet to compare
+ # @return [Boolean] true if wallets are equal
+ def ==(other)
+ other.class == self.class &&
+ other.id == id
+ end
+ alias eql? ==
+
+ # The hash value is used along with #eql? by the Hash class to determine if two objects
+ # reference the same hash key.
+ #
+ # @return [Integer]
+ def hash
+ id.hash
+ end
+
# 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:
@@ -67,18 +92,19 @@
# ==== Examples:
#
# wallet.account("nano_...") # => Nanook::WalletAccount
# wallet.account.create # => Nanook::WalletAccount
#
- # @param [String] account optional String of an account (starting with
+ # @param account [String] optional String of an account (starting with
# <tt>"xrb..."</tt>) to start working with. Must be an account within
# the wallet. When no account is given, the instance returned only
# allows you to call +create+ on it, to create a new account.
- # @raise [ArgumentError] if the wallet does no contain the account
+ # @raise [ArgumentError] if the wallet does not contain the account
# @return [Nanook::WalletAccount]
- def account(account=nil)
- Nanook::WalletAccount.new(@rpc, @wallet, account)
+ def account(account = nil)
+ check_wallet_required!
+ as_wallet_account(account)
end
# Array of {Nanook::WalletAccount} instances of accounts in the wallet.
#
# See {Nanook::WalletAccount} for all the methods you can call on the
@@ -88,17 +114,37 @@
#
# wallet.accounts # => [Nanook::WalletAccount, Nanook::WalletAccount...]
#
# @return [Array<Nanook::WalletAccount>] all accounts in the wallet
def accounts
- wallet_required!
- response = rpc(:account_list)[:accounts]
- Nanook::Util.coerce_empty_string_to_type(response, Array).map do |account|
- Nanook::WalletAccount.new(@rpc, @wallet, account)
+ rpc(:account_list, _access: :accounts, _coerce: Array).map do |account|
+ as_wallet_account(account)
end
end
+ # Move accounts from another {Nanook::Wallet} on the node to this {Nanook::Wallet}.
+ #
+ # ==== Example:
+ #
+ # wallet.move_accounts("0023200...", ["nano_3e3j5...", "nano_5f2a1..."]) # => true
+ #
+ # @return [Boolean] true when the move was successful
+ def move_accounts(wallet, accounts)
+ rpc(:account_move, source: wallet, accounts: accounts, _access: :moved) == 1
+ end
+
+ # Remove an {Nanook::Account} from this {Nanook::Wallet}.
+ #
+ # ==== Example:
+ #
+ # wallet.remove_account("nano_3e3j5...") # => true
+ #
+ # @return [Boolean] true when the remove was successful
+ def remove_account(account)
+ rpc(:account_remove, account: account, _access: :removed) == 1
+ end
+
# Balance of all accounts in the wallet, optionally breaking the balances down by account.
#
# ==== Examples:
# wallet.balance
#
@@ -135,55 +181,54 @@
# "balance"=>51.4,
# "pending"=>0
# },
# }
#
- # @param [Boolean] account_break_down (default is +false+). When +true+
+ # @param account_break_down [Boolean] (default is +false+). When +true+
# the response will contain balances per account.
# @param unit (see Nanook::Account#balance)
#
# @return [Hash{Symbol=>Integer|Float|Hash}]
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
def balance(account_break_down: false, unit: Nanook.default_unit)
- wallet_required!
+ validate_unit!(unit)
- unless Nanook::UNITS.include?(unit)
- raise ArgumentError.new("Unsupported unit: #{unit}")
- end
-
if account_break_down
- return Nanook::Util.coerce_empty_string_to_type(rpc(:wallet_balances)[:balances], Hash).tap do |r|
+ return rpc(:wallet_balances, _access: :balances, _coerce: Hash).tap do |r|
if unit == :nano
- r.each do |account, balances|
- r[account][:balance] = Nanook::Util.raw_to_NANO(r[account][:balance])
- r[account][:pending] = Nanook::Util.raw_to_NANO(r[account][:pending])
+ 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
- rpc(:wallet_balance_total).tap do |r|
- if unit == :nano
- r[:balance] = Nanook::Util.raw_to_NANO(r[:balance])
- r[:pending] = Nanook::Util.raw_to_NANO(r[:pending])
- 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
# Changes a wallet's seed.
#
# It's recommended to only change the seed of a wallet that contains
- # no accounts.
+ # 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
#
# @param seed [String] the seed to change to.
# @return [Boolean] indicating whether the change was successful.
def change_seed(seed)
- wallet_required!
- rpc(:wallet_change_seed, seed: seed).has_key?(:success)
+ rpc(:wallet_change_seed, seed: seed).key?(:success)
end
# Creates a new wallet.
#
# The wallet will be created only on this node. It's important that
@@ -197,11 +242,12 @@
# ==== Example:
# Nanook.new.wallet.create # => Nanook::Wallet
#
# @return [Nanook::Wallet]
def create
- @wallet = rpc(:wallet_create)[:wallet]
+ skip_wallet_required!
+ @wallet = rpc(:wallet_create, _access: :wallet)
self
end
# Destroys the wallet.
#
@@ -209,47 +255,55 @@
#
# wallet.destroy # => true
#
# @return [Boolean] indicating success of the action
def destroy
- wallet_required!
- rpc(:wallet_destroy)
- true
+ rpc(:wallet_destroy, _access: :destroyed) == 1
end
# 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"
+ # 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"
+ #
+ # @return [String]
def export
- wallet_required!
- rpc(:wallet_export)[:json]
+ rpc(:wallet_export, _access: :json)
end
+ # Returns true if wallet exists on the node.
+ #
+ # ==== Example:
+ #
+ # wallet.exists? # => true
+ #
+ # @return [Boolean] true if wallet exists on the node
+ def exists?
+ export
+ true
+ rescue Nanook::NodeRpcError
+ false
+ end
+
# Will return +true+ if the account exists in the wallet.
#
# ==== Example:
# wallet.contains?("nano_...") # => true
#
# @param account [String] id (will start with <tt>"nano_..."</tt>)
# @return [Boolean] indicating if the wallet contains the given account
def contains?(account)
- wallet_required!
- response = rpc(:wallet_contains, account: account)
- !response.empty? && response[:exists] == 1
+ rpc(:wallet_contains, account: account, _access: :exists) == 1
end
- # @return [String] the wallet id
- def id
- @wallet
- end
-
# @return [String]
- def inspect
- "#{self.class.name}(id: \"#{id}\", object_id: \"#{"0x00%x" % (object_id << 1)}\")"
+ def to_s
+ "#{self.class.name}(id: \"#{short_id}\")"
end
+ alias inspect to_s
# 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
@@ -261,21 +315,21 @@
# Work to be generated.</i>
#
# ==== 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..."
+ # wallet.pay(from: "nano_...", to: "nano_...", amount: 54000000000000, unit: :raw, id: "myUniqueId123")
+ # # => "9AE2311..."
#
# @param from [String] account id of an account in your wallet
# @param to (see Nanook::WalletAccount#pay)
# @param amount (see Nanook::WalletAccount#pay)
# @param unit (see Nanook::Account#balance)
# @params id (see Nanook::WalletAccount#pay)
# @return (see Nanook::WalletAccount#pay)
# @raise [Nanook::Error] if unsuccessful
- def pay(from:, to:, amount:, unit: Nanook.default_unit, id:)
- wallet_required!
+ 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
# Information about pending blocks (payments) that are waiting
@@ -292,120 +346,147 @@
# wallet.pending
#
# Example response:
#
# {
- # :nano_1111111111111111111111111111111111111111111111111117353trpda=>[
- # "142A538F36833D1CC78B94E11C766F75818F8B940771335C6C1B8AB880C5BB1D",
- # "718CC2121C3E641059BC1C2CFC45666C99E8AE922F7A807B7D07B62C995D79E2"
+ # Nanook::Account=>[
+ # Nanook::Block,
+ # Nanook::Block"
# ],
- # :nano_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3=>[
- # "4C1FEEF0BEA7F50BE35489A1233FE002B212DEA554B55B1B470D78BD8F210C74"
+ # Nanook::Account=>[
+ # Nanook::Block
# ]
# }
#
# Asking for more information:
#
# wallet.pending(detailed: true)
#
# Example response:
#
# {
- # :nano_1111111111111111111111111111111111111111111111111117353trpda=>[
+ # Nanook::Account=>[
# {
# :amount=>6.0,
- # :source=>"nano_3dcfozsmekr1tr9skf1oa5wbgmxt81qepfdnt7zicq5x3hk65fg4fqj58mbr",
- # :block=>:"142A538F36833D1CC78B94E11C766F75818F8B940771335C6C1B8AB880C5BB1D"
+ # :source=>Nanook::Account,
+ # :block=>Nanook::Block
# },
# {
# :amount=>12.0,
- # :source=>"nano_3dcfozsmekr1tr9skf1oa5wbgmxt81qepfdnt7zicq5x3hk65fg4fqj58mbr",
- # :block=>:"242A538F36833D1CC78B94E11C766F75818F8B940771335C6C1B8AB880C5BB1D"
+ # :source=>Nanook::Account,
+ # :block=>Nanook::Block
# }
# ],
- # :nano_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3=>[
+ # Nanook::Account=>[
# {
# :amount=>106.370018,
- # :source=>"nano_13ezf4od79h1tgj9aiu4djzcmmguendtjfuhwfukhuucboua8cpoihmh8byo",
- # :block=>:"4C1FEEF0BEA7F50BE35489A1233FE002B212DEA554B55B1B470D78BD8F210C74"
+ # :source=>Nanook::Account,
+ # :block=>Nanook::Block
# }
# ]
# }
- def pending(limit:1000, detailed:false, unit:Nanook.default_unit)
- wallet_required!
+ #
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
+ def pending(limit: 1000, detailed: false, unit: Nanook.default_unit)
+ validate_unit!(unit)
- unless Nanook::UNITS.include?(unit)
- raise ArgumentError.new("Unsupported unit: #{unit}")
- end
+ params = {
+ count: limit,
+ _access: :blocks,
+ _coerce: Hash
+ }
- params = { count: limit }
params[:source] = true if detailed
- response = rpc(:wallet_pending, params)[:blocks]
- response = Nanook::Util.coerce_empty_string_to_type(response, Hash)
+ response = rpc(:wallet_pending, params)
- return response unless detailed
+ 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 = amount_and_source.merge(block: block.to_s)
- if unit == :nano
- d[:amount] = Nanook::Util.raw_to_NANO(d[:amount])
- end
+ 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
- [account, new_data]
+ [as_account(account), new_data]
end
- Hash[x].to_symbolized_hash
+ Hash[x]
end
# 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 <i>receive</i> block hash id if a receive was successful,
+ # Returns a <i>receive</i> 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...") # => "9AE2311..."
- # wallet.receive("718CC21...", into: "xrb...") # => "9AE2311..."
+ # wallet.receive(into: "xrb...") # => Nanook::Block
+ # wallet.receive("718CC21...", into: "xrb...") # => Nanook::Block
#
# @param block (see Nanook::WalletAccount#receive)
# @param into [String] account id of account in your wallet to receive the
# payment into
# @return (see Nanook::WalletAccount#receive)
- def receive(block=nil, into:)
- wallet_required!
+ def receive(block = nil, into:)
validate_wallet_contains_account!(into)
account(into).receive(block)
end
+ # 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, ...
+ #
+ # @param limit [Integer] limit of blocks to publish. Default is 1000.
+ # @return [Array<Nanook::Block>] republished blocks
+ def republish_blocks(limit: 1000)
+ rpc(:wallet_republish, count: limit, _access: :blocks, _coerce: Array).map do |block|
+ as_block(block)
+ end
+ end
+
# 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..."
#
- # @return [String] Representative account of the account
+ # @return [Nanook::Account] Representative account. Can be nil.
def default_representative
- rpc(:wallet_representative)[:representative]
+ representative = rpc(:wallet_representative, _access: :representative)
+ as_account(representative) if representative
end
- alias_method :representative, :default_representative
+ alias representative default_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
@@ -413,27 +494,25 @@
#
# ==== Example:
#
# wallet.change_default_representative("nano_...") # => "nano_..."
#
- # @param [String] representative the id of the representative account
+ # @param representative [String] id of the representative account
# to set as this account's representative
- # @return [String] the representative account id
- # @raise [ArgumentError] if the representative account does not exist
+ # @return [Nanook::Account] the representative account
# @raise [Nanook::Error] if setting the representative fails
def change_default_representative(representative)
- unless Nanook::Account.new(@rpc, representative).exists?
- raise ArgumentError.new("Representative account does not exist: #{representative}")
+ unless as_account(representative).exists?
+ raise Nanook::Error, "Representative account does not exist: #{representative}"
end
- if rpc(:wallet_representative_set, representative: representative)[:set] == 1
- representative
- else
- raise Nanook::Error.new("Setting the representative failed")
- end
+ raise Nanook::Error, 'Setting the representative failed' \
+ unless rpc(:wallet_representative_set, representative: representative, _access: :set) == 1
+
+ as_account(representative)
end
- alias_method :change_representative, :change_default_representative
+ alias change_representative change_default_representative
# Restores a previously created wallet by its seed.
# A new wallet will be created on your node (with a new wallet id)
# and will have its seed set to the given seed.
#
@@ -444,141 +523,249 @@
# @param seed [String] the wallet seed to restore.
# @param accounts [Integer] optionally restore the given number of accounts for the wallet.
#
# @return [Nanook::Wallet] a new wallet
# @raise [Nanook::Error] if unsuccessful
- def restore(seed, accounts:0)
+ def restore(seed, accounts: 0)
+ skip_wallet_required!
+
create
- unless change_seed(seed)
- raise Nanook::Error.new("Unable to set seed for wallet")
- end
+ raise Nanook::Error, 'Unable to set seed for wallet' unless change_seed(seed)
- if accounts > 0
- account.create(accounts)
- end
+ account.create(accounts) if accounts.positive?
self
end
- # Information about this wallet and all of its accounts.
+ # 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 => { ... }
+ # }
+ #
+ # @param unit (see Nanook::Account#balance)
+ # @return [Hash{Nanook::Account=>Hash{Symbol=>Nanook::Block|Integer|Float|Time}}] ledger.
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
+ 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]) if data[:frontier]
+ data[:open_block] = as_block(data[:open_block]) if data[:open_block]
+ data[:representative_block] = as_block(data[:representative_block]) if data[:representative_block]
+ data[:balance] = raw_to_NANO(data[:balance]) if unit == :nano && data[:balance]
+ data[:last_modified_at] = as_time(data.delete(:modified_timestamp))
+
+ [as_account(account_id), data]
+ end
+
+ Hash[accounts]
+ end
+
+ # 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:
#
# {
- # id: "2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570",
- # accounts: [
- # {
- # id: "nano_11119gbh8hb4hj1duf7fdtfyf5s75okzxdgupgpgm1bj78ex3kgy7frt3s9n"
- # frontier: "E71AF3E9DD86BBD8B4620EFA63E065B34D358CFC091ACB4E103B965F95783321",
- # open_block: "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
- # representative_block: "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
- # balance: 1.45,
- # modified_timestamp: 1511476234,
- # block_count: 2
- # },
- # { ... }
- # ]
- # }
+ # balance: 1.0,
+ # pending: 2.3
+ # accounts_count: 3,
+ # adhoc_count: 1,
+ # deterministic_count: 2,
+ # deterministic_index: 2
+ # }
#
- # @param unit (see #balance)
- # @return [Hash{Symbol=>String|Array<Hash{Symbol=>String|Integer|Float}>}] information about the wallet.
- # See {Nanook::Account#info} for details of what is returned for each account.
+ # @param unit (see Nanook::Account#balance)
+ # @return [Hash{Symbol=>Integer|Float}] information about the wallet.
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
def info(unit: Nanook.default_unit)
- unless Nanook::UNITS.include?(unit)
- raise ArgumentError.new("Unsupported unit: #{unit}")
- end
+ validate_unit!(unit)
- wallet_required!
- accounts = rpc(:wallet_ledger)[:accounts].map do |account_id, payload|
- payload[:id] = account_id
- if unit == :nano
- payload[:balance] = Nanook::Util.raw_to_NANO(payload[:balance])
- end
- payload
+ response = rpc(:wallet_info, _coerce: Hash)
+
+ if unit == :nano
+ response[:balance] = raw_to_NANO(response[:balance])
+ response[:pending] = raw_to_NANO(response[:pending])
end
- {
- id: @wallet,
- accounts: accounts
- }.to_symbolized_hash
+ response
end
+ # 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
+ # },
+ # {
+ # ...
+ # }
+ # ]
+ #
+ # @param unit (see #balance)
+ # @return [Array<Hash{Symbol=>String|Nanook::Account|Nanook::WalletAccount|Nanook::Block|Integer|Float|Time}>]
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
+ 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
+
# Locks the wallet. A locked wallet cannot pocket pending transactions or make payments. See {#unlock}.
#
# ==== Example:
#
# wallet.lock #=> true
#
# @return [Boolean] indicates if the wallet was successfully locked
def lock
- wallet_required!
- response = rpc(:wallet_lock)
- !response.empty? && response[:locked] == 1
+ rpc(:wallet_lock, _access: :locked) == 1
end
# Returns +true+ if the wallet is locked.
#
# ==== Example:
#
# wallet.locked? #=> false
#
# @return [Boolean] indicates if the wallet is locked
def locked?
- wallet_required!
- response = rpc(:wallet_locked)
- !response.empty? && response[:locked] != 0
+ rpc(:wallet_locked, _access: :locked) == 1
end
# Unlocks a previously locked wallet.
#
# ==== Example:
#
# wallet.unlock("new_pass") #=> true
#
# @return [Boolean] indicates if the unlocking action was successful
- def unlock(password)
- wallet_required!
- rpc(:password_enter, password: password)[:valid] == 1
+ def unlock(password = nil)
+ rpc(:password_enter, password: password, _access: :valid) == 1
end
# Changes the password for a wallet.
#
# ==== Example:
#
# wallet.change_password("new_pass") #=> true
# @return [Boolean] indicates if the action was successful
def change_password(password)
- wallet_required!
- rpc(:password_change, password: password)[:changed] == 1
+ rpc(:password_change, password: password, _access: :changed) == 1
end
+ # Tells the node to look for pending blocks for any account in the wallet.
+ #
+ # ==== Example:
+ #
+ # wallet.search_pending #=> true
+ # @return [Boolean] indicates if the action was successful
+ def search_pending
+ rpc(:search_pending, _access: :started) == 1
+ end
+
+ # Returns a list of pairs of {Nanook::WalletAccount} and work for wallet.
+ #
+ # ==== Example:
+ #
+ # wallet.work
+ #
+ # ==== Example response:
+ #
+ # {
+ # Nanook::WalletAccount: "432e5cf728c90f4f",
+ # Nanook::WalletAccount: "4efec5f63fc902cf"
+ # }
+ # @return [Boolean] indicates if the action was successful
+ 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
+
private
- def rpc(action, params={})
- p = @wallet.nil? ? {} : { wallet: @wallet }
- @rpc.call(action, p.merge(params))
+ def rpc(action, params = {})
+ check_wallet_required!
+
+ p = { wallet: @wallet }.compact
+ @rpc.call(action, p.merge(params)).tap { reset_skip_wallet_required! }
end
- def wallet_required!
- if @wallet.nil?
- raise ArgumentError.new("Wallet must be present")
- end
+ def skip_wallet_required!
+ @skip_wallet_required_check = true
end
+ def reset_skip_wallet_required!
+ @skip_wallet_required_check = false
+ end
+
+ def check_wallet_required!
+ return if @wallet || @skip_wallet_required_check
+
+ raise ArgumentError, 'Wallet must be present'
+ end
+
def validate_wallet_contains_account!(account)
@known_valid_accounts ||= []
return if @known_valid_accounts.include?(account)
- if contains?(account)
- @known_valid_accounts << account
- else
- raise ArgumentError.new("Account does not exist in wallet. Account: #{account}, wallet: #{@wallet}")
+ unless contains?(account)
+ raise ArgumentError,
+ "Account does not exist in wallet. Account: #{account}, wallet: #{@wallet}"
end
- end
+ @known_valid_accounts << account
+ end
end
end