PCL 정책 구조
PCL 정책은 3계층 구조로 구성됩니다. PolicyTemplate(등록된 템플릿 타입)이 PolicySet(템플릿 + ABI 인코딩 파라미터 + 선택적 selector)으로 인스턴스화되고, 다시 PolicyConfig(적용되는 PolicySet 묶음)에 묶입니다.
PCL은 컴플라이언스 규칙을 JSON 객체가 아니라 Solidity로 정의된 ABI 튜플로 저장합니다. 구조는 3계층으로, PolicyTemplate(정책 관리자가 등록하는 규칙 타입)이 PolicySet(타입 ID + ABI 인코딩 파라미터 blob + 선택적 함수 selector)으로 인스턴스화되고, PolicyConfig(전역 또는 컨트랙트별 설정)에 묶입니다. 초기 초안에서 중간 계층을 PolicyRef라 불렀으나, IPcl 인터페이스의 정식 명칭은 PolicySet이며 policy 필드가 ABI 인코딩된 파라미터 바이트입니다.
세 가지 Solidity struct
IPcl.sol 그대로:struct PolicyTemplate {
string templateId; // 예: "DENYLIST_POLICY"
string name;
string description;
bytes paramSchema; // self-describing schema (호출자가 직접 소비하는 일은 드묾)
}
struct PolicySet {
string templateId; // 어느 템플릿의 인스턴스인지
bytes policy; // abi.encode(<템플릿별 struct>)
bytes selector; // 선택적 4바이트 함수 selector; empty bytes = 모든 호출에 적용
}
struct GlobalPolicyConfig {
PolicySet[] policies;
}
struct ContractPolicyConfig {
address _contract; // 적용 대상 컨트랙트
address admin; // 향후 이 설정 변경 권한자
PolicySet[] policies;
} 핵심 필드는
PolicySet.policy이며, 템플릿별 파라미터 struct의 ABI 인코딩을 담는 bytes입니다. JSON으로 만들지 마십시오. Solidity / ethers / viem 측에서 abi.encode(<struct>)를 사용합니다.호출자가 PolicySet을 만드는 법
각 템플릿은 자체 파라미터 struct를 정의합니다(전체 목록은
1. 템플릿별 struct를 구체 값으로 빌드합니다.
2.
3. 템플릿 id와 (선택적) 4바이트 함수 selector와 함께
예시 — 두 주소 denylist 등록:
pcl-policy-templates 참고). 정책 등록 절차는 다음과 같습니다.1. 템플릿별 struct를 구체 값으로 빌드합니다.
2.
abi.encode로 bytes를 인코딩합니다.3. 템플릿 id와 (선택적) 4바이트 함수 selector와 함께
PolicySet으로 래핑합니다.예시 — 두 주소 denylist 등록:
import { IPcl, PolicySet, ContractPolicyConfig, DenylistPolicy } from "@maroo-chain/contracts/IPcl.sol";
DenylistPolicy memory dl = DenylistPolicy({
addresses: new address[](2)
});
dl.addresses[0] = 0xAaaA...;
dl.addresses[1] = 0xBbBb...;
PolicySet memory ps = PolicySet({
templateId: "DENYLIST_POLICY",
policy: abi.encode(dl),
selector: "" // empty → 대상 컨트랙트의 모든 호출에 적용
}); 이후
ContractPolicyConfig에 첨부하고 IPcl.registerContractPolicies(...)로 제출합니다.Global vs Contract 정책
범위는 두 가지입니다.
트랜잭션 진입 시 PCL이 글로벌 설정의 모든 정책을 평가하고, 호출 대상이 ContractPolicyConfig가 등록된 컨트랙트이며 규제 경로를 사용한다면 그 컨트랙트 설정의 모든 정책도 평가합니다. 어느 하나라도 실패하면 해당 ReasonCode와 함께 전체 트랜잭션이 거절됩니다.
GlobalPolicyConfig— 체인의 모든 트랜잭션에 AnteHandler가 적용합니다. 체인 전역 정책 관리자(pcl-policy-admin참고)가 관리합니다. 전형적인 내용은 denylist, 미인증 사용자 주기 거래량 상한 등입니다.ContractPolicyConfig— 특정 컨트랙트 주소를 대상으로 하는 트랜잭션(규제 경로 /runOnPcl)에만 적용됩니다. 각 컨트랙트 설정은 자체admin을 가지므로 컨트랙트 owner가 체인 전역 admin과 무관하게 자기 정책을 갱신할 수 있습니다.
트랜잭션 진입 시 PCL이 글로벌 설정의 모든 정책을 평가하고, 호출 대상이 ContractPolicyConfig가 등록된 컨트랙트이며 규제 경로를 사용한다면 그 컨트랙트 설정의 모든 정책도 평가합니다. 어느 하나라도 실패하면 해당 ReasonCode와 함께 전체 트랜잭션이 거절됩니다.
selector 필드
각
PolicySet은 선택적 selector(4바이트 함수 selector를 bytes로 인코딩)를 가집니다. 비어있지 않으면 그 selector를 호출하는 함수에만 정책이 적용됩니다. 단일 컨트랙트가 함수별로 다른 규칙을 적용할 수 있습니다.// `foo(uint256)` 호출에만 적용:
bytes memory fooSel = abi.encodePacked(bytes4(keccak256("foo(uint256)")));
PolicySet memory ps = PolicySet({
templateId: "VOLUME_POLICY",
policy: abi.encode(volumePolicy),
selector: fooSel
}); Empty
selector("")는 "이 컨트랙트의 모든 호출"을 의미합니다.