import 'dart:convert' show utf8; import 'dart:typed_data'; import 'package:convert/convert.dart' show hex; import "package:pointycastle/digests/keccak.dart"; bool isHexPrefixed(String str) { ArgumentError.checkNotNull(str); return str.startsWith('0x'); } /// Is the string a hex string. bool isHexString(String value, {int length = 0}) { ArgumentError.checkNotNull(value); // if (!RegExp('^0x[0-9A-Fa-f]*\$').hasMatch(value)) return false; if (!RegExp(r'\b(?:0[xX])?[0-9a-fA-F]+\b').hasMatch(value)) return false; if (length > 0 && value.length != 2 + 2 * length) return false; return true; } String stripHexPrefix(String hexString) { ArgumentError.checkNotNull(hexString); if (hexString.startsWith("0x") || hexString.startsWith("0X")) { return hexString.substring(2); } return hexString; } String addHexPrefix(String hexString) { ArgumentError.checkNotNull(hexString); if (!hexString.startsWith("0x") && !hexString.startsWith("0X")) { return '0x$hexString'; } return hexString; } /// Pads a [String] to have an even length String padToEven(String value) { ArgumentError.checkNotNull(value); var a = '$value'; if (a.length % 2 == 1) a = '0${a}'; return a; } /// Converts a [int] into a hex [String] String intToHex(i) { ArgumentError.checkNotNull(i); return '0x${i.toRadixString(16)}'; } /// Converts an [int] or [BigInt] to a [Uint8List] Uint8List intToBytes(i) { return Uint8List.fromList(i == null || i == 0 || i == BigInt.zero ? [] : hex.decode(padToEven(intToHex(i).substring(2)))); } Uint8List stringToBytes(String str) { ArgumentError.checkNotNull(str); if (str.isEmpty) { return Uint8List(0); } // Use UTF-8 encoding to convert string to bytes return Uint8List.fromList(utf8.encode(str)); } /// Get the binary size of a string int getBinarySize(String str) { ArgumentError.checkNotNull(str); return utf8.encode(str).length; } /// Returns TRUE if the first specified array contains all elements from the second one. FALSE otherwise. bool arrayContainsArray(List superset, List subset, {bool some = false}) { ArgumentError.checkNotNull(superset); ArgumentError.checkNotNull(subset); if (some) return Set.from(superset).intersection(Set.from(subset)).length > 0; return Set.from(superset).containsAll(subset); } /// Should be called to get utf8 from it's hex representation String toUtf8(String hexString) { ArgumentError.checkNotNull(hexString); List bufferValue = hex.decode(padToEven(stripHexPrefix(hexString).replaceAll(RegExp('^0+|0+\$'), ''))); return utf8.decode(bufferValue); } /// Should be called to get ascii from it's hex representation String toAscii(String hexString) { ArgumentError.checkNotNull(hexString); var start = hexString.startsWith(RegExp('^0x')) ? 2 : 0; return String.fromCharCodes(hex.decode(hexString.substring(start))); } /// Should be called to get hex representation (prefixed by 0x) of utf8 string String fromUtf8(String stringValue) { ArgumentError.checkNotNull(stringValue); var stringBuffer = utf8.encode(stringValue); return "0x${padToEven(hex.encode(stringBuffer)).replaceAll(RegExp('^0+|0+\$'), '')}"; } /// Should be called to get hex representation (prefixed by 0x) of ascii string String fromAscii(String stringValue) { ArgumentError.checkNotNull(stringValue); var hexString = ''; for (var i = 0; i < stringValue.length; i++) { var code = stringValue.codeUnitAt(i); var n = hex.encode([code]); hexString += n.length < 2 ? '0${n}' : n; } return '0x$hexString'; } Uint8List hexToBytes(String hexStr) { final bytes = hex.decode(stripHexPrefix(hexStr)); if (bytes is Uint8List) return bytes; return Uint8List.fromList(bytes); } String bytesToHex(List bytes, {bool include0x = false, int? forcePadLength, bool padToEvenLength = false}) { var encoded = hex.encode(bytes); if (forcePadLength != null) { assert(forcePadLength >= encoded.length); final padding = forcePadLength - encoded.length; encoded = ('0' * padding) + encoded; } if (padToEvenLength && encoded.length % 2 != 0) encoded = '0$encoded'; return (include0x ? '0x' : '') + encoded; } // ------------ by myself ----------- String hex2ascii(String hexString) { try { if (isHexString(hexString)) { hexString = stripHexPrefix(hexString); List splitted = []; for (int i = 0; i < hexString.length; i = i + 2) { splitted.add(hexString.substring(i, i + 2)); } String ascii = List.generate(splitted.length, (i) => String.fromCharCode(int.parse(splitted[i], radix: 16))).join(); return ascii; } else { return ''; } } catch (error) { return ''; } } String ascii2hex(String asciiString) { try { if (asciiString.isNotEmpty) { String hexStr = ''; for (int i = 0; i < asciiString.length; ++i) { hexStr = hexStr + asciiString.codeUnitAt(i).toRadixString(16); } return hexStr; } else { return ''; } } catch (error) { return ''; } } String hex2dec(String hexString) { try { if (isHexString(hexString)) { BigInt? bgInt = BigInt.tryParse(stripHexPrefix(hexString), radix:16); if (bgInt != null) { return bgInt.toRadixString(10); } } // else return ''; } catch (error) { return ''; } } String dec2hex(String decString) { try { if (decString.isNotEmpty) { BigInt? bgInt = BigInt.tryParse(decString, radix:10); if (bgInt != null) { return addHexPrefix(bgInt.toRadixString(16)); } } // else return ''; } catch (error) { return ''; } } /// ----- to Hash ----- /// 注意:Hash256 和 Keccak256 其实不一样, /// 但由于最早接触 Ethereum 相关代码,其中 Hash 使用的是 Keccak256, /// 造成App其他地方的 Hash 也都沿用了 Keccak256。 /// 历史原因,不做修改。[2023-12-15 James.Zhang] String toHash(String anyString) { return toKeccak256(anyString); } String toKeccak256(String anyString, [int bitLength = 256]) { var keccak = KeccakDigest(bitLength); keccak.update( Uint8List.fromList(anyString.codeUnits), 0, anyString.codeUnits.length, ); var out = Uint8List(bitLength ~/ 8); keccak.doFinal(out, 0); return _uint8ListToHex(out); } /// Character `0`. const int _zero = 0x30; /// Character `a`. const int _a = 0x61; String _uint8ListToHex(List bytes) { final buffer = Uint8List(bytes.length * 2); int bufferIndex = 0; for (var i = 0; i < bytes.length; i++) { var byte = bytes[i]; // The bitwise arithmetic here is equivalent to `byte ~/ 16` and `byte % 16` // for valid byte values, but is easier for dart2js to optimize given that // it can't prove that [byte] will always be positive. buffer[bufferIndex++] = _codeUnitForDigit((byte & 0xF0) >> 4); buffer[bufferIndex++] = _codeUnitForDigit(byte & 0x0F); } return String.fromCharCodes(buffer); } /// Returns the ASCII/Unicode code unit corresponding to the hexadecimal digit /// [digit]. int _codeUnitForDigit(int digit) => digit < 10 ? digit + _zero : digit + _a - 10;