2026-06-11 10:47:40 +02:00
|
|
|
using System.Globalization;
|
|
|
|
|
|
|
|
|
|
namespace PalladiumWallet.Core.Wallet;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-06-16 14:40:06 +02:00
|
|
|
/// Satoshi ↔ coin unit conversion (8 decimal places) for display and input.
|
|
|
|
|
/// All internal computation uses satoshis; the formatted string is presentation only.
|
2026-06-11 10:47:40 +02:00
|
|
|
/// </summary>
|
|
|
|
|
public static class CoinAmount
|
|
|
|
|
{
|
|
|
|
|
public const long SatsPerCoin = 100_000_000;
|
|
|
|
|
|
2026-06-16 14:40:06 +02:00
|
|
|
/// <summary>Selectable display units (config §8).</summary>
|
2026-06-11 16:23:02 +02:00
|
|
|
public static readonly string[] Units = ["PLM", "mPLM", "µPLM", "sat"];
|
|
|
|
|
|
2026-06-16 14:40:06 +02:00
|
|
|
/// <summary>(satoshis per unit, displayed decimals) for each unit.</summary>
|
2026-06-11 16:23:02 +02:00
|
|
|
private static (long Factor, int Decimals) Of(string unit) => unit switch
|
|
|
|
|
{
|
2026-06-13 20:50:17 +02:00
|
|
|
"PLM" => (SatsPerCoin, 8),
|
2026-06-11 16:23:02 +02:00
|
|
|
"mPLM" => (100_000, 5),
|
|
|
|
|
"µPLM" => (100, 2),
|
2026-06-13 20:50:17 +02:00
|
|
|
"sat" => (1, 0),
|
|
|
|
|
_ => throw new ArgumentException($"Unknown coin unit: {unit}", nameof(unit)),
|
2026-06-11 16:23:02 +02:00
|
|
|
};
|
|
|
|
|
|
2026-06-11 10:47:40 +02:00
|
|
|
public static string Format(long sats, string unit = "") =>
|
|
|
|
|
(sats / (decimal)SatsPerCoin).ToString("0.00000000", CultureInfo.InvariantCulture)
|
|
|
|
|
+ (unit.Length > 0 ? " " + unit : "");
|
|
|
|
|
|
2026-06-16 14:40:06 +02:00
|
|
|
/// <summary>Formats in the chosen unit (e.g. 150000 sat → "1.50000 mPLM").</summary>
|
2026-06-11 16:23:02 +02:00
|
|
|
public static string FormatIn(long sats, string unit, bool withLabel = true)
|
|
|
|
|
{
|
|
|
|
|
var (factor, decimals) = Of(unit);
|
|
|
|
|
var value = (sats / (decimal)factor).ToString(
|
|
|
|
|
decimals == 0 ? "0" : "0." + new string('0', decimals), CultureInfo.InvariantCulture);
|
|
|
|
|
return withLabel ? $"{value} {unit}" : value;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 14:40:06 +02:00
|
|
|
/// <summary>Parses an amount expressed in the chosen unit into satoshis.</summary>
|
2026-06-11 16:23:02 +02:00
|
|
|
public static bool TryParseIn(string text, string unit, out long sats)
|
|
|
|
|
{
|
|
|
|
|
sats = 0;
|
|
|
|
|
var (factor, _) = Of(unit);
|
|
|
|
|
text = text.Trim().Replace(',', '.');
|
|
|
|
|
if (!decimal.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out var value)
|
|
|
|
|
|| value < 0)
|
|
|
|
|
return false;
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-06-13 20:39:05 +02:00
|
|
|
var satsDecimal = value * factor;
|
|
|
|
|
if (satsDecimal % 1 != 0)
|
|
|
|
|
return false;
|
|
|
|
|
sats = (long)satsDecimal;
|
2026-06-11 16:23:02 +02:00
|
|
|
}
|
|
|
|
|
catch (OverflowException)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 14:40:06 +02:00
|
|
|
/// <summary>Parses a coin amount (decimal point or comma) into satoshis.</summary>
|
2026-06-11 10:47:40 +02:00
|
|
|
public static bool TryParseCoins(string text, out long sats)
|
|
|
|
|
{
|
|
|
|
|
sats = 0;
|
|
|
|
|
text = text.Trim().Replace(',', '.');
|
|
|
|
|
if (!decimal.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out var coins)
|
|
|
|
|
|| coins < 0)
|
|
|
|
|
return false;
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-06-13 20:39:05 +02:00
|
|
|
var satsDecimal = coins * SatsPerCoin;
|
|
|
|
|
if (satsDecimal % 1 != 0)
|
|
|
|
|
return false;
|
|
|
|
|
sats = (long)satsDecimal;
|
2026-06-11 10:47:40 +02:00
|
|
|
}
|
|
|
|
|
catch (OverflowException)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|