diff --git a/.changeset/new-days-tease.md b/.changeset/new-days-tease.md new file mode 100644 index 00000000000..b8d1da7cdd4 --- /dev/null +++ b/.changeset/new-days-tease.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': minor +--- + +`Strings`: Add `toHexString(bytes)`. diff --git a/contracts/utils/Strings.sol b/contracts/utils/Strings.sol index 4cc597646f2..c67bff5a1b1 100644 --- a/contracts/utils/Strings.sol +++ b/contracts/utils/Strings.sol @@ -128,6 +128,23 @@ library Strings { return string(buffer); } + /** + * @dev Converts a `bytes` buffer to its ASCII `string` hexadecimal representation. + */ + function toHexString(bytes memory input) internal pure returns (string memory) { + unchecked { + bytes memory buffer = new bytes(2 * input.length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 0; i < input.length; ++i) { + uint8 v = uint8(input[i]); + buffer[2 * i + 2] = HEX_DIGITS[v >> 4]; + buffer[2 * i + 3] = HEX_DIGITS[v & 0xf]; + } + return string(buffer); + } + } + /** * @dev Returns true if the two strings are equal. */ diff --git a/test/utils/Strings.test.js b/test/utils/Strings.test.js index 8d5e2401ab1..751d6534ea9 100644 --- a/test/utils/Strings.test.js +++ b/test/utils/Strings.test.js @@ -186,6 +186,17 @@ describe('Strings', function () { }); }); + describe('bytes', function () { + describe('toHexString', function () { + for (const length of [0, 17, 20, 32, 42, 64, 512]) { + const input = ethers.hexlify(ethers.randomBytes(length)); + it(`hexlify buffer of length ${length}`, async function () { + expect(await this.mock.getFunction('$toHexString(bytes)')(input)).to.equal(input); + }); + } + }); + }); + describe('equal', function () { it('compares two empty strings', async function () { expect(await this.mock.$equal('', '')).to.be.true;