PCL 기본 제공 정책 템플릿
마루가 V1으로 제공하는 일곱 개의 리프 정책 템플릿(denylist, 거래량 상한, attestation 게이트, KYC 등급 변형, 에이전트별 전송 상한)과 이를 조합하는 두 개의 조합 템플릿(LOGICAL_POLICY, FOR_EACH_POLICY)을 소개합니다.
PCL은 가장 흔한 규제 및 비즈니스 컴플라이언스 게이트를 포괄하는 일곱 개의 리프 정책 템플릿을 기본 제공합니다. DENYLIST_POLICY, VOLUME_POLICY, PERIODIC_VOLUME_POLICY, EAS_POLICY, OKRW_EAS_TRANSFER_LIMIT_POLICY, OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY, AGENT_OKRW_TRANSFER_LIMIT_POLICY가 그 목록입니다. 각 템플릿은 IPcl.sol에 Solidity 파라미터 struct를 정의하며, 호출자는 이를 abi.encode하여 PolicySet.policy에 담습니다. 리프 위에 PCL은 두 개의 조합 템플릿도 노출합니다. LOGICAL_POLICY는 자식 PolicySet들을 AND/OR로 결합하고, FOR_EACH_POLICY는 에이전트의 소유자들에 자식 정책을 확장하여 적용합니다. 자세한 내용은 pcl-composite-policies 컨셉에서 다룹니다.
일곱 개 리프 템플릿 요약
모든 리프 템플릿은
각 템플릿의 정확한 struct, 기본값, revert 경로는
templateId 문자열과 IPcl.sol에 선언된 Solidity 파라미터 struct 한 쌍으로 구성됩니다.| templateId | 파라미터 struct | 용도 |
|---|---|---|
DENYLIST_POLICY | DenylistPolicy { address[] addresses; } | 특정 발신자/수신자를 차단합니다. |
VOLUME_POLICY | VolumePolicy { string[] tokens; VolumeUnitPolicy[] limits; } | 자산별 단건 최소/최대 금액 한도를 정합니다. |
PERIODIC_VOLUME_POLICY | PeriodicVolumePolicy { string[] tokens; UnitPeriodicVolumePolicy[] limits; } | 자산별 주기 누적 거래량 상한을 둡니다. |
EAS_POLICY | EasPolicy { address easContract; address indexContract; bytes32 schemaUid; } | 지정된 스키마의 유효한 EAS attestation을 요구합니다. |
OKRW_EAS_TRANSFER_LIMIT_POLICY | OkrwEasTransferLimitPolicy { …; uint256 transferLimitAmount; } | attestation 보유/미보유 상태에 따라 OKRW 단건 금액을 제한합니다. |
OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY | OkrwEasPeriodicVolumeLimitPolicy { …; uint256 maxAmount; uint64 resetPeriodSeconds; } | attestation 상태에 따라 OKRW 주기 거래량 상한을 게이팅합니다. |
AGENT_OKRW_TRANSFER_LIMIT_POLICY | AgentOkrwTransferLimitPolicy { uint256 reserved; } | 호출 에이전트의 TransferLimit 메타데이터에서 읽은 OKRW 단건 상한을 적용합니다. |
각 템플릿의 정확한 struct, 기본값, revert 경로는
/concepts/compliance/ 하위의 전용 컨셉 페이지에서 다룹니다.조합 템플릿
두 개의 추가 템플릿은 구조적입니다. 리프 파라미터 대신 다른 PolicySet을 담습니다.
조합 규칙과 오류 표면은
| templateId | 파라미터 struct | 용도 |
|---|---|---|
LOGICAL_POLICY | LogicalPolicy { LogicalQuantifier quantifier; PolicySet[] children; } | 자식을 AND(And) 또는 OR(Or)로 결합합니다. Or 전체 실패 시 AnyOfRejected(bytes[] childReverts)로 revert합니다. |
FOR_EACH_POLICY | ForEachPolicy { ForEachQuantifier quantifier; ForEachSubject subject; PolicySet child; } | 단일 자식을 주체 집합의 각 요소에 대해 한 번씩 적용합니다. V1은 AgentOwners를 Any / Every로 지원합니다. |
조합 규칙과 오류 표면은
pcl-composite-policies에서 자세히 다룹니다. 중첩은 폭넓게 허용됩니다 — 조합 정책이 조합 정책을 담을 수 있고 깊이는 MaxDepthExceeded(uint8 maxDepth)로 제한됩니다. 유일한 중첩 예외는 리프 OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY이며, 이 정책은 중첩 자식으로 사용할 수 없습니다(PolicyCannotBeNested).어느 템플릿에서든 PolicySet 만들기
패턴은 모든 템플릿에 동일합니다. 파라미터 struct를 빌드하고
abi.encode한 뒤 PolicySet으로 래핑합니다.// Denylist 예시 — 모든 리프 템플릿이 동일한 형태입니다.
DenylistPolicy memory dl = DenylistPolicy({ addresses: new address[](1) });
dl.addresses[0] = 0xAaaAAaAaaAaAAaAaAaaAAAaaAAaAaAaAaAaAAAaA;
PolicySet memory ps = PolicySet({
templateId: "DENYLIST_POLICY",
policy: abi.encode(dl),
selector: "" // 대상 컨트랙트의 모든 호출에 적용합니다.
}); 등록 전에 템플릿 활성 여부 확인
현재 네트워크에서 정책 관리자가 등록한 템플릿만 인스턴스화할 수 있습니다. 제출 전에
IPcl.policyTemplate(templateId)를 호출합니다. 등록되어 있으면 디스크립터를 반환하고, 그렇지 않으면 InvalidPolicyTemplate로 revert합니다. 템플릿 등록은 컨소시엄 거버넌스 작업이며 dApp이 직접 수행하지 않습니다.const PCL = "0x1000000000000000000000000000000000000005";
const descriptor = await publicClient.readContract({
address: PCL,
abi: pclAbi,
functionName: "policyTemplate",
args: ["EAS_POLICY"],
});
console.log(descriptor.templateId, descriptor.name);