IPcl.changeContractPolicies

changeContractPolicies(ContractPolicyConfig calldata policy) external

policy._contractContractPolicyConfig를 upsert합니다. 특정 컨트랙트 주소에 대한 최초 호출(즉 admin이 설정되지 않은 상태 — 보통 deployPclProxy로 배포되지 않은 컨트랙트에서만 발생)에는 누구든 초기 admin과 정책을 바인딩할 수 있습니다. 이후 호출은 현재 admin만 정책을 변경할 수 있습니다. 페이로드의 admin 필드가 앞으로의 게이트키퍼가 되므로, 한 호출에서 admin 회전과 정책 교체가 원자적으로 함께 처리됩니다. 별도의 registerContractPolicies 진입점은 없으며, 이 메서드가 컨트랙트 범위 정책의 유일한 쓰기 경로입니다.

파라미터

이름 타입 필수 설명
policy ContractPolicyConfig 완전한 교체 설정입니다. _contract는 정책이 바인딩되는 주소(보통 PCL 프록시 주소), admin은 앞으로의 변경을 승인받을 주소, policies는 적용될 PolicySet 항목의 전체 목록입니다. 호출은 기존 정책을 통째로 교체하며 부분 업데이트 모드는 없습니다.

반환값

타입: void

반환값이 없습니다. 성공 시 PCL은 ContractPoliciesChanged(address indexed contractAddress, address admin, PolicySet[] policies)를 발행합니다.

에러

코드 이름 설명
ContractPolicyNotRegistered ContractPolicyNotRegistered policy._contract에 기존 설정이 없고 PCL의 흐름이 그것을 요구할 때 ContractPolicyNotRegistered(address contractAddress)로 revert됩니다. deployPclProxy로 배포된 컨트랙트는 이미 admin이 초기화되어 있으므로, 그 admin이 아닌 계정에서 changeContractPolicies를 호출하면 이 오류가 아니라 PolicyAlreadyRegistered(admin 불일치)로 갑니다.
PolicyAlreadyRegistered PolicyAlreadyRegistered 대상에 이미 설정이 존재하고 호출자가 현재 admin이 아닐 때 PolicyAlreadyRegistered(address contractAddress)로 revert됩니다. 인자는 이제 컨트랙트 주소 자체입니다(최근 변경 — 이전 ABI는 문자열 인자였습니다).
PolicyNotRegistered PolicyNotRegistered PolicySet 항목 중 하나가 이 네트워크에 등록되지 않은 템플릿 id를 참조하면 PolicyNotRegistered(string templateId)로 revert됩니다. 제출 전에 policyTemplate(templateId)로 확인하십시오.
InvalidParameter InvalidParameter PolicySet.policy 바이트 페이로드가 참조한 템플릿의 파라미터 struct로 디코드되지 않으면 revert됩니다.
InvalidSelector InvalidSelector PolicySet.selector가 비어 있지 않으면서 정확히 4바이트가 아닐 때 revert됩니다.

예제

PCL 프록시에 DENYLIST_POLICY 바인딩

페이로드의 정책 목록은 이전 목록을 통째로 교체합니다. admin을 회전하려면 현재 admin이 페이로드의 admin 값을 다른 주소로 바꿔 같은 호출을 제출합니다.

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

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 address before production.
const proxyAddress = "0x8f3aC2b1D9e74C05a6B18Fe27dC4913E5A0f7b62";
// TODO: replace with the real admin address before production.
const adminAddress = "0x2c7F09b81A6D3fF1e5A0D4c6Bc2A8f7E19dC3a4B";

const denylistBytes = encodeAbiParameters(
  [{ type: "address[]", name: "addresses" }],
  [["0x5aB7c1e40b8dA46f9c7e29D3fA614e97b8f0Ac21"]],
);

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

바인딩되지 않은 대상에서 ContractPolicyNotRegistered 처리

ContractPolicyNotRegisteredPolicyAlreadyRegistered가 이제 모두 address contractAddress를 인자로 담으므로, 한 번의 디코드로 두 실패 유형 중 어느 쪽이든 문제의 대상 주소를 얻을 수 있습니다.

import { decodeErrorResult } from "viem";
import { pclAbi } from "@maroo-chain/contracts/abi";

try {
  await wallet.writeContract({
    address: PCL,
    abi: pclAbi,
    functionName: "changeContractPolicies",
    args: [payload],
  });
} catch (err: any) {
  const decoded = decodeErrorResult({ abi: pclAbi, data: err.data });
  if (decoded.errorName === "ContractPolicyNotRegistered") {
    // decoded.args[0] is the contract address that has no config yet.
    console.error(
      `No policy config exists for ${decoded.args[0]}. Deploy via deployPclProxy or bind the initial admin first.`,
    );
  } else if (decoded.errorName === "PolicyAlreadyRegistered") {
    // decoded.args[0] is the contract address whose current admin the caller is not.
    console.error(`Not the admin of ${decoded.args[0]}.`);
  } else {
    throw err;
  }
}
ESC
검색어를 입력하세요