// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Strings.sol)
pragma solidity ^ 0 . 8 . 20 ;
import { Math } from " ./math/Math.sol " ;
import { SafeCast } from " ./math/SafeCast.sol " ;
import { SignedMath } from " ./math/SignedMath.sol " ;
/**
* @ dev String operations .
* /
library Strings {
using SafeCast for * ;
bytes16 private constant HEX_DIGITS = " 0123456789abcdef " ;
uint8 private constant ADDRESS_LENGTH = 20 ;
/**
* @ dev The ` value ` string doesn ' t fit in the specified `length`.
* /
error StringsInsufficientHexLength ( uint256 value , uint256 length ) ;
/**
* @ dev The string being parsed contains characters that are not in scope of the given base .
* /
error StringsInvalidChar ( ) ;
/**
* @ dev The string being parsed is not a properly formatted address .
* /
error StringsInvalidAddressFormat ( ) ;
/**
* @ dev Converts a ` uint256 ` to its ASCII ` string ` decimal representation .
* /
function toString ( uint256 value ) internal pure returns ( string memory ) {
unchecked {
uint256 length = Math . log10 ( value ) + 1 ;
string memory buffer = new string ( length ) ;
uint256 ptr ;
assembly ( " memory-safe " ) {
ptr : = add ( buffer , add ( 32 , length ) )
}
while ( true ) {
ptr -- ;
assembly ( " memory-safe " ) {
mstore8 ( ptr , byte ( mod ( value , 10 ) , HEX_DIGITS ) )
}
value /= 10 ;
if ( value == 0 ) break ;
}
return buffer ;
}
}
/**
* @ dev Converts a ` int256 ` to its ASCII ` string ` decimal representation .
* /
function toStringSigned ( int256 value ) internal pure returns ( string memory ) {
return string . concat ( value < 0 ? " - " : " " , toString ( SignedMath . abs ( value ) ) ) ;
}
/**
* @ dev Converts a ` uint256 ` to its ASCII ` string ` hexadecimal representation .
* /
function toHexString ( uint256 value ) internal pure returns ( string memory ) {
unchecked {
return toHexString ( value , Math . log256 ( value ) + 1 ) ;
}
}
/**
* @ dev Converts a ` uint256 ` to its ASCII ` string ` hexadecimal representation with fixed length .
* /
function toHexString ( uint256 value , uint256 length ) internal pure returns ( string memory ) {
uint256 localValue = value ;
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 ] = HEX_DIGITS [ localValue & 0xf ] ;
localValue >>= 4 ;
}
if ( localValue != 0 ) {
revert StringsInsufficientHexLength ( value , length ) ;
}
return string ( buffer ) ;
}
/**
* @ dev Converts an ` address ` with fixed length of 20 bytes to its not checksummed ASCII ` string ` hexadecimal
* representation .
* /
function toHexString ( address addr ) internal pure returns ( string memory ) {
return toHexString ( uint256 ( uint160 ( addr ) ) , ADDRESS_LENGTH ) ;
}
/**
* @ dev Converts an ` address ` with fixed length of 20 bytes to its checksummed ASCII ` string ` hexadecimal
* representation , according to EIP - 55 .
* /
function toChecksumHexString ( address addr ) internal pure returns ( string memory ) {
bytes memory buffer = bytes ( toHexString ( addr ) ) ;
// hash the hex part of buffer (skip length + 2 bytes, length 40)
uint256 hashValue ;
assembly ( " memory-safe " ) {
hashValue : = shr ( 96 , keccak256 ( add ( buffer , 0x22 ) , 40 ) )
}
for ( uint256 i = 41 ; i > 1 ; -- i ) {
// possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
if ( hashValue & 0xf > 7 && uint8 ( buffer [ i ] ) > 96 ) {
// case shift by xoring with 0x20
buffer [ i ] ^= 0x20 ;
}
hashValue >>= 4 ;
}
return string ( buffer ) ;
}
/**
* @ dev Returns true if the two strings are equal .
* /
function equal ( string memory a , string memory b ) internal pure returns ( bool ) {
return bytes ( a ) . length == bytes ( b ) . length && keccak256 ( bytes ( a ) ) == keccak256 ( bytes ( b ) ) ;
}
/**
* @ dev Parse a decimal string and returns the value as a ` uint256 ` .
*
* Requirements :
* - The string must be formatted as ` [ 0 - 9 ] * `
* - The result must fit into an ` uint256 ` type
* /
function parseUint ( string memory input ) internal pure returns ( uint256 ) {
return parseUint ( input , 0 , bytes ( input ) . length ) ;
}
/**
* @ dev Variant of { parseUint } that parses a substring of ` input ` located between position ` begin ` ( included ) and
* ` end ` ( excluded ) .
*
* Requirements :
* - The substring must be formatted as ` [ 0 - 9 ] * `
* - The result must fit into an ` uint256 ` type
* /
function parseUint ( string memory input , uint256 begin , uint256 end ) internal pure returns ( uint256 ) {
( bool success , uint256 value ) = tryParseUint ( input , begin , end ) ;
if ( ! success ) revert StringsInvalidChar ( ) ;
return value ;
}
/**
* @ dev Variant of { parseUint - string } that returns false if the parsing fails because of an invalid character .
*
* NOTE : This function will revert if the result does not fit in a ` uint256 ` .
* /
function tryParseUint ( string memory input ) internal pure returns ( bool success , uint256 value ) {
return _tryParseUintUncheckedBounds ( input , 0 , bytes ( input ) . length ) ;
}
/**
* @ dev Variant of { parseUint - string - uint256 - uint256 } that returns false if the parsing fails because of an invalid
* character .
*
* NOTE : This function will revert if the result does not fit in a ` uint256 ` .
* /
function tryParseUint (
string memory input ,
uint256 begin ,
uint256 end
) internal pure returns ( bool success , uint256 value ) {
if ( end > bytes ( input ) . length || begin > end ) return ( false , 0 ) ;
return _tryParseUintUncheckedBounds ( input , begin , end ) ;
}
/**
* @ dev Implementation of { tryParseUint } that does not check bounds . Caller should make sure that
* ` begin <= end <= input . length ` . Other inputs would result in undefined behavior .
* /
function _tryParseUintUncheckedBounds (
string memory input ,
uint256 begin ,
uint256 end
) private pure returns ( bool success , uint256 value ) {
bytes memory buffer = bytes ( input ) ;
uint256 result = 0 ;
for ( uint256 i = begin ; i < end ; ++ i ) {
uint8 chr = _tryParseChr ( bytes1 ( _unsafeReadBytesOffset ( buffer , i ) ) ) ;
if ( chr > 9 ) return ( false , 0 ) ;
result *= 10 ;
result += chr ;
}
return ( true , result ) ;
}
/**
* @ dev Parse a decimal string and returns the value as a ` int256 ` .
*
* Requirements :
* - The string must be formatted as ` [ - + ] ? [ 0 - 9 ] * `
* - The result must fit in an ` int256 ` type .
* /
function parseInt ( string memory input ) internal pure returns ( int256 ) {
return parseInt ( input , 0 , bytes ( input ) . length ) ;
}
/**
* @ dev Variant of { parseInt - string } that parses a substring of ` input ` located between position ` begin ` ( included ) and
* ` end ` ( excluded ) .
*
* Requirements :
* - The substring must be formatted as ` [ - + ] ? [ 0 - 9 ] * `
* - The result must fit in an ` int256 ` type .
* /
function parseInt ( string memory input , uint256 begin , uint256 end ) internal pure returns ( int256 ) {
( bool success , int256 value ) = tryParseInt ( input , begin , end ) ;
if ( ! success ) revert StringsInvalidChar ( ) ;
return value ;
}
/**
* @ dev Variant of { parseInt - string } that returns false if the parsing fails because of an invalid character or if
* the result does not fit in a ` int256 ` .
*
* NOTE : This function will revert if the absolute value of the result does not fit in a ` uint256 ` .
* /
function tryParseInt ( string memory input ) internal pure returns ( bool success , int256 value ) {
return _tryParseIntUncheckedBounds ( input , 0 , bytes ( input ) . length ) ;
}
uint256 private constant ABS_MIN_INT256 = 2 ** 255 ;
/**
* @ dev Variant of { parseInt - string - uint256 - uint256 } that returns false if the parsing fails because of an invalid
* character or if the result does not fit in a ` int256 ` .
*
* NOTE : This function will revert if the absolute value of the result does not fit in a ` uint256 ` .
* /
function tryParseInt (
string memory input ,
uint256 begin ,
uint256 end
) internal pure returns ( bool success , int256 value ) {
if ( end > bytes ( input ) . length || begin > end ) return ( false , 0 ) ;
return _tryParseIntUncheckedBounds ( input , begin , end ) ;
}
/**
* @ dev Implementation of { tryParseInt } that does not check bounds . Caller should make sure that
* ` begin <= end <= input . length ` . Other inputs would result in undefined behavior .
* /
function _tryParseIntUncheckedBounds (
string memory input ,
uint256 begin ,
uint256 end
) private pure returns ( bool success , int256 value ) {
bytes memory buffer = bytes ( input ) ;
// Check presence of a negative sign.
bytes1 sign = begin == end ? bytes1 ( 0 ) : bytes1 ( _unsafeReadBytesOffset ( buffer , begin ) ) ; // don't do out-of-bound (possibly unsafe) read if sub-string is empty
bool positiveSign = sign == bytes1 ( " + " ) ;
bool negativeSign = sign == bytes1 ( " - " ) ;
uint256 offset = ( positiveSign || negativeSign ) . toUint ( ) ;
( bool absSuccess , uint256 absValue ) = tryParseUint ( input , begin + offset , end ) ;
if ( absSuccess && absValue < ABS_MIN_INT256 ) {
return ( true , negativeSign ? - int256 ( absValue ) : int256 ( absValue ) ) ;
} else if ( absSuccess && negativeSign && absValue == ABS_MIN_INT256 ) {
return ( true , type ( int256 ) . min ) ;
} else return ( false , 0 ) ;
}
/**
* @ dev Parse a hexadecimal string ( with or without " 0x " prefix ) , and returns the value as a ` uint256 ` .
*
* Requirements :
* - The string must be formatted as ` ( 0 x ) ? [ 0 - 9 a - fA - F ] * `
* - The result must fit in an ` uint256 ` type .
* /
function parseHexUint ( string memory input ) internal pure returns ( uint256 ) {
return parseHexUint ( input , 0 , bytes ( input ) . length ) ;
}
/**
* @ dev Variant of { parseHexUint } that parses a substring of ` input ` located between position ` begin ` ( included ) and
* ` end ` ( excluded ) .
*
* Requirements :
* - The substring must be formatted as ` ( 0 x ) ? [ 0 - 9 a - fA - F ] * `
* - The result must fit in an ` uint256 ` type .
* /
function parseHexUint ( string memory input , uint256 begin , uint256 end ) internal pure returns ( uint256 ) {
( bool success , uint256 value ) = tryParseHexUint ( input , begin , end ) ;
if ( ! success ) revert StringsInvalidChar ( ) ;
return value ;
}
/**
* @ dev Variant of { parseHexUint - string } that returns false if the parsing fails because of an invalid character .
*
* NOTE : This function will revert if the result does not fit in a ` uint256 ` .
* /
function tryParseHexUint ( string memory input ) internal pure returns ( bool success , uint256 value ) {
return _tryParseHexUintUncheckedBounds ( input , 0 , bytes ( input ) . length ) ;
}
/**
* @ dev Variant of { parseHexUint - string - uint256 - uint256 } that returns false if the parsing fails because of an
* invalid character .
*
* NOTE : This function will revert if the result does not fit in a ` uint256 ` .
* /
function tryParseHexUint (
string memory input ,
uint256 begin ,
uint256 end
) internal pure returns ( bool success , uint256 value ) {
if ( end > bytes ( input ) . length || begin > end ) return ( false , 0 ) ;
return _tryParseHexUintUncheckedBounds ( input , begin , end ) ;
}
/**
* @ dev Implementation of { tryParseHexUint } that does not check bounds . Caller should make sure that
* ` begin <= end <= input . length ` . Other inputs would result in undefined behavior .
* /
function _tryParseHexUintUncheckedBounds (
string memory input ,
uint256 begin ,
uint256 end
) private pure returns ( bool success , uint256 value ) {
bytes memory buffer = bytes ( input ) ;
// skip 0x prefix if present
bool hasPrefix = ( end > begin + 1 ) && bytes2 ( _unsafeReadBytesOffset ( buffer , begin ) ) == bytes2 ( " 0x " ) ; // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 offset = hasPrefix . toUint ( ) * 2 ;
uint256 result = 0 ;
for ( uint256 i = begin + offset ; i < end ; ++ i ) {
uint8 chr = _tryParseChr ( bytes1 ( _unsafeReadBytesOffset ( buffer , i ) ) ) ;
if ( chr > 15 ) return ( false , 0 ) ;
result *= 16 ;
unchecked {
// Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
// This guaratees that adding a value < 16 will not cause an overflow, hence the unchecked.
result += chr ;
}
}
return ( true , result ) ;
}
/**
* @ dev Parse a hexadecimal string ( with or without " 0x " prefix ) , and returns the value as an ` address ` .
*
* Requirements :
* - The string must be formatted as ` ( 0 x ) ? [ 0 - 9 a - fA - F ] { 40 } `
* /
function parseAddress ( string memory input ) internal pure returns ( address ) {
return parseAddress ( input , 0 , bytes ( input ) . length ) ;
}
/**
* @ dev Variant of { parseAddress } that parses a substring of ` input ` located between position ` begin ` ( included ) and
* ` end ` ( excluded ) .
*
* Requirements :
* - The substring must be formatted as ` ( 0 x ) ? [ 0 - 9 a - fA - F ] { 40 } `
* /
function parseAddress ( string memory input , uint256 begin , uint256 end ) internal pure returns ( address ) {
( bool success , address value ) = tryParseAddress ( input , begin , end ) ;
if ( ! success ) revert StringsInvalidAddressFormat ( ) ;
return value ;
}
/**
* @ dev Variant of { parseAddress - string } that returns false if the parsing fails because the input is not a properly
* formatted address . See { parseAddress } requirements .
* /
function tryParseAddress ( string memory input ) internal pure returns ( bool success , address value ) {
return tryParseAddress ( input , 0 , bytes ( input ) . length ) ;
}
/**
* @ dev Variant of { parseAddress - string - uint256 - uint256 } that returns false if the parsing fails because input is not a properly
* formatted address . See { parseAddress } requirements .
* /
function tryParseAddress (
string memory input ,
uint256 begin ,
uint256 end
) internal pure returns ( bool success , address value ) {
if ( end > bytes ( input ) . length || begin > end ) return ( false , address ( 0 ) ) ;
bool hasPrefix = ( end > begin + 1 ) && bytes2 ( _unsafeReadBytesOffset ( bytes ( input ) , begin ) ) == bytes2 ( " 0x " ) ; // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 expectedLength = 40 + hasPrefix . toUint ( ) * 2 ;
// check that input is the correct length
if ( end - begin == expectedLength ) {
// length guarantees that this does not overflow, and value is at most type(uint160).max
( bool s , uint256 v ) = _tryParseHexUintUncheckedBounds ( input , begin , end ) ;
return ( s , address ( uint160 ( v ) ) ) ;
} else {
return ( false , address ( 0 ) ) ;
}
}
function _tryParseChr ( bytes1 chr ) private pure returns ( uint8 ) {
uint8 value = uint8 ( chr ) ;
// Try to parse `chr`:
// - Case 1: [0-9]
// - Case 2: [a-f]
// - Case 3: [A-F]
// - otherwise not supported
unchecked {
if ( value > 47 && value < 58 ) value -= 48 ;
else if ( value > 96 && value < 103 ) value -= 87 ;
else if ( value > 64 && value < 71 ) value -= 55 ;
else return type ( uint8 ) . max ;
}
return value ;
}
/**
* @ dev Reads a bytes32 from a bytes array without bounds checking .
*
* NOTE : making this function internal would mean it could be used with memory unsafe offset , and marking the
* assembly block as such would prevent some optimizations .
* /
function _unsafeReadBytesOffset ( bytes memory buffer , uint256 offset ) private pure returns ( bytes32 value ) {
// This is not memory safe in the general case, but all calls to this private function are within bounds.
assembly ( " memory-safe " ) {
value : = mload ( add ( buffer , add ( 0x20 , offset ) ) )
}
}
}