Abstract
ERC-7753 extends classical on-chain escrow with proof-of-delivery semantics, time-to-live expiry, and a single-arbitrator dispute system. On createEscrow(), 1 % of the gross amount is immediately collected as a protocol fee; the remaining 99 % is locked until the creator submits a proofHash (delivery confirmation), the counterparty triggers a refund, or an appointed arbitrator resolves a dispute.
Motivation
Existing escrow standards:
- Lack a standardised proof structure.
- Do not define a TTL-based refund path.
- Omit protocol-level fee collection, leaving each implementation to invent its own.
ERC-7753 addresses all three while remaining gas-efficient (custom errors, single storage slot per escrow state transition).
Specification
Constants
| Name | Value | Description |
|---|---|---|
ESCROW_FEE_BPS | 100 | Protocol fee in basis points (1 %) |
Note: The address that receives the protocol fee (
feeRecipient) is implementation-defined. Implementations MUST makefeeRecipientconfigurable and MUST NOT hardcode a chain-specific address.
Enums
enum EscrowStatus { Locked, Released, Refunded, Disputed, Resolved }Structs
struct Escrow {
address creator;
address counterparty;
address token; // address(0) = native ETH
uint256 grossAmount;
uint256 feeAmount; // 1% → treasury
uint256 netAmount; // 99% → locked
bytes32 conditionHash; // keccak256(description)
bytes32 proofHash; // set on release
EscrowStatus status;
uint64 createdAt;
uint64 settledAt;
uint64 expiresAt;
string description;
}Methods
createEscrow
function createEscrow(
address counterparty,
string calldata description,
address token,
uint256 gross,
uint64 ttlSeconds
) external payable returns (bytes32 escrowId);MUST compute feeAmount = (gross * ESCROW_FEE_BPS) / 10_000 and forward to the implementation-defined fee recipient.
MUST set conditionHash = keccak256(bytes(description)).
MUST emit EscrowCreated(escrowId, creator, counterparty, feeAmount).
releaseEscrow
function releaseEscrow(bytes32 escrowId, bytes32 proofHash) external;MUST only be callable by escrow.creator.
MUST set status = Released and release netAmount to counterparty.
MUST emit EscrowReleased(escrowId, counterparty, proofHash).
refundEscrow
function refundEscrow(bytes32 escrowId) external;Callable by counterparty at any time, or by creator after expiresAt.
MUST refund netAmount to creator.
MUST emit EscrowRefunded(escrowId, creator).
disputeEscrow
function disputeEscrow(bytes32 escrowId) external;Callable by either party while status is Locked.
MUST set status = Disputed.
MUST emit EscrowDisputed(escrowId, initiator).
resolveDispute
function resolveDispute(bytes32 escrowId, bool releaseToCounterparty) external;MUST only be callable by the designated arbitrator.
MUST emit EscrowResolved(escrowId, recipient, releaseToCounterparty).
Events
event EscrowCreated (bytes32 indexed escrowId, address indexed creator, address indexed counterparty, uint256 feeAmount);
event EscrowReleased (bytes32 indexed escrowId, address indexed to, bytes32 proofHash);
event EscrowRefunded (bytes32 indexed escrowId, address indexed to);
event EscrowDisputed (bytes32 indexed escrowId, address indexed initiator);
event EscrowResolved (bytes32 indexed escrowId, address indexed recipient, bool releasedToCounterparty);ERC-165
Compliant contracts MUST implement IERC7753 and MUST return true from supportsInterface for type(IERC7753).interfaceId.
Rationale
- conditionHash – Binding the escrow to
keccak256(description)creates a lightweight commitment without storing arbitrary text on-chain. - Asymmetric refund – The counterparty can always trigger a refund (protecting buyers from rug-pull), while the creator can only refund after expiry (protecting sellers from premature cancellation).
- Single arbitrator – Decentralised arbitration is complex to standardise; a pluggable single-arbitrator model is pragmatic for V2 while leaving room for DAO integration.
Backwards Compatibility
ERC-7753 is a new standard. It does not modify ERC-20 (IERC20) beyond using transfer/transferFrom.
Reference Implementation
See contracts/ERC7753EscrowV2.sol in the Opacus-Standart repository.
Security Considerations
- Reentrancy – All fund-releasing functions MUST use a reentrancy guard.
- Arbitrator trust – The arbitrator is a privileged role. Implementations SHOULD use a time-lock or multi-sig.
- Clock manipulation – TTL-based expiry relies on
block.timestamp; attackers cannot manipulate it by more than ~15 seconds on Ethereum mainnet.
Copyright
Copyright and related rights waived via CC0.
