Skip to content

Proxy contract that allows dispatching calls to multiple implementations, based on Semantic Versioning.

License

Notifications You must be signed in to change notification settings

daoio/semver-proxy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SemVer Proxy

Proxy contract that allows dispatching calls to multiple implementations, based on Semantic Versioning. Automatically handles incrementation of major.minor.patch parts of a version during upgrades, but it doesn't enforce proper adherence to Semantic Versioning by developers on a smart-contract level - it's considered to be a responsibility of a smart-contract developer to properly adhere to Semantic Versioning.

Quick Start

git clone https://github.com/daoio/semver-proxy

## Install deps (note, oz contracts are already installed locally with --no-git)
forge install
## Run tests
forge test

Storage layout of SemVerProxy

SemVerProxy stores variables in the storage, starting at slot 1000. Therefore, implementation contracts can safely use all storage slots except 1000, 1001, and 1002. So, the storage layout of the proxy and an arbitrary implementation contract can be represented as follows:

Proxy storage layout:
┌───────────────┐
│ Slot 0-1000   │ ← empty
├───────────────┤
│ Slot 1000     │ ← `_latestVersion`
│ Slot 1001     │ ← `_releases` mapping
│ Slot 1002     │ ← `_subscribedClients` mapping
│ ...           │
└───────────────┘

Implementation's storage example:
┌───────────────┐
│ Slot 0        │ ← Implementation's 1st state variable
│ Slot 1        │ ← Implementation's 2nd state variable
│ ...           │
│ Slot 999      │ ← Implementation's 998th state variable
├───────────────┤
| Slot 1000     │ ← ⚠️ This will collide with proxy's storage
└───────────────┘

Releases and Versioning

A release refers to a versioned implementation contract stored in the proxy. Unlike standard proxies, SemVerProxy maintains multiple implementations simultaneously, each accessible through its semantic version.

Version Upgrade

Admins can release new version of implementation contract via the following function calls (effectively upgrading proxy to use new implemenation, though clients might still use previous versions of the implementation contract):

Function Behavior Example
releasePatch Increments patch 1.2.31.2.4
releaseMinor Increments minor, resets patch 1.2.31.3.0
releaseMajor Increments major, resets minor and patch 1.2.32.0.0

Subscribing to Specific Versions

By default, all non-admin calls that end up in fallback() function are dispatched to delegatecall to the latest release (stored in ERC-1967 slot). But, users can "subscribe" to use the implementation version they need, by calling:

function subscribeToVersion(Version memory version) external;

// Where {Version} is:
struct Version {
    uint64 major;
    uint64 minor;
    uint128 patch;
}

After this action all calls of a subscribed user will be dispatched to the major.minor.patch version they have specified for subscription. All subscriptions are stored inside SemVerProxy._subscribedClients mapping

flowchart TD
    A(["subscribed to 1.0.1"]) --> n4["SemVerProxy"]
    n1(["subscribed to 0.1.0"]) --> n4
    n2(["not subscribed"]) --> n4
    n4 --> C["Release v 1.0.1"] & D["Release v 0.1.0"] & n3["Latest Release v 2.0.0"]
    n4@{ shape: diam}
    n3@{ shape: rect}
    linkStyle 0 stroke:#D50000,fill:none
    linkStyle 1 stroke:#2962FF,fill:none
    linkStyle 2 stroke:#00C853,fill:none
    linkStyle 3 stroke:#D50000,fill:none
    linkStyle 4 stroke:#2962FF,fill:none
    linkStyle 5 stroke:#00C853
Loading

Users can unsubscribe from using specific version and use the latest one by calling:

function unsubscribeFromVersioning() external;

Security Considerations

  • Storing variables in implementation's storage at slots 1000, 1001, 1002 will collide with the proxy's storage.
  • SemVerProxy has externally accessable function, therefore there's a possibiliy of function selector clash (i.e., if implementation defines functions that have the same signature as external functions of SemVerProxy).

About

Proxy contract that allows dispatching calls to multiple implementations, based on Semantic Versioning.

Topics

Resources

License

Stars

Watchers

Forks