refactor: separate the logic of burn and permit from the ERC20Facet#211
Conversation
👷 Deploy request for compose-diamonds pending review.Visit the deploys page to approve it
|
Coverage Report
Last updated: Sat, 22 Nov 2025 17:11:50 GMT for commit |
Gas ReportComparing gas usage between Summary
Details
Showing top 20 changes out of 42 total. View all 42 changes
ℹ️ About this reportThis report compares gas usage between the base branch and this PR using
To run this locally: # Generate snapshot for current branch
forge snapshot
# Compare with another branch
git checkout main
forge snapshot --diff .gas-snapshotLast updated: Sat, 22 Nov 2025 17:12:16 GMT for commit |
There was a problem hiding this comment.
@akronim26 Thanks for this work! This work is really needed.
Our smallest building block for Compose is the facet. All external functions in a facet are expected to be added to a diamond. Right now it is not possible to add all the external functions in ERC20BurnFacet and all the external functions in ERC20Facet to the same diamond because they have some of the same external functions, such as balanceOf etc.
Please review the documentation for composition.
Please remove duplicate external functions among ERC20BurnFacet, ERC20Facet and ERC20PermitFacet.
The nonces variable needs to be defined in its own separate diamond storage in the ERC20PermitFacet.
I changed my mind about the order of variables for the ERC20Storage struct. Please change it to this order:
struct ERC20Storage {
mapping(address owner => uint256 balance) balanceOf;
uint256 totalSupply;
mapping(address owner => mapping(address spender => uint256 allowance)) allowances;
uint8 decimals;
string name;
string symbol;|
Thanks for reviewing @mudgen. I have a few doubts:
|
|
@akronim26 I am glad you are asking these questions to clarify these things and I appreciate it.
One of my major goals for this project is to write documentation that makes it clear to contributors how the project is designed and how to do things and how things should be. I need to keep improving the documentation until it can achieve that goal. Can you check to see if the documentation that I wrote for composing facets, the document Design for Composition, answers your above question? If there is anything unclear in the documentation, please let me know. Please also read the example given about extending facets and the summary below it. Facets are designed to be composed together, so a facet that needs to access storage that is defined in another facet (such as The
I am a lot less familiar with writing tests for Compose. Some documentation for writing tests for Compose is here: https://compose.diamonds/docs/contribution/testing. I also suggest looked at existing tests for other functionality to get ideas. I don't have a solution to the problem of lacking functions like |
|
Thanks for the clarification @mudgen.
Since the So in this facet we will have two storage structs. Regarding testing: |
|
Hi @akronim26
Yes, correct!
Inside bytes32 constant ERC20_STORAGE_POSITION = keccak256("compose.erc20");
/// The `ERC20PermitFacet` only uses the `allowances` and `name` variables inside
/// the `ERC20Storage` struct from `ERC20Facet`.
/// We cannot remove the `balanceOf`, `totalSupply`, and `decimals` variables from
/// the struct even though they aren't used. This is because we must maintain the
/// order of variables defined in structs. Only variables at the end of structs can be
/// removed. In this case there is only one variable at the end that isn't used and that
/// is the `symbol` variable so that is removed from the struct below.
/// @custom:storage-location erc8042:compose.erc20
struct ERC20Storage {
mapping(address owner => uint256 balance) balanceOf;
uint256 totalSupply;
mapping(address owner => mapping(address spender => uint256 allowance)) allowances;
uint8 decimals;
string name;
}
function getERC20Storage() internal pure returns (ERC20Storage storage s) {
bytes32 position = ERC20_STORAGE_POSITION;
assembly {
s.slot := position
}
}
bytes32 constant STORAGE_POSITION = keccak256("compose.erc20.permit");
/// @custom:storage-location erc8042:compose.erc20.permit
struct ERC20PermitStorage {
mapping(address owner => uint256) nonces;
}
function getStorage() internal pure returns (ERC20PermitStorage storage s) {
bytes32 position = STORAGE_POSITION;
assembly {
s.slot := position
}
}The above code for The The Does this make sense? |
Yes @mudgen. I'll resolve the requested changes in a few hours. |
Sounds good! |
This solution was not possible due to some functions and custom errors giving the same function selector error. Hence, a workaround, I defined only the required functions and errors in the harnesses instead of the facets. |
Summary
Fixes #207
Extracted the
burnandpermitlogic from theERC20Facetinto their separate facets. The storage position for both the facets are same asERC20Facetas they are extending the ERC20Facet functionality.Changes Made
Extracted the burn and the permit logic into separate facets and tested the facets in separate files with the help of harnesses.
Checklist
Before submitting this PR, please ensure:
Code follows the Solidity feature ban - No inheritance, constructors, modifiers, public/private variables, external library functions,
using fordirectives, orselfdestructCode follows Design Principles - Readable, uses diamond storage, favors composition over inheritance
Code matches the codebase style - Consistent formatting, documentation, and patterns (e.g. ERC20Facet.sol)
Code is formatted with
forge fmtExisting tests pass - Run tests to be sure existing tests pass.
New tests are optional - If you don't provide tests for new functionality or changes then please create a new issue so this can be assigned to someone.
All tests pass - Run
forge testand ensure everything worksDocumentation updated - If applicable, update relevant documentation
Make sure to follow the contributing guidelines.
Additional Notes