scale.gno
1.48 Kb · 50 lines
1package zkgm
2
3import (
4 "errors"
5 "math"
6
7 u256 "gno.land/p/onbloc/math/uint256"
8)
9
10// ErrAmountOverflow is returned when a (downscaled) amount does not fit int64,
11// the width of the grc20/banker ledger.
12var ErrAmountOverflow = errors.New("zkgm: amount overflows int64")
13
14// ErrAmountNotDivisible is returned when a source-precision amount carries precision
15// below the ledger's canonical scale,
16// so dividing by the token's scale factor leaves a remainder.
17// It is rejected so cross-chain value is conserved exactly.
18var ErrAmountNotDivisible = errors.New("zkgm: amount not divisible by token scale factor")
19
20// Pow10 returns 10^exp as a uint256.
21// exp is small (a token's decimal excess over the canonical precision),
22// so float pow is exact and sufficient.
23func Pow10(exp int) *u256.Uint {
24 result := math.Pow10(exp)
25
26 return u256.NewUint(uint64(result))
27}
28
29// ScaleDownToInt64 narrows an origin-precision amount to an int64 ledger amount
30// by dividing by 10^scaleExp.
31// scaleExp <= 0 is the identity narrow, used by native and low-decimal tokens.
32func ScaleDownToInt64(amount *u256.Uint, scaleExp int) (int64, error) {
33 value := amount
34
35 if scaleExp > 0 {
36 quotient, remainder := u256.Zero().DivMod(amount, Pow10(scaleExp), u256.Zero())
37 if !remainder.IsZero() {
38 return 0, ErrAmountNotDivisible
39 }
40
41 value = quotient
42 }
43
44 valueUint64, overflow := value.Uint64WithOverflow()
45 if overflow || valueUint64 > uint64(math.MaxInt64) {
46 return 0, ErrAmountOverflow
47 }
48
49 return int64(valueUint64), nil
50}