Closed mudgen closed 2 years ago
@mudgen In this case, there would be several initializations called within a single diamondCut
. But the DiamondCut
event is designed for a single initialization. So there could be two possible scenarios:
1/ Emit a DiamondCut
for each initialization. The first event will have the _diamondCut
field as provided in the function argument, while _init
and _calldata
correspond to the first initialization call. The subsequent events will use an empty array for the _diamondCut
field.
2/ Emit a single DiamondCut
event, effectively dropping the _init
and _calldata
fields.
I am inclined to go for 2/ as I am actually not really clear of what use the _init
and _calldata
fields can be for an event listener: they do not represent a state change and do not allow to make a conclusion as to what consequence the initialization call can have.
The _init
and _calldata
parameters in the DiamondCut
contain important information in an upgrade. They tell someone what function call was made with what arguments on what contract during an upgrade.
A person who wants to know all changes made in an upgrade would look at the DiamondCut
event to see what functions were added/replaced/removed with what facets. And the person would look at the _init
and _calldata
arguments of DiamondCut
event to see what function call was made with what arguments on what contract. Then the person could look at the verified source code of the contract the function was called on to see what the function did.
Only emitting one DiamondCut
event is needed to get full information about an upgrade. This assumes that full verified source code is available for all changes made.
The DiamondCut
event enables anyone to see all changes made in an upgrade. This is done by looking at the following things for any upgrade:
DiamondCut
event.DiamondCut
event, which includes the function selectors and the contract addresses of the functions added/replaced/removed. A person can look at the verified source code for the functions added, replaced and removed.DiamondCut
event contains function call information in the _init
and _calldata
parameters. Those parameters can be used to find the verified source code of a function and contract that were called during an upgrade, if any.Any number of initiation function calls can be done in single upgrade by the function call specified in the _init
and _calldata
arguments making other function calls. Information about these calls can be found by looking at the verified source code of the function making these function calls.
@mudgen Thank you for your comment.
One point about the
DiamondCut
event is that it would not be possible to emit all the initializations data in a single event. Emitting multiple events does not seem like an elegant solution, so I would rather use by default_init
=address(0)
and_calldata
=""
. From what I can see, this is still compatible with the standard, but that would be good to get your insight on this.I am thinking to create an extension ERC for this additional cut function, would this make sense?
I am sorry, I don't really understand what is the problem you are trying to solve or how you are trying to solve it. What is initializations data to you and why can't DiamondCut
provide it or link to the function call that has it?
I am sorry, I don't really understand what is the problem you are trying to solve or how you are trying to solve it. What is initializations data to you and why can't DiamondCut provide it or link to the function call that has it?
struct Initialization {
address initContract;
bytes initData;
}
function diamondCut(
FacetCut[] calldata _diamondCut,
Initialization[] calldata _initializations
) external;
This is about doing multiple initialization calls using a single function call for an upgrade.
The
_init
and_calldata
parameters in theDiamondCut
contain important information in an upgrade. They tell someone what function call was made with what arguments on what contract during an upgrade.A person who wants to know all changes made in an upgrade would look at the
DiamondCut
event to see what functions were added/replaced/removed with what facets. And the person would look at the_init
and_calldata
arguments ofDiamondCut
event to see what function call was made with what arguments on what contract. Then the person could look at the verified source code of the contract the function was called on to see what the function did.Only emitting one
DiamondCut
event is needed to get full information about an upgrade. This assumes that full verified source code is available for all changes made.The
DiamondCut
event enables anyone to see all changes made in an upgrade. This is done by looking at the following things for any upgrade:
- Look at the verified source code for the function that emitted the
DiamondCut
event.- Look at the functions added/replaced/removed in the upgrade. This information is contained in the
DiamondCut
event, which includes the function selectors and the contract addresses of the functions added/replaced/removed. A person can look at the verified source code for the functions added, replaced and removed.- The
DiamondCut
event contains function call information in the_init
and_calldata
parameters. Those parameters can be used to find the verified source code of a function and contract that were called during an upgrade, if any.Any number of initiation function calls can be done in single upgrade by the function call specified in the
_init
and_calldata
arguments making other function calls. Information about these calls can be found by looking at the verified source code of the function making these function calls.
I would argue that the information provided by the call arguments is not definitive:
1/ The source code may not be verified 2/ The call could be made on a code that has since changed when the event is processed (for example if the target is a proxy) 3/ The logic of the initialization function may depend on a current state which could be hard to track down
Concretely, if some information is absolutely relevant during an initialization call, it would seem more legitimate that the _init
contract would be responsible for emitting a tailored event about it.
would argue that the information provided by the call arguments is not definitive:
1/ The source code may not be verified 2/ The call could be made on a code that has since changed when the event is processed (for example if the target is a proxy) 3/ The logic of the initialization function may depend on a current state which could be hard to track down
Concretely, if some information is absolutely relevant during an initialization call, it would seem more legitimate that the _init contract would be responsible for emitting a tailored event about it.
@nataouze Excellent points. I agree with you.
Maybe a snippet will help to put things in perspective. This is how I would implement the function mentioned above:
function diamondCut(FacetCut[] memory diamondCut_, Initialization[] memory initializations_) external {
cutFacets(diamondCut_);
emit DiamondCut(diamondCut_, address(0), "");
for (uint256 i = 0; i < initializations_.length; i++) {
initializationCall(initializations_[i].initContract, initializations_[i].initData);
}
}
When I read the standard, I can see that the constraints on emitting a DiamondCut
event are related to the add/replace/remove actions. Except for this sentence:
The _diamondCut, _init, and _calldata arguments are passed directly to the DiamondCut event.
But cutting a facet could be operated outside of a call to diamondCut
, so this sentence would not be relevant in every case.
But cutting a facet could be operated outside of a call to diamondCut, so this sentence would not be relevant in every case.
Yes, you are correct.
It makes sense to me how you implemented the function you showed, thanks for showing that.
It makes sense to me how you implemented the function you showed, thanks for showing that.
Thanks for bearing with me @mudgen :blush: Glad to hear from you that managing the event in this way is acceptable.
Yes, thanks for bearing with me too. Yes, it is acceptable. I am thinking it is probably a good idea to change the diamondCut
parameters in the standard to what you are using, but I'm not sure how much disruption that might cause amongst projects and tools that have already adopted the current version of diamondCut
. But anyway, it is perfectly alright for people to make their own versions of diamondCut
or other upgrade functions, as long as they emit the DiamondCut
event to show functions added/replaced/removed.
@dotc-dev Hi there. I suggest verifying each facet separately. And I suggest verifying the diamond proxy contract separate from its facets. Verifying the diamond proxy contract or facets is done the same way any other contract is verified.
I did recently talk to someone who had trouble verifying his contracts for his diamond implementation. He was able to fix it. This is what he said:
As far as I can tell, something was wrong with my hardhat repo. After trying various methods I tried to verify an empty contract and it also failed. I created a clean repo and it worked immediately. So I'm not sure what the issue was exactly. Some thing I tried before was linking the libraries which wasn't necessary, I also cleaned artifacts and cache, and tried some code changes to get it to work. It may have been a bug with hardhat.
Once you do get everything verified check that it shows up in louper.dev, which is like etherscan for diamonds.
Once you do get everything verified check that it shows up in louper.dev, which is like etherscan for diamonds.
This is a great resource thank you. However is there any chance of getting Etherscan support for diamonds?
Once you do get everything verified check that it shows up in louper.dev, which is like etherscan for diamonds.
This is a great resource thank you. However is there any chance of getting Etherscan support for diamonds?
Yes, if someone will contact the Etherscan team and ask or convince them to add support for diamonds, or if enough people ask them to do this.
Once you do get everything verified check that it shows up in louper.dev, which is like etherscan for diamonds.
This is a great resource thank you. However is there any chance of getting Etherscan support for diamonds?
Yes, if someone will contact the Etherscan team and ask or convince them to add support for diamonds, or if enough people ask them to do this.
I just contacted asking: https://etherscan.io/contactus
I also reached out to Tenderly. Their response:
Unfortunately, we are not supporting Diamond contracts. We have this in plan but this is the lowest priority and there are chances we won't implement this in foreseeable future. Sorry for bringing you the bad news 😕
Would love to get this support so everyone who wants this should also reach out to Tenderly!
There has been no activity on this issue for six months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.
Just adding my agreement that diamondCut should have multiple init addresses and data. I know I can create my own function to do this, but one of the things I work on is a generic evm deployment system, and in that case I have to stick rigidly to the spec.
@anders-torbjornsen Stick rigidly to what spec?
@anders-torbjornsen Stick rigidly to what spec?
As in my deployment tool can only call the functions defined in this spec, I have to assume that there isn't a version of diamondCut which accepts multiple init addresses and data
Okay, understood. Out of curiosity is your deployment tool hardhat-deploy? Or what is it?
It's Zem, it came out of my first few NFT projects before I knew hardhat-deploy was a thing, and so here we are hehe. Still, competition is probably not a bad thing :)
I am glad to see Zem.
I'd argue that Zem and all deployment tools should add the standard diamondCut
function to diamonds so that those diamonds can interoperate with other tools.
However Zem and any other deployment tool can also (in addition) add and use their own upgrade function in the diamonds they deploy that better suits the deployment tool. The trick will be how to still emit a standard DiamondCut event with a custom upgrade function. Emitting a standard DiamondCut event is important for tools that show the history of diamond upgrades.
I do acknowledge that a diamondCut
that accepts multiple init addresses and data makes deployment/upgrades easier in some cases, especially more automated cases.
Unfortunately the technical aspects of this standard are finalized and won't change.
However it is possible and welcomed to propose a new standard about a new diamondCut
upgrade function for diamonds that is better suited for automation.
@anders-torbjornsen I think that you can still achieve what you want to achieve with the current diamondCut
.
Here is how:
Make a single initializer function in its own contract. Make that initializer function take as arguments an array of addresses of the facets or contracts with the init functions you want to call. The second argument is an array of function calldata. So then your initializer function loops through the addresses and calldata and makes a delegatecall with each one. This achieves the same thing as having a diamondCut that takes multiple addresses and calldata.
An example of a diamond int function that calls multiple init functions is here: https://github.com/mudgen/diamond-1-hardhat/blob/main/contracts/upgradeInitializers/DiamondMultiInit.sol
Yes that's true that'd work, and I'd only need to deploy that contract once and then could use for all future deployments
Yes that's true that'd work, and I'd only need to deploy that contract once and then could use for all future deployments
Yes
This appears to be superseded by https://ethereum-magicians.org/t/discussion-for-eip2535-diamonds/10459
Further EIP2535 diamonds discussion has moved here: https://ethereum-magicians.org/t/discussion-for-eip2535-diamonds/10459
This is great.
Would be curious to see a uniswap build using the Diamond pattern.
Recently a DEX using diamonds was launched. See here: https://www.bsc.news/post/croswap-dex-launches-with-strong-volume-in-first-days
EIP-2535 Diamonds exists here: https://eips.ethereum.org/EIPS/eip-2535
Below is a feedback and discussion of the standard.