// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title RLPEncode * @dev A simple RLP encoding library. * @author Bakaoh */ library RLPEncode { /* * Internal functions */ /** * @dev RLP encodes a byte string. * @param self The byte string to encode. * @return The RLP encoded string in bytes. */ function encodeBytes(bytes memory self) internal pure returns (bytes memory) { bytes memory encoded; if (self.length == 1 && uint8(self[0]) < 128) { encoded = self; } else { encoded = concat(encodeLength(self.length, 128), self); } return encoded; } /** * @dev RLP encodes a list of RLP encoded byte byte strings. * @param self The list of RLP encoded byte strings. * @return The RLP encoded list of items in bytes. */ function encodeList(bytes[] memory self) internal pure returns (bytes memory) { bytes memory list = flatten(self); return concat(encodeLength(list.length, 192), list); } /** * @dev RLP encodes a string. * @param self The string to encode. * @return The RLP encoded string in bytes. */ function encodeString(string memory self) internal pure returns (bytes memory) { return encodeBytes(bytes(self)); } /** * @dev RLP encodes an address. * @param self The address to encode. * @return The RLP encoded address in bytes. */ function encodeAddress(address self) internal pure returns (bytes memory) { bytes memory inputBytes; assembly { let m := mload(0x40) mstore(add(m, 20), xor(0x140000000000000000000000000000000000000000, self)) mstore(0x40, add(m, 52)) inputBytes := m } return encodeBytes(inputBytes); } /** * @dev RLP encodes a uint. * @param self The uint to encode. * @return The RLP encoded uint in bytes. */ function encodeUint(uint256 self) internal pure returns (bytes memory) { return encodeBytes(toBinary(self)); } /** * @dev RLP encodes an int. * @param self The int to encode. * @return The RLP encoded int in bytes. */ function encodeInt(int256 self) internal pure returns (bytes memory) { return encodeUint(uint256(self)); } /** * @dev RLP encodes a bool. * @param self The bool to encode. * @return The RLP encoded bool in bytes. */ function encodeBool(bool self) internal pure returns (bytes memory) { bytes memory encoded = new bytes(1); encoded[0] = (self ? bytes1(0x01) : bytes1(0x80)); return encoded; } /* * Private functions */ /** * @dev Encode the first byte, followed by the `len` in binary form if `length` is more than 55. * @param len The length of the string or the payload. * @param offset 128 if item is string, 192 if item is list. * @return RLP encoded bytes. */ function encodeLength(uint256 len, uint256 offset) private pure returns (bytes memory) { bytes memory encoded; if (len < 56) { encoded = new bytes(1); encoded[0] = bytes32(len + offset)[31]; } else { uint256 lenLen; uint256 i = 1; while (len / i != 0) { lenLen++; i *= 256; } encoded = new bytes(lenLen + 1); encoded[0] = bytes32(lenLen + offset + 55)[31]; for (i = 1; i <= lenLen; i++) { encoded[i] = bytes32((len / (256 ** (lenLen - i))) % 256)[31]; } } return encoded; } /** * @dev Encode integer in big endian binary form with no leading zeroes. * @notice TODO: This should be optimized with assembly to save gas costs. * @param _x The integer to encode. * @return RLP encoded bytes. */ function toBinary(uint256 _x) private pure returns (bytes memory) { bytes memory b = new bytes(32); assembly { mstore(add(b, 32), _x) } uint256 i; for (i = 0; i < 32; i++) { if (b[i] != 0) { break; } } bytes memory res = new bytes(32 - i); for (uint256 j = 0; j < res.length; j++) { res[j] = b[i++]; } return res; } /** * @dev Copies a piece of memory to another location. * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. * @param _dest Destination location. * @param _src Source location. * @param _len Length of memory to copy. */ function memcpy(uint256 _dest, uint256 _src, uint256 _len) private pure { uint256 dest = _dest; uint256 src = _src; uint256 len = _len; for (; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } if (len > 0) { uint256 mask = 256 ** (32 - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } } /** * @dev Flattens a list of byte strings into one byte string. * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. * @param _list List of byte strings to flatten. * @return The flattened byte string. */ function flatten(bytes[] memory _list) private pure returns (bytes memory) { if (_list.length == 0) { return new bytes(0); } uint256 len; uint256 i; for (i = 0; i < _list.length; i++) { len += _list[i].length; } bytes memory flattened = new bytes(len); uint256 flattenedPtr; assembly { flattenedPtr := add(flattened, 0x20) } for (i = 0; i < _list.length; i++) { bytes memory item = _list[i]; uint256 listPtr; assembly { listPtr := add(item, 0x20) } memcpy(flattenedPtr, listPtr, item.length); flattenedPtr += _list[i].length; } return flattened; } /** * @dev Concatenates two bytes. * @notice From: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol. * @param _preBytes First byte string. * @param _postBytes Second byte string. * @return Both byte string combined. */ function concat(bytes memory _preBytes, bytes memory _postBytes) private pure returns (bytes memory) { bytes memory tempBytes; assembly { tempBytes := mload(0x40) let length := mload(_preBytes) mstore(tempBytes, length) let mc := add(tempBytes, 0x20) let end := add(mc, length) for { let cc := add(_preBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) mc := end end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(0x40, and(add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31))) } return tempBytes; } }