1 package amt
2 3 import (
4 "errors"
5 "math"
6 "strconv"
7 )
8 9 // Unit describes a method of converting an Amount to something other than the base unit of a bitcoin. The value
10 // of the AmountUnit is the exponent component of the decadic multiple to convert from an amount in bitcoin to an amount
11 // counted in units.
12 type Unit int
13 14 // These constants define various units used when describing a bitcoin monetary amount.
15 const (
16 MegaDUO Unit = 6
17 KiloDUO Unit = 3
18 DUO Unit = 0
19 MilliDUO Unit = -3
20 MicroDUO Unit = -6
21 Satoshi Unit = -8
22 )
23 24 // String returns the unit as a string. For recognized units, the SI prefix is used, or "Satoshi" for the base unit. For
25 // all unrecognized units, "1eN DUO" is returned, where N is the AmountUnit.
26 func (u Unit) String() string {
27 switch u {
28 case MegaDUO:
29 return "MDUO"
30 case KiloDUO:
31 return "kDUO"
32 case DUO:
33 return "DUO"
34 case MilliDUO:
35 return "mDUO"
36 case MicroDUO:
37 return "μDUO"
38 case Satoshi:
39 return "Satoshi"
40 default:
41 return "1e" + strconv.FormatInt(int64(u), 10) + " DUO"
42 }
43 }
44 45 // Amount represents the base bitcoin monetary unit (colloquially referred to as a `Satoshi'). A single Amount is equal
46 // to 1e-8 of a bitcoin.
47 type Amount int64
48 49 func (a Amount) Int64() int64 {
50 return int64(a)
51 }
52 53 // round converts a floating point number, which may or may not be representable as an integer, to the Amount integer
54 // type by rounding to the nearest integer. This is performed by adding or subtracting 0.5 depending on the sign, and
55 // relying on integer truncation to round the value to the nearest Amount.
56 func round(f float64) Amount {
57 if f < 0 {
58 return Amount(f - 0.5)
59 }
60 return Amount(f + 0.5)
61 }
62 63 // NewAmount creates an Amount from a floating point value representing some value in bitcoin. NewAmount errors if f is
64 // NaN or +-Infinity, but does not check that the amount is within the total amount of bitcoin producible as f may not
65 // refer to an amount at a single moment in time. NewAmount is for specifically for converting DUO to Satoshi. For
66 // creating a new Amount with an int64 value which denotes a quantity of Satoshi, do a simple type conversion from type
67 // int64 to Amount. See GoDoc for example: http://godoc.org/github.com/p9c/monorepo/util#example-Amount
68 func NewAmount(f float64) (Amount, error) {
69 // The amount is only considered invalid if it cannot be represented as an integer type. This may happen if f is NaN
70 // or +-Infinity.
71 switch {
72 case math.IsNaN(f):
73 fallthrough
74 case math.IsInf(f, 1):
75 fallthrough
76 case math.IsInf(f, -1):
77 return 0, errors.New("invalid bitcoin amount")
78 }
79 return round(f * float64(SatoshiPerBitcoin)), nil
80 }
81 82 // ToUnit converts a monetary amount counted in bitcoin base units to a floating point value representing an amount of
83 // bitcoin.
84 func (a Amount) ToUnit(u Unit) float64 {
85 return float64(a) / math.Pow10(int(u+8))
86 }
87 88 // ToDUO is the equivalent of calling ToUnit with AmountDUO.
89 func (a Amount) ToDUO() float64 {
90 return a.ToUnit(DUO)
91 }
92 93 // Format formats a monetary amount counted in bitcoin base units as a string for a given unit. The conversion will
94 // succeed for any unit, however, known units will be formated with an appended label describing the units with SI
95 // notation, or "Satoshi" for the base unit.
96 func (a Amount) Format(u Unit) string {
97 units := " " + u.String()
98 return strconv.FormatFloat(a.ToUnit(u), 'f', -int(u+8), 64) + units
99 }
100 101 // String is the equivalent of calling Format with AmountDUO.
102 func (a Amount) String() string {
103 return a.Format(DUO)
104 }
105 106 // MulF64 multiplies an Amount by a floating point value. While this is not an operation that must typically be done by
107 // a full node or wallet, it is useful for services that podbuild on top of bitcoin (for example, calculating a fee by
108 // multiplying by a percentage).
109 func (a Amount) MulF64(f float64) Amount {
110 return round(float64(a) * f)
111 }
112