ADRResolverV3
On-chain Alternative Dispute Resolution with arbitration clause anchoring, an 11-state dispute lifecycle, evidence via hash anchors, multi-party settlement, deadline management, automated triggers, and EAS attestation integration.
Overview
ADRResolverV3 implements a complete on-chain arbitration workflow that mirrors real-world Alternative Dispute Resolution (ADR) processes. The resolver manages the full lifecycle from clause registration through dispute initiation, evidence submission, hearings, awards, compliance, and settlement -- all anchored to Integra records.
The contract spans three resolver categories simultaneously:
- Behavioral -- Lifecycle hooks notify the resolver of record events (registration, transfer, tokenizer association). Named actions (
check-conditions,check-compliance) allow callers to trigger automated evaluations. - Gatekeeper -- Active disputes block ownership transfers (past INITIATED state) and tokenization (any active dispute), preventing record manipulation during proceedings.
- Automation -- Trigger conditions (payment expiry, attestation expiry, custom evaluators) can automatically initiate disputes when evaluated by keeper bots.
ADRResolverV3 also implements IContractV2 for integration with IntegraLens and off-chain services.
Key Concepts
Pattern 1: Arbitration Clause Anchor
Before any dispute can be initiated, the record must have a registered and locked arbitration clause. The clause captures the terms under which disputes will be resolved:
struct ArbitrationClause {
bytes32 providerReference; // External provider identifier
bytes32 rulesHash; // Hash of arbitration rules document
bytes32 clauseTextHash; // Hash of full clause text
uint64 registeredAt; // When the clause was first registered
uint64 lockedAt; // When the clause was locked (immutable after this)
uint8 arbitratorCount; // Number of arbitrators (must be > 0)
bool locked; // Once locked, clause cannot be modified
}| Field | Type | Description |
|---|---|---|
providerReference | bytes32 | External provider identifier |
rulesHash | bytes32 | Hash of the arbitration rules document |
clauseTextHash | bytes32 | Hash of the full clause text |
registeredAt | uint64 | Timestamp when the clause was first registered |
lockedAt | uint64 | Timestamp when the clause was locked |
arbitratorCount | uint8 | Number of arbitrators (must be > 0) |
locked | bool | Once true, clause cannot be modified |
The clause lifecycle is simple: register (modifiable) then lock (immutable). Once locked, the clause cannot be changed and disputes can be initiated against the record. String fields (providerPortalURI, rulesReference, venue, governingLaw) are emitted in events only and not stored on-chain.
Pattern 2: Dispute Lifecycle State Machine
The dispute lifecycle is managed by the StateMachine library with 11 states organized into three groups:
Active states (dispute is in progress):
| State | Value | Description |
|---|---|---|
INITIATED | 1 | Dispute has been raised; no external case reference yet |
FILED | 2 | External case reference bound; response deadline set |
PENDING | 3 | Under review by the provider |
HEARING | 4 | Hearing in progress |
AWARDED | 5 | Transient state during recordAward -- never persists |
COMPLIANCE | 6 | Award issued with compliance deadline; parties must fulfill requirements |
Terminal states (dispute is resolved):
| State | Value | Description |
|---|---|---|
CLOSED | 7 | Dispute concluded normally (compliance confirmed or no compliance required) |
SETTLED | 8 | Parties reached a multi-party settlement |
WITHDRAWN | 9 | Initiator withdrew the dispute |
EXPIRED | 10 | Compliance deadline passed without confirmation |
Inactive:
| State | Value | Description |
|---|---|---|
NONE | 0 | No dispute exists for this record |
State Transition Diagram
stateDiagram-v2
[*] --> NONE
NONE --> INITIATED : initiateDispute() / evaluateConditions()
INITIATED --> FILED : bindCaseReference() [restricted]
INITIATED --> WITHDRAWN : withdrawDispute()
INITIATED --> SETTLED : confirmSettlement() [restricted]
FILED --> PENDING : advanceState()
FILED --> SETTLED : confirmSettlement() [restricted]
PENDING --> HEARING : advanceState()
PENDING --> SETTLED : confirmSettlement() [restricted]
HEARING --> AWARDED : recordAward() [restricted, transient]
HEARING --> SETTLED : confirmSettlement() [restricted]
AWARDED --> COMPLIANCE : recordAward() [atomic, if deadline > 0]
AWARDED --> CLOSED : recordAward() [atomic, if deadline == 0]
COMPLIANCE --> CLOSED : confirmCompliance() [restricted]
COMPLIANCE --> EXPIRED : checkCompliance() [restricted]
COMPLIANCE --> SETTLED : confirmSettlement() [restricted]
CLOSED --> [*]
SETTLED --> [*]
WITHDRAWN --> [*]
EXPIRED --> [*]Two-Tier Transition Model
Generic transitions (via advanceState, callable by authorized providers):
- INITIATED to WITHDRAWN
- FILED to PENDING
- PENDING to HEARING
Restricted transitions (only via specific domain functions with their own authorization):
- INITIATED to FILED -- only via
bindCaseReference() - HEARING to AWARDED -- only via
recordAward() - AWARDED to COMPLIANCE -- only via
recordAward()(atomic) - AWARDED to CLOSED -- only via
recordAward()(atomic) - COMPLIANCE to CLOSED -- only via
confirmCompliance() - COMPLIANCE to EXPIRED -- only via
checkCompliance() - Any active state to SETTLED -- only via
confirmSettlement()when quorum is reached
The AWARDED state is transient -- it is set and immediately transitioned within the same recordAward() call, either to COMPLIANCE (if complianceDeadline > 0) or to CLOSED (if no compliance is required). It never persists between transactions.
Evidence Anchoring (HashAnchor)
Evidence is submitted as content hashes with metadata, stored via the HashAnchor library in an append-only list per dispute. Limits: up to MAX_EVIDENCE_PER_DISPUTE (500) entries per dispute, descriptions limited to MAX_EVIDENCE_DESCRIPTION (200) characters.
Multi-Party Settlement (MultiPartyConfirmation)
Settlement is a multi-party consensus mechanism. Multiple parties must confirm the same settlement document hash before the dispute transitions to SETTLED.
- A party calls
confirmSettlement(integraHash, settlementHash). - If the tracker is not initialized, it is set with the record's settlement threshold (default: 2).
- If the
settlementHashdiffers from the previously confirmed hash, all prior confirmations are invalidated (nonce increments) and a 1-hour cooldown starts. - Each party can only confirm once per nonce (replay protection).
- When the confirmation count reaches the threshold, the dispute transitions to SETTLED and is archived.
Settlement can be confirmed by parties or agents -- not providers. Maximum 5 settlement proposal changes per dispute.
Deadline Management (DeadlineTracker)
| Deadline | When Set | Effect |
|---|---|---|
| Response deadline | When bindCaseReference() is called (INITIATED to FILED), or lazily when fileResponse() is first called | fileResponse() reverts if the deadline has passed |
| Compliance deadline | When recordAward() is called with complianceDeadline > 0 | checkCompliance() or evaluateConditions() can expire the dispute |
Default response deadline: 30 days from filing. Can be overridden by the record owner via setResponseDeadline(). Both deadlines are cleared when a dispute is archived.
Automation Triggers (IConditionEvaluator)
Three types of automated trigger conditions:
| Condition Type | Constant | Value | Description |
|---|---|---|---|
| Payment expiry | CONDITION_PAYMENT_EXPIRY | 0 | Checks if N payment requests have expired on the Signal contract |
| Attestation expiry | CONDITION_ATTESTATION_EXPIRY | 1 | Checks if an EAS attestation has been revoked or expired |
| Custom | CONDITION_CUSTOM | 2 | Delegates to an external IConditionEvaluator contract |
Conditions are configured per record and evaluated permissionlessly via evaluateConditions(). When a condition is met, it is deactivated (fires once) and a dispute is automatically initiated with TRIGGER_AUTOMATED type. Custom evaluators must be pre-approved by the governor via setApprovedEvaluator() and are called with a gas limit of EVALUATOR_GAS_LIMIT (50,000).
Role-Based Access
| Role | Constant | Authorized By | Can |
|---|---|---|---|
| Provider | PROVIDER_ROLE = keccak256("ADR_PROVIDER") | Record owner/executor | Advance dispute state, bind case references, record awards, add compliance requirements |
| Agent | AGENT_ROLE = keccak256("ADR_AGENT") | Record owner/executor | Act on behalf of a principal (record party) in disputes, evidence, settlement, compliance |
Agents require a principal -- a record party whom the agent represents. The principal must be validated as a record party at authorization time. Both providers and agents can be revoked, but revocation is blocked during active disputes past the INITIATED state.
Record Party Definition
A "record party" is any of:
- The record owner
- An authorized executor for the record
- A token holder on the record's tokenizer (detected via
ITokenParty.isTokenHolder, gas-limited to 100,000)
Contract Details
| Property | Value |
|---|---|
| Solidity | 0.8.28 |
| License | MIT |
| Source | src/resolvers/adr/ADRResolverV3.sol (1250 lines) |
| Inherits | BaseResolver, AuthorizedActors, IBehavioralResolver, IGatekeeperResolver, IAutomationResolver, ADREvents, IContractV2 |
| Uses | StateMachine, MultiPartyConfirmation, HashAnchor, DeadlineTracker |
| Upgradeable | No |
| Category bitmask | CATEGORY_BEHAVIORAL | CATEGORY_GATEKEEPER | CATEGORY_AUTOMATION (14) |
| Version | 3.0.0 ((3 << 32) | (0 << 16) | 0) |
| State schema | "ADRResolverV3" |
Constructor
constructor(
address integraRecord, // IntegraRecordV1 contract address
address trustedForwarder, // ERC-2771 trusted forwarder
address signal, // IntegraSignalV1 contract (0x0 to disable payment triggers)
address eas // EAS contract address (required, non-zero)
)Immutables
| Name | Type | Description |
|---|---|---|
INTEGRA_RECORD | address | IntegraRecordV1 contract for owner/executor lookups |
SIGNAL | address | IntegraSignalV1 contract for payment expiry checks (address(0) if disabled) |
EAS | IEAS | Ethereum Attestation Service contract for attestation expiry checks |
Constants (from ADRTypes.sol)
| Name | Value | Description |
|---|---|---|
MAX_TRIGGER_CONDITIONS | 10 | Maximum trigger conditions per record |
MAX_EVIDENCE_PER_DISPUTE | 500 | Maximum evidence entries per dispute (via HashAnchor) |
MAX_EVIDENCE_DESCRIPTION | 200 | Maximum description length for evidence entries |
MAX_COUNTERCLAIMS | 10 | Maximum counterclaims per dispute |
MAX_COMPLIANCE_REQUIREMENTS | 20 | Maximum compliance requirements per dispute |
MAX_PAYMENT_REQUESTS_TO_CHECK | 100 | Maximum Signal payment requests to scan per evaluation |
EVALUATOR_GAS_LIMIT | 50,000 | Gas cap for external evaluator calls |
DEFAULT_RESPONSE_DEADLINE | 30 days | Default response filing deadline |
SETTLEMENT_COOLDOWN | 1 hour | Minimum time between settlement hash changes |
MAX_SETTLEMENT_PROPOSALS | 5 | Maximum settlement proposal changes per dispute |
Structs (from ADRTypes.sol)
Dispute
struct Dispute {
bytes32 integraHash; // Record this dispute is against
bytes32 groundsHash; // Hash of the dispute grounds document
bytes32 caseReference; // External case reference (bound by provider)
bytes32 awardHash; // Hash of the award document (set by provider)
bytes32 settlementHash; // Hash of the settlement document (set by consensus)
address initiator; // Address that initiated the dispute
uint64 disputeId; // Sequential ID for this record's disputes
uint64 initiatedAt; // Block timestamp of initiation
uint64 complianceDeadline; // Unix timestamp for compliance deadline (0 = none)
uint8 triggerType; // TRIGGER_PARTY, TRIGGER_AGENT, TRIGGER_AUTOMATED, TRIGGER_THIRD_PARTY
}Response
struct Response {
bytes32 responseHash; // Hash of the response document
address respondent; // Address that filed the response
uint64 filedAt; // Block timestamp of filing
bool contestsJurisdiction; // Whether the respondent contests jurisdiction
}Counterclaim
struct Counterclaim {
bytes32 groundsHash; // Hash of the counterclaim grounds document
address claimant; // Address that filed the counterclaim
uint64 filedAt; // Block timestamp of filing
}ComplianceRequirement
struct ComplianceRequirement {
bytes32 descriptionHash; // Hash of the requirement description
bytes32 evidenceHash; // Hash of evidence satisfying the requirement (0 if unsatisfied)
uint64 deadline; // Optional per-requirement deadline (0 = no deadline)
bool satisfied; // Whether this requirement has been satisfied
}TriggerCondition
struct TriggerCondition {
uint8 conditionType; // CONDITION_PAYMENT_EXPIRY, CONDITION_ATTESTATION_EXPIRY, CONDITION_CUSTOM
bytes32 targetReference; // Condition-specific: attestation UID or evaluator address (as bytes32)
uint64 threshold; // Condition-specific: e.g., number of expired payments required
bool active; // Whether this condition is active (deactivated after triggering)
}State Bitmask Constants
uint16 constant ACTIVE_STATES_MASK = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 6);
uint16 constant TERMINAL_STATES_MASK = (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10);Functions
Arbitration Clause Management
registerClause
function registerClause(
bytes32 integraHash,
bytes32 providerReference,
bytes32 rulesHash,
bytes32 clauseTextHash,
uint8 arbitratorCount,
string calldata providerPortalURI,
string calldata rulesReference,
string calldata venue,
string calldata governingLaw
) external nonReentrant whenNotPausedAccess: Record owner or executor.
Description: Registers or updates an arbitration clause for a record. Can be called multiple times before locking. Emits ClauseRegistered on first registration, ClauseUpdated on subsequent calls.
Reverts: ClauseAlreadyLocked(), InvalidArbitratorCount()
lockClause
function lockClause(bytes32 integraHash) external nonReentrant whenNotPausedAccess: Record owner or executor.
Description: Locks the arbitration clause, making it immutable. A clause must be registered before it can be locked. Once locked, disputes can be initiated against the record.
Events: ClauseLocked(integraHash)
Reverts: ClauseNotFound(), ClauseAlreadyLocked()
Dispute Lifecycle
initiateDispute
function initiateDispute(
bytes32 integraHash,
bytes32 groundsHash,
uint8 triggerType
) external nonReentrant whenNotPausedAccess: Record party, authorized provider, or authorized agent.
Description: Initiates a dispute. Requires a locked clause, no existing active dispute, and a non-zero grounds hash. TRIGGER_AUTOMATED (2) is rejected -- automated triggers go through evaluateConditions().
Transitions state: NONE to INITIATED.
Events: DisputeInitiated(integraHash, disputeId, triggerType, initiator, groundsHash)
Reverts: AutomatedTriggerNotAllowed(), NotRecordParty(), ClauseNotLocked(), DisputeAlreadyActive(), EmptyGroundsHash()
withdrawDispute
function withdrawDispute(bytes32 integraHash) external nonReentrant whenNotPausedAccess: Dispute initiator only.
Description: Withdraws a dispute in INITIATED state. The dispute is archived after withdrawal.
Transitions state: INITIATED to WITHDRAWN.
Events: DisputeWithdrawn(integraHash, disputeId, withdrawnBy), DisputeArchived(integraHash, disputeId, STATE_WITHDRAWN)
Reverts: NoActiveDispute(), NotDisputeInitiator(), StateMachine.InvalidTransition(fromState, toState)
advanceState
function advanceState(bytes32 integraHash, uint8 newState) external nonReentrant whenNotPausedAccess: Authorized provider.
Description: Advances the dispute state machine through generic transitions only. Restricted transitions are blocked and must go through their domain-specific functions. Archives the dispute if the new state is terminal.
Events: DisputeStateAdvanced(integraHash, disputeId, fromState, toState, actor), optionally DisputeArchived(...)
Reverts: NotAuthorizedProvider(), NoActiveDispute(), StateMachine.InvalidTransition(fromState, toState)
bindCaseReference
function bindCaseReference(bytes32 integraHash, bytes32 caseReference) external nonReentrant whenNotPausedAccess: Authorized provider.
Description: Binds an external case reference to the dispute and atomically transitions INITIATED to FILED. The case reference must be unique and non-zero. Sets the response deadline to 30 days from the current timestamp.
Transitions state: INITIATED to FILED (restricted).
Events: CaseReferenceBound(integraHash, disputeId, caseReference, provider), DisputeStateAdvanced(...)
Reverts: NotAuthorizedProvider(), InvalidCaseReference(), CaseReferenceAlreadyBound(), NoActiveDispute(), StateMachine.InvalidTransition(...)
Award and Compliance
recordAward
function recordAward(
bytes32 integraHash,
bytes32 awardHash,
uint64 complianceDeadline
) external nonReentrant whenNotPausedAccess: Authorized provider.
Description: Records an arbitration award and atomically transitions the dispute:
- With compliance (
complianceDeadline > 0): HEARING to AWARDED to COMPLIANCE - Without compliance (
complianceDeadline == 0): HEARING to AWARDED to CLOSED (archived)
Events: AwardRecorded(integraHash, disputeId, awardHash, complianceDeadline), DisputeStateAdvanced(...), optionally DisputeClosed(...) and DisputeArchived(...)
Reverts: NotAuthorizedProvider(), InvalidAwardHash(), NoActiveDispute(), StateMachine.InvalidTransition(...), ComplianceDeadlineInPast()
confirmCompliance
function confirmCompliance(bytes32 integraHash, bytes32 evidenceHash) external nonReentrant whenNotPausedAccess: Record party, authorized provider, or authorized agent.
Description: Confirms compliance and closes the dispute. All compliance requirements must be satisfied first. The dispute must be in COMPLIANCE state.
Transitions state: COMPLIANCE to CLOSED.
Events: ComplianceConfirmed(integraHash, disputeId, evidenceHash, confirmedBy), DisputeClosed(...), DisputeArchived(...)
Reverts: NoActiveDispute(), NotInComplianceState(), NotRecordParty(), ComplianceRequirementsNotMet()
checkCompliance
function checkCompliance(bytes32 integraHash) external nonReentrantAccess: Permissionless.
Description: Checks if the compliance deadline has passed and expires the dispute if so. No-op if the deadline has not yet passed.
Transitions state: COMPLIANCE to EXPIRED (only if deadline has passed).
Events: ComplianceExpired(integraHash, disputeId), DisputeArchived(...)
Reverts: NoActiveDispute(), NotInComplianceState()
addComplianceRequirement
function addComplianceRequirement(
bytes32 integraHash,
bytes32 descriptionHash,
uint64 deadline
) external nonReentrant whenNotPausedAccess: Authorized provider.
Description: Adds a compliance requirement to an active dispute in COMPLIANCE state. Up to 20 requirements per dispute.
Events: ComplianceRequirementAdded(integraHash, disputeId, requirementIndex, descriptionHash)
Reverts: NotAuthorizedProvider(), NoActiveDispute(), NotInComplianceState(), MaxComplianceRequirementsReached(), ComplianceDeadlineInPast()
satisfyRequirement
function satisfyRequirement(
bytes32 integraHash,
uint256 index,
bytes32 evidenceHash
) external nonReentrant whenNotPausedAccess: Record party, authorized provider, or authorized agent.
Description: Marks a compliance requirement as satisfied. Dispute must be in COMPLIANCE state.
Events: ComplianceRequirementSatisfied(integraHash, disputeId, index, evidenceHash)
Reverts: NoActiveDispute(), NotInComplianceState(), NotRecordParty(), RequirementIndexOutOfBounds(), RequirementAlreadySatisfied()
Evidence
submitEvidence
function submitEvidence(
bytes32 integraHash,
bytes32 evidenceHash,
string calldata description
) external nonReentrant whenNotPausedAccess: Record party, authorized provider, or authorized agent.
Description: Submits evidence for an active dispute. Maximum 500 entries per dispute, descriptions limited to 200 characters.
Events: EvidenceSubmitted(integraHash, disputeId, evidenceHash, submitter, evidenceIndex)
Reverts: NoActiveDispute(), NotRecordParty(), HashAnchor.EmptyContentHash(), HashAnchor.StoreAtCapacity(...), HashAnchor.DescriptionTooLong(...)
Settlement
confirmSettlement
function confirmSettlement(bytes32 integraHash, bytes32 settlementHash) external nonReentrant whenNotPausedAccess: Record party or authorized agent (not providers).
Description: Records a settlement confirmation. When the required confirmations (default: 2) are reached for the same hash, the dispute transitions to SETTLED and is archived.
Events: SettlementConfirmed(integraHash, disputeId, settlementHash, confirmedBy, confirmationNumber), optionally DisputeSettled(...) and DisputeArchived(...)
Reverts: InvalidSettlementHash(), NoActiveDispute(), NotRecordParty(), MaxSettlementProposalsReached(), MultiPartyConfirmation.AlreadyConfirmed(...), MultiPartyConfirmation.CooldownActive(...)
setSettlementThreshold
function setSettlementThreshold(bytes32 integraHash, uint8 threshold) external nonReentrant whenNotPausedAccess: Record owner or executor.
Description: Sets the number of confirmations required for settlement. Must be at least 2. No active dispute allowed.
Events: SettlementThresholdSet(integraHash, threshold)
Reverts: ActiveDisputeExists(), MultiPartyConfirmation.ThresholdTooLow(threshold)
Response and Counterclaims
fileResponse
function fileResponse(
bytes32 integraHash,
bytes32 responseHash,
bool contestsJurisdiction
) external nonReentrant whenNotPausedAccess: Non-initiator record party only. One response per dispute. Dispute must be in INITIATED, FILED, or PENDING state.
Description: Files a formal response. Response deadline is enforced -- if not already set, it is computed as current time + 30 days.
Events: ResponseFiled(integraHash, disputeId, responseHash, respondent, contestsJurisdiction)
Reverts: NoActiveDispute(), EmptyResponseHash(), InvalidDisputeState(), CannotRespondToOwnDispute(), NotRecordParty(), ResponseAlreadyFiled(), ResponseDeadlinePassed()
fileCounterclaim
function fileCounterclaim(bytes32 integraHash, bytes32 groundsHash) external nonReentrant whenNotPausedAccess: Non-initiator record party only. One counterclaim per party per dispute, maximum 10 total. Dispute must be in INITIATED, FILED, or PENDING state.
Events: CounterclaimFiled(integraHash, disputeId, counterclaimIndex, groundsHash, claimant)
Reverts: NoActiveDispute(), EmptyGroundsHash(), InvalidDisputeState(), CannotRespondToOwnDispute(), NotRecordParty(), MaxCounterclaimsReached(), CounterclaimAlreadyFiled()
setResponseDeadline
function setResponseDeadline(bytes32 integraHash, uint64 deadline) external nonReentrant whenNotPausedAccess: Record owner or executor. Dispute must be in INITIATED or FILED state.
Events: ResponseDeadlineSet(integraHash, deadline)
Reverts: NoActiveDispute(), InvalidDisputeState(), DeadlineTracker.DeadlineInPast(...)
getResponse
function getResponse(bytes32 integraHash, uint64 disputeId) external view returns (Response memory)Access: Anyone (view).
Provider and Agent Authorization
authorizeProvider
function authorizeProvider(bytes32 integraHash, address provider) external nonReentrant whenNotPausedAccess: Record owner or executor. No active dispute allowed.
Events: ActorAuthorized(PROVIDER_ROLE, integraHash, provider, address(0), authorizedBy)
Reverts: ActiveDisputeExists(), ActorAlreadyAuthorized(...), InvalidActorAddress()
revokeProvider
function revokeProvider(bytes32 integraHash, address provider) external nonReentrant whenNotPausedAccess: Record owner or executor. Blocked during active disputes past INITIATED state.
Events: ActorRevoked(PROVIDER_ROLE, integraHash, provider, revokedBy)
Reverts: ActiveDisputeExists(), ActorNotAuthorized(...)
authorizeAgent
function authorizeAgent(bytes32 integraHash, address agent, address principal) external nonReentrant whenNotPausedAccess: Record owner or executor. Principal must be a record party.
Events: ActorAuthorized(AGENT_ROLE, integraHash, agent, principal, authorizedBy)
Reverts: NotRecordParty(), ActorAlreadyAuthorized(...), InvalidActorAddress()
revokeAgent
function revokeAgent(bytes32 integraHash, address agent) external nonReentrant whenNotPausedAccess: Record owner or executor. Blocked during active disputes past INITIATED state.
Events: ActorRevoked(AGENT_ROLE, integraHash, agent, revokedBy)
Reverts: ActiveDisputeExists(), ActorNotAuthorized(...)
Automation Triggers
addTriggerCondition
function addTriggerCondition(bytes32 integraHash, TriggerCondition calldata condition) external nonReentrant whenNotPausedAccess: Record owner or executor.
Events: TriggerConditionAdded(integraHash, conditionIndex, conditionType, targetReference)
Reverts: InvalidConditionType(), MaxTriggersReached()
deactivateTrigger
function deactivateTrigger(bytes32 integraHash, uint256 index) external nonReentrant whenNotPausedAccess: Record owner or executor.
Events: TriggerConditionDeactivated(integraHash, index)
Reverts: ConditionIndexOutOfBounds()
setApprovedEvaluator
function setApprovedEvaluator(address evaluator, bool approved) external onlyRole(GOVERNOR_ROLE)Access: Governor only.
evaluateConditions
function evaluateConditions(bytes32 integraHash)
external nonReentrant
returns (bool triggered, bytes32 conditionId, bytes memory data)Access: Permissionless (designed for keeper bots).
Description: Evaluates all active trigger conditions. If any condition is met, a dispute is initiated with TRIGGER_AUTOMATED type and the condition is deactivated. Returns early on the first match. Returns (false, ...) silently if there is an active dispute or clause is not locked. Reverts if no conditions are configured or none are met.
Events: TriggerConditionMet(...), DisputeInitiated(...)
Reverts: NoConditionsConfigured(), NoConditionsMet()
activeConditionCount
function activeConditionCount(bytes32 integraHash) external view returns (uint256)Access: Anyone (view).
Behavioral Hooks and Actions
onRegistered, onTransferred, onTokenizerAssociated
Lifecycle hooks called by IntegraRecordV1 (restricted via onlyRecord). Each emits a corresponding event (RecordRegistered, RecordTransferred, TokenizerAssociated).
executeAction
function executeAction(bytes32 integraHash, string calldata action, bytes calldata)
external nonReentrant whenNotPaused
returns (bool success, bytes memory result)Access: Permissionless.
Description: Executes a named action. Supported actions:
| Action | Description |
|---|---|
"check-conditions" | Evaluates trigger conditions and initiates dispute if met |
"check-compliance" | Checks compliance deadline and expires dispute if passed |
Returns (false, "") for unrecognized action names.
availableActions
function availableActions(bytes32) external pure returns (string[] memory)Returns empty array. ADR actions are determined dynamically via executeAction().
Gatekeeper Functions
canOwn
function canOwn(bytes32 integraHash, address) external view returns (bool, string memory)Blocks ownership transfers when an active dispute exists past INITIATED state.
canTokenize
function canTokenize(bytes32 integraHash, address) external view returns (bool, string memory)Blocks tokenization when any active dispute exists.
isExpired
function isExpired(bytes32) external pure returns (bool, uint256)Always returns (false, 0). ADR resolver does not implement expiry.
IContractV2 Functions
getRecordState
function getRecordState(bytes32 integraHash) external view returns (bytes memory)Returns ABI-encoded: (uint8 state, bool clauseLocked, uint64 disputeId, address initiator, bytes32 awardHash).
getAvailableActions
function getAvailableActions(bytes32 integraHash, address caller) external view returns (bytes4[] memory)Returns function selectors available to the caller based on current dispute state and authorization.
stateSchema
function stateSchema() external pure returns (string memory)Returns "ADRResolverV3".
Events
Clause Events
| Event | Parameters | When Emitted |
|---|---|---|
ClauseRegistered | integraHash (indexed), providerReference, clauseTextHash, rulesHash, arbitratorCount, providerPortalURI, rulesReference, venue, governingLaw | Clause registered |
ClauseUpdated | integraHash (indexed), providerReference, clauseTextHash, rulesHash, arbitratorCount, providerPortalURI, rulesReference, venue, governingLaw | Clause updated (before lock) |
ClauseLocked | integraHash (indexed) | Clause locked (immutable) |
Dispute Lifecycle Events
| Event | Parameters | When Emitted |
|---|---|---|
DisputeInitiated | integraHash (indexed), disputeId (indexed), triggerType, initiator, groundsHash | Dispute initiated |
DisputeStateAdvanced | integraHash (indexed), disputeId (indexed), fromState, toState, actor | State machine transition |
DisputeWithdrawn | integraHash (indexed), disputeId (indexed), withdrawnBy | Dispute withdrawn |
DisputeSettled | integraHash (indexed), disputeId (indexed), settlementHash | Settlement quorum reached |
DisputeClosed | integraHash (indexed), disputeId (indexed) | Dispute closed normally |
DisputeArchived | integraHash (indexed), disputeId (indexed), terminalState | Dispute moved to history |
Award and Compliance Events
| Event | Parameters | When Emitted |
|---|---|---|
AwardRecorded | integraHash (indexed), disputeId (indexed), awardHash, complianceDeadline | Award recorded |
ComplianceConfirmed | integraHash (indexed), disputeId (indexed), evidenceHash, confirmedBy | Compliance confirmed |
ComplianceExpired | integraHash (indexed), disputeId (indexed) | Compliance deadline passed |
ComplianceRequirementAdded | integraHash (indexed), disputeId (indexed), requirementIndex, descriptionHash | Requirement added |
ComplianceRequirementSatisfied | integraHash (indexed), disputeId (indexed), requirementIndex, evidenceHash | Requirement satisfied |
Settlement Events
| Event | Parameters | When Emitted |
|---|---|---|
SettlementConfirmed | integraHash (indexed), disputeId (indexed), settlementHash, confirmedBy, confirmationNumber | Party confirms settlement |
SettlementThresholdSet | integraHash (indexed), threshold | Threshold configured |
Evidence Events
| Event | Parameters | When Emitted |
|---|---|---|
EvidenceSubmitted | integraHash (indexed), disputeId (indexed), evidenceHash, submitter, evidenceIndex | Evidence anchored |
Provider Bridge Events
| Event | Parameters | When Emitted |
|---|---|---|
CaseReferenceBound | integraHash (indexed), disputeId (indexed), caseReference, provider | Case reference bound |
Response and Counterclaim Events
| Event | Parameters | When Emitted |
|---|---|---|
ResponseFiled | integraHash (indexed), disputeId (indexed), responseHash, respondent, contestsJurisdiction | Response filed |
CounterclaimFiled | integraHash (indexed), disputeId (indexed), counterclaimIndex, groundsHash, claimant | Counterclaim filed |
ResponseDeadlineSet | integraHash (indexed), deadline | Response deadline set |
Trigger Events
| Event | Parameters | When Emitted |
|---|---|---|
TriggerConditionAdded | integraHash (indexed), conditionIndex, conditionType, targetReference | Trigger added |
TriggerConditionDeactivated | integraHash (indexed), conditionIndex | Trigger deactivated |
TriggerConditionMet | integraHash (indexed), conditionIndex, groundsHash | Trigger condition met |
Lifecycle Hook Events
| Event | Parameters | When Emitted |
|---|---|---|
RecordRegistered | integraHash (indexed), owner | Record registered with this resolver |
RecordTransferred | integraHash (indexed), from, to | Record ownership transferred |
TokenizerAssociated | integraHash (indexed), tokenizer | Tokenizer associated with record |
Errors
Clause Errors
| Error | When Thrown |
|---|---|
ClauseAlreadyLocked() | Attempting to register or lock an already-locked clause |
ClauseNotFound() | Attempting to lock a clause that has not been registered |
ClauseNotLocked() | Attempting to initiate a dispute without a locked clause |
InvalidArbitratorCount() | Arbitrator count is 0 |
Dispute Errors
| Error | When Thrown |
|---|---|
DisputeAlreadyActive() | Attempting to initiate when one is already active |
NoActiveDispute() | Operating on a non-existent or archived dispute |
NotDisputeInitiator() | Non-initiator attempting to withdraw |
NotRecordParty() | Caller is not a record party, provider, or agent |
AutomatedTriggerNotAllowed() | Passing TRIGGER_AUTOMATED to initiateDispute() |
InvalidDisputeState() | Operation not allowed in the current state |
Response Errors
| Error | When Thrown |
|---|---|
ResponseAlreadyFiled() | Second response attempt |
ResponseDeadlinePassed() | Response deadline has elapsed |
CannotRespondToOwnDispute() | Initiator attempting to respond or counterclaim |
EmptyResponseHash() | Response hash is zero |
Counterclaim Errors
| Error | When Thrown |
|---|---|
MaxCounterclaimsReached() | More than 10 counterclaims |
EmptyGroundsHash() | Grounds hash is zero |
CounterclaimAlreadyFiled() | Same address filing a second counterclaim |
Compliance Errors
| Error | When Thrown |
|---|---|
MaxComplianceRequirementsReached() | More than 20 requirements |
RequirementAlreadySatisfied() | Requirement already satisfied |
RequirementIndexOutOfBounds() | Index >= requirements length |
ComplianceDeadlineInPast() | Deadline is not in the future |
NotInComplianceState() | Operation requires COMPLIANCE state |
ComplianceRequirementsNotMet() | Not all requirements satisfied when confirming compliance |
Settlement Errors
| Error | When Thrown |
|---|---|
InvalidSettlementHash() | Settlement hash is zero |
MaxSettlementProposalsReached() | More than 5 settlement proposal changes |
Provider Errors
| Error | When Thrown |
|---|---|
NotAuthorizedProvider() | Caller is not an authorized provider |
CaseReferenceAlreadyBound() | Case reference already bound to another record |
ActiveDisputeExists() | Attempting to revoke provider/agent during active dispute past INITIATED |
InvalidCaseReference() | Case reference is zero |
Trigger Errors
| Error | When Thrown |
|---|---|
MaxTriggersReached() | More than 10 trigger conditions |
ConditionIndexOutOfBounds() | Index >= conditions length |
NoConditionsConfigured() | evaluateConditions() called with no conditions |
NoConditionsMet() | No triggered conditions found |
InvalidConditionType() | Condition type > CONDITION_CUSTOM |
Award Errors
| Error | When Thrown |
|---|---|
InvalidAwardHash() | Award hash is zero |
Other Errors
| Error | When Thrown |
|---|---|
ZeroAddress() | EAS address is zero in constructor |
Security Considerations
-
Clause immutability. Once locked, an arbitration clause cannot be modified. The two-step register-then-lock process allows negotiation before commitment.
-
Dispute exclusivity. Only one dispute can be active per record at a time.
-
Provider revocation lockout. Providers and agents cannot be revoked during active disputes past INITIATED state.
-
Settlement cooldown. A 1-hour cooldown between settlement hash changes prevents rapid hash-switching attacks.
-
Gas-bounded external calls. Token holder checks (100,000 gas), custom condition evaluators (50,000 gas), and payment/attestation checks (try/catch) prevent DoS from malicious external contracts.
-
Automated trigger safety.
TRIGGER_AUTOMATEDcannot be passed directly toinitiateDispute(). Custom evaluators must be pre-approved by the governor. -
Transient AWARDED state. Never persists between transactions, preventing the dispute from getting stuck.
-
Case reference uniqueness. The
caseToEntityreverse mapping ensures each case reference is bound to one record. Cleared on archive. -
Reentrancy protection. All state-changing functions use
ReentrancyGuardTransient(EIP-1153). -
Archive isolation. Terminal disputes clear the active dispute, state machine, deadlines, and settlement proposal counter.
-
Permissionless compliance expiry.
checkCompliance()is permissionless, allowing keeper bots to expire overdue disputes.