testnet
GitHub

IPcl.runOnPcl

runOnPcl(address contractAddress, bytes calldata data, uint256 value) external returns (bytes memory)

Executes a call to a target contract within the PCL's regulated context. This function is the entry point to the 'Regulated Track'. It first triggers all applicable global and contract-specific policy checks based on the sender, target contract, and call data. If all checks pass, it then executes the provided call data on the target contract using a low-level call. If any check fails, the transaction reverts with a specific error.

Parameters

Name Type Required Description
contractAddress address The address of the target contract to call after PCL checks pass.
data bytes The ABI-encoded calldata for the function to be executed on the target contract.
value uint256 The amount of OKRW to send with the call (msg.value).

Returns

Type: bytes memory

The return data from the successful execution of the target contract call.

Errors

Code Name Description
InDenylist InDenylist Reverts if the sender's address is found in an active denylist policy.
EasAttestationRequired EasAttestationRequired Reverts if the sender lacks a required EAS attestation (e.g., for KYC).
VolumeAboveMaxLimit VolumeAboveMaxLimit Reverts if the transaction amount exceeds a configured volume limit.

Examples

Wrapping an ERC20 Transfer

This example shows a token contract that overrides its public `transfer` function. Instead of executing the logic directly, it calls `runOnPcl`, targeting itself (`address(this)`) and providing the encoded calldata for its own `internal` transfer logic. This ensures no transfer can occur without passing the PCL checks registered for this token.

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IPcl, PCL_CONTRACT } from "./IPcl.sol";

contract CompliantToken is IERC20 {
    // ... other ERC20 implementation ...

    function _transfer(address from, address to, uint256 amount) internal {
        // ... core transfer logic ...
    }

    // Public transfer function enforces PCL checks
    function transfer(address to, uint256 amount) public override returns (bool) {
        // Encode the call to the internal transfer function
        bytes memory data = abi.encodeWithSelector(
            this._transfer.selector,
            msg.sender,
            to,
            amount
        );

        // Execute via PCL. This contract calls itself through the PCL gate.
        PCL_CONTRACT.runOnPcl(address(this), data, 0);
        return true;
    }
}

Calling another contract with error handling

This example demonstrates how to use a `try/catch` block to handle potential reversions from `runOnPcl`. It specifically checks for the `InDenylist` custom error and provides a more user-friendly message.

import { IPcl, PCL_CONTRACT } from "./IPcl.sol";

contract PclCaller {
    function callCompliantService(address service, uint256 value) external {
        bytes memory data = abi.encodeWithSignature("deposit(uint256)", value);

        try PCL_CONTRACT.runOnPcl(service, data, 0) {
            // Success
        } catch (bytes memory reason) {
            // Attempt to decode a PCL custom error
            if (reason.length == 36) { // selector + 1 argument (e.g., address)
                bytes4 selector = bytes4(reason[:4]);
                if (selector == IPcl.InDenylist.selector) {
                    address sender = abi.decode(reason[4:], (address));
                    // Handle denylist error specifically
                    revert("Caller is on the denylist");
                }
            }
            // Revert with the original reason if it's not a known PCL error
            revert(string(reason));
        }
    }
}
ESC
Type to search