Extensible Safe Architecture

This document outlines an extensible Safe architecture, increasing the novel integrations / applications for Safe.

The following guiding principles have been strictly employed:

  1. State for the SafeProxy MUST not be modified, ensuring maximum security.
  2. No use of delegatecall within the architecture.

Use cases

Currently, a safe may only be interacted with via either:

  1. The owners via execTransaction; or
  2. An authorised module via execTransactionFromModule; or
  3. Third parties who wish to verify a signature (ERC-1271).

Use cases that this revised architecture seeks to enable include:

  1. Arbitrary method invocation on the SafeProxy. As an example, this would make it theoretically possible to convert a SafeProxy into an ERC20 token by implementing ERC20 methods.
  2. Authorise a 3rd party contract to verify EIP-712 signatures for a specific domain on behalf of the Safe. As an example, one could delegate the CoW Protocol EIP-712 domain to a contract that verified all signatures for selling any ERC20 token to ETH, ie. Max ETH
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

Current Architecture

The execution path of any method call that is not implemented in the Safe singleton follows this path:

flowchart TD
    A[SafeProxy] -->|delegatecall| B[Safe - `FallbackManager`]
    B -->|call| C[CompatibilityFallbackHandler]

CompatibilityFallbackHandler provides:

  1. ERC-1271 signing, using either approved hashes, or threshold signatures.
  2. Miscellaneous token callbacks for ERC721 / ERC1155 etc.

If a developer wishes to extend the functionality of a Safe, they have to replace the CompatibilityFallbackHandler, requiring significant heavy lifting, being careful to not remove any existing functionality so as to not break Safe (some parts of existing infrastructure require the use of threshold signatures, and some apps are using approved hashes such as CoW Protocol dapp).

A developer may extend CompatibilityFallbackHandler, though it can be made much easier if one could simply specify a custom handler for arbitrary individual methods. This is where ExtensibleFallbackHandler comes in.

ExtensibleFallbackHandler

This handler replaces CompatibilityFallbackHandler, but preserves backwards compatibility with:

  1. ERC-1271 Safe approved hashes / threshold signatures.
  2. Token callbacks.
  3. ERC165.

In addition to the backwards compatibility, a user may:

  1. Specify a handler for a custom method.
  2. Specify a set of handlers for custom methods and indicate that the Safe now supports an interface.
  3. Specify a custom verifier for an EIP-712 domain.

Custom Methods

Security requirements:

  • The Safe SHALL determine (based on the authorisation in the setter) whether a custom method is view or not (ie. whether or not the custom method can modify state on invocation).

Execution Path:

flowchart TD
    A[SafeProxy] -->|delegatecall| B[Safe - `FallbackManager`]
    B -->|call| C[ExtensibleFallbackHandler]
    C -->|call| D[IFallbackMethod]
    C -->|staticcall| E[IStaticFallbackMethod]

Custom EIP-712 Verifiers

User requirements:

  1. Custom verifiers MUST be able to accept an arbitrary, implementation specific bytes payload.

Security requirements:

  • EIP-712 domain separator MUST be checked against the hash, ensuring a 1:1 relationship between domain separator and ISafeSignatureVerifier.

Execution Path for isValidSignature(bytes32,bytes):

flowchart TD
    A[SafeProxy] -->|delegatecall| B[Safe - `FallbackManager`]
    B -->|call| C[ExtensibleFallbackHandler - `SignatureVerifierMuxer`]
    C --> D[Approved Hash]
    C --> E[Threshold Signature]
    C -->|call| F[ISafeSignatureVerifier]

Encoding of signature

  • approvedhash - signature is set to zero-length bytes.
  • threshold - signature is a multiple of 65 bytes (for r, s, v).
  • custom (ie. ISafeSignatureVerifier) - signature is an ABI encoded function call safeSignature(bytes32,bytes32,bytes32,bytes) where the tuple equates to (bytes32 domainSeparator, bytes32 typeHash, bytes32 encodeData, bytes payload). It is the 4bytes selector in the ABI encoded function call that is used to trigger the custom signer.

Custom Verifier Validation

Before SignatureVerifierMuxer calls a custom verifier, it MUST match the domainSeparator to the hash. Therefore, this then means that typeHash, and encodeData can be trusted within ISafeSignatureVerifier if called from SignatureVerifierMuxer.

ISafeSignatureVerifier

Security requirements:

  • An implementation MAY reassert _hash = h(abi.encodePacked("\x19\x01", domainSeparator, h(typeHash || encodeData))).

Storage

Storage of custom method handlers and custom domain verifiers is contained within the ExtensibleFallbackhandler deployment. Therefore, any update of the fallbackHandler for a Safe would in-effect "reset" the custom methods / domain verifiers for the Safe.

Select a repo