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