ERC-6224: Contracts Dependencies Registry

A registry for managing smart contracts with their dependencies.


Metadata
Status: ReviewStandards Track: ERCCreated: 2022-12-27
Authors
Artem Chystiakov (@arvolear)

Abstract


This EIP introduces an on-chain registry system that a decentralized protocol may use to manage its smart contracts.

The proposed system consists of two components: ContractsRegistry and Dependant. The ContractsRegistry contract stores references to every smart contract used within a protocol, optionally making them upgradeable by deploying self-managed proxies on top, and acts as a hub the Dependant contracts query to fetch their required dependencies from.

Motivation


In the ever-growing Ethereum ecosystem, projects tend to become more and more complex. Modern protocols require portability and agility to satisfy customer needs by continuously delivering new features and staying on pace with the industry. However, the requirement is hard to achieve due to the immutable nature of blockchains and smart contracts. Moreover, the increased complexity and continuous delivery bring bugs and entangle the dependencies between the contracts, making systems less supportable.

Applications that have a clear architectural facade; which are designed with forward compatibility in mind; which dependencies are transparent and clean are easier to develop and maintain. The given EIP tries to solve the aforementioned problems by presenting two smart contracts: the ContractsRegistry and the Dependant.

The advantages of using the provided system might be:

  • Structured smart contracts management via specialized contracts.
  • Ad-hoc upgradeability provision of a protocol.
  • Runtime addition, removal, and substitution of smart contracts.
  • Dependency injection mechanism to keep smart contracts' dependencies under control.
  • Ability to specify custom access control rules to maintain the protocol.

Specification


The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Overview

The system consists of two smart contracts:

  • ContractsRegistry that is a singleton registry to manage and upgrade a protocol's smart contracts.
  • Dependant that is a mix-in which enables a dependency injection mechanism.

The following diagram depicts the relationship between the registry and its dependants:

ContractsRegistry

The ContractsRegistry is the main contract of the proposed system. It MUST store the references to every standalone contract used within a protocol. The ContractRegistry MAY be configured to deploy a proxy contract of choice on top of the registered contracts.

Additionally, the ContractsRegistry MUST reject the registration of zero addresses.

The ContractsRegistry MUST implement the following interface:


Dependant

The ContractsRegistry works together with the Dependant contract. Every standalone contract of a protocol MUST inherit Dependant in order to support the dependency injection mechanism.

The required dependencies MUST be set in the overridden setDependencies method, not in the constructor or initializer methods.

Only the injector MUST be able to call the setDependencies and setInjector methods. The initial injector will be a zero address, in that case, the call MUST NOT revert on access control checks.

The Dependant contract MUST implement the following interface:


  • The Dependant contract MAY store the dependency injector (usually ContractsRegistry) address in the special slot 0x3d1f25f1ac447e55e7fec744471c4dab1c6a2b6ffb897825f9ea3d2e8c9be583 (obtained as bytes32(uint256(keccak256("eip6224.dependant.slot")) - 1)).

Rationale


There are a few design decisions that have to be explicitly specified:

ContractsRegistry Rationale

Contracts Identifier

The string contracts identifier is chosen over the uint256 and bytes32 to maintain code readability and reduce the human error chances when interacting with the ContractsRegistry. Being the topmost smart contract of a protocol, it MAY be typical for the users to interact with it via block explorers or DAOs. Clarity was prioritized over gas usage.

Due to the string identifier, the event parameters are not indexed. The string indexed parameter will become the keccak256 hash of the contract name if it is larger than 32 bytes. This fact reduces readability, which was prioritized.

Reverts

The getContract view function reverts if the requested contract is address(0). This is essential to minimize the risks of misinitialization of a protocol. Correct contracts SHOULD be added to the registry prior to any dependency injection actions.

The addContract, addProxyContract, addProxyContractAndCall, and justAddProxyContract methods revert if the provided address is address(0) for the same risk minimization reason.

Dependant Rationale

Dependencies

The data parameter is provided to carry additional application-specific context. It MAY be used to extend the method's behavior.

Injector

The setInjector function is made external to support the dependency injection mechanism for factory-made contracts. However, the method SHOULD be used with extra care.

Reference Implementation


Note that the reference implementation depends on OpenZeppelin contracts 4.9.2.

ContractsRegistry Implementation


Dependant Implementation


Security Considerations


It is crucial for the owner of ContractsRegistry to keep their keys in a safe place. The loss/leakage of credentials to the ContractsRegistry will lead to the application's point of no return. The ContractRegistry is a cornerstone of a protocol, access must be granted to the trusted parties only.

ContractsRegistry Security

  • The ContractsRegistry does not perform any upgradeability checks between the proxy upgrades. It is the user's responsibility to make sure that the new implementation is compatible with the old one.

Dependant Security

  • The Dependant contract MUST set its dependency injector no later than the first call to the setDependencies function is made. That being said, it is possible to front-run the first dependency injection.

Copyright


Copyright and related rights waived via CC0.