IPcl.changeContractPolicies

changeContractPolicies(ContractPolicyConfig calldata policy) external

Replaces the entire policy configuration for a contract, and — in the same call — optionally hands over admin authority. The caller must be the currently registered admin for policy._contract. On success the stored PolicySet[] is replaced wholesale by policy.policies, and the admin binding is set to policy.admin. To keep the current admin, pass the same address in policy.admin; to hand over, pass the new admin's address. This is the only supported admin-handover path — there is no separate transfer method.

Parameters

Name Type Required Description
policy ContractPolicyConfig The full replacement configuration. _contract identifies the target contract (must have an existing config). admin is the address that will be the admin after this call — set it equal to the current admin to keep authority, or to a different address to hand over. policies is the new complete PolicySet[] array (wholesale replacement, not a merge or delta).

Returns

Type: void

No return value. Reverts on failure.

Errors

Code Name Description
Unauthorized Unauthorized Reverts if the caller is not the currently registered admin for policy._contract.
PolicyNotRegistered PolicyNotRegistered Reverts if there is no existing contract policy configuration for policy._contract. Use registerContractPolicies for the first registration.
InvalidAddress InvalidAddress Reverts if _contract or admin cannot be normalized to a valid chain-side account.
UnknownPolicyType UnknownPolicyType Reverts if any PolicySet.templateId in policies does not match a registered policy template on this network.
InvalidParameter InvalidParameter Reverts if any PolicySet.policy bytes fail to ABI-decode into the template's expected parameter struct.

Examples

Update policies without changing admin

Standard policy update flow: the current admin submits a replacement PolicySet[] and keeps admin authority by echoing their own address in admin.

import { createWalletClient, http, encodeAbiParameters } from "viem";
import { privateKeyToAccount } from "viem/accounts";

const PCL = "0x1000000000000000000000000000000000000005";
const tokenAddress   = "0x1234567890123456789012345678901234567890"; // replace before production
const currentAdmin   = "0xAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa"; // replace before production

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

const wallet = createWalletClient({
  account: privateKeyToAccount(process.env.ADMIN_KEY as `0x${string}`),
  transport: http(process.env.MAROO_RPC),
});

await wallet.writeContract({
  address: PCL,
  abi: pclAbi,
  functionName: "changeContractPolicies",
  args: [{
    _contract: tokenAddress,
    admin:     currentAdmin,        // same address → no handover
    policies: [{
      templateId: "DENYLIST_POLICY",
      policy:     denylistBytes,
      selector:   "0x",
    }],
  }],
});

Hand over admin authority in the same call

Handover flow: the current admin sets admin to a different address. After the transaction confirms, only the new admin can further modify or remove the contract's policies. Handover and policy replacement happen atomically in the same call.

// Called by the CURRENT admin. After this transaction lands, `newAdmin`
// becomes the sole address able to call changeContractPolicies /
// removeContractPolicies for tokenAddress.
const newAdmin = "0xCcCcCcCcCcCcCcCcCcCcCcCcCcCcCcCcCcCcCcCc"; // replace before production

await wallet.writeContract({
  address: PCL,
  abi: pclAbi,
  functionName: "changeContractPolicies",
  args: [{
    _contract: tokenAddress,
    admin:     newAdmin,            // handover: authority transfers to newAdmin
    policies:  existingPolicySets,  // keep policies as-is, or replace at the same time
  }],
});

// A subsequent call from the old admin would now revert with Unauthorized.

Read-modify-write when only tweaking one policy

Because changeContractPolicies replaces the entire PolicySet[], always read the current configuration first and rebuild the array locally before submitting.

import { createPublicClient, http } from "viem";

const publicClient = createPublicClient({ transport: http(process.env.MAROO_RPC) });

// Read the current config first — the call is a wholesale replacement,
// so anything you omit gets dropped.
const current = await publicClient.readContract({
  address: PCL,
  abi: pclAbi,
  functionName: "contractPolicies",
  args: [tokenAddress],
});

// Rebuild the array with your edit; here we append a new PolicySet.
const nextPolicies = [...current.policies, additionalPolicySet];

await wallet.writeContract({
  address: PCL,
  abi: pclAbi,
  functionName: "changeContractPolicies",
  args: [{
    _contract: tokenAddress,
    admin:     current.admin,   // preserve current admin
    policies:  nextPolicies,
  }],
});
ESC
Type to search