Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Strings.toHexString function #2504

Merged
merged 15 commits into from
Feb 8, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Now targeting the 0.8.x line of Solidity compilers. For 0.6.x (resp 0.7.x) support, use version 3.4.0 (resp 3.4.0-solc-0.7) of OpenZeppelin.
* `Context`: making `_msgData` return `bytes calldata` instead of `bytes memory` ([#2492](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2492))
* `ERC20`: Removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502))
* `Strings`: addition of a `toHexString` function. ([#2504](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2504))

## 3.4.0 (2021-02-02)

Expand Down
6 changes: 6 additions & 0 deletions contracts/mocks/StringsMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ contract StringsMock {
function fromUint256(uint256 value) public pure returns (string memory) {
return Strings.toString(value);
}
function fromUint256Hex(uint256 value) public pure returns (string memory) {
return Strings.toHexString(value);
}
function fromUint256HexFixed(uint256 value, uint256 length) public pure returns (string memory) {
return Strings.toHexString(value, length);
}
}
43 changes: 38 additions & 5 deletions contracts/utils/Strings.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ pragma solidity ^0.8.0;
* @dev String operations.
*/
library Strings {
bytes16 private constant alphabet = "0123456789abcdef";

/**
* @dev Converts a `uint256` to its ASCII `string` representation.
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
Expand All @@ -23,12 +25,43 @@ library Strings {
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint256 index = digits;
temp = value;
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}

/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
buffer[--index] = bytes1(uint8(48 + uint256(temp % 10)));
temp /= 10;
length++;
temp >>= 8;
}
return toHexString(value, length);
}

/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = alphabet[value & 0xf];
value >>= 4;
}
Amxx marked this conversation as resolved.
Show resolved Hide resolved
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}

}
38 changes: 36 additions & 2 deletions test/utils/Strings.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { constants } = require('@openzeppelin/test-helpers');
const { constants, expectRevert } = require('@openzeppelin/test-helpers');

const { expect } = require('chai');

Expand All @@ -9,7 +9,7 @@ contract('Strings', function (accounts) {
this.strings = await StringsMock.new();
});

describe('from uint256', function () {
describe('from uint256 - decimal format', function () {
it('converts 0', async function () {
expect(await this.strings.fromUint256(0)).to.equal('0');
});
Expand All @@ -22,4 +22,38 @@ contract('Strings', function (accounts) {
expect(await this.strings.fromUint256(constants.MAX_UINT256)).to.equal(constants.MAX_UINT256.toString());
});
});

describe('from uint256 - hex format', function () {
it('converts 0', async function () {
expect(await this.strings.fromUint256Hex(0)).to.equal('0x00');
});

it('converts a positive number', async function () {
expect(await this.strings.fromUint256Hex(0x4132)).to.equal('0x4132');
});

it('converts MAX_UINT256', async function () {
expect(await this.strings.fromUint256Hex(constants.MAX_UINT256))
.to.equal(web3.utils.toHex(constants.MAX_UINT256));
});
});

describe('from uint256 - fixed hex format', function () {
it('converts a positive number (long)', async function () {
expect(await this.strings.fromUint256HexFixed(0x4132, 32))
.to.equal('0x0000000000000000000000000000000000000000000000000000000000004132');
});

it('converts a positive number (short)', async function () {
await expectRevert(
this.strings.fromUint256HexFixed(0x4132, 1),
'Strings: hex length insufficient',
);
});

it('converts MAX_UINT256', async function () {
expect(await this.strings.fromUint256HexFixed(constants.MAX_UINT256, 32))
.to.equal(web3.utils.toHex(constants.MAX_UINT256));
});
});
});