PCL 프리컴파일
0x1000…0005에서 온체인 컴플라이언스를 강제하는 표면입니다. 이제 getParams()가 policyAdmin과 규제 entrypoints 리스트를 함께 반환합니다.
프로그래머블 컴플라이언스 레이어(PCL) 프리컴파일은 고정 주소 0x1000000000000000000000000000000000000005에 위치하며, 컴플라이언스 관련 모든 작업의 정식 표면입니다. 정책 템플릿 등록, 컨트랙트별 PolicySet 등록, 현재 규칙 조회, runOnPcl을 통한 EVM 호출 사전 평가를 담당합니다. 이제 모듈 파라미터를 단일 getParams() view 한 번으로 얻을 수 있으며, 반환되는 PclParams struct는 policyAdmin(템플릿과 글로벌 정책 등록 권한을 가진 체인 전역 admin)과 entrypoints(호출 시 트랜잭션을 규제 EVM 경로로 라우팅하는 컨트랙트 주소 배열)를 포함합니다.
아키텍처
graph TD
subgraph EVM Space
A[DApp / User] -- EVM Call --> B{Your Smart Contract};
B -- `runOnPcl(target, data, value)` --> C[PCL Precompile @ 0x10...05];
end
subgraph Maroo Native Layer
D[x/pcl module];
E[Policy Storage];
F[Compliance Logic];
end
C -- Native Bridge --> D;
D -- Reads --> E[Policy Storage];
D -- Executes --> F[Compliance Logic];
F -- Pass/Fail --> D;
D -- Result --> C;
C -- Returns / Reverts --> B;
B -- `(bool success, bytes memory result) = target.call(data)` --> G{Execute Original Call};
G -- Returns --> B;
B -- Returns --> A; 준수 스마트 컨트랙트에 대한 사용자 호출은 가로채어져 PCL 프리컴파일로 전달됩니다. 프리컴파일은 호출을 네이티브 x/pcl 모듈로 연결하고, 관련 정책을 로드하여 규제 준수 로직을 실행합니다. 검사가 통과하면 제어권이 EVM으로 반환되어 원래 의도된 호출을 실행합니다.
표면 구성
IPcl.sol이 PCL_CONTRACT 상수를 export하므로 호출자가 주소를 하드코딩할 필요가 없습니다. 관련 타입은 다음과 같습니다.import { IPcl, PclParams, PCL_CONTRACT } from "@maroo-chain/contracts/precompiles/pcl/IPcl.sol";
struct PclParams {
address policyAdmin;
address[] entrypoints;
}
// 디스커버리
function getParams() external view returns (PclParams memory);
function policyAdmin() external view returns (address);
// 템플릿
function policyTemplate(string calldata templateId) external view returns (PolicyTemplate memory);
function registerPolicyTemplate(string calldata templateId) external;
// 컨트랙트 정책
function registerContractPolicies(ContractPolicyConfig calldata cfg) external;
function changeContractPolicies(ContractPolicyConfig calldata cfg) external;
function removeContractPolicies(address _contract) external;
function contractPolicies(address _contract) external view returns (ContractPolicyConfig memory);
// 사전 평가
function runOnPcl(address contractAddress, bytes calldata data, uint256 value) external returns (bytes memory); `getParams`와 `policyAdmin`의 차이
getParams()와 기존 policyAdmin()은 동일한 admin 주소를 반환합니다. 새 코드에서는 getParams()를 선호합니다. 한 번의 호출로 entrypoints 배열까지 받을 수 있고, dApp이 호출 대상이 규제 경로로 평가되는지 확인하려면 이 정보가 필요하기 때문입니다. policyAdmin()은 간결한 접근자로 남아 있습니다.PclParams memory p = PCL_CONTRACT.getParams();
address admin = p.policyAdmin;
address[] memory routes = p.entrypoints; `entrypoints`의 역할
PCL은 두 가지 트랜잭션 트랙을 구분합니다(
pcl-dual-track-model 참고). entrypoints 배열은 체인이 규제 진입점으로 인식하는 컨트랙트 주소의 목록입니다. 트랜잭션의 to가 이 중 하나와 일치하면 호출이 규제 EVM 경로로 라우팅되고, 하위 단계에서 부착된 ContractPolicyConfig가 평가됩니다. 목록에 없는 주소를 호출하면 AnteHandler를 통한 글로벌 정책 평가는 동일하게 수행되지만 컨트랙트 정책 단계는 건너뜁니다. 컴플라이언스를 통합하는 dApp은 이 리스트를 읽어 대상이 ContractPolicyConfig 검사를 트리거하지 않을 때 사용자에게 경고할 수 있습니다.import { createPublicClient, http } from "viem";
const pcl = await client.readContract({
address: "0x1000000000000000000000000000000000000005",
abi: pclAbi,
functionName: "getParams",
});
const isRegulated = pcl.entrypoints
.map((a: string) => a.toLowerCase())
.includes(callTarget.toLowerCase());
if (!isRegulated) {
console.warn("대상이 규제 진입점이 아닙니다. ContractPolicyConfig 평가를 건너뜁니다.");
} 누가 무엇을 호출하는지
registerPolicyTemplate과 글로벌 정책 변경자는 policyAdmin만 호출할 수 있습니다. 컨트랙트별 정책 변경자(registerContractPolicies, changeContractPolicies, removeContractPolicies)는 각 ContractPolicyConfig의 admin 필드로 게이트됩니다. 이 키는 등록 시점에 컨트랙트 owner가 직접 지정하며 체인 전역 admin과 독립적입니다. 외부 dApp은 이 두 번째 경로로 자기 컨트랙트 정책을 등록합니다.조회 경로는 무료입니다
getParams, policyAdmin, policyTemplate, contractPolicies는 모두 순수 view 메서드입니다. 상태를 변경하지 않으며 오프체인 인덱서나 프런트엔드 로드 경로에서 호출하기에 안전합니다. 특정 인코딩된 호출이 통과될지 확인하려면 더 무거운 runOnPcl 사전 평가를 사용합니다.