Agreement

Overview

The Agreement contract is a core component of the Permission Protocol that manages consent agreements between parties. It uses EIP-712 typed data signatures for secure, gasless agreement creation and prevents replay attacks through digest tracking.

Contract Details

Property
Value

Solidity Version

^0.8.28

License

MIT

Inheritance

EIP712, Context (OpenZeppelin)

Implements

IAgreement


Purpose

The Agreement contract serves as the foundation for creating legally-binding consent agreements on-chain. It enables:

  • Typed Data Signing: Counterparties sign agreements off-chain using EIP-712

  • Replay Protection: Each unique agreement can only be created once

  • Flexible Terms: Supports multiple purpose keys and conditions

  • Revocation Control: Configurable revocation eligibility with optional grace periods

  • Human-Readable Data: Provides parsed agreement data with string conversions

  • Batch Operations: Create multiple agreements in a single transaction


Data Structures

RevokeEligibility Enum

Defines the revocation rules for an agreement.

Value
Description

UnRevokable

Consent records cannot be revoked

InstantlyRevokable

Consent records can be revoked at any time

RevokableAfterGracePeriod

Consent records can only be revoked after a grace period

AgreementData (IAgreement Interface)

The primary structure for storing agreement details, defined in the IAgreement interface.

Field
Type
Description

kind

bytes32

The type/category of the agreement

purpose

bytes32[]

Array of purpose keys defining data usage

termsHash

bytes32

Keccak256 hash of the full terms document

conditions

bytes32

Hash of additional conditions/limitations

counterParty

address

The party consenting to the agreement

revokeGracePeriodSeconds

uint64

Seconds to wait before revocation (for RevokableAfterGracePeriod)

revokeEligibility

RevokeEligibility

Rules for when/if the agreement can be revoked

termsRef

string

URL or IPFS hash pointing to full terms

ParsedAgreementData

Human-readable version with strings instead of bytes32.

AgreementInput

Input structure for batch agreement creation.


State Variables

Variable
Type
Visibility
Description

agreementCounter

uint256

public

Counter for generating unique agreement IDs (starts at 1)

agreements

mapping(uint256 => AgreementData)

public

Storage mapping of agreement ID to data

usedDigests

mapping(bytes32 => uint256)

public

Tracks used EIP-712 digests to agreement IDs


Functions

Constructor

Initializes the contract with EIP-712 domain separator parameters.

Parameters:

Name
Type
Description

name

string

Domain name (e.g., "Agreement")

version

string

Domain version (e.g., "1")


createAgreement

Creates a new agreement with EIP-712 signature verification.

Parameters:

Name
Type
Description

kind

bytes32

The agreement type

purpose

bytes32[]

Array of purpose keys

termsHash

bytes32

Hash of terms content

conditions

bytes32

Conditions hash

counterParty

address

Address that must sign (or msg.sender)

revokeGracePeriodSeconds

uint64

Grace period before revocation allowed

revokeEligibility

RevokeEligibility

Revocation rules

termsRef

string

Reference to full terms

signature

bytes

EIP-712 signature from counterParty

Returns:

Name
Type
Description

agreementId

uint256

The unique ID of the created agreement

Requirements:

  • kind cannot be empty (bytes32(0))

  • If counterParty != msg.sender, signature must be valid

  • Agreement with same parameters cannot already exist

Emits: AgreementCreated


batchCreateAgreements

Creates multiple agreements in a single transaction.

Parameters:

Name
Type
Description

inputs

AgreementInput[]

Array of agreement inputs

Returns:

Name
Type
Description

agreementIds

uint256[]

Array of created agreement IDs

Requirements:

  • Input array must not be empty

Emits: AgreementCreated for each agreement


isRevokable

Checks if a consent record for this agreement can be revoked based on the agreement's revocation rules.

Parameters:

Name
Type
Description

agreementId

uint256

The agreement identifier

createdAt

uint64

Timestamp when the consent record was created

Returns:

Name
Type
Description

(bool)

bool

true if revocation is allowed, false otherwise

Logic:

  • UnRevokable: Always returns false

  • InstantlyRevokable: Always returns true

  • RevokableAfterGracePeriod: Returns true only if (block.timestamp - createdAt) >= revokeGracePeriodSeconds

Reverts: AgreementNotFound if agreement doesn't exist


getAgreementData

Retrieves raw agreement data by ID.

Parameters:

Name
Type
Description

agreementId

uint256

The agreement identifier

Returns: AgreementData struct

Reverts: AgreementNotFound if agreement doesn't exist


getParsedAgreementData

Returns agreement data with human-readable strings.

Parameters:

Name
Type
Description

agreementId

uint256

The agreement identifier

Returns: ParsedAgreementData struct with kind and purpose as strings

Reverts: AgreementNotFound if agreement doesn't exist


hashAgreementData

Computes the EIP-712 struct hash for agreement data.

Returns: The keccak256 hash of the encoded struct


getTypedDataHash

Returns the complete EIP-712 typed data hash for signing.

Returns: The hash that should be signed by the counterparty


Events

AgreementCreated

Emitted when a new agreement is successfully created.


Errors

Error
Description

AgreementNotFound()

The requested agreement ID does not exist (IAgreement)

Unauthorized()

Caller is not authorized for the action

InvalidSignature()

The provided signature is invalid or from wrong signer

AgreementAlreadyExists(uint256)

An agreement with the same parameters already exists

EmptyBatchInput()

The batch input array is empty


EIP-712 Type Definition

The contract uses the following EIP-712 type for signing:


Usage Examples

Creating an Agreement (TypeScript/Viem)

Creating an UnRevokable Agreement

Creating an Agreement with Grace Period

Checking Revocation Eligibility

Batch Creating Agreements

Reading Agreement Data


Security Considerations

  1. Replay Protection: Each unique agreement can only be created once via digest tracking

  2. Signature Verification: Counterparty signatures are verified using EIP-712 ECDSA recovery

  3. Self-Signing: If counterParty equals msg.sender, no signature verification is needed

  4. Immutability: Once created, agreements cannot be modified or deleted

  5. Revocation Control: The revokeEligibility setting is immutable and enforced by the Consent contract


  • IAgreement: Interface defining AgreementData struct and core functions

  • Consent: Creates consent records referencing agreements

  • DIDRegistry: Manages decentralized identities for parties


Deployment

The contract is deployed using Hardhat Ignition:

Last updated