@@ -64,4 +64,132 @@ library TWStrings {
6464 require (value == 0 , "Strings: hex length insufficient " );
6565 return string (buffer);
6666 }
67+
68+ /// @dev Returns the hexadecimal representation of `value`.
69+ /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
70+ /// and the alphabets are capitalized conditionally according to
71+ /// https://eips.ethereum.org/EIPS/eip-55
72+ function toHexStringChecksummed (address value ) internal pure returns (string memory str ) {
73+ str = toHexString (value);
74+ /// @solidity memory-safe-assembly
75+ assembly {
76+ let mask := shl (6 , div (not (0 ), 255 )) // `0b010000000100000000 ...`
77+ let o := add (str, 0x22 )
78+ let hashed := and (keccak256 (o, 40 ), mul (34 , mask)) // `0b10001000 ... `
79+ let t := shl (240 , 136 ) // `0b10001000 << 240`
80+ for {
81+ let i := 0
82+ } 1 {
83+
84+ } {
85+ mstore (add (i, i), mul (t, byte (i, hashed)))
86+ i := add (i, 1 )
87+ if eq (i, 20 ) {
88+ break
89+ }
90+ }
91+ mstore (o, xor (mload (o), shr (1 , and (mload (0x00 ), and (mload (o), mask)))))
92+ o := add (o, 0x20 )
93+ mstore (o, xor (mload (o), shr (1 , and (mload (0x20 ), and (mload (o), mask)))))
94+ }
95+ }
96+
97+ /// @dev Returns the hexadecimal representation of `value`.
98+ /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
99+ function toHexString (address value ) internal pure returns (string memory str ) {
100+ str = toHexStringNoPrefix (value);
101+ /// @solidity memory-safe-assembly
102+ assembly {
103+ let strLength := add (mload (str), 2 ) // Compute the length.
104+ mstore (str, 0x3078 ) // Write the "0x" prefix.
105+ str := sub (str, 2 ) // Move the pointer.
106+ mstore (str, strLength) // Write the length.
107+ }
108+ }
109+
110+ /// @dev Returns the hexadecimal representation of `value`.
111+ /// The output is encoded using 2 hexadecimal digits per byte.
112+ function toHexStringNoPrefix (address value ) internal pure returns (string memory str ) {
113+ /// @solidity memory-safe-assembly
114+ assembly {
115+ str := mload (0x40 )
116+
117+ // Allocate the memory.
118+ // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
119+ // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
120+ // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
121+ mstore (0x40 , add (str, 0x80 ))
122+
123+ // Store "0123456789abcdef" in scratch space.
124+ mstore (0x0f , 0x30313233343536373839616263646566 )
125+
126+ str := add (str, 2 )
127+ mstore (str, 40 )
128+
129+ let o := add (str, 0x20 )
130+ mstore (add (o, 40 ), 0 )
131+
132+ value := shl (96 , value)
133+
134+ // We write the string from rightmost digit to leftmost digit.
135+ // The following is essentially a do-while loop that also handles the zero case.
136+ for {
137+ let i := 0
138+ } 1 {
139+
140+ } {
141+ let p := add (o, add (i, i))
142+ let temp := byte (i, value)
143+ mstore8 (add (p, 1 ), mload (and (temp, 15 )))
144+ mstore8 (p, mload (shr (4 , temp)))
145+ i := add (i, 1 )
146+ if eq (i, 20 ) {
147+ break
148+ }
149+ }
150+ }
151+ }
152+
153+ /// @dev Returns the hex encoded string from the raw bytes.
154+ /// The output is encoded using 2 hexadecimal digits per byte.
155+ function toHexString (bytes memory raw ) internal pure returns (string memory str ) {
156+ str = toHexStringNoPrefix (raw);
157+ /// @solidity memory-safe-assembly
158+ assembly {
159+ let strLength := add (mload (str), 2 ) // Compute the length.
160+ mstore (str, 0x3078 ) // Write the "0x" prefix.
161+ str := sub (str, 2 ) // Move the pointer.
162+ mstore (str, strLength) // Write the length.
163+ }
164+ }
165+
166+ /// @dev Returns the hex encoded string from the raw bytes.
167+ /// The output is encoded using 2 hexadecimal digits per byte.
168+ function toHexStringNoPrefix (bytes memory raw ) internal pure returns (string memory str ) {
169+ /// @solidity memory-safe-assembly
170+ assembly {
171+ let length := mload (raw)
172+ str := add (mload (0x40 ), 2 ) // Skip 2 bytes for the optional prefix.
173+ mstore (str, add (length, length)) // Store the length of the output.
174+
175+ // Store "0123456789abcdef" in scratch space.
176+ mstore (0x0f , 0x30313233343536373839616263646566 )
177+
178+ let o := add (str, 0x20 )
179+ let end := add (raw, length)
180+
181+ for {
182+
183+ } iszero (eq (raw, end)) {
184+
185+ } {
186+ raw := add (raw, 1 )
187+ mstore8 (add (o, 1 ), mload (and (mload (raw), 15 )))
188+ mstore8 (o, mload (and (shr (4 , mload (raw)), 15 )))
189+ o := add (o, 2 )
190+ }
191+ mstore (o, 0 ) // Zeroize the slot after the string.
192+ mstore (0x40 , add (o, 0x20 )) // Allocate the memory.
193+ }
194+ }
67195}
0 commit comments