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
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,
}],
});