Skip to content

Commit

Permalink
Added useful currency methods back
Browse files Browse the repository at this point in the history
  • Loading branch information
mrz1836 committed Sep 21, 2020
1 parent 71d0a4b commit d5bc8d3
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 8 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ View the generated [documentation](https://pkg.go.dev/github.com/tonicpow/go-bsv
- Using default [heimdall http client](https://github.com/gojek/heimdall) with exponential backoff & more
- Use your own HTTP client
- Helpful currency conversion and formatting methods:
- [ConvertSatsToBSV()](currency.go)
- [ConvertFloatToIntBSV()](currency.go)
- [ConvertPriceToSatoshis()](currency.go)
- [ConvertSatsToBSV()](currency.go)
- [FormatCentsToDollars()](currency.go)
- [TransformCurrencyToInt()](currency.go)
- Supported Currencies:
- USD
- Supported Providers:
Expand Down
44 changes: 37 additions & 7 deletions currency.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,13 @@ func ConvertSatsToBSV(sats int) float64 {

// ConvertPriceToSatoshis will get the satoshis (amount) from the current rate.
// IE: 1 BSV = $150 and you want to know what $1 is in satoshis
func ConvertPriceToSatoshis(currentRate float64, amount float64) (satoshis int64, err error) {
func ConvertPriceToSatoshis(currentRate float64, amount float64) (int64, error) {

// Cannot use 0 (division by zero?!)
if amount == 0 {
err = fmt.Errorf("an amount must be set")
return
return 0, fmt.Errorf("an amount must be set")
} else if currentRate <= 0 {
err = fmt.Errorf("current rate must be a positive value")
return
return 0, fmt.Errorf("current rate must be a positive value")
}

// Do conversion to satoshis (percentage) using decimal package to avoid float issues
Expand All @@ -50,6 +48,38 @@ func ConvertPriceToSatoshis(currentRate float64, amount float64) (satoshis int64
satoshisDecimal := decimal.NewFromInt(SatoshisPerBitcoin).Mul(decimal.NewFromFloat(amount)).Div(decimal.NewFromFloat(currentRate))

// Drop decimals after since can only have whole Satoshis
satoshis = satoshisDecimal.Ceil().IntPart()
return
return satoshisDecimal.Ceil().IntPart(), nil
}

// FormatCentsToDollars formats the integer for currency in USD (cents to dollars)
func FormatCentsToDollars(cents int) string {
return strconv.FormatFloat(float64(cents)/100.0, 'f', 2, 64)
}

// ConvertFloatToIntUSD converts a float to int
func ConvertFloatToIntUSD(floatValue float64) int64 {
return int64(floatValue*100 + 0.5)
}

// TransformCurrencyToInt takes the decimal format of the currency and returns the integer value
// Currently only supports USD and BSV
func TransformCurrencyToInt(decimalValue float64, currency Currency) (int64, error) {
if currency == CurrencyDollars {
return ConvertFloatToIntUSD(decimalValue), nil
} else if currency == CurrencyBitcoin {
return ConvertFloatToIntBSV(decimalValue), nil
}
return 0, fmt.Errorf("currency %s cannot be transformed", currency.Name())
}

// ConvertFloatToIntBSV converts the BSV float value to the sats value
func ConvertFloatToIntBSV(floatValue float64) int64 {

// Do conversion to satoshis (percentage) using decimal package to avoid float issues
// => 1e8 * amount / currentRate
// (use 1e8 since rate is in Bitcoin not Satoshis)
satoshisDecimal := decimal.NewFromInt(SatoshisPerBitcoin).Mul(decimal.NewFromFloat(floatValue))

// Drop decimals after since can only have whole Satoshis
return satoshisDecimal.Ceil().IntPart()
}
129 changes: 129 additions & 0 deletions currency_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,132 @@ func ExampleConvertPriceToSatoshis() {
fmt.Printf("%d", val)
// Output:666667
}

// TestFormatCentsToDollars will test the method FormatCentsToDollars()
func TestFormatCentsToDollars(t *testing.T) {
t.Parallel()

// Create the list of tests
var tests = []struct {
integer int
expected string
}{
{0, "0.00"},
{-1, "-0.01"},
{127, "1.27"},
{199276, "1992.76"},
}

// Test all
for _, test := range tests {
if output := FormatCentsToDollars(test.integer); output != test.expected {
t.Errorf("%s Failed: [%d] inputted and [%s] expected, received: [%s]", t.Name(), test.integer, test.expected, output)
}
}
}

// BenchmarkFormatCentsToDollars benchmarks the method FormatCentsToDollars()
func BenchmarkFormatCentsToDollars(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = FormatCentsToDollars(1000)
}
}

// ExampleFormatCentsToDollars example using FormatCentsToDollars()
func ExampleFormatCentsToDollars() {
val := FormatCentsToDollars(1000)
fmt.Printf("%s", val)
// Output:10.00
}

// TestTransformCurrencyToInt will test the method TransformCurrencyToInt()
func TestTransformCurrencyToInt(t *testing.T) {
t.Parallel()

// Create the list of tests
var tests = []struct {
decimal float64
currency Currency
expected int64
expectedError bool
}{
{0, CurrencyDollars, 0, false},
{1.27, CurrencyDollars, 127, false},
{01.27, CurrencyDollars, 127, false},
{199.272, CurrencyDollars, 19927, false},
{199.276, CurrencyDollars, 19928, false},
{0.00000010, CurrencyBitcoin, 10, false},
{0.000010, CurrencyBitcoin, 1000, false},
{0.0010, CurrencyBitcoin, 100000, false},
{0.10, CurrencyBitcoin, 10000000, false},
{1, CurrencyBitcoin, 100000000, false},
{0.00000010, 123, 0, true},
}

// todo: issue with negative floats (-1.27 = -126)

// Test all
for _, test := range tests {
if output, err := TransformCurrencyToInt(test.decimal, test.currency); err == nil && test.expectedError {
t.Errorf("%s Failed: expected to throw an error, no error [%f] inputted [%s] currency", t.Name(), test.decimal, test.currency.Name())
} else if err != nil && !test.expectedError {
t.Errorf("%s Failed: [%f] inputted, received: [%v] error [%s]", t.Name(), test.decimal, output, err.Error())
} else if output != test.expected && !test.expectedError {
t.Errorf("%s Failed: [%f] inputted and [%d] expected, received: [%d]", t.Name(), test.decimal, test.expected, output)
}
}
}

// BenchmarkTransformCurrencyToInt benchmarks the method TransformCurrencyToInt()
func BenchmarkTransformCurrencyToInt(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = TransformCurrencyToInt(10.00, CurrencyDollars)
}
}

// ExampleTransformCurrencyToInt example using TransformCurrencyToInt()
func ExampleTransformCurrencyToInt() {
val, _ := TransformCurrencyToInt(10.00, CurrencyDollars)
fmt.Printf("%d", val)
// Output:1000
}

// TestConvertFloatToIntBSV will test the method ConvertFloatToIntBSV()
func TestConvertFloatToIntBSV(t *testing.T) {
t.Parallel()

// Create the list of tests
var tests = []struct {
float float64
expected int64
expectedError bool
}{
{0, 0, false},
{1.123456789, 112345679, false},
{0.00000001, 1, false},
{0.00000111, 111, false},
{-0.00000111, -111, false},
// {math.Inf(1), -111, true}, // This will produce a panic in decimal package
}

// Test all
for _, test := range tests {
if output := ConvertFloatToIntBSV(test.float); output != test.expected && !test.expectedError {
t.Errorf("%s Failed: [%f] inputted and [%d] expected, received: [%d]", t.Name(), test.float, test.expected, output)
}
}
}

// BenchmarkConvertFloatToIntBSV benchmarks the method ConvertFloatToIntBSV()
func BenchmarkConvertFloatToIntBSV(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = ConvertFloatToIntBSV(10.00)
}
}

// ExampleConvertFloatToIntBSV example using ConvertFloatToIntBSV()
func ExampleConvertFloatToIntBSV() {
val := ConvertFloatToIntBSV(10.01)
fmt.Printf("%d", val)
// Output:1001000000
}

0 comments on commit d5bc8d3

Please sign in to comment.