Skip to content

Conversation

@tr-Kalyan
Copy link

Description

This PR adds comprehensive test coverage for the decodeBatch error cases in ERC7579Utils, improving code coverage from 92.31% to 100%.

Changes

  • Added 5 new test cases covering malformed calldata scenarios:
    • Empty executionCalldata
    • executionCalldata shorter than 32 bytes
    • Array offset pointing outside buffer bounds
    • Array length exceeding uint64 max
    • Buffer too small for array element pointers

Fixes

Fixes #5395 (improves coverage for the sanity checks added in PR #5400)

Testing

  • All 7549 tests pass
  • Coverage for draft-ERC7579Utils.sol: 100%

@tr-Kalyan tr-Kalyan requested a review from a team as a code owner January 1, 2026 17:02
@changeset-bot
Copy link

changeset-bot bot commented Jan 1, 2026

🦋 Changeset detected

Latest commit: f6c1d8d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
openzeppelin-solidity Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 1, 2026

Walkthrough

A new test suite "decodeBatch error cases" has been added to validate error handling in the decodeBatch function. The test suite covers multiple scenarios including empty calldata, calldata that is too short, buffer offsets outside the valid range, lengths exceeding the uint64 maximum value, and cases where the buffer is insufficient to accommodate array element pointers. Each scenario verifies that decodeBatch reverts with an ERC7579DecodingError.

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding decodeBatch error case coverage for ERC7579Utils, which is the core objective of this PR.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the new test coverage being added for decodeBatch error cases and linking to the relevant issue.
Linked Issues check ✅ Passed The PR adds five new test cases covering the malformed calldata scenarios identified in issue #5395, validating that decodeBatch properly reverts with ERC7579DecodingError for various error conditions including empty calldata, truncated calldata, out-of-bounds offsets, length exceeding uint64 max, and insufficient buffer for pointers.
Out of Scope Changes check ✅ Passed All changes are in-scope test additions for draft-ERC7579Utils.test.js, directly addressing the error case coverage requirements specified in issue #5395 with no extraneous modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a83d9aa and 01eb334.

📒 Files selected for processing (1)
  • test/account/utils/draft-ERC7579Utils.test.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Redirect rules - solidity-contracts
  • GitHub Check: Header rules - solidity-contracts
  • GitHub Check: Pages changed - solidity-contracts
  • GitHub Check: tests
  • GitHub Check: tests-foundry
  • GitHub Check: slither
  • GitHub Check: coverage
  • GitHub Check: tests-upgradeable
  • GitHub Check: halmos

Comment on lines 401 to 439
describe('decodeBatch error cases', function () {
it('reverts when executionCalldata is empty', async function () {
const emptyData = '0x';
await expect(this.utils.$decodeBatch(emptyData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
});

it('reverts when executionCalldata is too short', async function () {
const shortData = '0x' + '00'.repeat(16);
await expect(this.utils.$decodeBatch(shortData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
});

it('reverts when array offset points outside buffer', async function () {
const malformedData = ethers.toBeHex(0x100, 32);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});

it('reverts when array length exceeds uint64 max', async function () {
const offsetData = ethers.toBeHex(32, 32);
const tooLargeLength = ethers.toBeHex(ethers.toBigInt('0xFFFFFFFFFFFFFFFF') + 1n, 32);
const malformedData = offsetData + tooLargeLength.slice(2);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});

it('reverts when buffer too small for array element pointers', async function () {
const offsetData = ethers.toBeHex(32, 32);
const arrayLength = ethers.toBeHex(10, 32);
const malformedData = offsetData + arrayLength.slice(2);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Test suite missing fixture setup.

This test suite is defined outside the main 'ERC7579Utils' describe block (which ends at line 399), so it doesn't have access to the beforeEach fixture that initializes this.utils (lines 29-31). All five tests will fail because this.utils is undefined.

🔎 Proposed fix

Option 1 (Recommended): Nest the test suite inside the main describe block

Move this test suite inside the main 'ERC7579Utils' describe block before line 399:

     });
   });
+
+  describe('decodeBatch error cases', function () {
+    it('reverts when executionCalldata is empty', async function () {
+      const emptyData = '0x';
+      await expect(this.utils.$decodeBatch(emptyData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
+    });
+
+    it('reverts when executionCalldata is too short', async function () {
+      const shortData = '0x' + '00'.repeat(16);
+      await expect(this.utils.$decodeBatch(shortData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
+    });
+
+    it('reverts when array offset points outside buffer', async function () {
+      const malformedData = ethers.toBeHex(0x100, 32);
+      await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
+        this.utils,
+        'ERC7579DecodingError',
+      );
+    });
+
+    it('reverts when array length exceeds uint64 max', async function () {
+      const offsetData = ethers.toBeHex(32, 32);
+      const tooLargeLength = ethers.toBeHex(ethers.toBigInt('0xFFFFFFFFFFFFFFFF') + 1n, 32);
+      const malformedData = offsetData + tooLargeLength.slice(2);
+      await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
+        this.utils,
+        'ERC7579DecodingError',
+      );
+    });
+
+    it('reverts when buffer too small for array element pointers', async function () {
+      const offsetData = ethers.toBeHex(32, 32);
+      const arrayLength = ethers.toBeHex(10, 32);
+      const malformedData = offsetData + arrayLength.slice(2);
+      await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
+        this.utils,
+        'ERC7579DecodingError',
+      );
+    });
+  });
 });
-
-describe('decodeBatch error cases', function () {
-  it('reverts when executionCalldata is empty', async function () {
-    const emptyData = '0x';
-    await expect(this.utils.$decodeBatch(emptyData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
-  });
-
-  it('reverts when executionCalldata is too short', async function () {
-    const shortData = '0x' + '00'.repeat(16);
-    await expect(this.utils.$decodeBatch(shortData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
-  });
-
-  it('reverts when array offset points outside buffer', async function () {
-    const malformedData = ethers.toBeHex(0x100, 32);
-    await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
-      this.utils,
-      'ERC7579DecodingError',
-    );
-  });
-
-  it('reverts when array length exceeds uint64 max', async function () {
-    const offsetData = ethers.toBeHex(32, 32);
-    const tooLargeLength = ethers.toBeHex(ethers.toBigInt('0xFFFFFFFFFFFFFFFF') + 1n, 32);
-    const malformedData = offsetData + tooLargeLength.slice(2);
-    await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
-      this.utils,
-      'ERC7579DecodingError',
-    );
-  });
-
-  it('reverts when buffer too small for array element pointers', async function () {
-    const offsetData = ethers.toBeHex(32, 32);
-    const arrayLength = ethers.toBeHex(10, 32);
-    const malformedData = offsetData + arrayLength.slice(2);
-    await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
-      this.utils,
-      'ERC7579DecodingError',
-    );
-  });
-});

Option 2: Add fixture setup to the new describe block

 describe('decodeBatch error cases', function () {
+  beforeEach(async function () {
+    Object.assign(this, await loadFixture(fixture));
+  });
+
   it('reverts when executionCalldata is empty', async function () {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
describe('decodeBatch error cases', function () {
it('reverts when executionCalldata is empty', async function () {
const emptyData = '0x';
await expect(this.utils.$decodeBatch(emptyData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
});
it('reverts when executionCalldata is too short', async function () {
const shortData = '0x' + '00'.repeat(16);
await expect(this.utils.$decodeBatch(shortData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
});
it('reverts when array offset points outside buffer', async function () {
const malformedData = ethers.toBeHex(0x100, 32);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});
it('reverts when array length exceeds uint64 max', async function () {
const offsetData = ethers.toBeHex(32, 32);
const tooLargeLength = ethers.toBeHex(ethers.toBigInt('0xFFFFFFFFFFFFFFFF') + 1n, 32);
const malformedData = offsetData + tooLargeLength.slice(2);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});
it('reverts when buffer too small for array element pointers', async function () {
const offsetData = ethers.toBeHex(32, 32);
const arrayLength = ethers.toBeHex(10, 32);
const malformedData = offsetData + arrayLength.slice(2);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});
});
describe('decodeBatch error cases', function () {
it('reverts when executionCalldata is empty', async function () {
const emptyData = '0x';
await expect(this.utils.$decodeBatch(emptyData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
});
it('reverts when executionCalldata is too short', async function () {
const shortData = '0x' + '00'.repeat(16);
await expect(this.utils.$decodeBatch(shortData)).to.be.revertedWithCustomError(this.utils, 'ERC7579DecodingError');
});
it('reverts when array offset points outside buffer', async function () {
const malformedData = ethers.toBeHex(0x100, 32);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});
it('reverts when array length exceeds uint64 max', async function () {
const offsetData = ethers.toBeHex(32, 32);
const tooLargeLength = ethers.toBeHex(ethers.toBigInt('0xFFFFFFFFFFFFFFFF') + 1n, 32);
const malformedData = offsetData + tooLargeLength.slice(2);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});
it('reverts when buffer too small for array element pointers', async function () {
const offsetData = ethers.toBeHex(32, 32);
const arrayLength = ethers.toBeHex(10, 32);
const malformedData = offsetData + arrayLength.slice(2);
await expect(this.utils.$decodeBatch(malformedData)).to.be.revertedWithCustomError(
this.utils,
'ERC7579DecodingError',
);
});
});
🤖 Prompt for AI Agents
test/account/utils/draft-ERC7579Utils.test.js around lines 401-439: the
"decodeBatch error cases" describe is defined outside the main "ERC7579Utils"
describe (which ends at line 399) so this suite lacks the beforeEach fixture
that sets this.utils and will fail; fix by moving the entire describe block into
the main "ERC7579Utils" describe (i.e., place it before line 399 inside that
outer block) so it inherits the existing beforeEach, or alternatively add a
local beforeEach in this describe that initializes this.utils the same way as
the outer fixture.

@tr-Kalyan
Copy link
Author

The EIP-7702 test failures appear unrelated to this PR.

  • This PR only modifies test/account/utils/draft-ERC7579Utils.test.js
  • Failures are in AccountEIP7702WithModules tests (test/helpers/erc4337.js:202)
  • Error is "leading zero" in Hardhat transaction encoding

All ERC7579Utils tests pass (including the 5 new error case tests).

Happy to help debug if these failures are somehow related to my changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Possible issue in ERC7579Utils.decodeBatch

1 participant