PCL 컨트랙트 admin 바인딩

mechanism compliance

컨트랙트의 PCL admin 슬롯이 초기화되는 방식을 설명합니다. PCL 프리컴파일로 프록시를 배포하면 배포자에게 자동 바인딩되며, 레거시 경로에서는 최초 registerContractPolicies 호출로 1회 바인딩됩니다. 바인딩은 단일 할당(single-assignment) 원칙을 따릅니다.

PCL의 모든 ContractPolicyConfig에는 admin 필드가 있으며, 이후 해당 컨트랙트에 대해 changeContractPoliciesremoveContractPolicies를 호출할 수 있는 유일한 주소입니다. 이 admin 슬롯은 컨트랙트 주소당 한 번만 바인딩됩니다. 바인딩 경로는 두 가지입니다. (1) 자동 바인딩 — IPcl.deployPclProxy로 컨트랙트를 배포하면 배포 호출의 EVM msg.sender가 배포 반환 전에 admin으로 기록됩니다. (2) 수동 바인딩 — 레거시 경로에서는 최초의 registerContractPolicies 성공 호출이 슬롯을 바인딩합니다(현재는 deprecated 되었으므로 슬롯이 바인딩된 후에는 changeContractPolicies를 사용합니다). 한 번 기록된 슬롯은 임의의 다른 호출자가 덮어쓸 수 없습니다. admin 교체는 현재 admin이 changeContractPolicies의 config에 새 admin을 지정하는 방식으로만 가능합니다.

두 가지 초기화 경로

PCL은 컨트랙트의 admin을 정확히 한 번만 바인딩합니다. 어느 호출이 그 바인딩을 수행하는지는 컨트랙트가 어떻게 배포되었는지에 따라 달라집니다.

  • 프록시 배포 시 자동 바인딩(권장). IPcl.deployPclProxy(kind, value, initData)는 PCL 래핑 프록시를 배포한 뒤, 반환 전에 프리컴파일 호출의 msg.sender를 프록시의 admin 슬롯에 기록합니다. admin을 설정하기 위한 별도의 registerContractPolicies 호출이 필요하지 않으며, 곧바로 changeContractPolicies로 정책을 붙입니다.
  • 레거시 수동 바인딩. PCL 프록시 팩토리 없이 배포된 컨트랙트의 경우, 최초의 성공적인 registerContractPolicies 호출이 config.admin 필드로 admin을 바인딩합니다. 이후 갱신은 changeContractPolicies를 사용해야 합니다. 이 경로는 deprecated 상태이며 곧 제거됩니다.

배포 시 자동 바인딩의 실제 동작

deployPclProxy 실행 중 프리컴파일은 표준 프록시 bytecode를 배포한 뒤, PclProxyDeployed 이벤트를 발행하기 전에 체인 측 InitializeContractPolicyAdmin(proxyAddress, msg.sender)을 호출합니다. 결과로 생성되는 ContractPolicyConfig의 admin 필드는 프록시 배포자이며, 프록시의 로직 컨트랙트 owner도 아니고 initializer 대상도 아닙니다. 이들은 서로 다른 개념입니다.
// EOA에서 프록시를 배포하면 해당 EOA가 PCL admin으로 바인딩됩니다.
IPcl pcl = IPcl(0x1000000000000000000000000000000000000005);
address proxy = pcl.deployPclProxy(
    PclProxyKind.Transparent,
    0,
    abi.encode(logicImpl, initialOwner, initializerCalldata)
);
// pcl.contractPolicies(proxy).admin == msg.sender (deployPclProxy를 호출한 EOA)
// 이 시점에는 정책이 아직 부착되지 않았습니다 — 다음으로 changeContractPolicies를 호출합니다.
pcl.changeContractPolicies(ContractPolicyConfig({
    _contract: proxy,
    admin:     msg.sender,       // 유지하거나 인계
    policies:  policies
}));

팩토리 컨트랙트에서는 팩토리가 admin이 됩니다

자동 바인딩은 deployPclProxy 호출의 EVM msg.sender를 사용하므로, 팩토리 컨트랙트를 통해 배포하면 팩토리를 호출한 최종 사용자가 아니라 팩토리 자체가 admin이 됩니다. 최종 사용자가 admin 슬롯을 소유하도록 하려면 세 가지 방법이 있습니다.

  • 최종 사용자가 deployPclProxy를 직접 호출합니다(팩토리 우회).
  • 팩토리에서 배포한 뒤, 같은 트랜잭션에서 팩토리가 changeContractPolicies를 호출해 최종 사용자에게 admin을 인계합니다.
  • 팩토리에 admin 권한을 영구 소유하는 위임 레이어를 만들고, 그 레이어가 사용자를 대신해 정책 변경을 중개합니다.


deployPclProxy에는 의도적으로 admin 파라미터가 없습니다. 배포 시점에 초기 admin을 msg.sender로 고정해 스푸핑 여지를 없애는 설계입니다.

단일 할당 원칙과 CREATE2 사전 등록

한 번 바인딩된 admin 슬롯은 임의의 다른 호출자가 덮어쓸 수 없습니다. InitializeContractPolicyAdmin은 슬롯이 비어 있을 때만 기록하며, 이미 저장된 admin과 동일한 admin이 다시 전달되면 no-op으로 처리합니다. 다른 admin을 초기화하려는 호출자는 PolicyAlreadyRegistered를 받습니다.

동일 admin에 대한 idempotency는 CREATE2 사전 등록을 지원하기 위한 것입니다. 배포자는 프록시가 존재하기 전에 미리 계산한 프록시 주소로 admin을 바인딩할 수 있으며, 실제 배포 시점의 자동 바인딩은 무해한 no-op이 됩니다. 반대로, 동일한 CREATE2 주소에 다른 배포자가 재배포하는 경우는 의도적으로 잠깁니다. 기존 admin이 changeContractPolicies로 명시적으로 인계하지 않는 한 원래 admin이 권한을 유지합니다.

인계: change vs. register

슬롯이 바인딩된 이후에는 admin 인계를 포함한 모든 갱신에 changeContractPolicies를 사용합니다. 정책 세트를 교체하는 같은 호출에서 config.admin을 새 admin 주소로 지정하며, 현재 admin이 트랜잭션 sender여야 검증을 통과합니다.
// 현재 admin이 정책 세트를 교체하는 같은 호출에서
// 새 주소로 권한을 인계합니다.
pcl.changeContractPolicies(ContractPolicyConfig({
    _contract: proxy,
    admin:     newAdmin,        // 인계 대상
    policies:  updatedPolicies
}));
// 이후 `proxy`에 대한 changeContractPolicies / removeContractPolicies는
// newAdmin만 호출할 수 있습니다.

클라이언트 측 판단: 어느 진입점을 호출할지

deployPclProxy로 배포한 프록시라면 admin 슬롯이 배포 시점에 이미 바인딩되어 있으므로, 정책을 붙이거나 갱신할 때 곧바로 changeContractPolicies를 호출합니다. 이런 프록시에 registerContractPolicies를 호출하면 호출자가 바인딩된 admin이 아닌 한 PolicyAlreadyRegistered로 revert됩니다. 호출자가 바인딩된 admin이면 no-op이 아니라 레거시 전체 재등록이 수행됩니다(정책 세트가 교체되고 슬롯이 config.admin으로 설정됩니다). 어느 쪽이든 이 경로는 deprecated입니다. 이식성을 위해 자신이 소유한 컨트랙트를 관리하는 코드는 먼저 IPcl.contractPolicies(contract)를 조회할 수 있습니다. admin == address(0)이면 레거시 registerContractPolicies를, 그렇지 않으면 changeContractPolicies를 사용합니다.
소스: maroo
ESC
검색어를 입력하세요