Class: Nanook::Wallet

Inherits:
Object
  • Object
show all
Defined in:
lib/nanook/wallet.rb

Overview

The Nanook::Wallet class lets you manage your nano wallets, as well as some account-specific things like making and receiving payments.

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 otherhand 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 –wallet_decrypt_unsafe CLI command.

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)

Instance Method Summary collapse

Constructor Details

#initialize(rpc, wallet) ⇒ Wallet

Returns a new instance of Wallet



52
53
54
55
# File 'lib/nanook/wallet.rb', line 52

def initialize(rpc, wallet)
  @rpc = rpc
  @wallet = wallet
end

Instance Method Details

#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..create     # => Nanook::WalletAccount

See Nanook::WalletAccount for all the methods you can call on the account object returned.

Examples:

wallet.("xrb_...") # => Nanook::WalletAccount
wallet..create     # => Nanook::WalletAccount

Parameters:

  • account (String) (defaults to: nil)

    optional String of an account (starting with "xrb...") 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.

Returns:

Raises:

  • (ArgumentError)

    if the wallet does no contain the account



79
80
81
# File 'lib/nanook/wallet.rb', line 79

def (=nil)
  Nanook::WalletAccount.new(@rpc, @wallet, )
end

#accountsArray<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...]

Returns:



93
94
95
96
97
98
99
# File 'lib/nanook/wallet.rb', line 93

def accounts
  wallet_required!
  response = rpc(:account_list)[:accounts]
  Nanook::Util.coerce_empty_string_to_type(response, Array).map do ||
    Nanook::WalletAccount.new(@rpc, @wallet, )
  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:

{
  "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"=>{
    "balance"=>2.5,
    "pending"=>1
  },
  "xrb_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"=>{
    "balance"=>51.4,
    "pending"=>0
  },
}

Parameters:

  • account_break_down (Boolean)

    (default is false). When true the response will contain balances per account.

  • unit (Symbol)

    default is Nanook.default_unit. Must be one of UNITS. Represents the unit that the balances will be returned in. Note: this method interprets :nano as NANO, which is technically Mnano. See What are Nano's Units

Returns:

  • (Hash{Symbol=>Integer|Float|Hash})


146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/nanook/wallet.rb', line 146

def balance(account_break_down: false, unit: Nanook.default_unit)
  wallet_required!

  unless Nanook::UNITS.include?(unit)
    raise ArgumentError.new("Unsupported unit: #{unit}")
  end

  if 
    return Nanook::Util.coerce_empty_string_to_type(rpc(:wallet_balances)[:balances], Hash).tap do |r|
      if unit == :nano
        r.each do |, balances|
          r[][:balance] = Nanook::Util.raw_to_NANO(r[][:balance])
          r[][:pending] = Nanook::Util.raw_to_NANO(r[][: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
end

#change_password(password) ⇒ Boolean

Changes the password for a wallet.

Example:

wallet.change_password("new_pass") #=> true

Returns:

  • (Boolean)

    indicates if the action was successful



448
449
450
451
# File 'lib/nanook/wallet.rb', line 448

def change_password(password)
  wallet_required!
  rpc(:password_change, password: password)[:changed] == 1
end

#change_seed(seed) ⇒ Boolean

Changes a wallet's seed.

Example:

wallet.change_seed("000D1BA...") # => true

Parameters:

  • seed (String)

    the seed to change to.

Returns:

  • (Boolean)

    indicating whether the change was successful.



180
181
182
183
# File 'lib/nanook/wallet.rb', line 180

def change_seed(seed)
  wallet_required!
  rpc(:wallet_change_seed, seed: seed).has_key?(:success)
end

#contains?(account) ⇒ Boolean

Will return true if the account exists in the wallet.

Example:

wallet.contains?("xrb_...") # => true

Parameters:

  • account (String)

    id (will start with "xrb_...")

Returns:

  • (Boolean)

    indicating if the wallet contains the given account



234
235
236
237
238
# File 'lib/nanook/wallet.rb', line 234

def contains?()
  wallet_required!
  response = rpc(:wallet_contains, account: )
  !response.empty? && response[:exists] == 1
end

#createNanook::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

Returns:



199
200
201
202
# File 'lib/nanook/wallet.rb', line 199

def create
  @wallet = rpc(:wallet_create)[:wallet]
  self
end

#destroyBoolean

Destroys the wallet.

Example:

wallet.destroy # => true

Returns:

  • (Boolean)

    indicating success of the action



211
212
213
214
215
# File 'lib/nanook/wallet.rb', line 211

def destroy
  wallet_required!
  rpc(:wallet_destroy)
  true
end

#exportObject

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"


222
223
224
225
# File 'lib/nanook/wallet.rb', line 222

def export
  wallet_required!
  rpc(:wallet_export)[:json]
end

#idString

Returns the wallet id

Returns:

  • (String)

    the wallet id



241
242
243
# File 'lib/nanook/wallet.rb', line 241

def id
  @wallet
end

#inspectString

Returns:

  • (String)


246
247
248
# File 'lib/nanook/wallet.rb', line 246

def inspect
  "#{self.class.name}(id: \"#{id}\", object_id: \"#{"0x00%x" % (object_id << 1)}\")"
end

#locked?Boolean

Returns true if the wallet is locked.

Example:

wallet.locked? #=> false

Returns:

  • (Boolean)

    indicates if the wallet is locked



424
425
426
427
428
# File 'lib/nanook/wallet.rb', line 424

def locked?
  wallet_required!
  response = rpc(:wallet_locked)
  !response.empty? && response[:locked] != 0
end

#pay(from:, to:, amount:, unit: Nanook.default_unit, id:) ⇒ String

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: "xrb_...", to: "xrb_...", amount: 1.1, id: "myUniqueId123") # => "9AE2311..."
wallet.pay(from: "xrb_...", to: "xrb_...", amount: 54000000000000, unit: :raw, id: "myUniqueId123") # => "9AE2311..."

Parameters:

  • from (String)

    account id of an account in your wallet

  • to (String)

    account id of the recipient of your payment

  • amount (Integer|Float)
  • unit (Symbol)

    default is Nanook.default_unit. Must be one of UNITS. Represents the unit that the balances will be returned in. Note: this method interprets :nano as NANO, which is technically Mnano. See What are Nano's Units

Returns:

  • (String)

    the send block id for the payment

Raises:



273
274
275
276
277
# File 'lib/nanook/wallet.rb', line 273

def pay(from:, to:, amount:, unit: Nanook.default_unit, id:)
  wallet_required!
  validate_wallet_contains_account!(from)
  (from).pay(to: to, amount: amount, unit: unit, id: id)
end

#pending(limit: 1000, detailed: 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:

{
  :xrb_1111111111111111111111111111111111111111111111111117353trpda=>[
    "142A538F36833D1CC78B94E11C766F75818F8B940771335C6C1B8AB880C5BB1D",
    "718CC2121C3E641059BC1C2CFC45666C99E8AE922F7A807B7D07B62C995D79E2"
  ],
  :xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3=>[
    "4C1FEEF0BEA7F50BE35489A1233FE002B212DEA554B55B1B470D78BD8F210C74"
  ]
}

Asking for more information:

wallet.pending(detailed: true)

Example response:

{
  :xrb_1111111111111111111111111111111111111111111111111117353trpda=>[
    {
      :amount=>6.0,
      :source=>"xrb_3dcfozsmekr1tr9skf1oa5wbgmxt81qepfdnt7zicq5x3hk65fg4fqj58mbr",
      :block=>:142A538F36833D1CC78B94E11C766F75818F8B940771335C6C1B8AB880C5BB1D"
    },
    {
      :amount=>12.0,
      :source=>"xrb_3dcfozsmekr1tr9skf1oa5wbgmxt81qepfdnt7zicq5x3hk65fg4fqj58mbr",
      :block=>:242A538F36833D1CC78B94E11C766F75818F8B940771335C6C1B8AB880C5BB1D"
    }
  ],
  :xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3=>[
    {
      :amount=>106.370018,
      :source=>"xrb_13ezf4od79h1tgj9aiu4djzcmmguendtjfuhwfukhuucboua8cpoihmh8byo",
      :block=>:4C1FEEF0BEA7F50BE35489A1233FE002B212DEA554B55B1B470D78BD8F210C74"
    }
  ]
}

Parameters:

  • limit (Integer)

    number of accounts with pending payments to return (default is 1000)

  • detailed (Boolean)

    return a more complex Hash of pending block information (default is false)

  • unit (Symbol)

    default is Nanook.default_unit. Must be one of UNITS. Represents the unit that the balances will be returned in. Note: this method interprets :nano as NANO, which is technically Mnano. See What are Nano's Units



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/nanook/wallet.rb', line 331

def pending(limit:1000, detailed:false, unit:Nanook.default_unit)
  wallet_required!

  unless Nanook::UNITS.include?(unit)
    raise ArgumentError.new("Unsupported unit: #{unit}")
  end

  params = { count: limit }
  params[:source] = true if detailed

  response = rpc(:wallet_pending, params)[:blocks]
  response = Nanook::Util.coerce_empty_string_to_type(response, Hash)

  return response unless detailed

  # Map the RPC response, which is:
  # account=>block=>[amount|source] into
  # account=>[block|amount|source]
  x = response.map do |, 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
    end

    [, new_data]
  end

  Hash[x].to_symbolized_hash
end

#receive(block = nil, into:) ⇒ String, 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 hash id 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..."

Parameters:

  • into (String)

    account id of account in your wallet to receive the payment into

  • block (String) (defaults to: nil)

    optional block id of pending payment. If not provided, the latest pending payment will be received

Returns:

  • (String)

    the receive block id

  • (false)

    if there was no block to receive



384
385
386
387
388
# File 'lib/nanook/wallet.rb', line 384

def receive(block=nil, into:)
  wallet_required!
  validate_wallet_contains_account!(into)
  (into).receive(block)
end

#restore(seed, accounts: 0) ⇒ Nanook::Wallet

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.

Example:

Nanook.new.wallet.restore(seed) # => Nanook::Wallet

Parameters:

  • seed (String)

    the wallet seed to restore.

  • accounts (Integer)

    optionally restore the given number of accounts for the wallet.

Returns:

Raises:



403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/nanook/wallet.rb', line 403

def restore(seed, accounts:0)
  create

  unless change_seed(seed)
    raise Nanook::Error.new("Unable to set seed for wallet")
  end

  if accounts > 0
    .create(accounts)
  end

  self
end

#unlock(password) ⇒ Boolean

Unlocks a previously locked wallet.

Example:

wallet.unlock("new_pass") #=> true

Returns:

  • (Boolean)

    indicates if the unlocking action was successful



437
438
439
440
# File 'lib/nanook/wallet.rb', line 437

def unlock(password)
  wallet_required!
  rpc(:password_enter, password: password)[:valid] == 1
end