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
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.
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.
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
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
string
Domain name (e.g., "Agreement")
version
string
Domain version (e.g., "1")
createAgreement
Creates a new agreement with EIP-712 signature verification.
Parameters:
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:
agreementId
uint256
The unique ID of the created agreement
Requirements:
kindcannot be empty (bytes32(0))If
counterParty!=msg.sender, signature must be validAgreement with same parameters cannot already exist
Emits: AgreementCreated
batchCreateAgreements
Creates multiple agreements in a single transaction.
Parameters:
inputs
AgreementInput[]
Array of agreement inputs
Returns:
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:
agreementId
uint256
The agreement identifier
createdAt
uint64
Timestamp when the consent record was created
Returns:
(bool)
bool
true if revocation is allowed, false otherwise
Logic:
UnRevokable: Always returnsfalseInstantlyRevokable: Always returnstrueRevokableAfterGracePeriod: Returnstrueonly if(block.timestamp - createdAt) >= revokeGracePeriodSeconds
Reverts: AgreementNotFound if agreement doesn't exist
getAgreementData
Retrieves raw agreement data by ID.
Parameters:
agreementId
uint256
The agreement identifier
Returns: AgreementData struct
Reverts: AgreementNotFound if agreement doesn't exist
getParsedAgreementData
Returns agreement data with human-readable strings.
Parameters:
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
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
Replay Protection: Each unique agreement can only be created once via digest tracking
Signature Verification: Counterparty signatures are verified using EIP-712 ECDSA recovery
Self-Signing: If
counterPartyequalsmsg.sender, no signature verification is neededImmutability: Once created, agreements cannot be modified or deleted
Revocation Control: The
revokeEligibilitysetting is immutable and enforced by the Consent contract
Related Contracts
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

