ERC-3000 - Optimistic enactment governance standard

Created 2020-09-24
Status Stagnant
Category ERC
Type Standards Track
Authors

Simple Summary

Interface for scheduling, executing and challenging contract executions based on off-chain approval

Abstract

ERC-3000 presents a basic on-chain spec for contracts to optimistically enact governance decisions made off-chain.

The standard is opinionated in defining the 6 entrypoint functions to contracts supporting the standard. But it allows for any sort of resolver mechanism for the challenge/response games characteristic of optimistic contracts.

While the authors currently believe resolving challenges using a subjective oracle is the right tradeoff, the standard has been designed such that changing to another mechanism is possible (a deterministic resolver like Optimism's OVM uses), even allowing to hot-swap it in the same live instance.

Specification

Data structures

Some data structures are defined which are later used in the standard interfaces:

library ERC3000Data {
    struct Container {
        Payload payload;
        Config config;
    }

    struct Payload {
        uint256 nonce;
        uint256 executionTime;
        address submitter;
        IERC3000Executor executor;
        Action[] actions;
        bytes proof;
    }

    struct Action {
        address to;
        uint256 value;
        bytes data;
    }

    struct Config {
        uint256 executionDelay;
        Collateral scheduleDeposit;
        Collateral challengeDeposit;
        Collateral vetoDeposit;
        address resolver;
        bytes rules;
    }

    struct Collateral {
        address token;
        uint256 amount;
    }
}

Interface and events

Given the data structures above, by taking advantage of the Solidity ABI encoder v2, we define four required functions and two optional functions as the interface for contracts to comply with ERC-3000.

All standard functions are expected to revert (whether to include error messages/revert reasons as part of the standard is yet to be determined) when pre-conditions are not met or an unexpected error occurs. On success, each function must emit its associated event once and only once.

abstract contract IERC3000 {
    /**
     * @notice Schedules an action for execution, allowing for challenges and vetos on a defined time window
     * @param container A Container struct holding both the paylaod being scheduled for execution and
       the current configuration of the system
     */
    function schedule(ERC3000Data.Container memory container) virtual public returns (bytes32 containerHash);
    event Scheduled(bytes32 indexed containerHash, ERC3000Data.Payload payload, ERC3000Data.Collateral collateral);

    /**
     * @notice Executes an action after its execution delayed has passed and its state hasn't been altered by a challenge or veto
     * @param container A ERC3000Data.Container struct holding both the paylaod being scheduled for execution and
       the current configuration of the system
     * should be a MUST payload.executor.exec(payload.actions)
     */
    function execute(ERC3000Data.Container memory container) virtual public returns (bytes[] memory execResults);
    event Executed(bytes32 indexed containerHash, address indexed actor, bytes[] execResults);

    /**
     * @notice Challenge a container in case its scheduling is illegal as per Config.rules. Pulls collateral and dispute fees from sender into contract
     * @param container A ERC3000Data.Container struct holding both the paylaod being scheduled for execution and
       the current configuration of the system
     * @param reason Hint for case reviewers as to why the scheduled container is illegal
     */
    function challenge(ERC3000Data.Container memory container, bytes memory reason) virtual public returns (uint256 resolverId);
    event Challenged(bytes32 indexed containerHash, address indexed actor, bytes reason, uint256 resolverId, ERC3000Data.Collateral collateral);

    /**
     * @notice Apply arbitrator's ruling over a challenge once it has come to a final ruling
     * @param container A ERC3000Data.Container struct holding both the paylaod being scheduled for execution and
       the current configuration of the system
     * @param resolverId disputeId in the arbitrator in which the dispute over the container was created
     */
    function resolve(ERC3000Data.Container memory container, uint256 resolverId) virtual public returns (bytes[] memory execResults);
    event Resolved(bytes32 indexed containerHash, address indexed actor, bool approved);

    /**
     * @dev OPTIONAL
     * @notice Apply arbitrator's ruling over a challenge once it has come to a final ruling
     * @param payloadHash Hash of the payload being vetoed
     * @param config A ERC3000Data.Config struct holding the config attached to the payload being vetoed
     */
    function veto(bytes32 payloadHash, ERC3000Data.Config memory config, bytes memory reason) virtual public;
    event Vetoed(bytes32 indexed containerHash, address indexed actor, bytes reason, ERC3000Data.Collateral collateral);

    /**
     * @dev OPTIONAL: implementer might choose not to implement (initial Configured event MUST be emitted)
     * @notice Apply a new configuration for all *new* containers to be scheduled
     * @param config A ERC3000Data.Config struct holding all the new params that will control the queue
     */
    function configure(ERC3000Data.Config memory config) virtual public returns (bytes32 configHash);
    event Configured(bytes32 indexed containerHash, address indexed actor, ERC3000Data.Config config);
}

Rationale

The authors believe that it is very important that this standard leaves the other open to any resolver mechanism to be implemented and adopted.

That's why a lot of the function and variable names were left intentionally bogus to be compatible with future resolvers without changing the standard.

ERC-3000 should be seen as a public good of top of which public infrastrastructure will be built, being way more important than any particular implementation or the interests of specific companies or projects.

Security Considerations

The standard allows for the resolver for challenges to be configured, and even have different resolvers for coexisting scheduled payloads. Choosing the right resolver requires making the right tradeoff between security, time to finality, implementation complexity, and external dependencies.

Using a subjective oracle as resolver has its risks, since security depends on the crypto-economic properties of the system. For an analysis of crypto-economic considerations of Aragon Court, you can check the following doc.

On the other hand, implementing a deterministic resolver is prone to dangerous bugs given its complexity, and will rely on a specific version of the off-chain protocol, which could rapidly evolve while the standard matures and gets adopted.

Implementations

1. Aragon Govern

Copyright

Copyright and related rights waived via CC0.