// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @custom:attribution https://github.com/bakaoh/solidity-rlp-encode /// @title RLPWriter /// @author RLPWriter is a library for encoding Solidity types to RLP bytes. Adapted from Bakaoh's /// RLPEncode library (https://github.com/bakaoh/solidity-rlp-encode) with minor /// modifications to improve legibility. library RLPWriter { /// @notice RLP encodes a byte string. /// @param _in The byte string to encode. /// @return out_ The RLP encoded string in bytes. function writeBytes(bytes memory _in) internal pure returns (bytes memory out_) { if (_in.length == 1 && uint8(_in[0]) < 128) { out_ = _in; } else { out_ = abi.encodePacked(_writeLength(_in.length, 128), _in); } } /// @notice RLP encodes a list of RLP encoded byte byte strings. /// @param _in The list of RLP encoded byte strings. /// @return list_ The RLP encoded list of items in bytes. function writeList(bytes[] memory _in) internal pure returns (bytes memory list_) { list_ = _flatten(_in); list_ = abi.encodePacked(_writeLength(list_.length, 192), list_); } /// @notice RLP encodes a string. /// @param _in The string to encode. /// @return out_ The RLP encoded string in bytes. function writeString(string memory _in) internal pure returns (bytes memory out_) { out_ = writeBytes(bytes(_in)); } /// @notice RLP encodes an address. /// @param _in The address to encode. /// @return out_ The RLP encoded address in bytes. function writeAddress(address _in) internal pure returns (bytes memory out_) { out_ = writeBytes(abi.encodePacked(_in)); } /// @notice RLP encodes a uint. /// @param _in The uint256 to encode. /// @return out_ The RLP encoded uint256 in bytes. function writeUint(uint256 _in) internal pure returns (bytes memory out_) { out_ = writeBytes(_toBinary(_in)); } /// @notice RLP encodes a bool. /// @param _in The bool to encode. /// @return out_ The RLP encoded bool in bytes. function writeBool(bool _in) internal pure returns (bytes memory out_) { out_ = new bytes(1); out_[0] = (_in ? bytes1(0x01) : bytes1(0x80)); } /// @notice Encode the first byte and then 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 out_ RLP encoded bytes. function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory out_) { if (_len < 56) { out_ = new bytes(1); out_[0] = bytes1(uint8(_len) + uint8(_offset)); } else { uint256 lenLen; uint256 i = 1; while (_len / i != 0) { lenLen++; i *= 256; } out_ = new bytes(lenLen + 1); out_[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55); for (i = 1; i <= lenLen; i++) { out_[i] = bytes1(uint8((_len / (256 ** (lenLen - i))) % 256)); } } } /// @notice Encode integer in big endian binary form with no leading zeroes. /// @param _x The integer to encode. /// @return out_ RLP encoded bytes. function _toBinary(uint256 _x) private pure returns (bytes memory out_) { bytes memory b = abi.encodePacked(_x); uint256 i = 0; for (; i < 32; i++) { if (b[i] != 0) { break; } } out_ = new bytes(32 - i); for (uint256 j = 0; j < out_.length; j++) { out_[j] = b[i++]; } } /// @custom:attribution https://github.com/Arachnid/solidity-stringutils /// @notice Copies a piece of memory to another location. /// @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; } uint256 mask; unchecked { mask = 256 ** (32 - len) - 1; } assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /// @custom:attribution https://github.com/sammayo/solidity-rlp-encoder /// @notice Flattens a list of byte strings into one byte string. /// @param _list List of byte strings to flatten. /// @return out_ The flattened byte string. function _flatten(bytes[] memory _list) private pure returns (bytes memory out_) { if (_list.length == 0) { return new bytes(0); } uint256 len; uint256 i = 0; for (; i < _list.length; i++) { len += _list[i].length; } out_ = new bytes(len); uint256 flattenedPtr; assembly { flattenedPtr := add(out_, 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; } } }