IPcl.contractPolicies / removeContractPolicies
contractPolicies(
address contractAddress
) external view returns (ContractPolicyConfig memory);
removeContractPolicies(address contractAddress) external; Two operations against the contract-scoped policy binding. contractPolicies(address) is a view returning the currently bound ContractPolicyConfig (or a zeroed struct if none is bound). removeContractPolicies(address) clears the PolicySet[] on a PCL-wrapped proxy — it does NOT remove the contract-admin binding itself, so the admin retains authority to re-attach policies later via changeContractPolicies. Two guardrails: (1) only the current contract admin can remove; (2) a policy-aware precompile — a set fixed in the chain binary, currently only the Privacy precompile at 0x100000000000000000000000000000000000000b — cannot be cleared this way — the call reverts with CannotEmpty("policy-aware precompile policies"). To relax rules on a policy-aware precompile, use changeContractPolicies with a permissive PolicySet instead.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
contractAddress | address | ✓ | Target contract. For the view, any address is acceptable (unbound targets return a zeroed struct). For removal, must be a PCL-wrapped proxy — policy-aware precompile addresses revert. |
Returns
ContractPolicyConfig | void contractPolicies returns ContractPolicyConfig — _contract, admin, and policies. removeContractPolicies returns nothing; success emits ContractPoliciesRemoved(contractAddress).
Errors
| Code | Name | Description |
|---|---|---|
Unauthorized | Unauthorized | Applies to removeContractPolicies. Reverts when msg.sender is not the target's current contract admin. |
CannotEmpty | CannotEmpty | Applies to removeContractPolicies. Reverts with CannotEmpty("policy-aware precompile policies") when contractAddress is a policy-aware precompile (currently only the Privacy precompile) — these targets must always retain at least one active PolicySet. |
PclProxyNotRegistered | PclProxyNotRegistered | Applies to removeContractPolicies. Reverts when the target has a contract policy binding (for example one set in genesis) but is not a PCL-wrapped proxy. A target with no binding gets ContractPolicyNotRegistered first, and a policy-aware precompile CannotEmpty. |
ContractPolicyNotRegistered | ContractPolicyNotRegistered | Applies to removeContractPolicies, and is checked first. Reverts when the target has no contract policy binding — it was not deployed through deployPclProxy and is not a policy-aware precompile (an EOA, an agent wallet, a contract deployed some other way). Encoded as ContractPolicyNotRegistered(address contractAddress). contractPolicies does not revert for such an address; it returns a zeroed struct. |
Examples
Read the current binding via viem
Returns a fully populated ContractPolicyConfig when the target has a binding; otherwise every field is zero.
import { createPublicClient, http } from "viem";
import { iPclAbi as pclAbi } from "@maroo-chain/contracts/abi/precompiles/pcl/IPcl";
const PCL = "0x1000000000000000000000000000000000000005" as const;
// TODO: replace with the real proxy address.
const PROXY = "0x8F3ac2B1d9E74c05A6B18FE27Dc4913e5A0F7b62" as const;
const publicClient = createPublicClient({ transport: http("https://rpc-testnet.maroo.io") });
const cfg = await publicClient.readContract({
address: PCL,
abi: pclAbi,
functionName: "contractPolicies",
args: [PROXY],
});
console.log(cfg.admin, cfg.policies.length); Refuse to clear policies on a policy-aware precompile
removeContractPolicies is intentionally blocked on policy-aware precompiles so the chain never runs one with a missing policy set. Use changeContractPolicies to submit a new (possibly permissive) PolicySet instead.
import { BaseError, ContractFunctionRevertedError, createWalletClient, http, decodeErrorResult } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const PCL = "0x1000000000000000000000000000000000000005" as const;
// Privacy precompile — a policy-aware precompile on Maroo.
const PRIVACY = "0x100000000000000000000000000000000000000b" as const;
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRECOMPILE_ADMIN_KEY as `0x${string}`),
transport: http("https://rpc-testnet.maroo.io"),
});
try {
await wallet.writeContract({
address: PCL,
abi: pclAbi,
functionName: "removeContractPolicies",
args: [PRIVACY],
});
} 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) throw err;
const decoded = decodeErrorResult({ abi: pclAbi, data: revert.raw });
// decoded.errorName === "CannotEmpty"
// decoded.args === ["policy-aware precompile policies"]
console.error("cannot clear policies on a policy-aware precompile:", decoded.args[0]);
}