Smart contract interface

Generic Solidity interface for onchain agreements with a designated resolver.

Auto Contract Standard: Interface

Abstract

This standard defines IAutoContract, a minimal Solidity interface for smart contracts whose settlement depends on a bounded subjective judgment — a decision constrained to a finite set of outcomes based on pre-agreed criteria — made by a designated resolver agent. The interface specifies a generic state machine, role-based access, and hash-anchored terms. It can be specialized for escrow, governance, disputes, milestones, bounties, insurance claims, or any other use case requiring off-chain judgment with on-chain finality.

Motivation

Existing smart contracts settle deterministic conditions well: token transfers trigger when a balance threshold is met, timelocks expire on schedule, multisig thresholds are counted. But a growing class of on-chain agreements depends on questions that have no deterministic answer:

  • Does a deliverable satisfy a milestone?
  • Is a governance proposal compliant with a DAO's charter?
  • Does submitted evidence substantiate an insurance claim?
  • Has a bounty been fulfilled to specification?

These questions require bounded judgment. Today, projects handle this with ad hoc contract designs that embed use-case-specific roles (buyer/seller), outcomes (release/refund), and state machines (funded/submitted). This makes each implementation incompatible with tooling, indexers, resolvers, and frontends built for any other.

This standard extracts the common structure: two parties create a contract anchored by hashed terms, one party submits evidence, a designated resolver renders a decision from a finite outcome set, and the implementation settles accordingly. By standardizing this lifecycle, any resolver agent, indexer, or interface can interact with any auto contract regardless of its domain.


Specification

Execution Flow

An auto contract is executed in two onchain transactions:

  1. Both parties verify offchain. The parties review the markdown agreement document and independently compute its hash. They agree on the resolver and terms before either party transacts.
  2. Creator proposes. The creator calls create() to register the contract onchain, anchoring the termsHash. The contract enters Created state. This is a proposal, not yet binding.
  3. Counterparty accepts. The counterparty calls accept() to confirm they have verified the terms and agree. The contract enters Active state. It is now binding — both parties have transacted onchain against the same terms hash.
  4. Evidence is submitted. A party calls submit() to record a deliverable or evidence hash. The contract enters Submitted state.
  5. Resolver settles. The resolver calls resolve() with an outcome and resolution hash. The contract enters Resolved state. Settlement effects execute atomically.

The contractURI() view function returns the URI where the agreement document can be retrieved. Any party can fetch the document, hash it, and verify it matches the onchain termsHash.

State Machine

Every contract instance moves through these states:

```text None -> Created -> Active -> Submitted -> Resolved \ \ \ +---> Cancelled +------------> Cancelled ```

StateMeaning
NoneDefault zero value. No contract instance exists at this identifier.
CreatedProposed onchain by the creator. Awaiting counterparty acceptance.
ActiveCounterparty has accepted. The agreement is binding.
SubmittedEvidence or deliverable hash recorded. Awaiting resolver judgment.
ResolvedResolver has rendered a final decision. Terminal state.
CancelledContract voided before resolution. Terminal state.

Transitions cannot skip states — Created never jumps straight to Resolved. Implementations may add domain-specific sub-states, but these five core states must be present and queryable.

Roles

An auto contract instance involves exactly three roles:

RoleDescription
CreatorThe address that calls create. Typically the party initiating the agreement.
CounterpartyThe second party to the agreement, designated at creation.
ResolverThe designated agent address authorized to render a decision.

Only the specified role may call its corresponding lifecycle function, and implementations must enforce this. The creator and counterparty must be distinct addresses. The resolver should be a third party — sharing an address with either is discouraged, and valid only where the domain explicitly permits it.

Outcome

The resolver's decision is a bytes32 value rather than a domain-specific enum. The agreement format standard defines named outcome labels; each is converted to bytes32 onchain via keccak256(bytes(label)). This lets any domain define its own outcome set without changing the interface.

ValueMeaning
bytes32(0)No decision rendered (default).
Any other bytes32Implementation-defined outcome, derived from the agreement's outcome labels.

A resolution with outcome bytes32(0) must be rejected. Implementations should document their outcome mapping in a companion standard or in contract-level NatSpec.

Hash Anchoring

Three hashes anchor off-chain artifacts to on-chain state:

HashAnchorsSet During
termsHashThe markdown agreement defining the subjective terms.create
submissionHashThe evidence, deliverable, or claim submitted for review.submit
resolutionHashThe resolver's decision document including rationale.resolve

All hashes are bytes32. This standard doesn't mandate a hashing algorithm, though SHA-256 and Keccak-256 are recommended; companion standards (such as the agreement format) should pin down the exact algorithm and canonicalization.

Document Storage

The contractURI view function returns a URI pointing to the markdown agreement document. The URI provides retrieval; the termsHash provides verification. Any party can fetch the document from the URI, hash it, and confirm it matches the onchain termsHash.

Implementations should store the URI onchain — passed to create or set separately. Common schemes are ipfs://, ar://, and https://. If the URI isn't stored onchain, contractURI may return an empty string.

For private agreements (visibility: private in the frontmatter), the document at the URI must be access-controlled so only the parties and resolver can retrieve it. The termsHash stays public onchain — anyone who obtains the document another way can still verify it against the hash. The principle: the hash is always public and verifiable; access to the document is controlled.

Interface

Conforming implementations MUST implement this interface, defined in IAutoContract.sol:

```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.26;

/// @title IAutoContract /// @notice Generic state-machine interface for markdown-defined agreements. /// @dev Terms, evidence, and decisions stay offchain; the contract stores only /// hashes, parties, and state. Outcomes are bytes32 so any domain can map its /// result (release, refund, approve, reject, split) to a settlement action. interface IAutoContract { /// @notice Lifecycle states for every contract instance. enum State { None, Created, Active, Submitted, Resolved, Cancelled }

event ContractCreated(
    uint256 indexed contractId,
    address indexed creator,
    address indexed counterparty,
    address resolver,
    bytes32 termsHash
);
event ContractAccepted(uint256 indexed contractId);
event ContractSubmitted(uint256 indexed contractId, bytes32 submissionHash);
event ContractResolved(uint256 indexed contractId, bytes32 outcome, bytes32 resolutionHash);
event ContractCancelled(uint256 indexed contractId);

function create(
    address counterparty,
    address resolver,
    bytes32 termsHash
) external returns (uint256 contractId);

function accept(uint256 contractId) external;
function submit(uint256 contractId, bytes32 submissionHash) external;
function resolve(uint256 contractId, bytes32 outcome, bytes32 resolutionHash) external;
function cancel(uint256 contractId) external;

function stateOf(uint256 contractId) external view returns (State);
function outcomeOf(uint256 contractId) external view returns (bytes32);
function contractURI(uint256 contractId) external view returns (string memory);

} ```

Function Requirements

create

  • MUST assign a monotonically increasing contractId.
  • MUST revert if counterparty is the zero address.
  • MUST revert if resolver is the zero address.
  • MUST revert if counterparty equals msg.sender.
  • MUST store termsHash immutably for this instance.
  • MAY accept additional domain-specific parameters (e.g., deadline, token, amount) by extending this signature.

accept

  • MUST revert if the current state is not Created.
  • MUST revert if msg.sender is not the designated counterparty.
  • MUST set state to Active and emit ContractAccepted.
  • By calling accept, the counterparty confirms they have verified the terms and agree.

submit

  • MUST revert if the current state is not Active.
  • MUST store submissionHash for this instance.
  • SHOULD restrict the caller to the role responsible for delivering evidence, as defined by the contract type.

resolve

  • MUST revert if the current state is not Submitted.
  • MUST revert if msg.sender is not the designated resolver.
  • MUST revert if outcome is bytes32(0).
  • MUST store resolutionHash for this instance.
  • MUST execute any settlement effects (token transfers, state changes) atomically within this call.

cancel

  • MUST set state to Cancelled.
  • SHOULD allow cancellation only from Created or Active states.
  • Which role(s) may cancel, and whether cancellation triggers refunds or other effects, is implementation-defined.

stateOf

  • MUST return State.None for identifiers that have not been created.

outcomeOf

  • MUST return bytes32(0) for any contract instance that has not reached Resolved.

contractURI

  • SHOULD return the URI of the markdown agreement document.
  • The document at the returned URI MUST hash to the termsHash stored at creation.
  • MAY return an empty string if the URI is not stored onchain.

Events

Every state transition emits its corresponding event — indexers and off-chain agents depend on these for discovery and monitoring. Events should not fire outside of actual state transitions.

Invariants

Every conforming IAutoContract implementation upholds these invariants:

  1. State monotonicity. Once a contract instance reaches a terminal state (Resolved or Cancelled), no further transitions are possible. Any call that would change a terminal instance's state reverts.

  2. Resolver exclusivity. Only the address stored as resolver at creation can call resolve. No one else — not the creator, counterparty, or contract owner — can render a decision.

  3. Outcome immutability. Once resolve sets an outcome, it never changes. outcomeOf returns the same value indefinitely.

  4. Hash immutability. Once termsHash, submissionHash, or resolutionHash is set to a non-zero value, it is never overwritten or cleared.

  5. Identity separation. The creator and counterparty are always distinct addresses, enforced at creation time.

  6. Sequential lifecycle. State transitions follow the edges in the state machine diagram; no state is skipped. Resolved is reachable only from Submitted, Submitted only from Active, and Active only from Created.

  7. Non-zero resolution. A resolved instance always has a non-zero outcome: if stateOf returns Resolved, outcomeOf returns a value other than bytes32(0).

Copyright

This document is placed in the public domain.