text: "use strict";
  //  Copyright (C) 2018 Zilliqa
  //
  //  This file is part of Zilliqa-Javascript-Library.
  //
  //  This program is free software: you can redistribute it and/or modify
  //  it under the terms of the GNU General Public License as published by
  //  the Free Software Foundation, either version 3 of the License, or
  //  (at your option) any later version.
  //
  //  This program is distributed in the hope that it will be useful,
  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  //  GNU General Public License for more details.
  //
  //  You should have received a copy of the GNU General Public License
  //  along with this program.  If not, see <https://www.gnu.org/licenses/>.
  Object.defineProperty(exports, "__esModule", { value: true });
  var tslib_1 = require("tslib");
  var bip39_1 = tslib_1.__importDefault(require("bip39"));
  var hdkey_1 = tslib_1.__importDefault(require("hdkey"));
  var core_1 = require("@zilliqa-js/core");
  var zcrypto = tslib_1.__importStar(require("@zilliqa-js/crypto"));
  var account_1 = require("./account");
  var bn_js_1 = tslib_1.__importDefault(require("bn.js"));
  var Wallet = /** @class */ (function (_super) {
      tslib_1.__extends(Wallet, _super);
      /**
       * constructor
       *
       * Takes an array of Account objects and instantiates a Wallet instance.
       *
       * @param {Account[]} accounts
       */
      function Wallet(provider, accounts) {
          if (accounts === void 0) { accounts = []; }
          var _this = _super.call(this) || this;
          _this.accounts = {};
          if (accounts.length) {
              _this.accounts = accounts.reduce(function (acc, account) {
                  var _a;
                  return tslib_1.__assign(tslib_1.__assign({}, acc), (_a = {}, _a[account.address] = account, _a));
              }, {});
          }
          _this.provider = provider;
          _this.defaultAccount = accounts[0];
          return _this;
      }
      /**
       * create
       *
       * Creates a new keypair with a randomly-generated private key. The new
       * account is accessible by address.
       *
       * @returns {string} - address of the new account
       */
      Wallet.prototype.create = function () {
          var _a;
          var privateKey = zcrypto.schnorr.generatePrivateKey();
          var newAccount = new account_1.Account(privateKey);
          this.accounts = tslib_1.__assign(tslib_1.__assign({}, this.accounts), (_a = {}, _a[newAccount.address] = newAccount, _a));
          if (!this.defaultAccount) {
              this.defaultAccount = newAccount;
          }
          return newAccount.address;
      };
      /**
       * addByPrivateKey
       *
       * Adds an account to the wallet by private key.
       *
       * @param {string} privateKey - hex-encoded private key
       * @returns {string} - the corresponing address, computer from the private
       * key.
       */
      Wallet.prototype.addByPrivateKey = function (privateKey) {
          var _a;
          var newAccount = new account_1.Account(privateKey);
          this.accounts = tslib_1.__assign(tslib_1.__assign({}, this.accounts), (_a = {}, _a[newAccount.address] = newAccount, _a));
          if (!this.defaultAccount) {
              this.defaultAccount = newAccount;
          }
          return newAccount.address;
      };
      /**
       * addByKeystore
       *
       * Adds an account by keystore. This method is asynchronous and returns
       * a Promise<string>, in order not to block on the underlying decryption
       * operation.
       *
       * @param {string} keystore
       * @param {string} passphrase
       * @returns {Promise<string>}
       */
      Wallet.prototype.addByKeystore = function (keystore, passphrase) {
          return tslib_1.__awaiter(this, void 0, void 0, function () {
              var newAccount;
              var _a;
              return tslib_1.__generator(this, function (_b) {
                  switch (_b.label) {
                      case 0: return [4 /*yield*/, account_1.Account.fromFile(keystore, passphrase)];
                      case 1:
                          newAccount = _b.sent();
                          this.accounts = tslib_1.__assign(tslib_1.__assign({}, this.accounts), (_a = {}, _a[newAccount.address] = newAccount, _a));
                          if (!this.defaultAccount) {
                              this.defaultAccount = newAccount;
                          }
                          return [2 /*return*/, newAccount.address];
                  }
              });
          });
      };
      /**
       * addByMnemonic
       *
       * Adds an `Account` by use of a mnemonic as specified in BIP-32 and BIP-39
       *
       * @param {string} phrase - 12-word mnemonic phrase
       * @param {number} index=0 - the number of the child key to add
       * @returns {string} - the corresponding address
       */
      Wallet.prototype.addByMnemonic = function (phrase, index) {
          if (index === void 0) { index = 0; }
          if (!this.isValidMnemonic(phrase)) {
              throw new Error("Invalid mnemonic phrase: " + phrase);
          }
          var seed = bip39_1.default.mnemonicToSeed(phrase);
          var hdKey = hdkey_1.default.fromMasterSeed(seed);
          var childKey = hdKey.derive("m/44'/313'/0'/0/" + index);
          var privateKey = childKey.privateKey.toString('hex');
          return this.addByPrivateKey(privateKey);
      };
      /**
       * addByMnemonicLedger
       *
       * Adds an `Account` by use of a mnemonic as specified in BIP-32 and BIP-39
       * The key derivation path used in Ledger is different from that of
       * addByMnemonic.
       *
       * @param {string} phrase - 12-word mnemonic phrase
       * @param {number} index=0 - the number of the child key to add
       * @returns {string} - the corresponding address
       */
      Wallet.prototype.addByMnemonicLedger = function (phrase, index) {
          if (index === void 0) { index = 0; }
          if (!this.isValidMnemonic(phrase)) {
              throw new Error("Invalid mnemonic phrase: " + phrase);
          }
          var seed = bip39_1.default.mnemonicToSeed(phrase);
          var hdKey = hdkey_1.default.fromMasterSeed(seed);
          var childKey = hdKey.derive("m/44'/313'/" + index + "'/0'/0'");
          var privateKey = childKey.privateKey.toString('hex');
          return this.addByPrivateKey(privateKey);
      };
      /**
       * export
       *
       * Exports the specified account as a keystore file.
       *
       * @param {string} address
       * @param {string} passphrase
       * @param {KDF} kdf='scrypt'
       * @returns {Promise<string>}
       */
      Wallet.prototype.export = function (address, passphrase, kdf) {
          if (kdf === void 0) { kdf = 'scrypt'; }
          if (!this.accounts[address]) {
              throw new Error("No account with address " + address + " exists");
          }
          return this.accounts[address].toFile(passphrase, kdf);
      };
      /**
       * remove
       *
       * Removes an account from the wallet and returns boolean to indicate
       * failure or success.
       *
       * @param {string} address
       * @returns {boolean}
       */
      Wallet.prototype.remove = function (address) {
          if (this.accounts[address]) {
              var _a = this.accounts, _b = address, toRemove = _a[_b], rest = tslib_1.__rest(_a, [typeof _b === "symbol" ? _b : _b + ""]);
              this.accounts = rest;
              return true;
          }
          return false;
      };
      /**
       * setDefault
       *
       * Sets the default account of the wallet.
       *
       * @param {string} address
       */
      Wallet.prototype.setDefault = function (address) {
          this.defaultAccount = this.accounts[address];
      };
      /**
       * sign
       *
       * signs an unsigned transaction with the default account.
       *
       * @param {Transaction} tx
       * @param {string} account
       * @returns {Transaction}
       */
      Wallet.prototype.sign = function (tx) {
          if (tx.txParams && tx.txParams.pubKey) {
              // attempt to find the address
              var senderAddress = zcrypto.getAddressFromPublicKey(tx.txParams.pubKey);
              if (!this.accounts[senderAddress]) {
                  throw new Error("Could not sign the transaction with " + senderAddress + " as it does not exist");
              }
              return this.signWith(tx, senderAddress);
          }
          if (!this.defaultAccount) {
              throw new Error('This wallet has no default account.');
          }
          return this.signWith(tx, this.defaultAccount.address);
      };
      /**
       * signWith
       *
       * @param {Transaction} tx
       * @param {string} account
       * @returns {Transaction}
       */
      Wallet.prototype.signWith = function (tx, account) {
          return tslib_1.__awaiter(this, void 0, void 0, function () {
              var signer, balance, gasPrice, gasLimit, debt, bal, withNonce_1, withPublicKey;
              return tslib_1.__generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          if (!this.accounts[account]) {
                              throw new Error('The selected account does not exist on this Wallet instance.');
                          }
                          signer = this.accounts[account];
                          return [4 /*yield*/, this.provider.send('GetBalance', signer.address.replace('0x', '').toLowerCase())];
                      case 1:
                          balance = _a.sent();
                          if (balance.result === undefined) {
                              throw new Error('Could not get balance');
                          }
                          gasPrice = tx.txParams.gasPrice;
                          gasLimit = new bn_js_1.default(tx.txParams.gasLimit.toString());
                          debt = gasPrice.mul(gasLimit).add(tx.txParams.amount);
                          bal = new bn_js_1.default(balance.result.balance);
                          if (debt.gt(bal)) {
                              throw new Error('You do not have enough funds, need ' +
                                  debt.toString() +
                                  ' but only have ' +
                                  bal.toString());
                          }
                          try {
                              if (!tx.txParams.nonce) {
                                  if (typeof balance.result.nonce !== 'number') {
                                      throw new Error('Could not get nonce');
                                  }
                                  withNonce_1 = tx.map(function (txObj) {
                                      return tslib_1.__assign(tslib_1.__assign({}, txObj), { nonce: txObj.nonce || balance.result.nonce + 1, pubKey: signer.publicKey });
                                  });
                                  return [2 /*return*/, withNonce_1.map(function (txObj) {
                                          // @ts-ignore
                                          return tslib_1.__assign(tslib_1.__assign({}, txObj), { signature: signer.signTransaction(withNonce_1.bytes) });
                                      })];
                              }
                              withPublicKey = tx.map(function (txObj) {
                                  return tslib_1.__assign(tslib_1.__assign({}, txObj), { pubKey: signer.publicKey });
                              });
                              return [2 /*return*/, withPublicKey.map(function (txObj) {
                                      return tslib_1.__assign(tslib_1.__assign({}, txObj), { signature: signer.signTransaction(tx.bytes) });
                                  })];
                          }
                          catch (err) {
                              throw err;
                          }
                          return [2 /*return*/];
                  }
              });
          });
      };
      Wallet.prototype.isValidMnemonic = function (phrase) {
          if (phrase.trim().split(/\s+/g).length < 12) {
              return false;
          }
          return bip39_1.default.validateMnemonic(phrase);
      };
      return Wallet;
  }(core_1.Signer));
  exports.Wallet = Wallet;
  //# sourceMappingURL=wallet.js.map