github.com/status-im/status-go@v1.1.0/abi-spec/utils.go (about) 1 package abispec 2 3 import ( 4 "fmt" 5 "math/big" 6 "regexp" 7 "strings" 8 "unicode" 9 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/status-im/status-go/eth-node/crypto" 12 ) 13 14 var unicodeZeroPattern = regexp.MustCompile("^(?:\u0000)*") 15 var hexZeroPattern = regexp.MustCompile("^(?:00)*") 16 var hexStringPattern = regexp.MustCompile("(?i)^[0-9a-f]+$") 17 var hexPrefixPattern = regexp.MustCompile("(?i)^0x") 18 19 var addressBasicPattern = regexp.MustCompile("(?i)^(0x)?[0-9a-f]{40}$") 20 var addressLowerCasePattern = regexp.MustCompile("^(0x|0X)?[0-9a-f]{40}$") 21 var addressUpperCasePattern = regexp.MustCompile("^(0x|0X)?[0-9A-F]{40}$") 22 23 func HexToNumber(hex string) string { 24 num, success := big.NewInt(0).SetString(hex, 16) 25 if success { 26 return num.String() 27 } 28 return "" 29 } 30 31 func NumberToHex(numString string) string { 32 num, success := big.NewInt(0).SetString(numString, 0) 33 if success { 34 return fmt.Sprintf("%x", num) 35 } 36 return "" 37 } 38 39 func Sha3(str string) string { 40 bytes := crypto.Keccak256([]byte(str)) 41 return common.Bytes2Hex(bytes) 42 } 43 44 func reverse(str string) string { 45 bytes := []byte(str) 46 var out []byte 47 for i := len(bytes) - 1; i >= 0; i-- { 48 out = append(out, bytes[i]) 49 } 50 return string(out) 51 } 52 53 // remove \u0000 padding from either side 54 func removeUnicodeZeroPadding(str string) string { 55 found := unicodeZeroPattern.FindString(str) 56 str = strings.Replace(str, found, "", 1) 57 str = reverse(str) 58 found = unicodeZeroPattern.FindString(str) 59 str = strings.Replace(str, found, "", 1) 60 return reverse(str) 61 } 62 63 // remove 00 padding from either side 64 func removeHexZeroPadding(str string) string { 65 found := hexZeroPattern.FindString(str) 66 str = strings.Replace(str, found, "", 1) 67 str = reverse(str) 68 found = hexZeroPattern.FindString(str) 69 str = strings.Replace(str, found, "", 1) 70 return reverse(str) 71 } 72 73 // implementation referenced from https://github.com/ChainSafe/web3.js/blob/edcd215bf657a4bba62fabaafd08e6e70040976e/packages/web3-utils/src/utils.js#L165 74 func Utf8ToHex(str string) (string, error) { 75 str, err := Utf8encode(str) 76 if err != nil { 77 return "", err 78 } 79 str = removeUnicodeZeroPadding(str) 80 81 var hex = "" 82 for _, r := range str { 83 n := fmt.Sprintf("%x", r) 84 if len(n) < 2 { 85 hex += "0" + n 86 } else { 87 hex += n 88 } 89 } 90 return "0x" + hex, nil 91 } 92 93 // implementation referenced from https://github.com/ChainSafe/web3.js/blob/edcd215bf657a4bba62fabaafd08e6e70040976e/packages/web3-utils/src/utils.js#L193 94 func HexToUtf8(hexString string) (string, error) { 95 hexString = removeHexPrefix(hexString) 96 if !hexStringPattern.MatchString(hexString) { 97 return "", fmt.Errorf("the parameter '%s' must be a valid HEX string", hexString) 98 } 99 if len(hexString)%2 != 0 { 100 return "", fmt.Errorf("the parameter '%s' must have a even number of digits", hexString) 101 } 102 103 hexString = removeHexZeroPadding(hexString) 104 105 n := big.NewInt(0) 106 var bytes []byte 107 for i := 0; i < len(hexString); i += 2 { 108 hex := hexString[i : i+2] 109 n, success := n.SetString(hex, 16) 110 if !success { 111 return "", fmt.Errorf("invalid hex value %s", hex) 112 } 113 r := rune(n.Int64()) 114 bs := stringFromCharCode(r) 115 bytes = appendBytes(bytes, bs) 116 } 117 118 bytes, err := Utf8decode(string(bytes)) 119 if err != nil { 120 return "", err 121 } 122 return string(bytes), nil 123 } 124 125 func removeHexPrefix(str string) string { 126 found := hexPrefixPattern.FindString(str) 127 return strings.Replace(str, found, "", 1) 128 } 129 130 // implementation referenced from https://github.com/ChainSafe/web3.js/blob/edcd215bf657a4bba62fabaafd08e6e70040976e/packages/web3-utils/src/utils.js#L107 131 func CheckAddressChecksum(address string) (bool, error) { 132 address = removeHexPrefix(address) 133 addressHash := Sha3(strings.ToLower(address)) 134 135 n := big.NewInt(0) 136 for i := 0; i < 40; i++ { 137 // the nth letter should be uppercase if the nth digit of casemap is 1 138 n, success := n.SetString(addressHash[i:i+1], 16) 139 if !success { 140 return false, fmt.Errorf("failed to convert hex value '%s' to int", addressHash[i:i+1]) 141 } 142 v := n.Int64() 143 144 if (v > 7 && uint8(unicode.ToUpper(rune(address[i]))) != address[i]) || (v <= 7 && uint8(unicode.ToLower(rune(address[i]))) != address[i]) { 145 return false, nil 146 } 147 } 148 return true, nil 149 } 150 151 // implementation referenced from https://github.com/ChainSafe/web3.js/blob/edcd215bf657a4bba62fabaafd08e6e70040976e/packages/web3-utils/src/utils.js#L85 152 func IsAddress(address string) (bool, error) { 153 // check if it has the basic requirements of an address 154 if !addressBasicPattern.MatchString(address) { 155 return false, nil 156 } else if addressLowerCasePattern.MatchString(address) || addressUpperCasePattern.MatchString(address) { 157 return true, nil 158 } else { 159 return CheckAddressChecksum(address) 160 } 161 } 162 163 // implementation referenced from https://github.com/ChainSafe/web3.js/blob/2022b17d52d31ce95559d18d5530d18c83eb4d1c/packages/web3-utils/src/index.js#L283 164 func ToChecksumAddress(address string) (string, error) { 165 if strings.Trim(address, "") == "" { 166 return "", nil 167 } 168 if !addressBasicPattern.MatchString(address) { 169 return "", fmt.Errorf("given address '%s' is not a valid Ethereum address", address) 170 } 171 172 address = strings.ToLower(address) 173 address = removeHexPrefix(address) 174 addressHash := Sha3(address) 175 176 var checksumAddress = []rune("0x") 177 var n = big.NewInt(0) 178 for i := 0; i < len(address); i++ { 179 // If ith character is 9 to f then make it uppercase 180 n, success := n.SetString(addressHash[i:i+1], 16) 181 if !success { 182 return "", fmt.Errorf("failed to convert hex value '%s' to int", addressHash[i:i+1]) 183 } 184 if n.Int64() > 7 { 185 upper := unicode.ToUpper(rune(address[i])) 186 checksumAddress = append(checksumAddress, upper) 187 } else { 188 checksumAddress = append(checksumAddress, rune(address[i])) 189 } 190 } 191 return string(checksumAddress), nil 192 }