ERC-8327 방향별 전송 도메인 레지스트리 (Directional Transfer Domain Registry)

Final · 기타 · 2026-07-05 작성

출발 도메인, 도착 도메인, 자산 분류의 순서쌍별로 전송 경로가 지금 허용되는지 조회하는 공용 레지스트리 인터페이스를 정의합니다. 여러 토큰과 컨트랙트가 하나의 경로 정책을 함께 쓸 수 있습니다.

왜 필요한가

토큰 전송을 제한하는 규칙은 보통 토큰 자체 코드나 주소 단위 로직에 들어 있습니다. 보유자나 잔액 같은 개별 조건에는 맞지만 관할권, 규제받는 거래 장소, 기업 네트워크, 게임 경제, DAO 금고 경계 같은 논리적 영역(도메인) 사이의 전송 가능 여부를 여러 토큰이 함께 조회할 공용 창구가 없습니다. 이런 호환성은 방향이 있어서 A에서 B로 보낼 수 있어도 B에서 A로는 안 될 수 있고 같은 경로라도 자산 종류에 따라 다를 수도 있습니다. 표준 인터페이스가 없으면 토큰이나 전송 컨트롤러마다 자기만의 경로 표를 넣거나 독자 레지스트리에 붙어야 해서 정책 상태가 중복되고 통합하는 쪽은 구현마다 다른 조회 방법을 알아야 합니다. 이 표준은 '이 출발 도메인에서 이 도착 도메인으로 이 자산 분류의 전송이 지금 허용되는가'라는 좁은 질문 하나를 표준화합니다. 전송이 최종적으로 성공할 수 있는지는 답하지 않으며 잔액, 보유자 자격, 동결, 제재 같은 검사는 별도로 남습니다.

심층 설명

경로는 방향과 자산 분류로 완전히 구분됩니다

경로(route)는 sourceDomain, destinationDomain, assetClass의 순서쌍입니다. 도메인과 자산 분류는 모두 0이 아닌 bytes32 식별자이고, 레지스트리는 그 의미를 해석하지 않습니다. 경로는 반드시 방향성을 가져서 (A, B, C)가 허용이어도 (B, A, C)가 허용이라고 볼 수 없고 양방향 허용은 독립적으로 허용된 경로 두 개가 필요합니다. 자산 분류 단위로도 구분되어 (A, B, C)의 허용이 (A, B, D)의 허용을 뜻하지 않습니다. 레지스트리는 각 순서쌍을 서로 독립적으로 다뤄야 합니다.

조회 결과는 호출자와 무관하게 결정적입니다

isRoutePermitted는 정확히 그 순서쌍의 현재 허용 상태를 돌려줍니다. 한 번도 허용된 적 없는 경로, 즉시 철회된 경로, 유예 철회의 발효 시각이 지난 경로는 false입니다. 같은 블록에서는 결과가 결정적이어야 하고 msg.sender, tx.origin, 호출자별 상태에 의존하면 안 됩니다. getRoute는 permitted, effectiveAt, permissionEvidenceHash, revocationEvidenceHash를 담은 Route 구조체를 돌려주며 알려지지 않은 경로는 모든 필드가 0인 기본값입니다. permitted가 true면 effectiveAt은 현재 허용이 발효된 시각이고 false인데 effectiveAt이 0이 아니면 마지막 철회가 발효된 시각입니다.

설정과 즉시 철회의 상태 규칙

setRoute와 revokeRoute는 권한 있는 레지스트라만 호출할 수 있고 0인 도메인, 자산 분류, 증거 해시는 거부해야 합니다. setRoute가 성공하면 permitted는 true, effectiveAt은 uint64(block.timestamp), permissionEvidenceHash는 전달한 값, revocationEvidenceHash는 0이 되고 RouteSet 이벤트가 나갑니다. 이미 허용되었거나 철회된 경로에 다시 호출해도 되며 새 호출이 상태와 증거를 교체하고 이전 조치는 이벤트로 남습니다. revokeRoute는 경로가 알려지지 않았거나 이미 철회되었다는 이유만으로 되돌리면 안 됩니다. 알려지지 않은 경로를 철회하면 허용 증거가 0인 비허용 레코드가 만들어지고 반복 철회는 최신 시각과 증거를 교체하며 새 이벤트를 냅니다. 두 함수 모두 block.timestamp가 uint64 최대값을 넘으면 거부해야 합니다.

증거 해시는 검증되지 않는 약속값입니다

증거 해시는 경로 조치를 뒷받침하는 애플리케이션 정의 자료에 대한 0이 아닌 bytes32 약속값(commitment)입니다. 레지스트리는 이를 불투명하게 다루며 원본, 해시 방식, 권위, 정확성, 가용성을 검증하지 않습니다. permissionEvidenceHash는 현재 허용 상태의 증거이고 revocationEvidenceHash는 현재 철회 상태의 증거이며 경로가 허용 상태인 동안에는 반드시 bytes32(0)이어야 합니다. 상태에는 가장 최근 증거만 남으므로, 전체 이력을 재구성하려는 소비자는 경로 이벤트를 인덱싱해야 합니다. 0 해시를 금지한 이유는 증거가 없는 경우와 의미 있는 약속이 섞이는 모호함을 없애기 위해서입니다.

배치 조회는 개별 조회와 결과가 같아야 합니다

isRoutePermittedBatch는 세 배열의 길이가 다르면 반드시 되돌려야 하고 그렇지 않으면 각 원소 i가 같은 인덱스의 개별 isRoutePermitted 호출 결과와 같은 배열을 돌려줘야 합니다. 구현은 문서화된 최대 배치 크기를 둘 수 있습니다. 상태를 바꾸는 실행에서 배치 함수를 호출하는 소비자는 입력 길이를 제한하는 것이 좋습니다. view 함수라도 다른 컨트랙트가 상태 변경 트랜잭션 안에서 부르면 무제한 배열이 가스를 과도하게 써서 그 작업 자체를 못 쓰게 만들 수 있기 때문입니다.

유예 철회 확장과 게으른 발효

유예 철회는 선택 확장(IGracefulRouteRevocation)입니다. initiateRevocation은 경로가 현재 허용 상태이고 진행 중인 유예 철회가 없을 때만 성공하며 initiatedAt을 현재 시각으로, effectiveAt을 그보다 엄격히 뒤인 시각으로 기록하고 RouteRevocationInitiated를 냅니다. 유예 기간의 길이와 설정 방식은 구현이 정하되 반드시 문서화해야 합니다. 게으른 발효(lazy effectiveness)가 핵심입니다. block.timestamp가 effectiveAt에 이르면 finalizeRevocation 트랜잭션 없이도 isRoutePermitted가 false를 돌려줍니다. 마무리 트랜잭션이 누락되거나 검열되어도 경로가 무한정 열려 있지 않게 하려는 설계입니다. cancelRevocation은 effectiveAt 전에만 가능하고 경로는 허용 상태로 남습니다. finalizeRevocation은 effectiveAt 이후에 비허용 상태를 저장하고 RouteRevoked를 정확히 한 번 내며 발효 자체에 영향이 없으므로 누구나 호출할 수 있게 해도 됩니다. setRoute나 revokeRoute가 호출되면 유예 철회 기록은 새 상태를 설치하기 전에 지워집니다.

레지스트리가 하지 않는 일과 소비자의 책임

이 표준은 주소나 토큰을 도메인에 대응시키지 않고, 도메인과 자산 분류의 분류 체계도 정의하지 않습니다. 식별자를 어떻게 만드는지는 애플리케이션이 문서화해야 하고 서로 다른 레지스트리의 같은 bytes32 값이 같은 뜻이라고 가정하면 안 됩니다. 레지스트라 권한 부여 방식도 소유권, 역할 기반 접근 제어, 거버넌스, 서명 등 구현이 정하되, 권한 없는 호출자가 setRoute, revokeRoute, initiateRevocation, cancelRevocation을 성공시킬 수 없어야 합니다. 경로 결정을 집행하는 소비자는 전송과 같은 트랜잭션 안에서 도메인과 자산 분류를 결정하고 isRoutePermitted를 호출하고 false면 전송을 거부한 뒤, 잔액·동결·제재 같은 독립 검사를 따로 적용하는 것이 좋습니다. 호환 레지스트리는 ERC-165를 구현해 ITransferDomainRegistry의 interfaceId에 true를 돌려줘야 하고 유예 철회를 구현하면 IGracefulRouteRevocation의 interfaceId에도 true를 돌려줘야 합니다.

쓸 때 주의할 점

ERC 원문 · ← ERCs