From f43725722bd4a199095a6e4c39f75255954334a2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 13 Nov 2025 20:14:59 +0100 Subject: [PATCH 1/3] Test encoding of addresses with leading zeros --- test/utils/RLP.test.js | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/test/utils/RLP.test.js b/test/utils/RLP.test.js index c4d3dda7209..cfceb49a306 100644 --- a/test/utils/RLP.test.js +++ b/test/utils/RLP.test.js @@ -2,6 +2,7 @@ const { ethers } = require('hardhat'); const { expect } = require('chai'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +const { product } = require('../helpers/iterate'); const { generators } = require('../helpers/random'); async function fixture() { @@ -33,11 +34,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 heading 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 () { @@ -146,4 +151,23 @@ 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, ethers.MaxUint256], + )) { + await expect( + this.mock + .$encode_list([this.mock.$encode_address(from), this.mock.$encode_uint256(nonce)]) + .then(encoded => ethers.keccak256(encoded)) // hash the encoded content + .then(hash => ethers.dataSlice(hash, 12)) // take the last 20 bytes + .then(ethers.getAddress), // format as (checksummed) address + ).to.eventually.equal(ethers.getCreateAddress({ from, nonce })); + } + }); }); From 60b70b7b9c5c275691167ae87e33ec5d181896cc Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 14 Nov 2025 10:12:47 +0100 Subject: [PATCH 2/3] Update test/utils/RLP.test.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- test/utils/RLP.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/RLP.test.js b/test/utils/RLP.test.js index cfceb49a306..8a5138aa230 100644 --- a/test/utils/RLP.test.js +++ b/test/utils/RLP.test.js @@ -36,7 +36,7 @@ describe('RLP', function () { it('encode/decode addresses', async function () { for (const addr of [ ethers.ZeroAddress, // zero address - '0x0000F90827F1C53a10cb7A02335B175320002935', // address with heading zeros + '0x0000F90827F1C53a10cb7A02335B175320002935', // address with leading zeros generators.address(), // random address ]) { const expected = ethers.encodeRlp(addr); From 6a86e313e8013a0e40721f962293b2e943c270c1 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 14 Nov 2025 10:23:53 +0100 Subject: [PATCH 3/3] add fuzzing test --- test/utils/RLP.t.sol | 9 +++++++++ test/utils/RLP.test.js | 7 +++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/test/utils/RLP.t.sol b/test/utils/RLP.t.sol index 7bc40d6024d..3a4db866616 100644 --- a/test/utils/RLP.t.sol +++ b/test/utils/RLP.t.sol @@ -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) + ); + } } diff --git a/test/utils/RLP.test.js b/test/utils/RLP.test.js index cfceb49a306..a06e5a38382 100644 --- a/test/utils/RLP.test.js +++ b/test/utils/RLP.test.js @@ -2,6 +2,7 @@ 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'); @@ -159,14 +160,12 @@ describe('RLP', function () { '0x0000F90827F1C53a10cb7A02335B175320002935', // address with heading zeros generators.address(), // random address ], - [0n, 1n, 42n, 65535n, ethers.MaxUint256], + [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.keccak256(encoded)) // hash the encoded content - .then(hash => ethers.dataSlice(hash, 12)) // take the last 20 bytes - .then(ethers.getAddress), // format as (checksummed) address + .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 })); } });