diff --git a/contracts/libs/IbcErrors.sol b/contracts/libs/IbcErrors.sol index 5d7a7427..7654baa8 100644 --- a/contracts/libs/IbcErrors.sol +++ b/contracts/libs/IbcErrors.sol @@ -12,6 +12,7 @@ library IBCErrors { error invalidAddress(); error invalidPortPrefix(); error notEnoughGas(); + error invalidCharacter(); // packet sequence related errors. error invalidPacketSequence(); diff --git a/contracts/libs/IbcUtils.sol b/contracts/libs/IbcUtils.sol index 746c2729..e4aa0497 100644 --- a/contracts/libs/IbcUtils.sol +++ b/contracts/libs/IbcUtils.sol @@ -35,32 +35,46 @@ library IbcUtils { * hexStr is case-insensitive. */ function hexStrToAddress(string memory hexStr) public pure returns (address addr) { + bytes memory hexBytes = bytes(hexStr); if (bytes(hexStr).length != 40) { - revert IBCErrors.invalidHexStringLength(); + revert IBCErrors.invalidHexStringLength(); // Addresses must always be 20 bytes long; equal to 40 nibbles } - bytes memory strBytes = bytes(hexStr); - bytes memory addrBytes = new bytes(20); - - for (uint256 i = 0; i < 20; i++) { - uint8 high = uint8(strBytes[i * 2]); - uint8 low = uint8(strBytes[1 + i * 2]); - // Convert to lowercase if the character is in uppercase - if (high >= 65 && high <= 90) { - high += 32; - } - if (low >= 65 && low <= 90) { - low += 32; + uint256 total = 0; + uint256 base = 1; + uint256 i = 40; + while (i > 0) { + i--; + uint256 digit; + // Convert ASCII to integer value + if (uint8(hexBytes[i]) >= 48 && uint8(hexBytes[i]) <= 57) { + /** + * This triggers if hexBytes[i] is equal to '0' to '9' + * '0' - '9' are 48-57 in ascii, and we want to map this into 0-9, so we subtract 48 + */ + digit = uint160(uint8(hexBytes[i]) - 48); + } else if (uint8(hexBytes[i]) >= 65 && uint8(hexBytes[i]) <= 70) { + /** + * This triggers if hexBytes[i] is equal to 'A' to 'F' + * 'A' - 'F' are 65-70 in ascii, and we want to map this into 0xa-0xf (equal to 10-15), so we + * sutract 55 + */ + digit = uint160(uint8(hexBytes[i]) - 55); + } else if (uint8(hexBytes[i]) >= 97 && uint8(hexBytes[i]) <= 102) { + /** + * This triggers if hexBytes[i] is equal to 'a' to 'f' + * 'a' to 'f' are 97-102 in ascii, and we want to amp this into 0xa-0xf (equal to 10-15), so we + * sutract 87 + */ + digit = uint160(uint8(hexBytes[i]) - 87); + } else { + revert IBCErrors.invalidCharacter(); } - uint8 digit = (high - (high >= 97 ? 87 : 48)) * 16 + (low - (low >= 97 ? 87 : 48)); - addrBytes[i] = bytes1(digit); - } - - assembly { - addr := mload(add(addrBytes, 20)) + total += digit * base; + base *= 16; } - return addr; + addr = address(uint160(total)); } function toUniversalPacketBytes(UniversalPacket memory data) internal pure returns (bytes memory packetBytes) { diff --git a/test/Ibc.t.sol b/test/Ibc.t.sol index 6930a329..4133a6b6 100644 --- a/test/Ibc.t.sol +++ b/test/Ibc.t.sol @@ -88,4 +88,26 @@ contract IbcTest is Test { "daf;lkdsajflkasjdv;lkjzdljga;lkgfjda;iocjvz;lkjval;dsjkf;alkdj;zlkjv;lkjaeg;ijafd;lkjzvc,mnb.kahgd;ajkfaj;dgoij;zlckjv;lzkjv;kaldfjg;alkjgf;lkzvjcx;lkvja;lkjg;aslgjdz;adf;kasjg;lkjwaea;lkjg;io1j;4kjrda;lkfjaleot8ywp89yz;dvhlsdkj" ); } + + function test_InvalidHexStr_To_Address() public { + string memory validString = "2B5AD5c4795c026514f8317c7a215E218DcCD6cF"; // Can't start with g + assertEq(IbcUtils.hexStrToAddress(validString), vm.addr(2)); + + string memory invalidHexStr1 = "123"; // Too Short + vm.expectRevert(abi.encodeWithSelector(IBCErrors.invalidHexStringLength.selector)); + IbcUtils.hexStrToAddress(invalidHexStr1); + + string memory invalidHexStr2 = "2B5AD5c4795c026514f8317c7a215E218DcCD6cFa"; // Too long + vm.expectRevert(abi.encodeWithSelector(IBCErrors.invalidHexStringLength.selector)); + IbcUtils.hexStrToAddress(invalidHexStr2); + + string memory invalidHexStr3 = "2G5AD5c4795c026514f8317c7a215E218DcCD6cF"; // Can't have G + vm.expectRevert(abi.encodeWithSelector(IBCErrors.invalidCharacter.selector)); + IbcUtils.hexStrToAddress(invalidHexStr3); + } + + function test_To_From_addr_hexStr(address addr) public { + bytes memory hexStr = IbcUtils.toHexStr(addr); + assertEq(addr, IbcUtils.hexStrToAddress(string(hexStr))); + } }