ERC-8328 주체 연동 컴플라이언스 이벤트 로그 (Subject-Linked Compliance Event Log)

Last Call · 기타 · 2026-07-05 작성

토큰 발행, 동결, 고객확인(KYC) 변경 같은 규제 관련 조치를 주체별로 기록하는 추가 전용 공용 로그를 정의합니다. 기록은 지울 수 없고 정정 이력까지 연결해 보존합니다.

왜 필요한가

규제 대상 자산을 다루는 시스템은 발행, 상환, 동결, 고객확인(KYC) 상태 변경, 규제 보류, 강제 이전 같은 조치를 수행합니다. 지금은 이런 기록이 앱마다 다른 이벤트, 토큰별 상태 변경, 범용 증명, 오프체인 데이터베이스에 흩어져 있습니다. 형식이 제각각이라 컨트랙트, 인덱서, 감사인, 보고 시스템이 여러 구현에서 비교 가능한 기록을 조회하기 어렵습니다. 기존 토큰·컴플라이언스 표준은 토큰 동작, 보유 자격, 이전 검증, 기관 분류를 정의할 뿐, 주체 단위 조치를 담는 공통 저장 기록은 제공하지 않습니다. 이 표준은 기술적으로 기록한 주소와 그 기록이 주장하는 권한 근거를 구분하고 증거 해시와 정정 이력까지 남기는 하나의 조회 창구를 정의합니다. 다만 이것은 보고용 인터페이스라서 기록이 사실이거나 적법하다는 증명은 아닙니다.

심층 설명

기록 구조와 기록 규칙

각 이벤트는 주체(subjectId, subjectType), 이벤트 유형, 결과, 기록자(actor), 권한 주장(authority), 당사자 배열, 증거 해시, 선택적 증거 URI, 페이로드 프로필과 페이로드, 작업 참조(operationRef), 발생 시각과 기록 시각, 정정 포인터를 담습니다. 인덱스는 주체별로 0부터 시작하고 반환되는 eventIndex는 추가 직전 그 주체의 이벤트 개수와 같아야 합니다. 구현은 actor를 msg.sender로, recordedAt을 uint64(block.timestamp)로 설정하고 correctedByIndex를 NO_CORRECTED_BY로 초기화합니다. recordEvent는 권한 있는 기록자만 호출할 수 있어야 하며 권한 방식은 구현이 정하되 문서화해야 합니다. evidenceHash는 bytes32(0)일 수 없지만 evidenceURI는 비워 둘 수 있습니다. 당사자 배열은 최대 10개, 페이로드는 최대 2048바이트입니다.

발생 시각과 기록 시각의 분리

occurredAt은 보고된 조치가 실제로 일어났다고 주장하는 시각이고 recordedAt은 온체인에 기록된 시각입니다. occurredAt이 block.timestamp보다 크면 recordEvent는 되돌려야(revert) 합니다. 과거로 소급하는 기록의 최대 허용 기간은 구현이 정해 문서화하도록 권장(SHOULD)하되 표준이 기간을 고정하지는 않습니다. 참조 구현은 30일을 넘는 소급 기록을 거부하지만 이는 배포 선택이지 표준 요구사항이 아닙니다. 로그는 occurredAt을 독립적으로 검증하지 않으며 이는 기록자의 주장일 뿐입니다.

추가 전용과 정정 체인

기록된 이벤트의 필드는 correctedByIndex를 빼면 모두 불변이고 이벤트 삭제는 금지됩니다. 일반 이벤트는 correctsIndex를 NO_CORRECTION(type(uint256).max)으로 둡니다. 정정 이벤트는 유형을 EVT_CORRECTION으로 하고 correctsIndex에 고칠 이벤트의 인덱스를 넣습니다. 정정 대상은 같은 주체의 더 이른 이벤트여야 하고 그 correctedByIndex가 NO_CORRECTED_BY(0)여야 합니다. 정정 인덱스는 항상 대상보다 크므로 0은 안전한 센티널입니다. 각 이벤트는 한 번만 정정될 수 있어 분기가 생기지 않고 정정 이벤트 자체를 다시 정정하면 선형 체인이 됩니다. 정정 정책은 단지 기록 권한이 있다는 이유만으로 다른 기록자의 이벤트를 고치도록 허용해서는 안 되며 원래 기록자나 지정 정정자, 관리자를 허용할 수 있고 반드시 문서화해야 합니다. 정정은 원래 이벤트를 고쳐 쓰지 않고 새 이벤트에 바로잡은 내용을 담습니다.

조회 함수의 정확한 동작

getEvent, currentEventIndex, isEventCurrent는 eventIndex가 eventCount(subjectId) 이상이면 되돌려야 합니다. currentEventIndex는 correctedByIndex를 따라가 NO_CORRECTED_BY에 도달한 터미널 이벤트의 인덱스를 돌려주고 터미널 이벤트에 대해 호출하면 입력한 인덱스를 그대로 돌려줍니다. lastRecordedEventByType은 해당 유형으로 기록된 가장 큰 이벤트 인덱스를 돌려주는데, 이는 기록 순서 기준이지 occurredAt이 가장 큰 이벤트를 고르는 것도, 정정 체인을 해석하는 것도 아닙니다. 정정 이벤트는 원래 유형이 아니라 EVT_CORRECTION 아래에 색인되므로, 소비자는 특정 유형의 마지막 이벤트가 곧 현재 상태라고 가정하지 말고 currentEventIndex를 써야 합니다.

표준 식별자 체계

식별자는 모두 네임스페이스가 들어간 문자열의 keccak256 해시입니다. 주체 유형은 SUBJECT_TOKEN, SUBJECT_ADDRESS, SUBJECT_ASSET, SUBJECT_CASE 네 가지가 정의됩니다. 이벤트 유형은 발행, 이전, 상환, 동결·해제, 강제 이전, KYC 승인·철회·갱신, 규제 보류·해제, 허용목록 추가·제거, 정책 변경, 정정까지 15가지가 정의됩니다. 결과는 APPROVED, DENIED, PENDING, EXECUTED, EXPIRED, REVOKED 여섯 가지, 당사자 역할은 SENDER, RECEIVER, TARGET, BENEFICIARY, CONTROLLER, SUBJECT 여섯 가지, 권한 식별자는 INTERNAL_POLICY, COURT_ORDER, REGULATOR 세 가지입니다. 권한 필드는 주장을 기록할 뿐 해당 권한을 인증하지 않습니다. 표준은 이벤트 유형과 결과의 조합을 검증하지 않으므로 로그가 받아들였다고 해서 조합이 유효하다고 추론하면 안 됩니다. EVT_TRANSFER는 컴플라이언스상 의미 있는 이전 기록용이며 일상적인 토큰 이전 이벤트를 대체하려는 것이 아닙니다.

버전 있는 페이로드 프로필

이벤트별 추가 데이터는 payloadProfileId로 선언한 스키마에 따라 인코딩합니다. 표준은 PAYLOAD_TRANSFER_V1(from, to, amount, routeRef), PAYLOAD_FREEZE_V1(target, amount, expiresAt, reason), PAYLOAD_KYC_V1(subject, jurisdiction, riskTier, expiresAt), PAYLOAD_FORCED_TRANSFER_V1(from, to, amount, legalBasis)의 abi.encode 인코딩을 정의합니다. 이 프로필을 선언한 기록자는 정확히 그 인코딩을 따라야 하고 소비자는 디코딩 전에 payloadProfileId를 확인해야 합니다. 로그 자체는 페이로드를 디코딩하거나 프로필과 이벤트 유형의 호환성을 검증할 의무가 없습니다. 모르는 프로필 식별자는 불투명한 바이트로 취급해야 합니다.

인터페이스 감지와 이 표준의 한계

호환 로그는 ERC-165를 구현하고 type(IComplianceEventLog).interfaceId에 true를 돌려줘야 합니다. 다만 ERC-165는 인터페이스 지원만 나타낼 뿐 기록자의 신뢰성, 증거 유효성, 권한, 법적 준수를 보장하지 않습니다. 이 표준은 보고 인터페이스로, 컴플라이언스 정책이나 신원 검증, 이전 제한, 법적 권한을 정의하지 않습니다. 저장된 기록은 출처를 추적할 수 있는 주장일 뿐 보고된 조치가 실제로 일어났거나 적법했다는 증명이 아닙니다. 원문은 현재 Last Call 상태로 마감일이 2026년 10월 19일이며 최종 확정 전까지 내용이 바뀔 수 있습니다.

쓸 때 주의할 점

ERC 원문 · ← ERCs