diff --git a/cmd/kaspawallet/utils/format_kas.go b/cmd/kaspawallet/utils/format_kas.go index 7b2e616ad..3009c1490 100644 --- a/cmd/kaspawallet/utils/format_kas.go +++ b/cmd/kaspawallet/utils/format_kas.go @@ -2,10 +2,13 @@ package utils import ( "fmt" + "math" + "regexp" "strconv" "strings" "github.com/kaspanet/kaspad/domain/consensus/utils/constants" + "github.com/pkg/errors" ) // FormatKas takes the amount of sompis as uint64, and returns amount of KAS with 8 decimal places @@ -17,9 +20,9 @@ func FormatKas(amount uint64) string { return res } -// Takes in a string representation of the Kas value to convert to Sompi +// KasToSompi takes in a string representation of the Kas value to convert to Sompi func KasToSompi(amount string) (uint64, error) { - err := ValidateAmountFormat(amount) + err := validateAmountFormat(amount) if err != nil { return 0, err @@ -30,14 +33,36 @@ func KasToSompi(amount string) (uint64, error) { parts := strings.Split(amount, ".") amountStr := "" - if len(parts) == 2 { - amountStr = fmt.Sprintf("%s%-*s", parts[0], 8, parts[1]) // Padded with spaces at the end to fill for missing decimals: Sample "0.01234 " - amountStr = strings.ReplaceAll(amountStr, " ", "0") // Make the spaces be 0s. Sample "0.012340000" - } else { - amountStr = fmt.Sprintf("%s00000000", parts[0]) + if constants.SompiPerKaspa%10 != 0 { + return 0, errors.Errorf("Unable to convert to sompi when SompiPerKaspa is not a multiple of 10") } + decimalPlaces := int(math.Log10(constants.SompiPerKaspa)) + decimalStr := "" + + if len(parts) == 2 { + decimalStr = parts[1] + } + + amountStr = fmt.Sprintf("%s%-*s", parts[0], decimalPlaces, decimalStr) // Padded with spaces at the end to fill for missing decimals: Sample "0.01234 " + amountStr = strings.ReplaceAll(amountStr, " ", "0") // Make the spaces be 0s. Sample "0.012340000" + convertedAmount, err := strconv.ParseUint(amountStr, 10, 64) return convertedAmount, err } + +func validateAmountFormat(amount string) error { + // Check whether it's an integer, or a float with max 8 digits + match, err := regexp.MatchString("^([1-9]\\d{0,11}|0)(\\.\\d{0,8})?$", amount) + + if !match { + return errors.Errorf("Invalid send amount") + } + + if err != nil { + return err + } + + return nil +} diff --git a/cmd/kaspawallet/utils/format_kas_test.go b/cmd/kaspawallet/utils/format_kas_test.go index 56b04e1df..a9d6579ea 100644 --- a/cmd/kaspawallet/utils/format_kas_test.go +++ b/cmd/kaspawallet/utils/format_kas_test.go @@ -42,3 +42,49 @@ func TestKasToSompi(t *testing.T) { } } } + +func TestValidateAmountFormat(t *testing.T) { + validCases := []string{ + "0", + "1", + "1.0", + "0.1", + "0.12345678", + "111111111111.11111111", // 12 digits to the left of decimal, 8 digits to the right + "184467440737.09551615", // Maximum input that can be represented in sompi later + "184467440737.09551616", // Cannot be represented in sompi, but we'll acccept for "correct format" + "999999999999.99999999", // Cannot be represented in sompi, but we'll acccept for "correct format" + } + + for _, testCase := range validCases { + err := validateAmountFormat(testCase) + + if err != nil { + t.Error(err) + } + } + + invalidCases := []string{ + "", + "a", + "-1", + "0.123456789", // 9 decimal digits + ".1", // decimal but no integer component + "0a", // Extra character + "0000000000000", // 13 zeros + "012", // Int padded with zero + "00.1", // Decimal padded with zeros + "111111111111111111111", // all digits + "111111111111A11111111", // non-period/non-digit where decimal would be + "000000000000.00000000", // all zeros + "kaspa", // all text + } + + for _, testCase := range invalidCases { + err := validateAmountFormat(testCase) + + if err == nil { + t.Errorf("Expected an error but succeeded validation for test case %s", testCase) + } + } +} diff --git a/cmd/kaspawallet/utils/validate_amount.go b/cmd/kaspawallet/utils/validate_amount.go deleted file mode 100644 index bbcb5065f..000000000 --- a/cmd/kaspawallet/utils/validate_amount.go +++ /dev/null @@ -1,26 +0,0 @@ -package utils - -import ( - "regexp" - - "github.com/pkg/errors" -) - -/** - * 1. May be an integer (no decimal components) - * 2. May be float with up to 8 decimal places - */ -func ValidateAmountFormat(amount string) error { - // Check whether it's an integer, or a float with max 8 digits - match, err := regexp.MatchString("^([1-9]\\d{0,11}|0)(\\.\\d{0,8})?$", amount) - - if !match { - return errors.Errorf("Invalid send amount") - } - - if err != nil { - return err - } - - return nil -} diff --git a/cmd/kaspawallet/utils/validate_amount_test.go b/cmd/kaspawallet/utils/validate_amount_test.go deleted file mode 100644 index 383d596de..000000000 --- a/cmd/kaspawallet/utils/validate_amount_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package utils - -import ( - "testing" -) - -func TestValidateAmountFormat(t *testing.T) { - validCases := []string{ - "0", - "1", - "1.0", - "0.1", - "0.12345678", - "111111111111.11111111", // 12 digits to the left of decimal, 8 digits to the right - "184467440737.09551615", // Maximum input that can be represented in sompi later - "184467440737.09551616", // Cannot be represented in sompi, but we'll acccept for "correct format" - "999999999999.99999999", // Cannot be represented in sompi, but we'll acccept for "correct format" - } - - for _, testCase := range validCases { - err := ValidateAmountFormat(testCase) - - if err != nil { - t.Error(err) - } - } - - invalidCases := []string{ - "", - "a", - "-1", - "0.123456789", // 9 decimal digits - ".1", // decimal but no integer component - "0a", // Extra character - "0000000000000", // 13 zeros - "012", // Int padded with zero - "00.1", // Decimal padded with zeros - "111111111111111111111", // all digits - "111111111111A11111111", // non-period/non-digit where decimal would be - "000000000000.00000000", // all zeros - "kaspa", // all text - } - - for _, testCase := range invalidCases { - err := ValidateAmountFormat(testCase) - - if err == nil { - t.Errorf("Expected an error but succeeded validation for test case %s", testCase) - } - } -}