/// <inheritdoc />
        public HdAccount CreateNewAccount(Wallet wallet, CoinType coinType, string password)
        {
            // get the accounts for this type of coin
            var accounts = wallet.AccountsRoot.Single(a => a.CoinType == coinType).Accounts.ToList();

            int newAccountIndex = 0;

            if (accounts.Any())
            {
                newAccountIndex = accounts.Max(a => a.Index) + 1;
            }

            // get the extended pub key used to generate addresses for this account
            var       privateKey       = Key.Parse(wallet.EncryptedSeed, password, wallet.Network);
            var       seedExtKey       = new ExtKey(privateKey, wallet.ChainCode);
            var       accountHdPath    = $"m/44'/{(int)coinType}'/{newAccountIndex}'";
            KeyPath   keyPath          = new KeyPath(accountHdPath);
            ExtKey    accountExtKey    = seedExtKey.Derive(keyPath);
            ExtPubKey accountExtPubKey = accountExtKey.Neuter();

            var newAccount = new HdAccount
            {
                Index             = newAccountIndex,
                ExtendedPubKey    = accountExtPubKey.ToString(wallet.Network),
                ExternalAddresses = new List <HdAddress>(),
                InternalAddresses = new List <HdAddress>(),
                Name         = $"account {newAccountIndex}",
                HdPath       = accountHdPath,
                CreationTime = DateTimeOffset.Now
            };

            accounts.Add(newAccount);
            wallet.AccountsRoot.Single(a => a.CoinType == coinType).Accounts = accounts;

            return(newAccount);
        }
        /// <inheritdoc />
        public Mnemonic CreateWallet(string password, string folderPath, string name, string network, string passphrase = null)
        {
            // for now the passphrase is set to be the password by default.
            if (passphrase == null)
            {
                passphrase = password;
            }

            // generate the root seed used to generate keys from a mnemonic picked at random
            // and a passphrase optionally provided by the user
            Mnemonic mnemonic    = new Mnemonic(Wordlist.English, WordCount.Twelve);
            ExtKey   extendedKey = mnemonic.DeriveExtKey(passphrase);

            Network coinNetwork = WalletHelpers.GetNetwork(network);

            // create a wallet file
            Wallet wallet = this.GenerateWalletFile(password, folderPath, name, coinNetwork, extendedKey);

            // generate multiple accounts and addresses from the get-go
            for (int i = 0; i < WalletCreationAccountsCount; i++)
            {
                HdAccount account = CreateNewAccount(wallet, this.coinType, password);
                this.CreateAddressesInAccount(account, coinNetwork, UnusedAddressesBuffer);
                this.CreateAddressesInAccount(account, coinNetwork, UnusedAddressesBuffer, true);
            }

            // update the height of the we start syncing from
            this.UpdateLastBlockSyncedHeight(wallet, this.chain.Tip.Height);

            // save the changes to the file and add addresses to be tracked
            this.SaveToFile(wallet);
            this.Load(wallet);
            this.LoadKeysLookup();

            return(mnemonic);
        }
        /// <inheritdoc />
        public (string hex, uint256 transactionId, Money fee) BuildTransaction(string walletName, string accountName, string password, string destinationAddress, Money amount, string feeType, int minConfirmations)
        {
            if (amount == Money.Zero)
            {
                throw new WalletException($"Cannot send transaction with 0 {this.coinType}");
            }

            // get the wallet and the account
            Wallet    wallet  = this.GetWalletByName(walletName);
            HdAccount account = wallet.AccountsRoot.Single(a => a.CoinType == this.coinType).GetAccountByName(accountName);

            // get a list of transactions outputs that have not been spent
            var spendableTransactions = account.GetSpendableTransactions().ToList();

            // remove whats under min confirmations
            var currentHeight = this.chain.Height;

            spendableTransactions = spendableTransactions.Where(s => currentHeight - s.BlockHeight >= minConfirmations).ToList();

            // get total spendable balance in the account.
            var balance = spendableTransactions.Sum(t => t.Amount);

            // make sure we have enough funds
            if (balance < amount)
            {
                throw new WalletException("Not enough funds.");
            }

            // calculate which addresses needs to be used as well as the fee to be charged
            var calculationResult = this.CalculateFees(spendableTransactions, amount);

            // get extended private key
            var privateKey = Key.Parse(wallet.EncryptedSeed, password, wallet.Network);
            var seedExtKey = new ExtKey(privateKey, wallet.ChainCode);

            var signingKeys = new HashSet <ISecret>();
            var coins       = new List <Coin>();

            foreach (var transactionToUse in calculationResult.transactionsToUse)
            {
                var           address           = account.FindAddressesForTransaction(t => t.Id == transactionToUse.Id && t.Index == transactionToUse.Index && t.Amount > 0).Single();
                ExtKey        addressExtKey     = seedExtKey.Derive(new KeyPath(address.HdPath));
                BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(wallet.Network);
                signingKeys.Add(addressPrivateKey);

                coins.Add(new Coin(transactionToUse.Id, (uint)transactionToUse.Index, transactionToUse.Amount, transactionToUse.ScriptPubKey));
            }

            // get address to send the change to
            var changeAddress = account.GetFirstUnusedChangeAddress();

            // get script destination address
            Script destinationScript = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(new BitcoinPubKeyAddress(destinationAddress, wallet.Network));

            // build transaction
            var         builder = new TransactionBuilder();
            Transaction tx      = builder
                                  .AddCoins(coins)
                                  .AddKeys(signingKeys.ToArray())
                                  .Send(destinationScript, amount)
                                  .SetChange(changeAddress.ScriptPubKey)
                                  .SendFees(calculationResult.fee)
                                  .BuildTransaction(true);

            if (!builder.Verify(tx))
            {
                throw new WalletException("Could not build transaction, please make sure you entered the correct data.");
            }

            return(tx.ToHex(), tx.GetHash(), calculationResult.fee);
        }