Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5800339
fix L-03
luiz-lvj Nov 13, 2025
47c8048
fix N-06
luiz-lvj Nov 13, 2025
78f643d
fix N-07
luiz-lvj Nov 13, 2025
55b33a0
fix N-09
luiz-lvj Nov 13, 2025
e0a6e6b
add changeset
luiz-lvj Nov 13, 2025
f437257
Test encoding of addresses with leading zeros
Amxx Nov 13, 2025
0ac8a92
Merge branch 'testing/rlp-encoding-addresses' into audit-fixes/RLP
Amxx Nov 13, 2025
60b70b7
Update test/utils/RLP.test.js
Amxx Nov 14, 2025
6a86e31
add fuzzing test
Amxx Nov 14, 2025
08d217f
Merge branch 'testing/rlp-encoding-addresses' of https://github.com/A…
Amxx Nov 14, 2025
5f11e08
Merge branch 'testing/rlp-encoding-addresses' into audit-fixes/RLP
Amxx Nov 14, 2025
1992bed
revert back to address
luiz-lvj Nov 14, 2025
be65eb8
remove changeset
luiz-lvj Nov 14, 2025
8f2dd16
update changeset
luiz-lvj Nov 14, 2025
61b695f
Solve: Inconsistent Integer Base in Inline Assembly When Setting RLP …
luiz-lvj Nov 14, 2025
fe917b7
test string qith leading zeros
luiz-lvj Nov 14, 2025
3cea1c4
Delete .changeset/slow-lemons-rest.md
ernestognw Nov 24, 2025
2088e8e
Merge branch 'master' into audit-fixes/RLP
ernestognw Nov 24, 2025
e27858d
Update numeric literals for guidelines consistency
ernestognw Nov 24, 2025
dd70a70
Update numeric literals for guidelines consistency
luiz-lvj Nov 25, 2025
88d7ab3
Merge branch 'master' into audit-fixes/RLP
luiz-lvj Nov 25, 2025
9116839
Merge branch 'master' into audit-fixes/RLP
luiz-lvj Nov 27, 2025
f31d743
Merge branch 'master' into audit-fixes/RLP
luiz-lvj Dec 1, 2025
b1899ae
Merge branch 'master' into audit-fixes/RLP
luiz-lvj Dec 1, 2025
6cb5180
Improve NatSpec around canonical and non-canonical representations. A…
ernestognw Dec 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/slow-lemons-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

clarify natspec for encode functions on RLP library
25 changes: 16 additions & 9 deletions contracts/utils/RLP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,22 @@ library RLP {
function encode(bool input) internal pure returns (bytes memory result) {
assembly ("memory-safe") {
result := mload(0x40)
mstore(result, 0x01) // length of the encoded data: 1 byte
mstore(result, 1) // length of the encoded data: 1 byte
mstore8(add(result, 0x20), add(mul(iszero(input), 0x7f), 1)) // input
mstore(0x40, add(result, 0x21)) // reserve memory
}
}

/// @dev Encode an address as RLP.
/**
* @dev Encode an address as RLP.
*
* The address is encoded with its leading zeros (if it has any). If someone wants to encode the address as a scalar,
* they can cast it to an uint256 and then call the corresponding {encode} function.
*/
function encode(address input) internal pure returns (bytes memory result) {
assembly ("memory-safe") {
result := mload(0x40)
mstore(result, 0x15) // length of the encoded data: 1 (prefix) + 0x14 (address)
mstore(result, 21) // length of the encoded data: 1 (prefix) + 0x14 (address)
mstore(add(result, 0x20), or(shl(248, 0x94), shl(88, input))) // prefix (0x94 = SHORT_OFFSET + 0x14) + input
mstore(0x40, add(result, 0x35)) // reserve memory
}
Expand Down Expand Up @@ -167,13 +172,17 @@ library RLP {
return encode(bytes(input));
}

/// @dev Encode an array of bytes as RLP.
/**
* @dev Encode an array of bytes as RLP.
* This function expects an array of already encoded bytes, not raw bytes.
* Users should call {encode} on each element of the array before calling it.
*/
function encode(bytes[] memory input) internal pure returns (bytes memory) {
return _encode(input.concat(), LONG_OFFSET);
}

/// @dev Encode an encoder (list of bytes) as RLP
function encode(Encoder memory self) internal pure returns (bytes memory result) {
function encode(Encoder memory self) internal pure returns (bytes memory) {
return _encode(self.acc.flatten(), LONG_OFFSET);
}

Expand Down Expand Up @@ -241,7 +250,7 @@ library RLP {
(uint256 offset, uint256 length, ItemType itemType) = _decodeLength(item);
require(itemType == ItemType.Data, RLPInvalidEncoding());

// Length is checked by {toBytes}
// Length is checked by {slice}
return item.slice(offset, length).toBytes();
}

Expand Down Expand Up @@ -326,9 +335,7 @@ library RLP {
* @dev Decodes an RLP `item`'s `length and type from its prefix.
* Returns the offset, length, and type of the RLP item based on the encoding rules.
*/
function _decodeLength(
Memory.Slice item
) private pure returns (uint256 _offset, uint256 _length, ItemType _itemtype) {
function _decodeLength(Memory.Slice item) private pure returns (uint256, uint256, ItemType) {
uint256 itemLength = item.length();

require(itemLength != 0, RLPInvalidEncoding());
Expand Down
9 changes: 9 additions & 0 deletions test/utils/RLP.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,13 @@ contract RLPTest is Test {

assertEq(RLP.encoder().push(b).push(a).push(u).encode(), RLP.encode(list));
}

function testComputeCreateAddress(address deployer, uint256 nonce) external pure {
nonce = bound(nonce, 0, type(uint64).max);

assertEq(
address(uint160(uint256(keccak256(RLP.encoder().push(deployer).push(nonce).encode())))),
vm.computeCreateAddress(deployer, nonce)
);
}
}
34 changes: 29 additions & 5 deletions test/utils/RLP.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');

const { MAX_UINT64 } = require('../helpers/constants');
const { product } = require('../helpers/iterate');
const { generators } = require('../helpers/random');

async function fixture() {
Expand Down Expand Up @@ -33,11 +35,15 @@ describe('RLP', function () {
});

it('encode/decode addresses', async function () {
const addr = generators.address();
const expected = ethers.encodeRlp(addr);

await expect(this.mock.$encode_address(addr)).to.eventually.equal(expected);
await expect(this.mock.$decodeAddress(expected)).to.eventually.equal(addr);
for (const addr of [
ethers.ZeroAddress, // zero address
'0x0000F90827F1C53a10cb7A02335B175320002935', // address with leading zeros
generators.address(), // random address
]) {
const expected = ethers.encodeRlp(addr);
await expect(this.mock.$encode_address(addr)).to.eventually.equal(expected);
await expect(this.mock.$decodeAddress(expected)).to.eventually.equal(addr);
}
});

it('encode/decode uint256', async function () {
Expand Down Expand Up @@ -117,6 +123,7 @@ describe('RLP', function () {
it('encode/decode strings', async function () {
for (const input of [
'', // empty string
'000000000cat', // string with leading zeros
'dog',
'Lorem ipsum dolor sit amet, consectetur adipisicing elit',
]) {
Expand Down Expand Up @@ -146,4 +153,21 @@ describe('RLP', function () {
await expect(this.mock.$decodeBytes(input)).to.be.revertedWithCustomError(this.mock, 'RLPInvalidEncoding');
});
});

it('RLP encoder predict create addresses', async function () {
for (const [from, nonce] of product(
[
ethers.ZeroAddress, // zero address
'0x0000F90827F1C53a10cb7A02335B175320002935', // address with heading zeros
generators.address(), // random address
],
[0n, 1n, 42n, 65535n, MAX_UINT64],
)) {
await expect(
this.mock
.$encode_list([this.mock.$encode_address(from), this.mock.$encode_uint256(nonce)])
.then(encoded => ethers.getAddress(ethers.dataSlice(ethers.keccak256(encoded), 12))), // hash the encoded content, take the last 20 bytes and format as (checksummed) address
).to.eventually.equal(ethers.getCreateAddress({ from, nonce }));
}
});
});