Skip to content
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

VIP: Cloning #2326

Open
Tracked by #2324
fubuloubu opened this issue Feb 27, 2021 · 2 comments
Open
Tracked by #2324

VIP: Cloning #2326

fubuloubu opened this issue Feb 27, 2021 · 2 comments
Labels
VIP: Approved VIP Approved

Comments

@fubuloubu
Copy link
Member

fubuloubu commented Feb 27, 2021

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 (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

Dependencies

No dependencies to other VIPs

References

#2251 can make this easier to implement

Copyright

Copyright and related rights waived via CC0

@fubuloubu fubuloubu added the VIP: Discussion Used to denote VIPs and more complex issues that are waiting discussion in a meeting label Feb 27, 2021
@fubuloubu fubuloubu mentioned this issue Feb 27, 2021
4 tasks
@fubuloubu fubuloubu added VIP: Approved VIP Approved and removed VIP: Discussion Used to denote VIPs and more complex issues that are waiting discussion in a meeting labels Mar 1, 2021
@fubuloubu
Copy link
Member Author

fubuloubu commented Mar 29, 2021

NOTE: Any use of immutable constants (set during deployment) cannot work with this feature (see #1164)

@charles-cooper
Copy link
Member

related: #3557

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
VIP: Approved VIP Approved
Projects
None yet
Development

No branches or pull requests

2 participants