ERC-8004 Identity Registry (Preinstall)

component identity

AI 에이전트 신원을 위한 온체인 프리미티브입니다. 고정 주소에 사전 설치된 ERC-8004 Identity Registry이며, 컨트랙트 소유자가 없습니다.

마루는 AI 에이전트 신원을 체인 레벨 프리미티브로 다루기 위해 ERC-8004 Identity Registry를 preinstall로 설치합니다. dapp마다 따로 배포하는 대신 고정된 널리 알려진 주소(0x8004000000000000000000000000000000000001)에 하나만 둡니다. 마루 테스트넷에서는 제네시스가 아니라 v0.4.0 체인 업그레이드(블록 6,372,905)로 설치되었습니다. 등록, 에이전트 URI, 에이전트별 메타데이터, 에이전트 지갑은 ERC-8004 v1 Identity Registry ABI를 따르므로 이 ABI를 쓰는 도구는 그대로 동작합니다. 레지스트리에는 attestation이나 폐기(revoke) 함수가 없습니다. 마루 고유의 agent 프리컴파일이 그 옆에서 레지스트리 주소를 돌려주고, 에이전트 지갑을 에이전트 ID에 매핑합니다(agent-precompile-overview 참조).

왜 preinstall인가 (dapp 배포 컨트랙트가 아닌)

IdentityRegistry를 단일 dapp이 배포한다면(더 나쁘게는 발행자별로 배포한다면) 에이전트 신원이 경쟁하는 레지스트리들로 파편화됩니다. 정확히 ERC-8004가 해결하려던 문제입니다. 마루는 레지스트리를 고정 주소에 사전 설치해 이 파편화를 막습니다. 체인 위에는 정식 IdentityRegistry가 정확히 하나 있고, x/agent 인덱싱과 agent 프리컴파일이 그 컨트랙트를 추적합니다.

OKRW나 PCL이 프리컴파일(고정 주소의 Go 네이티브 코드)인 것과 달리, IdentityRegistry는 일반 Solidity 컨트랙트입니다. 익스플로러에는 검증된 PreinstallIdentityRegistry로 표시되며, ERC-721 위에 ERC-8004 Identity 인터페이스를 구현한 업그레이드 불가 컨트랙트이고 컨트랙트 소유자가 없습니다. Preinstall은 체인 운영자가 "이것은 기반이므로 직접 배포할 필요가 없다"고 선언하는 수단입니다.

V1에서 다루는 범위

ERC-8004은 Identity, Reputation, Validation 세 가지 레지스트리를 정의합니다. 마루 테스트넷에는 그중 두 개가 설치되어 있습니다.

  • Identity Registry — 0x8004000000000000000000000000000000000001. 이 페이지가 다루는 컨트랙트입니다.
  • Reputation Registry — 0x8004000000000000000000000000000000000002. 배포되어 Identity Registry와 연결되어 있습니다(giveFeedback, readFeedback, revokeFeedback, appendResponse 등). ERC-8004 Reputation 인터페이스의 일부만 구현하며, getSummary와 readAllFeedback은 없습니다.
  • Validation Registry는 설치되어 있지 않습니다.


IAgent.getParams()가 Identity와 Reputation 레지스트리 주소를 돌려줍니다.

Identity Registry가 하는 일은 다음과 같습니다.
  • 에이전트 등록 — register(), register(agentURI), register(agentURI, metadata)가 호출자에게 ERC-721 agentId(0부터 순서대로 발급되는 uint256)를 발행합니다. 호출자가 소유자이자 기본 에이전트 지갑이 되며, 새 주소가 만들어지지는 않습니다.
  • 소유자 연결 — 소유자는 agentId NFT를 보유한 계정입니다. 레지스트리는 KYC/KYB attestation을 확인하거나 저장하지 않습니다. 이 증명은 EAS 컨트랙트에 있으며, PCL 정책이 확인하는 경우에만 의미가 있습니다.
  • 메타데이터 저장 — 에이전트마다 자유로운 키 → bytes 값을 둡니다(setMetadata / getMetadata). 체인 코드가 레지스트리에서 읽는 것은 두 가지입니다. agent 프리컴파일 인덱스에 쓰는 에이전트 지갑(setAgentWallet으로 설정하며, 새 지갑의 EIP-712 서명이 필요합니다)과, AGENT_OKRW_TRANSFER_LIMIT_POLICY가 읽는 TransferLimit 키(32바이트 uint256)입니다. MAWS의 spendingLimit이나 status 같은 다른 키는 오프체인에서만 읽힙니다.
  • 폐기 기능 없음 — revoke나 burn 함수가 없습니다. 소유자는 NFT를 이전하거나 unsetAgentWallet을 호출할 수 있으며, 메타데이터에 "revoked" 상태를 적는 것은 체인이 강제하지 않는 관례입니다.

Owner ↔ Agent 연결과 KYA

마루 라이트페이퍼에 따르면 KYA(Know Your Agent)는 KYC의 확장이 아닙니다. 세 가지 컴포넌트가 결합해 발현되는 속성입니다.

1. 소유자 신원 — 에이전트를 소유한 계정이 보유한 KYC/KYB EAS attestation입니다. 레지스트리가 아니라 EAS 컨트랙트(0x1000000000000000000000000000000000000007, 인덱서 0x1000000000000000000000000000000000000008)에 있습니다.
2. 위임 범위 — 지출 한도와 허용 대상입니다. 마루에서는 에이전트 메타데이터(spendingLimit, allowedTargets, 그리고 status 플래그)로 저장되며, MAWS가 기록하고 오프체인에서 강제합니다. 온체인에서 강제되는 한도는 TransferLimit 하나로, 해당 정책이 설정된 경우 AGENT_OKRW_TRANSFER_LIMIT_POLICY가 읽습니다.
3. 행위 제약 — 에이전트 지갑이 기술적으로 수행할 수 있는 행동의 범위로, 실행 시점에 PCL이 강제합니다.

PCL은 EAS 조건을 체인의 EAS 연동으로, 에이전트의 TransferLimit 메타데이터를 x/agent로 평가하며, 실제로 설정된 정책에 대해서만 평가합니다. 전체 스택이 결합해 KYA를 구성하므로 어떤 단일 레지스트리도 KYA를 모놀리딕하게 인코딩할 필요가 없습니다.

레지스트리 호출

Solidity 컨트랙트에서는 ERC-8004 Identity 인터페이스로 IdentityRegistry를 호출합니다. 등록은 호출자에게 ERC-721 토큰을 발행하므로, 스스로를 등록하는 컨트랙트는 그 토큰을 받을 수 있어야 합니다(onERC721Received). 전체 ABI는 @maroo-chain/viem의 identityRegistryAbi로 제공됩니다.
// IdentityRegistry의 ERC-8004 Identity 함수입니다(ERC-721이기도 합니다).
struct MetadataEntry {
    string metadataKey;
    bytes metadataValue;
}

interface IIdentityRegistry {
    function register() external returns (uint256 agentId);
    function register(string calldata agentURI) external returns (uint256 agentId);
    function register(string calldata agentURI, MetadataEntry[] calldata metadata) external returns (uint256 agentId);
    function setAgentURI(uint256 agentId, string calldata newURI) external;
    function getMetadata(uint256 agentId, string calldata metadataKey) external view returns (bytes memory);
    function setMetadata(uint256 agentId, string calldata metadataKey, bytes calldata metadataValue) external;
    function getAgentWallet(uint256 agentId) external view returns (address);
    function setAgentWallet(uint256 agentId, address newWallet, uint256 deadline, bytes calldata signature) external;
    function unsetAgentWallet(uint256 agentId) external;
    function ownerOf(uint256 tokenId) external view returns (address);
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

contract MyAgentDeployer {
    IIdentityRegistry constant REGISTRY = IIdentityRegistry(0x8004000000000000000000000000000000000001); // 마루 preinstall 주소

    function registerSelf(string calldata agentURI) external returns (uint256 agentId) {
        agentId = REGISTRY.register(agentURI); // agentId는 0부터 순서대로 발급되는 uint256입니다
    }

    // register()는 _safeMint로 발행하므로 받는 컨트랙트가 ERC-721 토큰을 받을 수 있어야 합니다.
    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return this.onERC721Received.selector;
    }
}
주소는 테스트넷에서 고정되어 있으며 IAgent.getParams()도 같은 주소를 돌려줍니다. 여러 네트워크를 대상으로 한다면 런타임에 조회합니다. 어떤 주소가 소유한 에이전트 목록은 레지스트리의 ERC-721 기능으로 구합니다. 개수는 balanceOf(owner)로, ID는 Transfer 이벤트(또는 익스플로러의 NFT API)로 확인합니다. agent 프리컴파일의 getAgentIds는 소유자가 아니라 에이전트 지갑을 키로 하며, 에이전트 NFT가 이전되면 해당 항목이 지워집니다.

참고

ESC
검색어를 입력하세요