IPcl.changeContractPolicies
changeContractPolicies(ContractPolicyConfig calldata policy) external policy._contract의 ContractPolicyConfig를 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 처리
ContractPolicyNotRegistered와 PolicyAlreadyRegistered가 이제 모두 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;
}
}