Add Constructor Support to Soroban #1501
Replies: 9 comments 21 replies
-
I have seen other examples where a constructor would simplify a contract. One example is the smart wallet contract that @kalepail wrote (#1499) that is two contracts and requires a factory to ensure it is initialised and setup appropriately. Support for constructors that carry a set of required inputs would reduce that contract to one. |
Beta Was this translation helpful? Give feedback.
-
We have excluded constructor from the initial Soroban release due to auth-related difficulties, that we've actually solved prior to protocol 20 release. Currently I don't see any technical issue with adding that. In terms of implementation approach, I've considered a built-in factory contract at some point, but I don't think that solves the issue raised here. So, I think we can just proceed with extending the contract creation functionality (host functions/XDR operation) to accept additional arguments. The change should be reasonably simple and we should be able to sketch a CAP for that pretty quickly. |
Beta Was this translation helpful? Give feedback.
-
There are some design and implementation questions to figure out. A single constructor is probably trivial as it'll just be one new underscore function such as Another issue is one vs many constructors. Solidity supports multiple constructors where each can receive different parameters. If we intend to support same then we need to give each constructor a unique name because WASM and Rust don't allow us to specify multiple functions with the same name, so if we wanted to support multiple those functions would need to have different names. |
Beta Was this translation helpful? Give feedback.
-
Reading CAP-58@50dde06 (#1505):
I think this design decision needs some discussion. I think it will reduce the ergonomics. It will be surprising because in most programming languages that have constructors, there is no distinction between a lack of a constructor, and a constructor with zero arguments. A zero parameter constructor is largely an implementation detail, something that can be added or taken away with no impact. If a contracts wants to upgrade to use a zero-parameter constructor, or upgrade and remove a zero-parameter constructor, that change will be a breaking change. And a non-obvious breaking change that will break any existing deployers.
What is the security issue of a zero-parameter constructor being called? No auth will be supplied unless the user specifically signs for auth for the constructor. Contract invocations in transactions do not capture the intended call tree so any invocation can always result in the invocation of contracts unexpected. When a contract calls another contract the calling contract has no control over whether the called contract calls on to other contracts and those additional calls can execute arbitrary code. Calling deploy and having a zero parameter constructor be called seems no different. If the issue is that we need to preserve for contracts that exist today that their deploy calls will never call a constructor because the developers didn't know it would be possible, we could disallow calling contracts with constructors for contracts built with v21 or earlier and that would harm the ergonomics less. |
Beta Was this translation helpful? Give feedback.
-
Reading CAP-58@50dde06 (#1505): The proposal doesn't discuss what should happen with upgrades, and it should probably speak on that even if the answer is nothing and that constructors do not get called on upgrades. However, whether a constructor should be called or not during an upgrade is not obvious to me. Both calling or not calling a constructor for upgrades could make sense depending on the circumstances. If I fix a bug in an existing contract and upgrade, the upgrade likely wouldn't need the constructor to be executed. However, if I have a smart wallet account and I want to entirely replace my smart wallet contract with a new contract, then it might be imperative that the constructor runs.
This guarantee ☝🏻 can't be guaranteed for all contract instantiations due to upgrades, which weakens the overall guarantee it provides. It's not possible to look at the current network state and say for sure that the constructor of a contract has been run because the code may have been installed by upgrade. |
Beta Was this translation helpful? Give feedback.
-
I've put together a table that covers the constructor behavior depending on the constructor presence, number of arguments, and the 'create contract' call arguments. This covers the idea of relaxing the requirements expressed above. I'll merge this into CAP if we came to an agreement about that not introducing any significant vulnerabilities. There is also an issue that I've noticed while sketching a prototype implementation. Unfortunately, |
Beta Was this translation helpful? Give feedback.
-
Reading CAP-58@f0a26bc (#1523):
|
Beta Was this translation helpful? Give feedback.
-
Yes, I'll try to rephrase this for the sake of clarity.
Sure, that's just a regular function and it can panic or return errors. In either case it will be considered to have failed.
It causes the
This behaves as any other host function - a failure causes the current stack frame to rollback without returning control to the contract. It is not different from the current behavior - e.g. if one tries to call
I mean a contract returning e.g. a |
Beta Was this translation helpful? Give feedback.
-
Re CAP-58@4502e34:
Comments here follow on from my question in Discord (ref) on the same topic. We resolved previously to make zero-parameter constructors an implementation detail, which means any caller shouldn't need to know or do anything different to deploy a contract with no ctor vs a contract with a zero-parameter and zero-auth ctor. However the above deprecation will cause deployments with zero-parameter ctors to fail. Tooling and SDKs don't build deployment operations by first calling out to the internet to figure out which protocol version is in use to know to use v2 for all deployment, and aren't required to inspect contracts or parse the wasm being deployed to know if they have a zero parameter ctor. This means that the CLI, Lab, SDKs, will build any deployment that isn't accompanied by parameters as a protocol 21 style deployment so that someone with a v22 CLI will work on protocol 21. It's not ideal for tooling to have to poll the network to figure out when to switch behavior to use v2 for everything, or to have to contain a wasm parser to open up the wasm and figure out if there is a zero-parameter ctor inside. Some tooling is used offline to produce these txns too which makes this more difficult.
Can an example of signature malleability be shared? I'm not seeing how a signature could be malleable. |
Beta Was this translation helpful? Give feedback.
-
This is a bit of a story rather than a formulated argument:
While working on Hyperledger Solang (Solidity compiler to
wasm
target), to support a simple Solidity contract:The compiler here has to loop over the storage variables and insert instructions that initializes them. The function that does so, let's call it
init_storage
, has to be called manually by a Soroban Solidity dev (which is not what a Solidity dev would expect).If Soroban supports constructors, that is, a function that is called automatically upon a contract instantiation, then
init_storage
could be called from the constructor (the compiler will insertinit_storage
instructions under the hood).Another reason I have found, for supporting constructors, is this:
In https://github.com/stellar/soroban-examples/blob/main/increment/src/lib.rs
In the
increment
contract, each and every retrieval of thecounter
key would require the developer to return the value the counter is indented to be initiated with throughout the code, making it repetitive.What do you think?
Beta Was this translation helpful? Give feedback.
All reactions