ERC-7656 컨트랙트 연결 서비스 팩토리 (Generalized Contract-Linked Services)
Final · 계정·지갑 · 2024-03-15 작성
NFT나 스마트 계정 같은 기존 컨트랙트에, 원래 코드를 바꾸지 않고 기능을 더하는 '연결 서비스'를 미리 계산 가능한 주소로 배포하는 공용 팩토리 표준입니다.
왜 필요한가
한 번 배포된 컨트랙트는 코드를 바꿀 수 없습니다. 그래서 이미 쓰고 있는 NFT나 스마트 계정에 새 기능을 더하려면 지금까지는 토큰 바운드 계정(token-bound account) 같은 방식에 기대야 했습니다. 그런데 기존 표준에는 두 가지 한계가 있습니다. 첫째, 배포되는 컨트랙트가 자산을 다루고 거래를 실행하는 '계정' 역할을 하도록 특정 인터페이스를 사실상 강제합니다. 둘째, 연결 대상이 NFT로 제한되어 ERC-4337 스마트 계정처럼 NFT가 아닌 컨트랙트에 소셜 복구나 권한 관리 같은 기능을 붙이고 싶어도 표준화된 방법이 없었습니다. 이 표준은 어떤 컨트랙트에든 연결되는 서비스를 배포하는 범용 팩토리를 정의해 원래 컨트랙트를 고치지 않고도 소유 관계를 유지한 채 기능을 확장할 수 있게 합니다.
구현해야 하는 것
function linkedData() external view returns (uint256 chainId, bytes12 mode, address linkedContract, uint256 linkedId): 연결 서비스가 자기 바이트코드에 새겨진 체인 ID, 모드, 연결 대상 컨트랙트, 연결 ID를 돌려줍니다.
심층 설명
IERC7656Factory: create와 compute
팩토리는 두 함수를 제공합니다. create(implementation, salt, chainId, mode, linkedContract, linkedId)는 구현을 가리키는 프록시를 배포해 주소를 반환하고 Created 이벤트를 내며 실패 시 CreationFailed 오류를 냅니다. compute는 같은 인자로 배포될 주소를 배포 없이 계산하는 view 함수입니다. 모든 ERC7656Factory 구현은 IERC7656Factory 인터페이스 ID 0x9e23230a를 지원해야 합니다(MUST). 참조 구현의 바이트코드는 이더리움 메인넷 0x76565d90eeB1ce12D05d55D142510dBA634a128F에 배포되어 있으며 이후 주요 메인넷과 일부 테스트넷에도 같은 주소로 배포될 예정입니다.
mode 파라미터: 두 가지 연결 모드
mode는 연결 대상을 어떻게 해석할지 정하는 bytes12 선택자입니다. LINKED_ID(0x000000000000000000000000)는 NFT처럼 토큰·엔티티 ID가 필요한 컨트랙트에 연결할 때 쓰며 기존 토큰 바운드 계정 시스템과의 호환성을 보장합니다. NO_LINKED_ID(0x000000000000000000000001)는 ERC-4337 계정처럼 ID가 필요 없는 컨트랙트용입니다. 이때 linkedId는 일관성을 위해 인터페이스에 남아 있지만 서비스에 필요한 다른 데이터를 담지 않는다면 0으로 두는 것이 좋습니다(SHOULD). 불리언 대신 12바이트를 쓰는 이유는 미래 확장 여지 때문입니다. 원문은 ERC-1155 토큰 잔액이 1000개 이상이어야 하는 서비스에서 최하위 바이트 0x02를 기본 모드로 쓰고 나머지에 최소 잔액을 담는 예, ERC-20 필요 잔액을 linkedId에 담는 예를 듭니다.
배포 바이트코드 구조: 183바이트
모든 연결 서비스는 ERC-1167 최소 프록시로 배포해야 하며(MUST), 바이트코드 뒤에 불변 상수 데이터를 덧붙입니다. 구조는 ERC-1167 헤더 10바이트, implementation 주소 20바이트, ERC-1167 푸터 15바이트, 이어서 salt 32바이트, chainId 32바이트, mode 12바이트, linkedContract 20바이트, linkedId 32바이트로 전체 183바이트입니다. 연결 정보를 스토리지가 아니라 코드 자체에 새기므로, 서비스는 배포 후 바뀌지 않는 자기 연결 정보를 갖습니다.
결정적 주소 설계의 이점
연결 데이터를 스토리지가 아닌 바이트코드에 붙이는 설계는 세 가지를 보장합니다. 첫째, 연결 서비스의 주소를 오프체인에서 미리 계산할 수 있습니다. 둘째, 팩토리가 상태를 저장하지 않아 가스 비용이 줄어듭니다. 셋째, 서비스를 필요한 시점에 배포하거나, 아직 배포되지 않은 서비스의 주소를 미리 참조할 수 있습니다. 또 이 표준은 연결 서비스의 구현 세부에 특정 인터페이스나 동작을 강제하지 않아 일관된 배포·소유 모델을 유지하면서 개발자가 자유롭게 특화 서비스를 만들 수 있습니다.
IERC7656Service와 linkedData 읽기
연결 서비스는 IERC7656Service 인터페이스(ID 0x7e110a1d)를 구현하는 것이 좋습니다(SHOULD). linkedData()는 chainId, mode, linkedContract, linkedId를 돌려줍니다. 원문의 구현 예시는 extcodecopy로 서비스 코드의 끝부분 0x60바이트를 읽어 이 값들을 복원하고 mode는 읽은 워드의 앞 12바이트에서, 주소는 나머지에서 분리합니다. 같은 예시의 owner()는 chainId가 현재 체인과 다르면 address(0)을 돌려주고 같으면 연결된 ERC-721 토큰의 ownerOf 결과를 소유자로 돌려줍니다.
권장 구현 패턴 세 가지
서비스 구현 시 세 가지 패턴이 권장됩니다(SHOULD). 첫째, 소유권 검증: 연결된 컨트랙트나 토큰의 현재 소유자가 허가한 작업인지 확인하는 장치를 둡니다. 둘째, 모드별 로직: mode 값에 따라 NFT 연결 시나리오와 계정 연결 시나리오를 구분해 처리합니다. 셋째, 크로스체인 인지: 작업이 chainId 파라미터에 지정된 체인에서 실행되는지 확인해 크로스체인 재전송(replay) 공격을 막습니다.
하위 호환성과 표준 상태
LINKED_ID 모드(0x000000000000000000000000)로 쓰면 기존 토큰 바운드 계정과 호환되어 이를 지원하는 기존 애플리케이션과 인프라가 수정 없이 계속 동작합니다. NO_LINKED_ID 모드(0x000000000000000000000001)를 쓰는 컨트랙트는 별도 인터페이스가 필요할 수 있지만 핵심 팩토리 메커니즘은 동일하게 유지됩니다. 이 제안은 Final 상태로 확정된 표준입니다.
쓸 때 주의할 점
- 소유권 순환: NFT에 연결된 스마트 지갑이 그 NFT를 다시 보유하면 소유권 순환이 생겨 자산에 접근할 수 없게 될 수 있습니다. 구현자는 순환을 막거나 감지하는 안전장치를 두어야 합니다.
- 판매 직전 권한 변경 사기: 악의적인 판매자가 판매 확정 직전에 서비스 권한을 바꾸거나 회수할 수 있습니다. 특히 ERC-7656 서비스와 연동하는 NFT 마켓플레이스는 막판 변경을 막는 잠금 메커니즘을 구현할 수 있습니다.
- 악성 구현: 레지스트리는 서비스 연결 시 정당한 소유를 강제할 수 없습니다. 사용자는 배포 전에 구현을 검토하거나 감사해야 하고 연동하는 프런트엔드는 검증되지 않은 구현과 상호작용할 때 경고를 표시해야 합니다.
- 업그레이드 위험: 업그레이드 가능한 연결 서비스는 예기치 않은 변경이나 자산 유출 위험이 있습니다. 업그레이드가 필요하다면 타임락이나 다중 서명 거버넌스를 갖춘 안전한 업그레이드 메커니즘을 구현해야 합니다.
- 재진입과 크로스 컨트랙트 공격: 자산이나 외부 프로토콜과 상호작용하는 연결 서비스는 재진입(reentrancy) 공격에 취약할 수 있습니다. 검사-효과-상호작용 패턴을 따르고 재진입 가드를 고려해야 합니다.