You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
What other implementations and functionality do you think would be useful to add to Compose #8
Nice that you are evolving the diamond standard foundation. I used diamond standard for my project so I will add some ideas to the wishlist comig out of my own frustration. Not sure if in scope, just commenting to see if someone meet the ends. The particular features of diamond standard can provide tools that cannot be obtained with traditional solidity contracts.
(1) Confidence. There is a big problem with investors now: they lost confidence. 99.99% of projects are rug pulls or scam. As a consequence there are not new launchs, not ICOs and mostly the utility token market is abandoned in favour of stablecoins and RWA. No trust to retail issuers anymore. I think there is room for technology to offer some scam-proof standards that brings confidence in tokens back to investors. One direct application of this view could be some kind of reputation driven tokens that could help issuers to fund those projects that VCs do not want to fund based, e.g. on commissions on transactions. One project that I came across and looks to follows this appraoch is : https://bags.fm/how-it-works
(2) Interoperability. In partcular for ERC-20 tokens. There is not a good solution to move liquidity across networks unless you use messaging or bridges and have a large development team. I do not specially like the approaches of Wormhole, LayerZero or Axelar as in all cases you depend on a third party. Maybe something could be done considering the espcial storage features of diamond standard.
(3) Composition. I think should be possible to compose ERC-20 tokens and create an algebra of currencies, including merges, splits, migrations.... taking advantage of the diamond standard again, in particular the storage layer.
(4) Version control. As diamond is a proxy some enhanced features should be provided to guarantee investors that they have a say over new releases.
(5) Backups and restore. Again backed on the especial storage features, it should be possible to protect against hacks by allowing to backup and restore the storage maybe with some workflow managed by the investors.
I realised of these things, and other, when using diamond standard for my project and I thought there was room for evolving the standard, instead of reinventing the wheel over and over myself.
@pellyadolfo, great ideas, I appreciate these and I hope Compose and diamonds are used to implement and solve these things.
You gave me a good idea. Compose is creating sets of standard facets like ERC20, ERC721 and other functionality. It would be easy to detect if a particular project's diamond contract only used Compose deployed facets and if the diamond was immutable. And if that was the case, the project's diamond only used Compose deployed facets and was immutable, then it would be impossible to rug pull the projects at the smart contract level, since none of facets that Compose deploys has backdoor or rugpull functions. So this becomes a very easy way, which can be easily automated, to check if a project's diamond contract has no possibility of rugpull at the smart contract level.
I imagine access control implementations would be extremely useful. One pattern that I've used quite a bit with compositional SC design is mimicking auth modifiers with a guard clause -> add assert-prefixed functions (making them very explicit and readable) that go into the target function body. In the Compose context, users would use it like this:
function foo() external {
LibOwnable.assertOnlyOwner();
// do something
}
function bar() external {
LibAccessControl.assertOnlyRole(ROLE_ID);
// do something
}
To make this even more readable, we could do assertCallerIsOwner and assertCallerHasRole
@andrew-fleming I completely agree. I like that "assert" style naming you suggest. I made a new issue for implementing ownership and role based facets here: #14 Do you want to work on this and/or oversee the development of this?
The assert prefix seems to me like it ought to be require, otherwise it might encroach on the assert space that is used already for "this should never fail" (e.g. assert, Foundry's assert* functions)
i guess the "assert" will just be a prefix in the compose library role functions? so it could be called like compose.assertOwner(), or im wrong ?? if yes, then that shouldn't be an issue @0xlynett
@mudgen In my understanding, Diamond Standard brings a huge value over plain solidity which is decoupling storage from logic, and decoupling pieces of logic of each other, so they can be created and updated separately.
There is a big benefit on this. For the first time, developer and issuer does not need to be the same actor. Developers can contribute their functionalities as facets and deploy them on blockchains. Later on, issuers can build their contracts by assembling existing facets. If done properly (some standards are needed I think) this can bring back confidence to investors since token issuers would be using trusted contracts wth known behaviour.
I built my project extending this idea (which, in fact, is offered by adopting Diamind Standard itself), so I am very excited to see how this idea can be standarized. If there is a consensus adoption would be much higher.
So, yes, I see a big business value on facets composition and I am interested.
i guess the "assert" will just be a prefix in the compose library role functions? so it could be called like compose.assertOwner(), or im wrong ?? if yes, then that shouldn't be an issue
@iconthegreat yeah it's perfectly valid solidity but it just feels wrong to me to call it an assert
We definitely need to be very very careful when it comes to designing stuff like a payment streamer because unexpected behavior can pop up in the cracks where all the facets are composed together.
For example, if a payment streaming facet has a function to sweep any unaccounted assets while a different facet collects some money for something else, that money could be stolen. Or if we don't account for the possibility of a balance shortfall, we could brick a payment stream if a different facet is using that money for something else
Also, when implementing a facet such as token voting, if we want the ability for an entire dapp to be contained in one address, the facets should be designed to support multiple instances (not sure if this is the best word) An example would be that a hypothetical token voting facet should support the creation of multiple different governance bodies to avoid having to install TokenVotingSubDAO1Facet, TokenVotingSubDAO2Facet, etc...
Defined naming conventions and API standardization.
I'm pulling from past work on other OS frameworks across languages. Defining naming conventions and project structure early, and clearly is going to save a lot of trouble. And defining a standard for library usage will make it easier to use.
So far, it looks like the implicit standard is Lib, with the API standard for getting the storage struct being getStorage(). Defining that in a CONTRIBUTING.md will set that expectation.
Personally, I'd rather see a more descriptive naming convention. ERC20Repo, or ERC20Storage for example. That way the name states the purpose of the library. Otherwise, if I read Address LibERC20, and SafeERC20 I have no idea what they're supposed to be doing.
Same with project structure. Probably reuse the structure from Open Zeppelin since it's the most common. That would make it easier to adopt by reducing the learning curve.
Naming Convention proposal draft
Utils suffix - Libraries for local view/pure operations, MUST NEVER include a state change
Service suffix - Libraries for multi-step external calls. MUST NEVER include a local state change.
Repo suffix - ONLY local state load and atomic write operations.
While you expect users to reuse the deployed facets, they should import and use the storage libraries to make sure they're using the same slot and layout.
Also, I notice the current storage libraries wrap the getStorage() with the atomic logic. Burying the storage load is helpful so developers don't need to remember how to load the storage pointer bound to the correct struct. But when reusing these functions in other facets, that means there's redundant gas spent on the memory for the pointer, and the assignment operation.
I'd like to see either an explicit statement that this is intended, and why the redundant cost is preferable. Or, a standard similar to how the OZ EnumerableSet is implemented, where it accepts the storage pointer as the first argument. I know you don't want to use type binding for the libraries. But providing the option to pass a loaded pointer gives users the option to avoid the additional gas cost.
Example use case, I want to make a custom facet to use with the existing ERC20Facet. I need to include an atomic burn then mint operation. Currently, I'd either have to accept the current LibERC20 will load the ERC20Storage pointer twice, or reimplement the logic myself; introducing the risk of an error.
Example, where do I get the correct ERC20 interface for calling proxies that use the ERC20Facet?
Yeah, implicit answer is to make your own, or just reuse any existing interface from another framework.
But for a framework you intend others to adopt, that should be clearly stated. Is there a standard compatibility target? Can I expect ERC20Facet to match the ERC definition? What can I expect when the ERC changes, like when the ERC20 metadata changed from being an optional extension to mandated inclusion? Or will it target the interface implementations from an existing framework, like Open Zeppelin? Can I expect a new facet to be deployed when a ERC changes?
When should I expect a facet for a new ERC? Don't mean a date, or timeframe, but what about while a EIP is in review? Can I expect facets for pending ERCs? What will happen when it moves our of review and is ratified?
What is the project's position on designating canonical deployments? Is there going to be a list of addresses for the deployed facets? If so, what's the process for getting a facet added to the list? Is that even possible, or will the project only list what it deploys itself?
How much control is the project willing to exert over these deployments?
A potential huge benefit would be using CREATE3 and salting convention so developers can find the same facet across chains. But, that will require an approval and deployment process using a restricted factory to avoid malicious users "stealing" an address if this project were to reuse the existing CREATE3 factory.
And, if this project will go that far, maybe adopting a ERC1820 type registry to declare the project canonical facet addresses. Maybe to include deploying the storage libraries so users can use the deployed address to get the library source code, and know they're getting the actual library being used in a canonical facet.
Storage namespace convention. There's an implicit one , since you've been consistent in your implementation. A defined standard will help.
Proposal example
Must begin with compose for libraries directly in this project.
Must include the relevant EIP type and number for any that implement a specification.
Must include a reference to originating project for non-standard, but still conventional implementations.
Must include a version number to ensure future modifications only overlap when desired.
Can I get clarification on whether I can violate the no inheritance, no interfaces, and using for rules regarding tests?
I made a declarative test framework for standard interfaces. The DiamondLoupe behavior is here.
The purpose is to simplify testing Diamond proxies by letting you deploy the proxy, declare/record your expectations, and let the Behavior validate conformity. For example, that one solves the complication of making sure all the Diamond Loupe functions return values consistent across the functions.
I have some more/better implementations for other standards in other repos I could port over.
If you consider this sort of thing in-scope for this project, and provide me directive so I know what, if anything, I would need to refactor I can port them over to Compose.
Activity
pellyadolfo commentedon Oct 18, 2025
Nice that you are evolving the diamond standard foundation. I used diamond standard for my project so I will add some ideas to the wishlist comig out of my own frustration. Not sure if in scope, just commenting to see if someone meet the ends. The particular features of diamond standard can provide tools that cannot be obtained with traditional solidity contracts.
(1) Confidence. There is a big problem with investors now: they lost confidence. 99.99% of projects are rug pulls or scam. As a consequence there are not new launchs, not ICOs and mostly the utility token market is abandoned in favour of stablecoins and RWA. No trust to retail issuers anymore. I think there is room for technology to offer some scam-proof standards that brings confidence in tokens back to investors. One direct application of this view could be some kind of reputation driven tokens that could help issuers to fund those projects that VCs do not want to fund based, e.g. on commissions on transactions. One project that I came across and looks to follows this appraoch is : https://bags.fm/how-it-works
(2) Interoperability. In partcular for ERC-20 tokens. There is not a good solution to move liquidity across networks unless you use messaging or bridges and have a large development team. I do not specially like the approaches of Wormhole, LayerZero or Axelar as in all cases you depend on a third party. Maybe something could be done considering the espcial storage features of diamond standard.
(3) Composition. I think should be possible to compose ERC-20 tokens and create an algebra of currencies, including merges, splits, migrations.... taking advantage of the diamond standard again, in particular the storage layer.
(4) Version control. As diamond is a proxy some enhanced features should be provided to guarantee investors that they have a say over new releases.
(5) Backups and restore. Again backed on the especial storage features, it should be possible to protect against hacks by allowing to backup and restore the storage maybe with some workflow managed by the investors.
I realised of these things, and other, when using diamond standard for my project and I thought there was room for evolving the standard, instead of reinventing the wheel over and over myself.
mudgen commentedon Oct 18, 2025
@pellyadolfo, great ideas, I appreciate these and I hope Compose and diamonds are used to implement and solve these things.
You gave me a good idea. Compose is creating sets of standard facets like ERC20, ERC721 and other functionality. It would be easy to detect if a particular project's diamond contract only used Compose deployed facets and if the diamond was immutable. And if that was the case, the project's diamond only used Compose deployed facets and was immutable, then it would be impossible to rug pull the projects at the smart contract level, since none of facets that Compose deploys has backdoor or rugpull functions. So this becomes a very easy way, which can be easily automated, to check if a project's diamond contract has no possibility of rugpull at the smart contract level.
andrew-fleming commentedon Oct 19, 2025
I imagine access control implementations would be extremely useful. One pattern that I've used quite a bit with compositional SC design is mimicking auth modifiers with a guard clause -> add
assert-prefixed functions (making them very explicit and readable) that go into the target function body. In the Compose context, users would use it like this:To make this even more readable, we could do
assertCallerIsOwnerandassertCallerHasRolemudgen commentedon Oct 19, 2025
@andrew-fleming I completely agree. I like that "assert" style naming you suggest. I made a new issue for implementing ownership and role based facets here: #14 Do you want to work on this and/or oversee the development of this?
0xlynett commentedon Oct 19, 2025
The assert prefix seems to me like it ought to be require, otherwise it might encroach on the assert space that is used already for "this should never fail" (e.g. assert, Foundry's assert* functions)
iconthegreat commentedon Oct 19, 2025
i guess the "assert" will just be a prefix in the compose library role functions? so it could be called like compose.assertOwner(), or im wrong ?? if yes, then that shouldn't be an issue @0xlynett
pellyadolfo commentedon Oct 19, 2025
@mudgen In my understanding, Diamond Standard brings a huge value over plain solidity which is decoupling storage from logic, and decoupling pieces of logic of each other, so they can be created and updated separately.
There is a big benefit on this. For the first time, developer and issuer does not need to be the same actor. Developers can contribute their functionalities as facets and deploy them on blockchains. Later on, issuers can build their contracts by assembling existing facets. If done properly (some standards are needed I think) this can bring back confidence to investors since token issuers would be using trusted contracts wth known behaviour.
I built my project extending this idea (which, in fact, is offered by adopting Diamind Standard itself), so I am very excited to see how this idea can be standarized. If there is a consensus adoption would be much higher.
So, yes, I see a big business value on facets composition and I am interested.
mudgen commentedon Oct 19, 2025
@pellyadolfo Yes, absolutely! I really love your description. I posted your comment on X.com because I want more people to know what we are building. https://x.com/mudgen/status/1979972585380106698
andrew-fleming commentedon Oct 19, 2025
@mudgen The dev fam is already on it :) but yeah, happy to help oversee development if everything's being worked on
0xlynett commentedon Oct 19, 2025
@iconthegreat yeah it's perfectly valid solidity but it just feels wrong to me to call it an assert
0xlynett commentedon Oct 19, 2025
We definitely need to be very very careful when it comes to designing stuff like a payment streamer because unexpected behavior can pop up in the cracks where all the facets are composed together.
For example, if a payment streaming facet has a function to sweep any unaccounted assets while a different facet collects some money for something else, that money could be stolen. Or if we don't account for the possibility of a balance shortfall, we could brick a payment stream if a different facet is using that money for something else
Also, when implementing a facet such as token voting, if we want the ability for an entire dapp to be contained in one address, the facets should be designed to support multiple instances (not sure if this is the best word) An example would be that a hypothetical token voting facet should support the creation of multiple different governance bodies to avoid having to install TokenVotingSubDAO1Facet, TokenVotingSubDAO2Facet, etc...
cyotee commentedon Oct 20, 2025
Defined naming conventions and API standardization.
I'm pulling from past work on other OS frameworks across languages. Defining naming conventions and project structure early, and clearly is going to save a lot of trouble. And defining a standard for library usage will make it easier to use.
So far, it looks like the implicit standard is Lib, with the API standard for getting the storage struct being
getStorage(). Defining that in a CONTRIBUTING.md will set that expectation.Personally, I'd rather see a more descriptive naming convention. ERC20Repo, or ERC20Storage for example. That way the name states the purpose of the library. Otherwise, if I read Address LibERC20, and SafeERC20 I have no idea what they're supposed to be doing.
Same with project structure. Probably reuse the structure from Open Zeppelin since it's the most common. That would make it easier to adopt by reducing the learning curve.
Naming Convention proposal draft
While you expect users to reuse the deployed facets, they should import and use the storage libraries to make sure they're using the same slot and layout.
Also, I notice the current storage libraries wrap the
getStorage()with the atomic logic. Burying the storage load is helpful so developers don't need to remember how to load the storage pointer bound to the correct struct. But when reusing these functions in other facets, that means there's redundant gas spent on the memory for the pointer, and the assignment operation.I'd like to see either an explicit statement that this is intended, and why the redundant cost is preferable. Or, a standard similar to how the OZ EnumerableSet is implemented, where it accepts the storage pointer as the first argument. I know you don't want to use type binding for the libraries. But providing the option to pass a loaded pointer gives users the option to avoid the additional gas cost.
Example use case, I want to make a custom facet to use with the existing ERC20Facet. I need to include an atomic burn then mint operation. Currently, I'd either have to accept the current LibERC20 will load the ERC20Storage pointer twice, or reimplement the logic myself; introducing the risk of an error.
cyotee commentedon Oct 20, 2025
Clarification on how to use this repo.
Example, where do I get the correct ERC20 interface for calling proxies that use the ERC20Facet?
Yeah, implicit answer is to make your own, or just reuse any existing interface from another framework.
But for a framework you intend others to adopt, that should be clearly stated. Is there a standard compatibility target? Can I expect ERC20Facet to match the ERC definition? What can I expect when the ERC changes, like when the ERC20 metadata changed from being an optional extension to mandated inclusion? Or will it target the interface implementations from an existing framework, like Open Zeppelin? Can I expect a new facet to be deployed when a ERC changes?
When should I expect a facet for a new ERC? Don't mean a date, or timeframe, but what about while a EIP is in review? Can I expect facets for pending ERCs? What will happen when it moves our of review and is ratified?
cyotee commentedon Oct 20, 2025
What is the project's position on designating canonical deployments? Is there going to be a list of addresses for the deployed facets? If so, what's the process for getting a facet added to the list? Is that even possible, or will the project only list what it deploys itself?
How much control is the project willing to exert over these deployments?
A potential huge benefit would be using CREATE3 and salting convention so developers can find the same facet across chains. But, that will require an approval and deployment process using a restricted factory to avoid malicious users "stealing" an address if this project were to reuse the existing CREATE3 factory.
And, if this project will go that far, maybe adopting a ERC1820 type registry to declare the project canonical facet addresses. Maybe to include deploying the storage libraries so users can use the deployed address to get the library source code, and know they're getting the actual library being used in a canonical facet.
cyotee commentedon Oct 20, 2025
Storage namespace convention. There's an implicit one , since you've been consistent in your implementation. A defined standard will help.
Proposal example
Must begin with
composefor libraries directly in this project.Must include the relevant EIP type and number for any that implement a specification.
Must include a reference to originating project for non-standard, but still conventional implementations.
Must include a version number to ensure future modifications only overlap when desired.
Examples
compose.erc20.1balancer.pool.weighted.1cyotee commentedon Oct 20, 2025
And specific for my own potential contributions.
Can I get clarification on whether I can violate the no inheritance, no interfaces, and
using forrules regarding tests?I made a declarative test framework for standard interfaces. The DiamondLoupe behavior is here.
The purpose is to simplify testing Diamond proxies by letting you deploy the proxy, declare/record your expectations, and let the Behavior validate conformity. For example, that one solves the complication of making sure all the Diamond Loupe functions return values consistent across the functions.
I have some more/better implementations for other standards in other repos I could port over.
If you consider this sort of thing in-scope for this project, and provide me directive so I know what, if anything, I would need to refactor I can port them over to Compose.
cyotee commentedon Oct 20, 2025
Install Solday and Solmate so we have the utility libraries like CREATE3 and EfficientHashLib available for contributions.
Rushikesh0125 commentedon Oct 21, 2025
it would be awesome to have a collective of facets for
to support cross-chain contracts.
mudgen commentedon Oct 21, 2025
I am converting this issue into a discussion. So let's use Github discussion functionality, to discuss!