Skip to content

Conversation

Amxx
Copy link
Collaborator

@Amxx Amxx commented Aug 25, 2025

Mentionned in https://github.com/OpenZeppelin/openzeppelin-contracts/pull/5680/files#diff-0399261b2cd954212dda0147b86044380b879ab21e1d1b89c564cb48f3552216R217

See #5680

PR Checklist

  • Tests
  • Documentation
  • Changeset entry (run npx changeset add)

Summary by Sourcery

Introduce a new concat function in the Bytes library to merge multiple byte buffers into a single bytes value, with comprehensive tests and documentation.

New Features:

  • Add Bytes.concat function to concatenate an array of byte buffers into one bytes object.

Documentation:

  • Add NatSpec documentation for the new Bytes.concat function.

Tests:

  • Add unit tests for concat covering empty arrays, single-item arrays, and multiple-item arrays.

Chores:

  • Add changeset entry for a minor version bump.

@Amxx Amxx added this to the 5.5 milestone Aug 25, 2025
@Amxx Amxx requested a review from a team as a code owner August 25, 2025 13:59
Copy link

changeset-bot bot commented Aug 25, 2025

🦋 Changeset detected

Latest commit: bbda17f

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 Minor

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

Copy link
Contributor

sourcery-ai bot commented Aug 25, 2025

Reviewer's Guide

Introduces a new Bytes.concat function to merge an array of byte buffers into a single bytes object, along with targeted unit tests and a changeset entry.

Class diagram for the updated Bytes library with concat function

classDiagram
    class Bytes {
        <<library>>
        +concat(buffers: bytes[]) bytes
        +equal(a: bytes, b: bytes) bool
        ...
    }
Loading

Flow diagram for Bytes.concat function logic

flowchart TD
    A[Start] --> B[Initialize length = 0]
    B --> C[Loop over buffers to sum lengths]
    C --> D[Create result bytes of total length]
    D --> E[Set offset = 0x20]
    E --> F[Loop over buffers]
    F --> G[Copy buffer into result at offset]
    G --> H[Increment offset by buffer length]
    H --> I{All buffers processed?}
    I -- No --> F
    I -- Yes --> J[Return result]
    J --> K[End]
Loading

File-Level Changes

Change Details Files
Implement Bytes.concat utility
  • Sum lengths of all input buffers
  • Allocate a new bytes array of the combined length
  • Copy each input into the result using an assembly mcopy loop
contracts/utils/Bytes.sol
Add unit tests for concat
  • Verify empty array returns zero bytes
  • Verify single item returns itself
  • Verify multiple items match ethers.concat
test/utils/Bytes.test.js
Add changeset entry for concat
  • Document the new minor release for Bytes.concat
.changeset/old-memes-dress.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • Consider wrapping the offset += input.length arithmetic inside an unchecked block to avoid redundant overflow checks and save gas.
  • Add a test case where some buffers are empty and others are non‐empty (e.g. interleaved zero‐length elements) to ensure correct concatenation behavior in edge cases.
  • Double‐check that the mcopy memory‐safe assembly macro is supported on all targeted Solidity compiler versions and consider providing a fallback memcpy implementation if not.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider wrapping the `offset += input.length` arithmetic inside an `unchecked` block to avoid redundant overflow checks and save gas.
- Add a test case where some buffers are empty and others are non‐empty (e.g. interleaved zero‐length elements) to ensure correct concatenation behavior in edge cases.
- Double‐check that the `mcopy` memory‐safe assembly macro is supported on all targeted Solidity compiler versions and consider providing a fallback memcpy implementation if not.

## Individual Comments

### Comment 1
<location> `contracts/utils/Bytes.sol:134` </location>
<code_context>
+    /**
+     * @dev Concatenate an array of bytes into a single bytes object.
+     *
+     * For fixed bytes types, we recommand using the solidity built-in `bytes.concat` or (equivalent)
+     *`abi.encodePacked`.
+     *
</code_context>

<issue_to_address>
Typo in documentation: 'recommand' should be 'recommend'.

Fixing the typo enhances documentation quality.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
     * For fixed bytes types, we recommand using the solidity built-in `bytes.concat` or (equivalent)
     *`abi.encodePacked`.
=======
     * For fixed bytes types, we recommend using the solidity built-in `bytes.concat` or (equivalent)
     *`abi.encodePacked`.
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
ernestognw
ernestognw previously approved these changes Aug 25, 2025
Co-authored-by: Ernesto García <[email protected]>
Copy link
Member

@ernestognw ernestognw left a comment

Choose a reason for hiding this comment

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

Looks good.

I initially thought about this function to be in Arrays mimicking the Javascript's API for flatten. If you guys think this is a good approach, then it may be worth exploring.

@Amxx
Copy link
Collaborator Author

Amxx commented Aug 26, 2025

Javascript's flatten is "Array of Array => Array", which makes sens in the case of javascript because Arrays can contain multiple different types.

IMO, Buffer or Strings would be a better match to solidity's bytes than Array. In javascript, Array.flat does NOT merge strings or buffer, which would mean that this concat function (equivalent to ethers.concat) doesn't really match Array.flat.

Also IMO, this "bytes concatenate" function is more bytes specific then it is array specific. I guess that can be disputed, but I'm not sure it would make sens to have a similar function for arrays of uint256, or arrays of bytes32. It would make sens to have something similar for arrays of strings, but I would put that in Strings.sol, not in Array.sol.

@ernestognw ernestognw merged commit 005c9c9 into OpenZeppelin:master Aug 26, 2025
20 checks passed
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.

3 participants