-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A smart contract hub that routes calls to various child smart contracts to bypass the 24KB bytecode limit #11102
Comments
On |
|
Thanks for the proposal! I think it should be clarified a bit more on the way to construct the hub. I guess the idea is to split the creation into multiple transactions, so that they can go over the tx size limit, right? Another fundamental question I have is about inheritance / polymorphism and the way the dispatch is implemented: If we know that the delegated contracts / modules do not have additional publicly visible functions and also no fallback functions, we can use some optimization techniques in the dispatch routine. We have to be a bit careful here, though, since whenever we use contract types in Solidity, it is perfectly fine to use a derived contract instance instead, so there can be additional functions. The only situation where we know that this is not the case is when a contract creates another contract. Furthermore, it would be very nice if this proposal could eliminate the need for fallback functions altogether. This means it would be great if this could also be used to implement contract upgrades. And finally, what do you think about specifying instances of the sub-contracts directly as in In general, I somehow feel that the main new feature this adds to the language is the dispatch, isn't it? Most of the other features can be implemented using structs and library functions. |
Every module will be deployed in a separate transaction, thus you can have as many modules as you want. The hub will be deployed in a single transaction through so there's a practical limit on the size of routing table you can fit in but that should be quite large.
I don't think we can assume that. How substantial are the optimizations mentioned here? We can consider adding these constraints but we'll then have to educate the users as well.
I thought about this a bit but couldn't find an elegant way to do it. Do you have any suggestions?
Dispatcher is the main bit but another important piece of the puzzle is namespaced storage on per module basis. Namespacing storage allows different modules to statically calculate storage slots for each other while still allowing individual modules to add items to their storage. It also prevents clashes between same name storages between different modules. |
Ok, I didn't consider that you cannot change the interface of a module on an upgrade, that would be a rather important feature. Still, maybe we can somehow allow this if there is only a single module? The optimizations I was talking about are using bloom filters. If this proposal is implemented naively, we need at least one comparison with each function signature in each module. Bloom filters would need some mathematical operation (maybe a keccak hash, but maybe also just a multiplication with a specially crafted value suffices), but only a single comparison. Such filters can have false positives, but no false negatives. If we know that the interface to the modules does not change and that every module simply reverts on mismatch, we can do this optimization. |
If there's only a single module, it defeats the purpose of having a hub/router. Having this feature won't harm anyone but I also can't see anyone using it.
The interface can technically change if the module was also a proxy or created with create2 for example but I think we can just assume that if the interface changes, the user must deploy a new hub/router or have undefined behaviour. This should be a relatively simple/natural concept to grasp for the developers if we do not allow changing addresses of modules. We can further expand on it in the documentation around this new feature.
Why is this needed? Since there's no false negative, the hub doesn't really need to care what the module does IMO. Am I missing something? |
@chriseth @maxsam4 I think upgradeability should not be considered in this proposal. Users should implement it at a higher level, for example, by using a universal proxy whose implementation is a router/hub. When one or more of its modules needs to be updated, these are deployed, a new router is constructed and deployed, and the proxy implementation is updated. At least this is how I see upgradeability for this architecture. Or am I missing something? |
People are already building on EIP-2535 Diamonds. I think EIP-2535 already solves the problems that this proposal aims to solve. I think it would be great it Solidity could automate the creation of diamonds. It is possible for EIP-2535 to be modified to better fit or include an automated Solidity solution. One thing this proposal doesn't seem to support that EIP-2535 does support is upgrades. EIP-2535 standardization helps with tooling for deployment, testing, upgrading, integration with other smart contracts and user interfaces. Here's a user interface that can be used with all diamonds: https://louper.dev/ |
I wrote a blog post about some of the reasons for the Loupe functions which provide introspection into a diamond: https://dev.to/mudgen/why-loupe-functions-for-diamonds-1kc3 Loupe functions (introspection) on diamonds is important for upgrades: Loupe functions enable software to discover/verify/validate/test/audit upgrades on diamonds, and retrieve external information about upgrades like new verified source code and ABI info. I am just mentioning this to give more data about why EIP-2535 Diamonds was designed the way it is. It is way past an idea stage. Its design has evolved a lot from experience implementing applications with it. Though EIP-2535 was published in February 2020, actual work on it began in 2018 with EIP-1538 which EIP-2535 replaced. |
Since this proposal uses The main problem is that all these solutions are rather complex and try to solve multiple problems at the same time. During a meeting with the team, the idea was brought up that the routing functionality itself should maybe just be an extension of the fallback function. The syntax could be something like
Here, what comes after This of course does not solve the problem of separating storage slots. |
This would be compatible with the routing. It is essentially the same as what I meant by "stateful libraries" above, but maybe calling them "contracts" would be more logical. It allows upgrades to a certain degree. A proxy would just be
What it does not allow is accessing state variables of other "modules" of the same "collective contract" without a specific reference. |
This issue has been marked as stale due to inactivity for the last 90 days. |
Hi everyone! This issue has been automatically closed due to inactivity. |
Abstract
Allow the compiler to create a smart contract hub that connects multiple smart contracts together to have a single entry point without being limited to the 24 kb limit of Solidity.
The proposal is to introduce syntax for creating a hub contract which acts as a single entry point for complex smart contract systems and proxies calls to different implementation contracts using delegate calls. The hub contract will store all of the storage for its child contracts under separate namespaces. The child contract can be standard contracts or new abstract/library like contracts.
Motivation
Complex dApps require complex smart contracts. I have seen a lot of dApp developers struggle with this. As Ethereum and Solidity have progressed over the years, you can’t really do as much in 24KB as you were used to. Solc bakes in more checks, reason strings take a lot of space, and L2 solutions like Optimism bloat the compiled artifacts with their changes.
A lot of alternative non-standard solutions are being used right now that introduce another attack surface and added complexity. Having something inbuilt in the compiler will make it safer and easier for developers to build complex smart contract systems.
Specification
contract Hub routing Foo, Bar
. It must work alongside theis
keyword. I.e.contract Hub routing Foo, Bar is Proxy
should also work.import ./Hub.sol
.Hub.variable
,Hub.Foo.variable
,Hub.Bar.function()
etc.Example Syntax
ps Thanks to @ajsantander, @chriseth, @cameel, and others who have helped form this draft proposal.
The text was updated successfully, but these errors were encountered: