github.com/decred/dcrlnd@v0.7.6/zpay32/amountunits.go (about) 1 package zpay32 2 3 import ( 4 "fmt" 5 "strconv" 6 7 "github.com/decred/dcrlnd/lnwire" 8 ) 9 10 var ( 11 // toMAtoms is a map from a unit to a function that converts an amount 12 // of that unit to MilliAtoms. 13 toMAtoms = map[byte]func(uint64) (lnwire.MilliAtom, error){ 14 'm': mDcrToMAtoms, 15 'u': uDcrToMAtoms, 16 'n': nDcrToMAtoms, 17 'p': pDcrToMAtoms, 18 } 19 20 // fromMAtoms is a map from a unit to a function that converts an amount 21 // in MilliAtoms to an amount of that unit. 22 fromMAtoms = map[byte]func(lnwire.MilliAtom) (uint64, error){ 23 'm': mAtToMDcr, 24 'u': mAtToUDcr, 25 'n': mAtToNDcr, 26 'p': mAtToPDcr, 27 } 28 ) 29 30 // mDcrToMAtoms converts the given amount in milliDCR to MilliAtoms. 31 func mDcrToMAtoms(m uint64) (lnwire.MilliAtom, error) { 32 return lnwire.MilliAtom(m) * 100000000, nil 33 } 34 35 // uDcrToMAtoms converts the given amount in microDCR to MilliAtoms. 36 func uDcrToMAtoms(u uint64) (lnwire.MilliAtom, error) { 37 return lnwire.MilliAtom(u * 100000), nil 38 } 39 40 // nDcrToMAtoms converts the given amount in nanoDCR to MilliAtoms. 41 func nDcrToMAtoms(n uint64) (lnwire.MilliAtom, error) { 42 return lnwire.MilliAtom(n * 100), nil 43 } 44 45 // pDcrToMAtoms converts the given amount in picoDCR to MilliAtoms. 46 func pDcrToMAtoms(p uint64) (lnwire.MilliAtom, error) { 47 if p < 10 { 48 return 0, fmt.Errorf("minimum amount is 10p") 49 } 50 if p%10 != 0 { 51 return 0, fmt.Errorf("amount %d pDCR not expressible in mAt", 52 p) 53 } 54 return lnwire.MilliAtom(p / 10), nil 55 } 56 57 // mAtToMDcr converts the given amount in MilliAtoms to milliDCR. 58 func mAtToMDcr(mat lnwire.MilliAtom) (uint64, error) { 59 if mat%100000000 != 0 { 60 return 0, fmt.Errorf("%d mAt not expressible "+ 61 "in mDCR", mat) 62 } 63 return uint64(mat / 100000000), nil 64 } 65 66 // mAtToUDcr converts the given amount in MilliAtoms to microDCR. 67 func mAtToUDcr(mat lnwire.MilliAtom) (uint64, error) { 68 if mat%100000 != 0 { 69 return 0, fmt.Errorf("%d mAt not expressible "+ 70 "in uDCR", mat) 71 } 72 return uint64(mat / 100000), nil 73 } 74 75 // mAtToNDcr converts the given amount in MilliAtoms to nanoDCR. 76 func mAtToNDcr(mat lnwire.MilliAtom) (uint64, error) { 77 if mat%100 != 0 { 78 return 0, fmt.Errorf("%d mAt not expressible in nDCR", mat) 79 } 80 return uint64(mat / 100), nil 81 } 82 83 // mAtToPDcr converts the given amount in MilliAtoms to picoDCR. 84 func mAtToPDcr(mat lnwire.MilliAtom) (uint64, error) { 85 return uint64(mat * 10), nil 86 } 87 88 // decodeAmount returns the amount encoded by the provided string in 89 // MilliAtom. 90 func decodeAmount(amount string) (lnwire.MilliAtom, error) { 91 if len(amount) < 1 { 92 return 0, fmt.Errorf("amount must be non-empty") 93 } 94 95 // If last character is a digit, then the amount can just be 96 // interpreted as DCR. 97 char := amount[len(amount)-1] 98 digit := char - '0' 99 if digit <= 9 { 100 dcr, err := strconv.ParseUint(amount, 10, 64) 101 if err != nil { 102 return 0, err 103 } 104 return lnwire.MilliAtom(dcr) * mAtPerDcr, nil 105 } 106 107 // If not a digit, it must be part of the known units. 108 conv, ok := toMAtoms[char] 109 if !ok { 110 return 0, fmt.Errorf("unknown multiplier %c", char) 111 } 112 113 // Known unit. 114 num := amount[:len(amount)-1] 115 if len(num) < 1 { 116 return 0, fmt.Errorf("number must be non-empty") 117 } 118 119 am, err := strconv.ParseUint(num, 10, 64) 120 if err != nil { 121 return 0, err 122 } 123 124 return conv(am) 125 } 126 127 // encodeAmount encodes the provided MilliAtom amount using as few characters 128 // as possible. 129 func encodeAmount(mat lnwire.MilliAtom) (string, error) { 130 // If possible to express in DCR, that will always be the shortest 131 // representation. 132 if mat%mAtPerDcr == 0 { 133 return strconv.FormatInt(int64(mat/mAtPerDcr), 10), nil 134 } 135 136 // Should always be expressible in pico DCR. 137 pico, err := fromMAtoms['p'](mat) 138 if err != nil { 139 return "", fmt.Errorf("unable to express %d mAt as pDCR: %v", 140 mat, err) 141 } 142 shortened := strconv.FormatUint(pico, 10) + "p" 143 for unit, conv := range fromMAtoms { 144 am, err := conv(mat) 145 if err != nil { 146 // Not expressible using this unit. 147 continue 148 } 149 150 // Save the shortest found representation. 151 str := strconv.FormatUint(am, 10) + string(unit) 152 if len(str) < len(shortened) { 153 shortened = str 154 } 155 } 156 157 return shortened, nil 158 }