IPcl.changeContractPolicies

changeContractPolicies(ContractPolicyConfig calldata policy) external

대상 주소의 ContractPolicyConfig를 upsert합니다. 대상은 deployPclProxy로 배포한 PCL 등록 프록시이거나, 바이너리가 지원하는 정책 인식 프리컴파일이어야 합니다. 정책 인식 프리컴파일은 현재 0x100000000000000000000000000000000000000b의 Privacy 프리컴파일 하나입니다. msg.sender는 대상의 현재 컨트랙트 admin이어야 합니다. 프록시라면 deployPclProxy를 호출한 계정(그 호출에서 admin이 되었습니다)이고, 정책 인식 프리컴파일이라면 체인이 설정한 admin입니다. 페이로드의 새 admin 값은 정책 교체와 함께 원자적으로 이전 값을 대체합니다. 정책 인식 프리컴파일에 빈 policies 배열을 전달하는 호출은 거절됩니다. 해당 대상은 항상 최소 하나 이상의 PolicySet을 유지해야 합니다. 일반 PCL 프록시는 빈 policies 배열을 허용하며, 이는 컨트랙트 범위 규칙이 없는 상태와 같습니다. 체인 전역 policyAdmin은 저장된 컨트랙트 admin과 우연히 같은 주소가 아닌 이상 이 설정을 변경할 수 없습니다.

파라미터

이름 타입 필수 설명
policy ContractPolicyConfig ✓ 새 설정 페이로드입니다. _contract는 대상 주소(등록된 PCL 프록시 또는 정책 인식 프리컴파일 주소)입니다. admin은 이 대상의 정책 설정에 대한 다음 게이트키퍼입니다. policies는 전체를 대체할 PolicySet[]입니다. _contract가 둘 다 아니면(EOA, 에이전트 지갑, 기본 구현체, 다른 방식으로 배포된 컨트랙트에는 컨트랙트 정책 바인딩이 없습니다) ContractPolicyNotRegistered(contractAddress)로 revert됩니다. _contract가 정책 인식 프리컴파일이고 policies가 비어 있으면 CannotEmpty("policy-aware precompile policies")로 revert됩니다.

반환값

타입: void

반환 값이 없습니다. 성공 시 ContractPoliciesChanged(contractAddress, admin, policies) 이벤트를 발행합니다.

에러

코드 이름 설명
Unauthorized Unauthorized msg.sender가 대상에 저장된 현재 admin이 아닐 때 revert됩니다. 새 프록시에 대한 첫 호출도 마찬가지이며, 그 admin은 이미 배포자입니다. 체인 전역 policyAdmin도 이 검사를 우회하지 못합니다.
ContractPolicyNotRegistered ContractPolicyNotRegistered _contract에 컨트랙트 정책 바인딩이 없을 때 revert됩니다. 즉 deployPclProxy로 배포되지 않았고 정책 인식 프리컴파일도 아닌 경우입니다(EOA, 에이전트 지갑, 기본 구현체, 다른 방식으로 배포된 컨트랙트). 이 검사는 입력을 디코딩한 뒤 가장 먼저 실행됩니다. ContractPolicyNotRegistered(address contractAddress) 형태로 인코딩됩니다.
PclProxyNotRegistered PclProxyNotRegistered _contract에 컨트랙트 정책 바인딩은 있지만(예: genesis에서 설정된 경우) 등록된 PCL 프록시도 아니고 정책 인식 프리컴파일도 아닐 때 revert됩니다. 바인딩이 없는 대상은 대신 ContractPolicyNotRegistered로 revert됩니다. PclProxyNotRegistered(address contractAddress) 형태로 인코딩됩니다.
CannotEmpty CannotEmpty 대상이 정책 인식 프리컴파일인데 policies가 빈 배열일 때 revert됩니다. CannotEmpty(string field) 형태이며 field 값은 policy-aware precompile policies입니다. 정책 인식 프리컴파일은 항상 최소 하나 이상의 PolicySet을 유지해야 합니다.
UnknownPolicyType UnknownPolicyType policies의 어떤 PolicySet.templateId가 알려진 정책 유형이 아닐 때 revert됩니다. UnknownPolicyType(string templateId) 형태로 인코딩되며, 관리자 확인보다 앞서 입력을 디코딩하는 단계에서 발생합니다.
PolicyNotRegistered PolicyNotRegistered policies의 어떤 정책(LOGICAL_POLICY / FOR_EACH_POLICY의 하위 정책 포함)이 이 네트워크에 등록되지 않은 템플릿을 사용할 때 revert됩니다. 정책 관리자가 등록하지 않았거나 제거한 경우입니다. PolicyNotRegistered(string templateId) 형태로 인코딩됩니다.

예제

PCL 래핑 ERC20 프록시에 denylist 정책 바인딩

PCL 등록 프록시에 대한 표준 사용 예시입니다. msg.sender는 프록시에 저장된 admin이어야 하며, 배포 직후에는 deployPclProxy를 호출한 계정입니다.

import { createWalletClient, http, encodeAbiParameters, toHex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { iPclAbi as pclAbi } from "@maroo-chain/contracts/abi/precompiles/pcl/IPcl";

const PCL = "0x1000000000000000000000000000000000000005" as const;

const wallet = createWalletClient({
  account: privateKeyToAccount(process.env.ADMIN_KEY as `0x${string}`),
  transport: http("https://rpc-testnet.maroo.io"),
});

// TODO: replace with the real proxy and admin addresses before production.
const proxyAddress = "0x8F3ac2B1d9E74c05A6B18FE27Dc4913e5A0F7b62";
const adminAddress = "0x2c7f09B81a6D3FF1e5A0d4c6bC2a8f7E19dc3a4B";

// abi.encode(DenylistPolicy({ addresses })) — a tuple, as the chain decodes it
const denylistBytes = encodeAbiParameters(
  [{ type: "tuple", components: [{ type: "address[]", name: "addresses" }] }],
  [{ addresses: [
    "0x5AB7c1e40b8DA46F9C7E29d3fA614e97b8F0ac21",
    "0x6BC8d2A03e91fFa5b8C4E0F3bd7A0e15c24Bda8E",
  ] }],
);

await wallet.writeContract({
  address: PCL,
  abi: pclAbi,
  functionName: "changeContractPolicies",
  args: [{
    _contract: proxyAddress,
    admin:     adminAddress,
    policies:  [{
      templateId: "DENYLIST_POLICY",
      policy:     denylistBytes,
      selector:   toHex("", { size: 0 }),
    }],
  }],
});

Privacy 프리컴파일에 정책 세트 부착 (정책 인식 프리컴파일)

정책 인식 프리컴파일은 PCL 프록시와 동일한 진입점을 사용하지만, 대상 주소가 프리컴파일 주소 자체입니다. 빈 정책 세트는 거절되므로, 규칙을 회전할 때는 정책을 비운 뒤 다시 추가하는 대신 새로운 비어 있지 않은 PolicySet[]을 제출합니다.

import { encodeAbiParameters, toHex } from "viem";

const PCL     = "0x1000000000000000000000000000000000000005" as const;
const PRIVACY = "0x100000000000000000000000000000000000000b" as const;

// TODO: replace with the real privacy-admin multisig before production.
const privacyAdmin = "0x2c7f09B81a6D3FF1e5A0d4c6bC2a8f7E19dc3a4B";

// Example: a DENYLIST_POLICY applied to every Privacy call.
// abi.encode(DenylistPolicy({ addresses })) — a tuple, as the chain decodes it
const denylistBytes = encodeAbiParameters(
  [{ type: "tuple", components: [{ type: "address[]", name: "addresses" }] }],
  [{ addresses: ["0x5AB7c1e40b8DA46F9C7E29d3fA614e97b8F0ac21"] }],
);

await wallet.writeContract({
  address: PCL,
  abi: pclAbi,
  functionName: "changeContractPolicies",
  args: [{
    _contract: PRIVACY,
    admin:     privacyAdmin,
    policies:  [{
      templateId: "DENYLIST_POLICY",
      policy:     denylistBytes,
      selector:   toHex("", { size: 0 }),
    }],
  }],
});

// NOTE: passing an empty policies array here reverts with
// CannotEmpty("policy-aware precompile policies"). To rotate rules,
// always submit a non-empty replacement set.

빈 정책으로 호출 시 CannotEmpty revert 디코드

revert 데이터는 CannotEmpty(string field) selector로 ABI 인코딩되며, 페이로드로 PCL의 다른 빈 필드 위반과 구분됩니다.

import { BaseError, ContractFunctionRevertedError, decodeErrorResult } from "viem";

try {
  await wallet.writeContract({
    address: PCL,
    abi: pclAbi,
    functionName: "changeContractPolicies",
    args: [{
      _contract: PRIVACY,
      admin:     privacyAdmin,
      policies:  [],  // empty — will revert for policy-aware precompiles
    }],
  });
} catch (err) {
  // viem keeps the revert data on the ContractFunctionRevertedError in the cause chain, not on err.
  const revert = err instanceof BaseError ? err.walk((e) => e instanceof ContractFunctionRevertedError) : null;
  if (revert instanceof ContractFunctionRevertedError && revert.raw) {
    const decoded = decodeErrorResult({ abi: pclAbi, data: revert.raw });
    // decoded.errorName === "CannotEmpty"
    // decoded.args[0] === "policy-aware precompile policies"
    console.error(`changeContractPolicies reverted: ${decoded.errorName}`, decoded.args);
  } else {
    throw err;
  }
}
ESC
검색어를 입력하세요