github.com/lmittmann/w3@v0.20.0/util.go (about) 1 package w3 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "math/big" 7 "strings" 8 9 "github.com/ethereum/go-ethereum/common" 10 ) 11 12 // Common [big.Int]'s. 13 var ( 14 Big0 = new(big.Int) 15 Big1 = big.NewInt(1) 16 Big2 = big.NewInt(2) 17 Big10 = big.NewInt(10) 18 BigGwei = big.NewInt(1_000000000) 19 BigEther = big.NewInt(1_000000000_000000000) 20 21 // Max Uint Values. 22 BigMaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 256), Big1) 23 BigMaxUint248 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 248), Big1) 24 BigMaxUint240 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 240), Big1) 25 BigMaxUint232 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 232), Big1) 26 BigMaxUint224 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 224), Big1) 27 BigMaxUint216 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 216), Big1) 28 BigMaxUint208 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 208), Big1) 29 BigMaxUint200 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 200), Big1) 30 BigMaxUint192 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 192), Big1) 31 BigMaxUint184 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 184), Big1) 32 BigMaxUint176 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 176), Big1) 33 BigMaxUint168 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 168), Big1) 34 BigMaxUint160 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 160), Big1) 35 BigMaxUint152 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 152), Big1) 36 BigMaxUint144 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 144), Big1) 37 BigMaxUint136 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 136), Big1) 38 BigMaxUint128 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 128), Big1) 39 BigMaxUint120 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 120), Big1) 40 BigMaxUint112 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 112), Big1) 41 BigMaxUint104 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 104), Big1) 42 BigMaxUint96 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 96), Big1) 43 BigMaxUint88 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 88), Big1) 44 BigMaxUint80 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 80), Big1) 45 BigMaxUint72 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 72), Big1) 46 BigMaxUint64 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 64), Big1) 47 BigMaxUint56 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 56), Big1) 48 BigMaxUint48 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 48), Big1) 49 BigMaxUint40 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 40), Big1) 50 BigMaxUint32 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 32), Big1) 51 BigMaxUint24 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 24), Big1) 52 BigMaxUint16 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 16), Big1) 53 BigMaxUint8 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 8), Big1) 54 ) 55 56 // Zero Values. 57 var ( 58 Addr0 common.Address 59 Hash0 common.Hash 60 ) 61 62 // A returns an address from a hexstring or panics if the hexstring does not 63 // represent a valid address. 64 // 65 // Use [common.HexToAddress] to get the address from a hexstring without 66 // panicking. 67 func A(hexAddr string) (addr common.Address) { 68 if has0xPrefix(hexAddr) { 69 hexAddr = hexAddr[2:] 70 } 71 72 n, err := hex.Decode(addr[:], []byte(hexAddr)) 73 if err != nil { 74 panic(fmt.Sprintf("invalid address %q: %v", hexAddr, err)) 75 } else if n != 20 { 76 panic(fmt.Sprintf("invalid address %q: must have 20 bytes", hexAddr)) 77 } 78 return addr 79 } 80 81 // APtr returns an address pointer from a hexstring or panics if the hexstring 82 // does not represent a valid address. 83 func APtr(hexAddress string) *common.Address { 84 addr := A(hexAddress) 85 return &addr 86 } 87 88 // B returns a byte slice from a hexstring or panics if the hexstring does not 89 // represent a valid byte slice. 90 // 91 // Use [common.FromHex] to get the byte slice from a hexstring without 92 // panicking. 93 func B(hexBytes ...string) (bytes []byte) { 94 for _, s := range hexBytes { 95 if has0xPrefix(s) { 96 s = s[2:] 97 } 98 99 b, err := hex.DecodeString(s) 100 if err != nil { 101 panic(fmt.Sprintf("invalid bytes %q: %v", s, err)) 102 } 103 bytes = append(bytes, b...) 104 } 105 return bytes 106 } 107 108 // H returns a hash from a hexstring or panics if the hexstring does not 109 // represent a valid hash. 110 // 111 // Use [common.HexToHash] to get the hash from a hexstring without panicking. 112 func H(hexHash string) (hash common.Hash) { 113 if has0xPrefix(hexHash) { 114 hexHash = hexHash[2:] 115 } 116 117 n, err := hex.Decode(hash[:], []byte(hexHash)) 118 if err != nil { 119 panic(fmt.Sprintf("invalid hash %q: %v", hexHash, err)) 120 } else if n != 32 { 121 panic(fmt.Sprintf("invalid hash %q: must have 32 bytes", hexHash)) 122 } 123 return hash 124 } 125 126 // I returns a [big.Int] from a hexstring or decimal number string (with 127 // optional unit) or panics if the parsing fails. 128 // 129 // I supports the units "ether" or "eth" and "gwei" for decimal number strings. 130 // E.g.: 131 // 132 // w3.I("1 ether") -> 1000000000000000000 133 // w3.I("10 gwei") -> 10000000000 134 // 135 // Fractional digits that exceed the units maximum number of fractional digits 136 // are ignored. E.g.: 137 // 138 // w3.I("0.000000123456 gwei") -> 123 139 func I(strInt string) *big.Int { 140 if has0xPrefix(strInt) { 141 return parseHexBig(strInt[2:]) 142 } 143 return parseDecimal(strInt) 144 } 145 146 func parseHexBig(hexBig string) *big.Int { 147 bigInt, ok := new(big.Int).SetString(hexBig, 16) 148 if !ok { 149 panic(fmt.Sprintf("invalid hex big %q", "0x"+hexBig)) 150 } 151 return bigInt 152 } 153 154 func parseDecimal(strBig string) *big.Int { 155 numberUnit := strings.SplitN(strBig, " ", 2) 156 integerFraction := strings.SplitN(numberUnit[0], ".", 2) 157 integer, ok := new(big.Int).SetString(integerFraction[0], 10) 158 if !ok { 159 panic(fmt.Sprintf("str big %q must be number", strBig)) 160 } 161 162 // len == 1 163 if len(numberUnit) == 1 { 164 if len(integerFraction) > 1 { 165 panic(fmt.Sprintf("str big %q without unit must be integer", strBig)) 166 } 167 return integer 168 } 169 170 // len == 2 171 unit := strings.ToLower(numberUnit[1]) 172 switch unit { 173 case "ether", "eth": 174 integer.Mul(integer, BigEther) 175 case "gwei": 176 integer.Mul(integer, BigGwei) 177 default: 178 panic(fmt.Sprintf("str big %q has invalid unit %q", strBig, unit)) 179 } 180 181 // integer 182 if len(integerFraction) == 1 { 183 return integer 184 } 185 186 // float 187 fraction, ok := new(big.Int).SetString(integerFraction[1], 10) 188 if !ok { 189 panic(fmt.Sprintf("str big %q must be number", strBig)) 190 } 191 192 decimals := len(integerFraction[1]) 193 switch unit { 194 case "ether", "eth": 195 if fraction.Cmp(BigEther) >= 0 { 196 panic(fmt.Sprintf("str big %q exceeds precision", strBig)) 197 } 198 fraction.Mul(fraction, new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(18-decimals)), nil)) 199 case "gwei": 200 if fraction.Cmp(BigGwei) >= 0 { 201 panic(fmt.Sprintf("str big %q exceeds precision", strBig)) 202 } 203 fraction.Mul(fraction, new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(9-decimals)), nil)) 204 } 205 206 return integer.Add(integer, fraction) 207 } 208 209 // FromWei returns the given Wei as decimal with the given number of decimals. 210 func FromWei(wei *big.Int, decimals uint8) string { 211 if wei == nil { 212 return fmt.Sprint(nil) 213 } 214 215 d := new(big.Int).Exp(Big10, big.NewInt(int64(decimals)), nil) 216 217 sign := "" 218 if wei.Sign() < 0 { 219 sign = "-" 220 } 221 wei = new(big.Int).Abs(wei) 222 223 z, m := new(big.Int).DivMod(wei, d, new(big.Int)) 224 if m.Cmp(new(big.Int)) == 0 { 225 return sign + z.String() 226 } 227 s := strings.TrimRight(fmt.Sprintf("%0*s", decimals, m.String()), "0") 228 return sign + z.String() + "." + s 229 } 230 231 // has0xPrefix validates hexStr begins with '0x' or '0X'. 232 func has0xPrefix(hexStr string) bool { 233 return len(hexStr) >= 2 && hexStr[0] == '0' && (hexStr[1] == 'x' || hexStr[1] == 'X') 234 } 235 236 // BigMin returns the smaller of the two big integers. 237 func BigMin(a, b *big.Int) *big.Int { 238 if a.Cmp(b) < 0 { 239 return a 240 } 241 return b 242 } 243 244 // BigMax returns the larger of the two big integers. 245 func BigMax(a, b *big.Int) *big.Int { 246 if a.Cmp(b) > 0 { 247 return a 248 } 249 return b 250 }