IPcl.preCall

preCall(address contractAddress, address principal, bytes calldata data, uint256 value) external returns (bytes32 sessionId)

Opens a PCL policy-evaluation session for a proxied call. Invoked by the PCL-wrapped proxy hook before the proxy delegatecalls its implementation. preCall evaluates both the contract-scoped ContractPolicyConfig (if one exists for contractAddress) and the chain-wide GlobalPolicyConfig against the supplied principal / data / value, snapshots the pre-call state needed for accumulation-style policies (e.g. periodic volume), and returns a sessionId that the matching postCall must present. If any policy rejects, the call reverts with the corresponding PCL ReasonCode.

Parameters

Name Type Required Description
contractAddress address The proxy address used as the PCL policy-lookup key. Registered ContractPolicyConfig entries are matched by this address.
principal address The actual originator of the call (EOA or AA smart account) as observed by the proxy. Forwarded explicitly because the immediate msg.sender seen by PCL is the proxy itself; without this argument, sender / principal denylist checks would not fire.
data bytes The original inner calldata forwarded to the implementation. Its first four bytes (when present) are extracted as the function selector and matched against selector-scoped PolicySets.
value uint256 The native value (in aokrw) attached to the call. Feeds into volume-based policies.

Returns

Type: bytes32

A session identifier that the caller must pass back to postCall after the delegatecall completes. The session holds the pre-call log offset and native-transfer collector needed to evaluate accumulation policies at post-call time.

Errors

Code Name Description
Unauthorized Unauthorized Reverts when the immediate caller is not an allowed PCL proxy entrypoint (only PCL-registered proxies may open sessions).
InDenylist InDenylist Reverts when the principal (or the target contract) matches an active DENYLIST_POLICY at global or contract scope.
EasAttestationRequired EasAttestationRequired Reverts when an EAS_POLICY requires an attestation the principal does not hold.
ExceededPeriodicVolume ExceededPeriodicVolume Reverts when a periodic-volume policy's accumulated amount plus value exceeds the configured cap.

Examples

Invoked by the PCL proxy hook

The canonical caller of preCall is the PCL proxy hook. It captures the returned sessionId and pairs it with a matching postCall after the delegatecall. External dApps interact with the proxy directly; the hook wiring is invisible to them.

// Excerpt from PclProxyHook.sol — external dApps do NOT call preCall directly;
// the PCL-wrapped proxy calls it before delegatecalling its implementation.
address constant PCL = 0x1000000000000000000000000000000000000005;

function hookedDelegate(address addrForPolicy, address impl) internal {
    address principal = msg.sender;
    bytes32 sessionId = IPcl(PCL).preCall(
        addrForPolicy,   // proxy address = PCL policy key
        principal,       // actual EOA / AA caller
        msg.data,
        msg.value
    );

    (bool ok, bytes memory ret) = impl.delegatecall(msg.data);

    IPcl(PCL).postCall(sessionId, addrForPolicy, principal, msg.data, msg.value, ok);
    if (!ok) { assembly { revert(add(ret, 0x20), mload(ret)) } }
    assembly { return(add(ret, 0x20), mload(ret)) }
}

Decoding a PCL revert from a proxy call

When preCall rejects, the revert bubbles through the proxy to the client. Decode with the IPcl ABI to surface the ReasonCode.

import { createWalletClient, decodeErrorResult, http } from "viem";

try {
  await walletClient.writeContract({
    address: pclProxyAddress,
    abi: myTokenAbi,
    functionName: "transfer",
    args: [recipient, 10_000_000n * 10n ** 18n], // 10,000,000 OKRW
  });
} catch (err: any) {
  const decoded = decodeErrorResult({ abi: pclAbi, data: err.data });
  console.log("PCL ReasonCode:", decoded.errorName, decoded.args);
  // e.g. EasAttestationRequired { sender: 0x... }
}
ESC
Type to search