Last active
May 18, 2022 06:16
-
-
Save Vectorized/b2c70a8552babac60a8a7852a451357c to your computer and use it in GitHub Desktop.
Solidity uint256 to string (base10)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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