Skip to content

Instantly share code, notes, and snippets.

@Vectorized
Last active May 18, 2022 06:16
Show Gist options
  • Save Vectorized/b2c70a8552babac60a8a7852a451357c to your computer and use it in GitHub Desktop.
Save Vectorized/b2c70a8552babac60a8a7852a451357c to your computer and use it in GitHub Desktop.
Solidity uint256 to string (base10)
// SPDX-License-Identifier: MIT
// Author: vectorized.eth
pragma solidity ^0.8.0;
pragma abicoder v2;
contract ToStringExamples {
constructor() {}
function toStringOriginal(uint256 value) public pure returns (string memory) {
// solc v0.8.7 --optimizer-runs=200. Input: 987654321, Gas: 6012
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return '0';
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
++digits;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
--digits;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toStringOriginalUnchecked(uint256 value) public pure returns (string memory) {
// solc v0.8.7 --optimizer-runs=200. Input: 987654321, Gas: 3038
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
unchecked {
if (value == 0) {
return '0';
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
++digits;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
--digits;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
function toStringAssembly(uint256 value) public pure returns (string memory result) {
// solc v0.8.7 --optimizer-runs=200. Input: 987654321, Gas: 1535
assembly {
// The maximum value of a uint256 contains 78 digits,
// but we allocate 128 bytes to keep the free memory pointer 32-byte word aliged.
// 128 = 32 + 3 * 32.
// We need a minimal of 3 32-byte words to store 78 digits.
let ptr := add(mload(0x40), 128)
// Update the free memory pointer to allocate.
mstore(0x40, ptr)
// Cache the end of the memory to calculate the length later.
let end := ptr
// We write the string from rightmost digit to leftmost digit.
// This is essentially a do-while loop that also handles the zero case.
// Costs a bit more vs early return for the zero case, but otherwise cheaper.
let temp := value
ptr := sub(ptr, 1)
mstore8(ptr, add(48, mod(temp, 10))) // 48 is the ASCII index of '0'.
temp := div(temp, 10)
for {} temp {} {
ptr := sub(ptr, 1)
mstore8(ptr, add(48, mod(temp, 10)))
temp := div(temp, 10)
}
temp := sub(end, ptr)
// Move the pointer 32 bytes lower to make room for the length.
ptr := sub(ptr, 32)
// Store the length.
mstore(ptr, temp)
// Assign the string's memory location to the result.
result := ptr
}
}
function toHexString(uint256 value) public pure returns (string memory ptr) {
assembly {
// You can easily modify this algorithm for non-left padded or addresses (160-bits)
let start := mload(0x40)
// We need 1 32-byte word for the length, and 2 32-byte words for the digits.
// Total: 32 + 2 * 32 = 96
ptr := add(start, 96)
// Allocate the memory.
mstore(0x40, ptr)
// Store the length.
mstore(start, 64)
// Write all zeros.
mstore(add(start, 32), "00000000000000000000000000000000")
mstore(add(start, 64), "00000000000000000000000000000000")
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// Costs a bit more vs early return for the zero case,
// but otherwise cheaper in terms of deployment and overall runtime costs.
for {
// Initialize and perform the first pass without check.
let temp := value
ptr := sub(ptr, 1)
// Write the character to the pointer.
mstore8(ptr, byte(and(temp, 15), "0123456789abcdef"))
temp := shr(4, temp)
} temp {
// Keep right shifting temp by a byte until zero.
temp := shr(4, temp)
} {
ptr := sub(ptr, 1)
mstore8(ptr, byte(and(temp, 15), "0123456789abcdef"))
}
ptr := start
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment