IPcl.pclProxy
pclProxy(address proxy) external view returns (PclProxyEntry memory) Returns the on-chain registry entry for a PCL-wrapped proxy address. The PclProxyEntry tuple contains the proxy variant (kind), the currently-bound policy admin (admin), and the registered proxy address (proxy). If proxy is not registered, every field of the returned entry is zero (kind = Unspecified, admin = 0x0, proxy = 0x0) — the call does not revert. The admin field is a projection of the canonical contract-admin record: it is set the moment deployPclProxy runs and is kept in sync when a subsequent changeContractPolicies rotates the admin, so a client reading this view sees the current gatekeeper without a second query to contractPolicies.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
proxy | address | ✓ | The proxy address to look up in the PCL proxy registry. This is the address a user calls when transacting through the regulated path (not the underlying implementation address). |
Returns
PclProxyEntry A tuple of (PclProxyKind kind, address admin, address proxy). kind is one of Unspecified | Transparent | UUPS | Beacon. admin is the address currently authorized to call changeContractPolicies and removeContractPolicies against this proxy. proxy is the registered proxy address itself, echoed back for convenience when iterating multiple entries client-side. When the address is not registered, all three fields are zero.
Examples
Read a proxy registry entry with viem
The view returns zero-valued fields for unregistered addresses; branch on kind === Unspecified (0) to detect that case rather than catching a revert.
import { createPublicClient, http } from "viem";
const PCL = "0x1000000000000000000000000000000000000005" as const;
const pclAbi = [{
name: "pclProxy", type: "function", stateMutability: "view",
inputs: [{ name: "proxy", type: "address" }],
outputs: [{
type: "tuple",
components: [
{ name: "kind", type: "uint8" },
{ name: "admin", type: "address" },
{ name: "proxy", type: "address" },
],
}],
}] as const;
const client = createPublicClient({ transport: http("https://rpc-testnet.maroo.io") });
// TODO: replace with the real proxy address before production.
const proxyAddress = "0x8f3aC2b1D9e74C05a6B18Fe27dC4913E5A0f7b62";
const entry = await client.readContract({
address: PCL,
abi: pclAbi,
functionName: "pclProxy",
args: [proxyAddress],
});
if (entry.kind === 0) {
console.log("not a registered PCL proxy");
} else {
const kindName = ["Unspecified", "Transparent", "UUPS", "Beacon"][entry.kind];
console.log(`kind=${kindName} admin=${entry.admin} proxy=${entry.proxy}`);
} Verify the caller of changeContractPolicies is still the registered admin
Because admin mirrors the contract-admin binding, on-chain automation can gate follow-up policy updates on the current admin without a second call to contractPolicies.
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
import { IPcl, PclProxyEntry, PclProxyKind } from "@maroo-chain/contracts/precompiles/pcl/IPcl.sol";
contract AdminGuard {
IPcl constant PCL = IPcl(0x1000000000000000000000000000000000000005);
/// @notice Reverts unless `expectedAdmin` is currently the admin recorded
/// in the PCL proxy registry for `proxy`.
function requireProxyAdmin(address proxy, address expectedAdmin) external view {
PclProxyEntry memory entry = PCL.pclProxy(proxy);
require(entry.kind != PclProxyKind.Unspecified, "not a registered PCL proxy");
require(entry.admin == expectedAdmin, "admin rotated");
}
}