This repository contains a system for creating, managing, and verifying two-party agreements on top of the Ethereum Attestation Service (EAS). It provides a standardized and secure way for a primary entity (such as a DAO, protocol, or individual) to enter into on-chain agreements with various counterparties.
The core of the system is the AgreementAnchor
contract, which serves as an immutable, on-chain record of an agreement about some off-chain content, identified by a bytes32
content hash.
The EAS-Anchor system is designed to solve the problem of creating formal, bilateral agreements on-chain. While EAS provides a primitive for making attestations, this system builds a higher-level abstraction for agreements where two signers are required.
The primary use case is for a single, canonical Signer (e.g., a DAO) that needs to enter into many standardized agreements with different Counter-Signers. The system ensures that:
- All agreements associated with the primary Signer are created through a single, controlled factory.
- The content of the agreement is set before attestations are made.
- The consent of each party is explicitly tracked via individual attestations.
- The agreement's lifecycle (attestation and revocation) is governed by a strict set of rules defined in a central resolver.
The system is composed of three main contracts that work together to manage the agreement lifecycle.
AgreementResolver.sol
: This is the logic layer of the system. It is deployed once for a canonicalsigner
. It serves as a custom EAS Schema Resolver, meaning its hooks (onAttest
,onRevoke
) are automatically called by the EAS contract whenever an attestation or revocation is made using its schema. It is responsible for enforcing most business rules.AgreementAnchorFactory.sol
: EachAgreementResolver
deploys its own private factory. This factory is responsible for creating allAgreementAnchor
contracts associated with the resolver's primarysigner
. This ensures that the resolver only ever acts upon anchors that it has created, preventing unauthorized contracts from interacting with the system.AgreementAnchor.sol
: This contract is the stateful representation of a single agreement. It stores the immutable details of the agreement (contentHash
,partyA
,partyB
) and the state of each party's consent (their latest attestation UID and whether the agreement has been revoked). AnAgreementAnchor
's address is used in therecipient
field of an EAS attestation.
- Deployment: The primary
signer
deploys a singleAgreementResolver
contract. The resolver's constructor automatically deploys its ownAgreementAnchorFactory
. - Schema Registration: A schema is registered on EAS (e.g.,
bytes32 contentHash
) with the deployedAgreementResolver
as its official resolver. - Agreement Creation: When the
signer
and acounterSigner
agree on some off-chain content (e.g., a legal document), they calculate its 32 byte hash. They then callcreateAgreementAnchor
on the resolver's factory, creating a newAgreementAnchor
contract for this specific agreement. - Attestation (Signing): Both parties "sign" the agreement by making an EAS attestation.
- Recipient: The address of the newly created
AgreementAnchor
. - Schema: The UID of the registered schema.
- Data: The
contentHash
of the agreement.
- Recipient: The address of the newly created
- Validation: The
onAttest
hook in theAgreementResolver
is triggered. It validates that the attester is a party to the agreement, the anchor is legitimate, and the content hash matches. If valid, it updates the state of theAgreementAnchor
. - Revocation: If a party wishes to revoke their consent, they revoke their attestation through EAS. The
onRevoke
hook is triggered, and if the revocation corresponds to the latest attestation for that party, theAgreementAnchor
is marked as revoked.
To install dependencies, run:
forge install
This project uses Foundry for testing. To run the full test suite:
forge test
To see test coverage:
forge coverage
The repository includes scripts for deploying the AgreementResolver
and registering its schema on EAS. These can be found in the script/
directory.
To deploy to a network (e.g., Sepolia), first set the required environment variables in a .env
file (see .env.example
). Then, run the deployment script:
forge script script/sepolia/DeployAndRegisterSchema.s.sol --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY --broadcast --verify -vvvv
The security of the system relies on a few key principles:
- Single Authority: The
AgreementResolver
is the single source of truth and authority for the business logic of all agreements created under its schema. - Factory-Controlled Anchors: The resolver will only process attestations and revocations for
AgreementAnchor
contracts that were deployed by its own internal factory. This prevents malicious actors from creating their own anchors and having the resolver interact with them. - EAS as the State Layer: The system leverages the security and immutability of the core EAS contracts for all other management of attestations. The resolver acts as a "guard" layer on top of EAS.