< Summary - Combined Code Coverage

Information
Class: NLightning.Domain.Money.LightningMoney
Assembly: NLightning.Domain
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Domain/Money/LightningMoney.cs
Tag: 36_15743069263
Line coverage
74%
Covered lines: 109
Uncovered lines: 37
Coverable lines: 146
Total lines: 541
Line coverage: 74.6%
Branch coverage
58%
Covered branches: 27
Total branches: 46
Branch coverage: 58.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_MilliSatoshi()100%11100%
set_MilliSatoshi(...)100%11100%
get_Satoshi()100%11100%
set_Satoshi(...)100%22100%
get_Zero()100%11100%
get_IsZero()100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
TryParse(...)100%2.06275%
Parse(...)100%22100%
ToUnit(...)100%11100%
ToDecimal(...)100%11100%
Split()83.33%6.07687.5%
FromUnit(...)100%11100%
Coins(...)100%11100%
Bits(...)100%11100%
Cents(...)100%11100%
Satoshis(...)100%11100%
Satoshis(...)100%11100%
Satoshis(...)100%11100%
MilliSatoshis(...)100%11100%
MilliSatoshis(...)100%11100%
Equals(...)100%22100%
CompareTo(...)0%620%
CompareTo(...)0%2040%
op_Subtraction(...)50%2.03280%
op_UnaryNegation(...)100%210%
op_Addition(...)100%11100%
op_Multiply(...)100%210%
op_Multiply(...)100%210%
op_Multiply(...)100%210%
op_Multiply(...)100%210%
op_Multiply(...)100%210%
op_Multiply(...)100%210%
op_Division(...)100%210%
op_LessThan(...)100%11100%
op_GreaterThan(...)100%11100%
op_LessThanOrEqual(...)100%11100%
op_GreaterThanOrEqual(...)100%11100%
op_Implicit(...)100%210%
op_Implicit(...)100%11100%
op_Implicit(...)0%620%
op_Implicit(...)100%210%
op_Implicit(...)100%11100%
Equals(...)50%22100%
GetHashCode()100%210%
op_Equality(...)50%8.3660%
op_Inequality(...)100%11100%
ToString()100%210%
ToString(...)100%22100%
Almost(...)100%11100%
Almost(...)50%6.29680%
Min(...)50%22100%
Max(...)50%22100%
Add(...)100%210%
Sub(...)100%210%
Negate()100%11100%
CheckLightningMoneyUnit(...)100%22100%
DivRem(...)100%11100%

File(s)

/home/runner/work/nlightning/nlightning/src/NLightning.Domain/Money/LightningMoney.cs

#LineLine coverage
 1using System.Globalization;
 2
 3namespace NLightning.Domain.Money;
 4
 5using Enums;
 6
 7public class LightningMoney
 8{
 9    // For decimal.TryParse. None of the NumberStyles' composed values is useful for bitcoin style
 10    private const NumberStyles BitcoinStyle = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite
 11                                                                             | NumberStyles.AllowDecimalPoint;
 12
 13    private ulong _milliSatoshi;
 14
 15    public const ulong Coin = 100 * 1000 * 1000 * 1000UL;
 16    public const ulong Cent = Coin / 100;
 17    public const ulong Nano = Cent / 100;
 18
 19    public ulong MilliSatoshi
 20    {
 113621        get => _milliSatoshi;
 22        set
 23        {
 516424            _milliSatoshi = value;
 516425        }
 26    }
 27
 28    public long Satoshi
 29    {
 30        // Should round up to the nearest Satoshi
 438031        get => checked((long)Math.Round(_milliSatoshi / 1_000D, MidpointRounding.ToNegativeInfinity));
 32        set
 33        {
 1634            if (value < 0)
 435                throw new ArgumentOutOfRangeException(nameof(value), "Satoshi value cannot be negative");
 36
 37            checked
 38            {
 1239                _milliSatoshi = (ulong)(value * 1000);
 40            }
 1241        }
 42    }
 43
 74844    public static LightningMoney Zero => 0UL;
 40445    public bool IsZero => _milliSatoshi == 0;
 46
 47    #region Constructors
 48
 196449    public LightningMoney(ulong milliSatoshi)
 50    {
 196451        MilliSatoshi = milliSatoshi;
 196452    }
 53
 11654    public LightningMoney(decimal amount, LightningMoneyUnit unit)
 55    {
 56        // sanity check. Only valid units are allowed
 11657        CheckLightningMoneyUnit(unit, nameof(unit));
 58        checked
 59        {
 11260            var milliSats = amount * (long)unit;
 11261            MilliSatoshi = (ulong)milliSats;
 62        }
 10863    }
 64
 4465    public LightningMoney(long amount, LightningMoneyUnit unit)
 66    {
 67        // Sanity check. Only valid units are allowed
 4468        CheckLightningMoneyUnit(unit, nameof(unit));
 69        checked
 70        {
 4471            var milliSats = amount * (long)unit;
 4472            MilliSatoshi = (ulong)milliSats;
 73        }
 4474    }
 75
 304876    public LightningMoney(ulong amount, LightningMoneyUnit unit)
 77    {
 78        // Sanity check. Only valid units are allowed
 304879        CheckLightningMoneyUnit(unit, nameof(unit));
 80        checked
 81        {
 304882            var milliSats = amount * (ulong)unit;
 304883            MilliSatoshi = milliSats;
 84        }
 304885    }
 86
 87    #endregion
 88
 89    #region Parsers
 90
 91    /// <summary>
 92    /// Parse a bitcoin amount (Culture Invariant)
 93    /// </summary>
 94    /// <param name="bitcoin"></param>
 95    /// <param name="nRet"></param>
 96    /// <returns></returns>
 97    public static bool TryParse(string bitcoin, out LightningMoney? nRet)
 98    {
 1699        nRet = null;
 100
 16101        if (!decimal.TryParse(bitcoin, BitcoinStyle, CultureInfo.InvariantCulture, out var value))
 102        {
 8103            return false;
 104        }
 105
 106        try
 107        {
 8108            nRet = new LightningMoney(value, LightningMoneyUnit.Btc);
 8109            return true;
 110        }
 0111        catch (OverflowException)
 112        {
 0113            return false;
 114        }
 8115    }
 116
 117    /// <summary>
 118    /// Parse a bitcoin amount (Culture Invariant)
 119    /// </summary>
 120    /// <param name="bitcoin"></param>
 121    /// <returns></returns>
 122    public static LightningMoney? Parse(string bitcoin)
 123    {
 8124        if (TryParse(bitcoin, out var result))
 125        {
 4126            return result;
 127        }
 128
 4129        throw new FormatException("Impossible to parse the string in a bitcoin amount");
 130    }
 131
 132    #endregion
 133
 134    #region Conversions
 135
 136    /// <summary>
 137    /// Convert Money to decimal (same as ToDecimal)
 138    /// </summary>
 139    /// <param name="unit"></param>
 140    /// <returns></returns>
 141    public decimal ToUnit(LightningMoneyUnit unit)
 142    {
 184143        CheckLightningMoneyUnit(unit, nameof(unit));
 144        // overflow safe because (long / int) always fit in decimal
 145        // decimal operations are checked by default
 184146        return (decimal)MilliSatoshi / (ulong)unit;
 147    }
 148
 149    /// <summary>
 150    /// Convert Money to decimal (same as ToUnit)
 151    /// </summary>
 152    /// <param name="unit"></param>
 153    /// <returns></returns>
 154    public decimal ToDecimal(LightningMoneyUnit unit)
 155    {
 8156        return ToUnit(unit);
 157    }
 158
 159    #endregion
 160
 161    /// <summary>
 162    /// Split the Money in parts without loss
 163    /// </summary>
 164    /// <param name="parts">The number of parts (must be more than 0)</param>
 165    /// <returns>The split money</returns>
 166    public IEnumerable<LightningMoney> Split(int parts)
 167    {
 4168        if (parts <= 0)
 0169            throw new ArgumentOutOfRangeException(nameof(parts), "Parts should be more than 0");
 170
 4171        var result = DivRem(_milliSatoshi, (ulong)parts, out var remain);
 172
 32173        for (var i = 0; i < parts; i++)
 174        {
 12175            yield return MilliSatoshis(result + (remain > 0 ? 1UL : 0UL));
 12176            if (remain > 0)
 177            {
 4178                remain--;
 179            }
 180        }
 4181    }
 182
 183    #region Static Converters
 184
 185    public static LightningMoney FromUnit(decimal amount, LightningMoneyUnit unit)
 186    {
 80187        return new LightningMoney(amount, unit);
 188    }
 189
 190    public static LightningMoney Coins(decimal coins)
 191    {
 192        // overflow safe.
 193        // decimal operations are checked by default
 4194        return new LightningMoney(coins * Coin, LightningMoneyUnit.MilliSatoshi);
 195    }
 196
 197    public static LightningMoney Bits(decimal bits)
 198    {
 199        // overflow safe.
 200        // decimal operations are checked by default
 4201        return new LightningMoney(bits * Cent, LightningMoneyUnit.MilliSatoshi);
 202    }
 203
 204    public static LightningMoney Cents(decimal cents)
 205    {
 206        // overflow safe.
 207        // decimal operations are checked by default
 4208        return new LightningMoney(cents * Cent, LightningMoneyUnit.MilliSatoshi);
 209    }
 210
 211    public static LightningMoney Satoshis(decimal sats)
 212    {
 4213        return new LightningMoney(sats, LightningMoneyUnit.Satoshi);
 214    }
 215
 216    public static LightningMoney Satoshis(ulong sats)
 217    {
 48218        return new LightningMoney(sats, LightningMoneyUnit.Satoshi);
 219    }
 220
 221    public static LightningMoney Satoshis(long sats)
 222    {
 2996223        return new LightningMoney((ulong)sats, LightningMoneyUnit.Satoshi);
 224    }
 225
 226    public static LightningMoney MilliSatoshis(ulong milliSats)
 227    {
 16228        return new LightningMoney(milliSats);
 229    }
 230
 231    public static LightningMoney MilliSatoshis(long sats)
 232    {
 276233        return new LightningMoney((ulong)sats);
 234    }
 235
 236    #endregion
 237
 238    #region IEquatable<Money> Members
 239
 240    public bool Equals(LightningMoney? other)
 241    {
 24242        return other is not null && _milliSatoshi.Equals(other._milliSatoshi);
 243    }
 244
 245    public int CompareTo(LightningMoney? other)
 246    {
 0247        return other is null ? 1 : _milliSatoshi.CompareTo(other._milliSatoshi);
 248    }
 249
 250    #endregion
 251
 252    #region IComparable Members
 253
 254    public int CompareTo(object? obj)
 255    {
 0256        return obj switch
 0257        {
 0258            null => 1,
 0259            LightningMoney m => _milliSatoshi.CompareTo(m._milliSatoshi),
 0260            _ => _milliSatoshi.CompareTo((ulong)obj)
 0261        };
 262    }
 263
 264    #endregion
 265
 266    #region Unary Operators
 267
 268    public static LightningMoney operator -(LightningMoney left, LightningMoney right)
 269    {
 304270        ArgumentNullException.ThrowIfNull(left);
 304271        ArgumentNullException.ThrowIfNull(right);
 272
 304273        if (left._milliSatoshi < right._milliSatoshi)
 0274            throw new ArithmeticException("LightningMoney does not support negative values");
 275
 304276        return new LightningMoney(checked(left._milliSatoshi - right._milliSatoshi));
 277    }
 278
 279    public static LightningMoney operator -(LightningMoney _)
 280    {
 0281        throw new ArithmeticException("LightningMoney does not support unary negation");
 282    }
 283
 284    public static LightningMoney operator +(LightningMoney left, LightningMoney right)
 285    {
 4286        ArgumentNullException.ThrowIfNull(left);
 4287        ArgumentNullException.ThrowIfNull(right);
 288
 4289        return new LightningMoney(checked(left._milliSatoshi + right._milliSatoshi));
 290    }
 291
 292    public static LightningMoney operator *(ulong left, LightningMoney right)
 293    {
 0294        ArgumentNullException.ThrowIfNull(right);
 295
 0296        return MilliSatoshis(checked(left * right._milliSatoshi));
 297    }
 298
 299    public static LightningMoney operator *(LightningMoney left, ulong right)
 300    {
 0301        ArgumentNullException.ThrowIfNull(left);
 302
 0303        return MilliSatoshis(checked(left._milliSatoshi * right));
 304    }
 305
 306    public static LightningMoney operator *(long left, LightningMoney right)
 307    {
 0308        ArgumentNullException.ThrowIfNull(right);
 309
 0310        return MilliSatoshis(checked((ulong)left * right._milliSatoshi));
 311    }
 312
 313    public static LightningMoney operator *(LightningMoney left, long right)
 314    {
 0315        ArgumentNullException.ThrowIfNull(left);
 316
 0317        return MilliSatoshis(checked((ulong)right * left._milliSatoshi));
 318    }
 319
 320    public static LightningMoney operator *(decimal left, LightningMoney right)
 321    {
 0322        ArgumentNullException.ThrowIfNull(right);
 323
 0324        return MilliSatoshis((ulong)(left * right._milliSatoshi));
 325    }
 326
 327    public static LightningMoney operator *(LightningMoney left, decimal right)
 328    {
 0329        ArgumentNullException.ThrowIfNull(left);
 330
 0331        return MilliSatoshis((ulong)(right * left._milliSatoshi));
 332    }
 333
 334    public static LightningMoney operator /(LightningMoney left, ulong right)
 335    {
 0336        ArgumentNullException.ThrowIfNull(left);
 337
 0338        return new LightningMoney(left._milliSatoshi / right);
 339    }
 340
 341    public static bool operator <(LightningMoney left, LightningMoney right)
 342    {
 4343        ArgumentNullException.ThrowIfNull(left);
 4344        ArgumentNullException.ThrowIfNull(right);
 345
 4346        return left._milliSatoshi < right._milliSatoshi;
 347    }
 348
 349    public static bool operator >(LightningMoney left, LightningMoney right)
 350    {
 296351        ArgumentNullException.ThrowIfNull(left);
 296352        ArgumentNullException.ThrowIfNull(right);
 353
 296354        return left._milliSatoshi > right._milliSatoshi;
 355    }
 356
 357    public static bool operator <=(LightningMoney left, LightningMoney right)
 358    {
 16359        ArgumentNullException.ThrowIfNull(left);
 16360        ArgumentNullException.ThrowIfNull(right);
 361
 16362        return left._milliSatoshi <= right._milliSatoshi;
 363    }
 364
 365    public static bool operator >=(LightningMoney left, LightningMoney right)
 366    {
 8367        ArgumentNullException.ThrowIfNull(left);
 8368        ArgumentNullException.ThrowIfNull(right);
 369
 8370        return left._milliSatoshi >= right._milliSatoshi;
 371    }
 372
 373    #endregion
 374
 375    #region Implicit Operators
 376
 377    public static implicit operator LightningMoney(long value)
 378    {
 0379        return new LightningMoney((ulong)value);
 380    }
 381
 382    public static implicit operator LightningMoney(ulong value)
 383    {
 1188384        return new LightningMoney(value);
 385    }
 386
 387    public static implicit operator LightningMoney(string value)
 388    {
 0389        return Parse(value) ??
 0390               throw new ArgumentException("Cannot parse value into a valid LightningMoney", nameof(value));
 391    }
 392
 393    public static implicit operator long(LightningMoney value)
 394    {
 0395        return checked((long)value.MilliSatoshi);
 396    }
 397
 398    public static implicit operator ulong(LightningMoney value)
 399    {
 100400        return value.MilliSatoshi;
 401    }
 402
 403    #endregion
 404
 405    #region Equality Operators
 406
 407    public override bool Equals(object? obj)
 408    {
 120409        return obj is LightningMoney item && _milliSatoshi.Equals(item._milliSatoshi);
 410    }
 411
 412    public override int GetHashCode()
 413    {
 0414        return _milliSatoshi.GetHashCode();
 415    }
 416
 417    public static bool operator ==(LightningMoney? a, LightningMoney? b)
 418    {
 8419        if (ReferenceEquals(a, b))
 0420            return true;
 8421        if (a is null || b is null)
 0422            return false;
 8423        return a._milliSatoshi == b._milliSatoshi;
 424    }
 425
 426    public static bool operator !=(LightningMoney a, LightningMoney b)
 427    {
 4428        return !(a == b);
 429    }
 430
 431    #endregion
 432
 433    #region ToString
 434
 435    /// <summary>
 436    /// Returns a culture invariant string representation of Bitcoin amount
 437    /// </summary>
 438    /// <returns></returns>
 439    public override string ToString()
 440    {
 0441        return ToString(false);
 442    }
 443
 444    /// <summary>
 445    /// Returns a culture invariant string representation of Bitcoin amount
 446    /// </summary>
 447    /// <param name="trimExcessZero">True if trim excess zeros</param>
 448    /// <returns></returns>
 449    public string ToString(bool trimExcessZero = true)
 450    {
 12451        var val = (decimal)_milliSatoshi / (ulong)LightningMoneyUnit.Btc;
 12452        var decPos = trimExcessZero ? 2 : 11;
 12453        var zeros = new string('0', decPos);
 12454        var rest = new string('#', 11 - decPos);
 12455        var fmt = "{0:0" + ("." + zeros + rest) + "}";
 456
 12457        return string.Format(CultureInfo.InvariantCulture, fmt, val);
 458    }
 459
 460    #endregion
 461
 462    /// <summary>
 463    /// Tell if the amount is almost equal to this instance
 464    /// </summary>
 465    /// <param name="amount"></param>
 466    /// <param name="dust">more or less amount</param>
 467    /// <returns>true if equals, else false</returns>
 468    public bool Almost(LightningMoney amount, LightningMoney dust)
 469    {
 8470        ArgumentNullException.ThrowIfNull(amount);
 8471        ArgumentNullException.ThrowIfNull(dust);
 472
 8473        return amount - this <= dust;
 474    }
 475
 476    /// <summary>
 477    /// Tell if the amount is almost equal to this instance
 478    /// </summary>
 479    /// <param name="amount"></param>
 480    /// <param name="margin">error margin (between 0 and 1)</param>
 481    /// <returns>true if equals, else false</returns>
 482    public bool Almost(LightningMoney amount, decimal margin)
 483    {
 4484        ArgumentNullException.ThrowIfNull(amount);
 4485        if (margin is < 0.0m or > 1.0m)
 0486            throw new ArgumentOutOfRangeException(nameof(margin), "margin should be between 0 and 1");
 487
 4488        var dust = new LightningMoney(MilliSatoshi * margin, LightningMoneyUnit.MilliSatoshi);
 4489        return Almost(amount, dust);
 490    }
 491
 492    public static LightningMoney Min(LightningMoney a, LightningMoney b)
 493    {
 4494        ArgumentNullException.ThrowIfNull(a);
 4495        ArgumentNullException.ThrowIfNull(b);
 496
 4497        return a <= b ? a : b;
 498    }
 499
 500    public static LightningMoney Max(LightningMoney a, LightningMoney b)
 501    {
 4502        ArgumentNullException.ThrowIfNull(a);
 4503        ArgumentNullException.ThrowIfNull(b);
 504
 4505        return a >= b ? a : b;
 506    }
 507
 508    #region IMoney Members
 509
 510    public LightningMoney Add(LightningMoney money)
 511    {
 0512        return this + money;
 513    }
 514
 515    public LightningMoney Sub(LightningMoney money)
 516    {
 0517        return this - money;
 518    }
 519
 520    public LightningMoney Negate()
 521    {
 4522        throw new ArithmeticException("LightningMoney does not support unary negation");
 523    }
 524
 525    #endregion
 526
 527    private static void CheckLightningMoneyUnit(LightningMoneyUnit value, string paramName)
 528    {
 3392529        var typeOfMoneyUnit = typeof(LightningMoneyUnit);
 3392530        if (!Enum.IsDefined(typeOfMoneyUnit, value))
 531        {
 4532            throw new ArgumentException("Invalid value for LightningMoneyUnit", paramName);
 533        }
 3388534    }
 535
 536    private static ulong DivRem(ulong a, ulong b, out ulong result)
 537    {
 4538        result = a % b;
 4539        return a / b;
 540    }
 541}

Methods/Properties

get_MilliSatoshi()
set_MilliSatoshi(System.UInt64)
get_Satoshi()
set_Satoshi(System.Int64)
get_Zero()
get_IsZero()
.ctor(System.UInt64)
.ctor(System.Decimal,NLightning.Domain.Enums.LightningMoneyUnit)
.ctor(System.Int64,NLightning.Domain.Enums.LightningMoneyUnit)
.ctor(System.UInt64,NLightning.Domain.Enums.LightningMoneyUnit)
TryParse(System.String,NLightning.Domain.Money.LightningMoney&)
Parse(System.String)
ToUnit(NLightning.Domain.Enums.LightningMoneyUnit)
ToDecimal(NLightning.Domain.Enums.LightningMoneyUnit)
Split()
FromUnit(System.Decimal,NLightning.Domain.Enums.LightningMoneyUnit)
Coins(System.Decimal)
Bits(System.Decimal)
Cents(System.Decimal)
Satoshis(System.Decimal)
Satoshis(System.UInt64)
Satoshis(System.Int64)
MilliSatoshis(System.UInt64)
MilliSatoshis(System.Int64)
Equals(NLightning.Domain.Money.LightningMoney)
CompareTo(NLightning.Domain.Money.LightningMoney)
CompareTo(System.Object)
op_Subtraction(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
op_UnaryNegation(NLightning.Domain.Money.LightningMoney)
op_Addition(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
op_Multiply(System.UInt64,NLightning.Domain.Money.LightningMoney)
op_Multiply(NLightning.Domain.Money.LightningMoney,System.UInt64)
op_Multiply(System.Int64,NLightning.Domain.Money.LightningMoney)
op_Multiply(NLightning.Domain.Money.LightningMoney,System.Int64)
op_Multiply(System.Decimal,NLightning.Domain.Money.LightningMoney)
op_Multiply(NLightning.Domain.Money.LightningMoney,System.Decimal)
op_Division(NLightning.Domain.Money.LightningMoney,System.UInt64)
op_LessThan(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
op_GreaterThan(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
op_LessThanOrEqual(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
op_GreaterThanOrEqual(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
op_Implicit(System.Int64)
op_Implicit(System.UInt64)
op_Implicit(System.String)
op_Implicit(NLightning.Domain.Money.LightningMoney)
op_Implicit(NLightning.Domain.Money.LightningMoney)
Equals(System.Object)
GetHashCode()
op_Equality(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
op_Inequality(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
ToString()
ToString(System.Boolean)
Almost(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
Almost(NLightning.Domain.Money.LightningMoney,System.Decimal)
Min(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
Max(NLightning.Domain.Money.LightningMoney,NLightning.Domain.Money.LightningMoney)
Add(NLightning.Domain.Money.LightningMoney)
Sub(NLightning.Domain.Money.LightningMoney)
Negate()
CheckLightningMoneyUnit(NLightning.Domain.Enums.LightningMoneyUnit,System.String)
DivRem(System.UInt64,System.UInt64,System.UInt64&)