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  }