Overview

A basic overview of the Extendable Contract Pattern

Typically, contract mutability has been approached using the Proxy pattern which solves upgradeability in the following way:

A proxy which acts as the façade of the contract points to an implementation contract which contains all the functional logic. When it needs to be upgraded, a new implementation contract is deployed and the proxy is reconfigured to point to it where now any function calls are directed there instead of the old implementation.

But what if I only wanted to update a single function in my contract? Now I have to redeploy the entire contract, pay excessive transaction fees to do so and only have changed a few lines of code.

Extendable

Our approach is mindful of this problem amongst others and we have devised a methodology that attempts to maximise flexibility and re-usability as a core tenet of smart contract design.

A contract that is extendable is given its functionality through the attachment of individual extensions that house specific functional logic. Instead of a monolithic approach where an entire contract has to be redeployed, we allow the developer to decide how granular an extension can be for their contract stack and extend it accordingly. This means that Extensions can be as thick or thin as desired and form the base functional unit of every Extendable contract.

A contract is extended simply through calling YourContract.extend(extension).

Contract state is now stored in Storage modules that are written to and read from by the functional logic that requires them (in the Extensions).

Interfaces

Whereas previously a contract's interface would never change as it was just the underlying implementation of an interface that would be upgraded, with Extendable contracts the interface is also subject to evolution as a set of functions can be extended beyond what was originally envisaged.

In this way, a contract's interface becomes a union of all its available extensions which can themselves be a collective implementation of certain known constructions such as ERC20 tokens or other standards.

This is made available through calling YourContract.getCurrentInterface()which returns an interface definition based on the contract in the format:

interface IExtended {
    // generated from the ExtendLogic extension
    function extend(address extension) external;
    function getCurrentInterface() external view returns(string memory);
    function getExtensions() external view returns(bytes4[] memory);
    function getExtensionAddresses() external view returns(address[] memory);
    
    // list of other functions with visibility and returns from extensions
}

Integrating

Due to the additional complexity of the Extendable approach and the mutability of contracts, it can be hard to integrate against as interfaces are not always going to be the same. With traditional smart contracts, the ABI does not change which makes it easier for third-party products and services to plug well with them.

To alleviate such issues, the Extendable approach also provides some lightweight tools for introspection allowing anyone to check what interface an Extended contract currently exposes at runtime. Integrating with such a contract is now as simple as querying it for its full interface to retrieve an up-to-date view on its functions and calling them.

Last updated