private string FormatJson(HotBitcoinAddresses addresses)
        {
            StringBuilder result = new StringBuilder(16384);

            Dictionary <BitcoinChain, double> conversionRateLookup = new Dictionary <BitcoinChain, double>();
            Dictionary <BitcoinChain, Int64>  satoshisTotalLookup  = new Dictionary <BitcoinChain, long>();

            long fiatCentsPerCoreCoin = new Money(BitcoinUtility.SatoshisPerBitcoin, Currency.BitcoinCore).ToCurrency(_authenticationData.CurrentOrganization.Currency).Cents;
            long fiatCentsPerCashCoin = new Money(BitcoinUtility.SatoshisPerBitcoin, Currency.BitcoinCash).ToCurrency(_authenticationData.CurrentOrganization.Currency).Cents;

            conversionRateLookup[BitcoinChain.Cash] = fiatCentsPerCashCoin / 1.0 / BitcoinUtility.SatoshisPerBitcoin;   // the "/1.0" converts to double implicitly
            conversionRateLookup[BitcoinChain.Core] = fiatCentsPerCoreCoin / 1.0 / BitcoinUtility.SatoshisPerBitcoin;

            result.Append("{\"rows\":[");

            int addressesWithFunds = 0;

            foreach (HotBitcoinAddress address in addresses)
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddressUnspents.ForAddress(address);
                Int64 satoshisUnspentAddress       = 0;

                StringBuilder childResult = new StringBuilder(16384);
                foreach (HotBitcoinAddressUnspent unspent in unspents)
                {
                    childResult.Append("{");
                    childResult.AppendFormat(
                        "\"id\":\"UTXO{0}\"," +
                        "\"derivePath\":\"{1}\"," +
                        "\"address\":\"{2}\"," +
                        "\"balanceMicrocoins\":\"{3}\"," +
                        "\"balanceFiat\":\"{4}\"",
                        unspent.Identity,
                        Resources.Pages.Ledgers.BitcoinHotWallet_UnspentTransaction,
                        unspent.TransactionHash,
                        (unspent.AmountSatoshis / 100.0).ToString("N2"),
                        (unspent.AmountSatoshis / 100.0 * conversionRateLookup[unspent.Address.Chain]).ToString("N2")
                        );
                    satoshisUnspentAddress += unspent.AmountSatoshis;
                    childResult.Append("},");
                }
                if (unspents.Count > 0)
                {
                    childResult.Remove(childResult.Length - 1, 1);  // remove last comma
                }

                if (satoshisUnspentAddress > 0)
                {
                    result.Append("{");
                    result.AppendFormat(
                        "\"id\":\"{0}\"," +
                        "\"derivePath\":\"{1}\"," +
                        "\"address\":\"{2}\"," +
                        "\"balanceMicrocoins\":\"{3}\"," +
                        "\"balanceFiat\":\"{4}\",",
                        address.Identity,
                        address.Chain.ToString() + " " + address.DerivationPath,
                        (address.Chain == BitcoinChain.Cash? address.HumanAddress.Substring("bitcoincash:".Length): address.ProtocolLevelAddress),
                        JsonExpandingString(address.Identity, satoshisUnspentAddress),
                        JsonExpandingString(address.Identity, (Int64)(satoshisUnspentAddress * conversionRateLookup[address.Chain]))
                        );
                    result.Append("\"state\":\"closed\",\"children\":[" + childResult.ToString() + "]");
                    result.Append("},");
                    addressesWithFunds++;

                    if (!satoshisTotalLookup.ContainsKey(address.Chain))
                    {
                        satoshisTotalLookup[address.Chain] = 0;
                    }

                    satoshisTotalLookup[address.Chain] += satoshisUnspentAddress;
                }
            }

            if (addressesWithFunds > 0)
            {
                result.Remove(result.Length - 1, 1); // remove last comma
            }
            else // no funds at all in hotwallet
            {
                result.Append("{");
                result.AppendFormat(
                    "\"id\":\"0\"," +
                    "\"derivePath\":\"0\"," +
                    "\"address\":\"{0}\"," +
                    "\"balanceMicrocoins\":\"{1}\"," +
                    "\"balanceFiat\":\"{2}\"",
                    Resources.Pages.Ledgers.BitcoinHotWallet_Empty,
                    0.0.ToString("N2"),
                    0.0.ToString("N2")
                    );
                result.Append("}");
            }

            result.Append("]");

            if (satoshisTotalLookup.Count > 0)
            {
                // We should also have a footer, because we need a total

                result.Append(",\"footer\":[");

                bool previousFooterRow = false;

                foreach (BitcoinChain chain in satoshisTotalLookup.Keys)
                {
                    if (previousFooterRow)
                    {
                        result.Append(",");
                    }

                    result.Append("{");

                    result.AppendFormat(
                        "\"derivePath\":\"" + Resources.Global.Global_Total.ToUpperInvariant() + " " +
                        (string)chain.ToString().ToUpperInvariant() + "\",\"balanceMicrocoins\":\"{0}\",\"balanceFiat\":\"{1}\"",
                        (satoshisTotalLookup[chain] / 100.0).ToString("N2"), (satoshisTotalLookup[chain] / 100.0 * conversionRateLookup[chain]).ToString("N2"));

                    result.Append("}"); // on separate line to suppress warning*/

                    previousFooterRow = true;
                }

                result.Append("]");
            }

            result.Append("}");

            return(result.ToString());
        }
        private string FormatJson(HotBitcoinAddresses addresses)
        {
            StringBuilder result = new StringBuilder(16384);

            double conversionRate = 1.0;

            if (!this._authenticationData.CurrentOrganization.Currency.IsBitcoin)
            {
                long fiatCentsPerCoin = new Money(100000000, Currency.Bitcoin).ToCurrency(_authenticationData.CurrentOrganization.Currency).Cents;
                conversionRate = fiatCentsPerCoin / 100000000.0; // on satoshi level
            }

            result.Append("{\"rows\":[");

            Int64 satoshisTotal      = 0;
            int   addressesWithFunds = 0;

            foreach (HotBitcoinAddress address in addresses)
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddressUnspents.ForAddress(address);
                Int64 satoshisUnspentAddress       = 0;

                StringBuilder childResult = new StringBuilder(16384);
                foreach (HotBitcoinAddressUnspent unspent in unspents)
                {
                    childResult.Append("{");
                    childResult.AppendFormat(
                        "\"id\":\"UTXO{0}\"," +
                        "\"derivePath\":\"{1}\"," +
                        "\"address\":\"{2}\"," +
                        "\"balanceMicrocoins\":\"{3}\"," +
                        "\"balanceFiat\":\"{4}\"",
                        unspent.Identity,
                        Resources.Pages.Ledgers.BitcoinHotWallet_UnspentTransaction,
                        unspent.TransactionHash,
                        (unspent.AmountSatoshis / 100.0).ToString("N2"),
                        (unspent.AmountSatoshis / 100.0 * conversionRate).ToString("N2")
                        );
                    satoshisUnspentAddress += unspent.AmountSatoshis;
                    childResult.Append("},");
                }
                if (unspents.Count > 0)
                {
                    childResult.Remove(childResult.Length - 1, 1);  // remove last comma
                }

                if (satoshisUnspentAddress > 0)
                {
                    result.Append("{");
                    result.AppendFormat(
                        "\"id\":\"{0}\"," +
                        "\"derivePath\":\"{1}\"," +
                        "\"address\":\"{2}\"," +
                        "\"balanceMicrocoins\":\"{3}\"," +
                        "\"balanceFiat\":\"{4}\",",
                        address.Identity,
                        address.DerivationPath,
                        address.Address,
                        JsonExpandingString(address.Identity, satoshisUnspentAddress),
                        JsonExpandingString(address.Identity, (Int64)(satoshisUnspentAddress * conversionRate))
                        );
                    result.Append("\"state\":\"closed\",\"children\":[" + childResult.ToString() + "]");
                    result.Append("},");
                    satoshisTotal += satoshisUnspentAddress;
                    addressesWithFunds++;
                }
            }

            if (addressesWithFunds > 0)
            {
                result.Remove(result.Length - 1, 1); // remove last comma
            }
            else // no funds at all in hotwallet
            {
                result.Append("{");
                result.AppendFormat(
                    "\"id\":\"0\"," +
                    "\"derivePath\":\"0\"," +
                    "\"address\":\"{0}\"," +
                    "\"balanceMicrocoins\":\"{1}\"," +
                    "\"balanceFiat\":\"{2}\"",
                    Resources.Pages.Ledgers.BitcoinHotWallet_Empty,
                    0.0.ToString("N2"),
                    0.0.ToString("N2")
                    );
                result.Append("}");
            }

            result.Append("],\"footer\":[");

            result.Append("{");

            result.AppendFormat("\"derivePath\":\"" + Resources.Global.Global_Total.ToUpperInvariant() + "\",\"balanceMicrocoins\":\"{0}\",\"balanceFiat\":\"{1}\"",
                                (satoshisTotal / 100.0).ToString("N2"), (satoshisTotal / 100.0 * conversionRate).ToString("N2"));

            result.Append("}]}"); // on separate line to suppress warning

            return(result.ToString());
        }