class Nanook # The Nanook::Account class contains methods to discover # publicly-available information about accounts on the nano network. # # === Initializing # # Initialize this class through the convenient {Nanook#account} method: # # nanook = Nanook.new # account = nanook.account("xrb_...") # # Or compose the longhand way like this: # # rpc_conn = Nanook::Rpc.new # account = Nanook::Account.new(rpc_conn, "xrb_...") class Account def initialize(rpc, account) @rpc = rpc @account = account end # Returns information about this account's delegators. # === Example response: # { # :xrb_13bqhi1cdqq8yb9szneoc38qk899d58i5rcrgdk5mkdm86hekpoez3zxw5sd=>500000000000000000000000000000000000, # :xrb_17k6ug685154an8gri9whhe5kb5z1mf5w6y39gokc1657sh95fegm8ht1zpn=>961647970820730000000000000000000000 # } # # @return [Hash{Symbol=>String}] Delegators def delegators rpc(:delegators)[:delegators] end # Returns a boolean indicating if the account exists. # # Existence is determined by if the account has an _open_ block. # An _open_ block is a special kind of block that gets published when # an account receives a payment for the first time. # # The reliability of this check depends on the node host having # synchronized itself with most of the blocks on the nano network, # otherwise you may get +false+ when the account does exist. # You can check if a node's synchronization is particular low # using {Nanook::Node#sync_progress}. # # @return [Boolean] Indicates if this account exists in the nano network def exists? response = rpc(:account_info) !response.empty? && !response[:open_block].nil? end # Returns an account's history of send and receive payments. # # ==== Example: # # account.history # account.history(limit: 1) # # ==== Example response: # [ # { # :type=>"send", # :account=>"xrb_1kdc5u48j3hr5r7eof9iao47szqh81ndqgq5e5hrsn1g9a3sa4hkkcotn3uq", # :amount=>2, # :hash=>"2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570" # } # ] # ==== Example: # # account.history # account.history(unit: :raw) # # ==== Example response: # [ # { # :type=>"send", # :account=>"xrb_1kdc5u48j3hr5r7eof9iao47szqh81ndqgq5e5hrsn1g9a3sa4hkkcotn3uq", # :amount=>2000000000000000000000000000000, # :hash=>"2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570" # } # ] # # @param limit [Integer] maximum number of history items to return # @param unit (see #balance) # @return [ArrayString}>] the history of send and receive payments for this account def history(limit: 1000, unit: Nanook.default_unit) unless Nanook::UNITS.include?(unit) raise ArgumentError.new("Unsupported unit: #{unit}") end response = rpc(:account_history, count: limit)[:history] if unit == :raw return response end response.map! do |history| history[:amount] = Nanook::Util.raw_to_NANO(history[:amount]) history end end # @return [Time] last modified time of the account in UTC time zone. def last_modified_at response = rpc(:account_info) Time.at(response[:modified_timestamp]) end # Returns the public key belonging to an account. # # ==== Example response: # "3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039" # # @return [String] public key of this account def public_key rpc(:account_key)[:key] end # Returns the representative account for the account. # Representatives are accounts which cast votes in the case of a # fork in the network. # # ==== Example response # # "xrb_3pczxuorp48td8645bs3m6c3xotxd3idskrenmi65rbrga5zmkemzhwkaznh" # # @return [String] Representative account of this account def representative rpc(:account_representative)[:representative] end # Returns a Hash containing the account's balance. # # ==== Example: # # account.balance # # # => # # { # # balance=>2, # Account balance # # pending=>1.1 # Amount pending and not yet received by the account # # } # # ==== Example balance returned in raw: # # account.balance(unit: :raw) # # # => # # { # # balance: 2000000000000000000000000000000, # # pending: 1100000000000000000000000000000 # # } # # @param unit [Symbol] default is {Nanook.default_unit}. # Must be one of {Nanook::UNITS}. # Represents the unit that the balances will be returned in. # Note: this method interprets # +:nano+ as NANO, which is technically Mnano # See {https://nano.org/en/faq#what-are-nano-units- What are Nano's Units} # # @raise ArgumentError if an invalid +unit+ was given. def balance(unit: Nanook.default_unit) unless Nanook::UNITS.include?(unit) raise ArgumentError.new("Unsupported unit: #{unit}") end rpc(:account_balance).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 end # Returns the id of the account. # @return [String] the id of the account def id @account end # Returns a Hash containing information about the account. # # ==== Example 1 # # account.info # # ==== Example 1 response # { # :id=>"xrb_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5" # :balance=>11.439597000000001, # :block_count=>4 # :frontier=>"2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570", # :modified_timestamp=>1520500357, # :open_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9", # :representative_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9", # } # # ==== Example 2 # # account.info(detailed: true) # # ==== Example 2 response # { # :id=>"xrb_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5" # :balance=>11.439597000000001, # :block_count=>4, # :frontier=>"2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570", # :modified_timestamp=>1520500357, # :open_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9", # :pending=>0, # :public_key=>"A82C906460046D230D7D37C6663723DC3EFCECC4B3254EBF45294B66746F4FEF", # :representative=>"xrb_3pczxuorp48td8645bs3m6c3xotxd3idskrenmi65rbrga5zmkemzhwkaznh", # :representative_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9", # :weight=>0 # } # # @param detailed [Boolean] (default is false). When +true+, four # additional calls are made to the RPC to return more information # @param unit (see #balance) # @return [Hash] information about the account containing: # [+id] The account id # [+frontier+] The latest block hash # [+open_block+] The first block in every account's blockchain. When this block was published the account was officially open # [+representative_block+] The block that named the representative for the account # [+balance+] Amount in {NANO}[https://nano.org/en/faq#what-are-nano-units-] # [+last_modified+] Unix timestamp # [+block_count+] Number of blocks in the account's blockchain # # When detailed: true is passed as an argument, this method # makes four additional calls to the RPC to return more information # about an account: # # [+weight+] See {#weight} # [+pending+] See {#balance} # [+representative+] See {#representative} # [+public_key+] See {#public_key} def info(detailed: false, unit: Nanook.default_unit) unless Nanook::UNITS.include?(unit) raise ArgumentError.new("Unsupported unit: #{unit}") end response = rpc(:account_info) response.merge!(id: @account) if unit == :nano response[:balance] = Nanook::Util.raw_to_NANO(response[:balance]) end # Return the response if we don't need any more info return response unless detailed # Otherwise make additional calls response.merge!({ weight: weight, pending: balance(unit: unit)[:pending], representative: representative, public_key: public_key }) # Sort this new hash by keys Hash[response.sort].to_symbolized_hash end def inspect "#{self.class.name}(id: \"#{id}\", object_id: \"#{"0x00%x" % (object_id << 1)}\")" end # Returns information about the given account as well as other # accounts up the ledger. The number of accounts returned is determined # by the limit: argument. # # The information in each Hash is the same as what the # #info(detailed: false) method returns. # # ==== Arguments # # [+limit:+] Number of accounts to return in the ledger (default is 1) # # ==== Example # # account.ledger(limit: 2) # # ==== Example response # { # :xrb_3c3ek3k8135f6e8qtfy8eruk9q3yzmpebes7btzncccdest8ymzhjmnr196j=>{ # :frontier=>"2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570", # :open_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9", # :representative_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9", # :balance=>11439597000000000000000000000000, # :modified_timestamp=>1520500357, # :block_count=>4 # }, # :xrb_3c3ettq59kijuuad5fnaq35itc9schtr4r7r6rjhmwjbairowzq3wi5ap7h8=>{ ... } # } def ledger(limit: 1) rpc(:ledger, count: limit)[:accounts] end # Returns information about pending blocks (payments) that are # waiting to be received by the account. # # See also the #receive method of this class for how to receive a pending payment. # # The default response is an Array of block ids. # # With the +detailed:+ argument, the method returns an Array of Hashes, # which contain the source account id, amount pending and block id. # # ==== Example 1: # # account.pending # => ["000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"] # # ==== Example 2: # # account.pending(detailed: true) # # => # # [ # # { # # :block=>"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F", # # :amount=>6, # # :source=>"xrb_3dcfozsmekr1tr9skf1oa5wbgmxt81qepfdnt7zicq5x3hk65fg4fqj58mbr" # # }, # # { ... } # # ] # # ==== Example 3: # # account.pending(detailed: true, unit: raw).first[:amount] # => 6000000000000000000000000000000 # @param limit [Integer] number of pending blocks to return (default is 1000) # @param detailed [Boolean]return a more complex Hash of pending block information (default is +false+) # @param unit (see #balance) # # @return [Array] # @return [ArrayString|Integer}>] def pending(limit: 1000, detailed: false, unit: Nanook.default_unit) unless Nanook::UNITS.include?(unit) raise ArgumentError.new("Unsupported unit: #{unit}") end params = { count: limit } params[:source] = true if detailed response = rpc(:pending, params)[:blocks] response = Nanook::Util.coerce_empty_string_to_type(response, (detailed ? Hash : Array)) return response unless detailed response.map do |key, val| p = val.merge(block: key.to_s) if unit == :nano p[:amount] = Nanook::Util.raw_to_NANO(p[:amount]) end p end end # Returns the account's weight. # # Weight is determined by the account's balance, and represents # the voting weight that account has on the network. Only accounts # with greater than 256 weight can vote. # # ==== Example: # account.weight # => 0 # # @return [Integer] the account's weight def weight rpc(:account_weight)[:weight] end private def rpc(action, params={}) p = @account.nil? ? {} : { account: @account } @rpc.call(action, p.merge(params)) end end end