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  }