Enable a popular "cloning" pattern using simple forwarder proxies (e.g. create_forwarder_to)
Motivation
A popular pattern with smart contracts is to create a "cloning factory" using 1 implementation contract that implements a construction sequence (usually deployment without initializing anything and an initialize() method that serves as the constructor). This can sometimes cause issues if not handled appropriately (see devops199) by enforcing that the initialize method is called when deploying a clone of that contract. To avoid the potential pitfalls of failing to appropiately handle these scenarios, a methodology that "clone-enabled" contracts to be simply used is preferred.
The pattern in general looks like this:
(requires enabling internal methods to be callable in constructors e.g. #2251)
@internal
def _init(*args):
... # do stuff with args
@external
def __init__(*args):
self._init(*args)
# Set internal variable to prevent contract from being re-initialized
@external
def init(*args):
# Additional internal check that contract is not initialized
self._init(*args)
# Set internal variable to prevent contract from being re-initialized
@external
def clone(*args) -> address:
clone: address = create_forwarder_to(self)
Self(clone).init(*args)
return clone
Specification
Introduce the @cloneable decorator, only applicable to constructors, which wraps the above common implementation into the following macro:
@external
@cloneable # Checks internal `initialized` variable, only on `initialize()` call
def __init__(*args):
... # do stuff with args
# Internally set `initialized` variable to avoid re-initialization
# `init(*args)`
# function that works exactly the same as `__init__` is added,
# with the additional `initialized` check
# `clone(*args)`
# function that creates a forwarder proxy of this contract,
# calls `init(*args)` on it, and returns the new address
Note that clone and initialize should be reserved keywords so that functions with those names cannot exist in the contract if @cloneable is used.
Backwards Compatibility
No backwards incompatibilities except the clone() function name becomes a protected keyword
Simple Summary
Enable a popular "cloning" pattern using simple forwarder proxies (e.g.
create_forwarder_to
)Motivation
A popular pattern with smart contracts is to create a "cloning factory" using 1 implementation contract that implements a construction sequence (usually deployment without initializing anything and an
initialize()
method that serves as the constructor). This can sometimes cause issues if not handled appropriately (seedevops199
) by enforcing that theinitialize
method is called when deploying a clone of that contract. To avoid the potential pitfalls of failing to appropiately handle these scenarios, a methodology that "clone-enabled" contracts to be simply used is preferred.The pattern in general looks like this: (requires enabling internal methods to be callable in constructors e.g. #2251)
Specification
Introduce the
@cloneable
decorator, only applicable to constructors, which wraps the above common implementation into the following macro:Note that
clone
andinitialize
should be reserved keywords so that functions with those names cannot exist in the contract if@cloneable
is used.Backwards Compatibility
No backwards incompatibilities except the
clone()
function name becomes a protected keywordDependencies
No dependencies to other VIPs
References
2251 can make this easier to implement
Copyright
Copyright and related rights waived via CC0