DENYLIST_POLICY

component compliance

관찰된 호출 경로가 denylist 주소에 도달하는 트랜잭션을 거절합니다. 가치를 옮기지 않는 호출도 포함됩니다.

DENYLIST_POLICY는 주소 목록을 파라미터로 받는 리프 정책 템플릿입니다. PCL은 실행 중에 관찰된 모든 비-PCL 관리 CALL 경로에 대해 이 정책을 평가합니다. 직접 호출뿐 아니라 PCL 래핑 프록시를 거치는 중첩 경로도 포함합니다. 평가는 자금 이동이 아니라 호출 경로 기반이므로, Transfer 로그가 발생하지 않는 0-value 호출이라도 경로가 denylist 주소에 닿으면 정책이 발동합니다. 일치가 발견되면 InDenylist(address sender)로 revert됩니다.

파라미터 struct

IPcl.sol 그대로입니다. 주소 목록 하나만 있으며, 주소별 만료나 사유 필드는 없습니다. 추가 메타데이터가 필요하면 오프체인에 보관합니다.
struct DenylistPolicy {
    address[] addresses;
}
// ABI 튜플: (address[])

자금 이동이 아니라 호출 경로 기반 평가

PCL의 postCall 훅은 트랜잭션 실행 중 관찰된 모든 비-PCL 관리 CALL 경로를 검사합니다. delegate call은 실행 컨텍스트가 호출자에 남으므로 제외합니다. 각 경로마다 발신자와 대상 주소를 denylist와 대조합니다. 이 평가는 경로가 자금을 옮겼는지 여부와 무관합니다. 중첩된 0-value call이 라우터를 거쳐 denylist 주소를 대상으로 하더라도 전체 트랜잭션이 거절됩니다. 이전에는 경로가 native 또는 ERC-20 Transfer 로그를 하나 이상 발생시켰을 때만 검사가 수행되었으나, 0-value 호출로 우회할 수 없도록 이 조건이 제거되었습니다.

정책이 적용되는 지점

적용 지점은 두 곳이며, 둘 다 동일한 호출 경로 관찰 모델을 사용합니다.

  • 전역(GlobalPolicyConfig) — 체인의 모든 트랜잭션에 평가됩니다. 전역 denylist 주소는 어떤 규제 경로로도 도달할 수 없습니다.
  • 컨트랙트 범위(ContractPolicyConfig) — 트랜잭션이 대상의 PCL 래핑 프록시를 경유할 때만 평가됩니다. 프록시를 우회해 구현 컨트랙트를 직접 호출하는 경로는 컨트랙트 범위 denylist를 발동시키지 않으므로, 프록시 주소를 표준 진입점으로 공개합니다.


정책 첨부 방식은 동일합니다. 인코딩된 파라미터를 templateId "DENYLIST_POLICY"PolicySet으로 래핑합니다.
import { IPcl, PolicySet, ContractPolicyConfig, DenylistPolicy } from "@maroo-chain/contracts/precompiles/pcl/IPcl.sol";

DenylistPolicy memory dl = DenylistPolicy({ addresses: new address[](2) });
dl.addresses[0] = 0x8F3ac2B1d9E74c05A6B18FE27Dc4913e5A0F7b62;
dl.addresses[1] = 0x2c7f09B81a6D3FF1e5A0d4c6bC2a8f7E19dc3a4B;

PolicySet memory ps = PolicySet({
    templateId: "DENYLIST_POLICY",
    policy:     abi.encode(dl),
    selector:   ""  // empty → 대상의 모든 호출에 적용
});

거절 페이로드

denylist 매치는 타입 오류 InDenylist(address sender)로 revert됩니다. sender 인자는 매치를 유발한 주소로, 어느 쪽이 매치되었는지에 따라 관찰된 경로의 principal 또는 대상 주소입니다. 클라이언트에서는 IPcl ABI로 decodeErrorResult를 사용해 디코드합니다.
import { decodeErrorResult } from "viem";

try {
  await wallet.writeContract({ /* 프록시 호출... */ });
} catch (err: any) {
  const decoded = decodeErrorResult({ abi: pclAbi, data: err.data });
  if (decoded.errorName === "InDenylist") {
    // decoded.args[0]가 문제 주소이며, UI에 표시할 수 있습니다.
  }
}

다른 템플릿과의 조합

DENYLIST_POLICY는 리프 정책이며 attestation이나 거래량을 검사하지 않습니다. 적용 범위를 넓히려면 attestation과 상한 템플릿을 함께 사용합니다.

  • attestation 검사는 EAS_POLICY, OKRW_EAS_TRANSFER_LIMIT_POLICY, OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY에서 담당합니다.
  • 금액 상한은 VOLUME_POLICY, PERIODIC_VOLUME_POLICY가 담당합니다.
  • 같은 PolicyConfig에 여러 PolicySet을 넣으면 AND로 결합됩니다. 어느 하나라도 실패하면 해당 정책의 ReasonCode와 함께 전체 트랜잭션이 거절됩니다.
ESC
검색어를 입력하세요