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 | ✓ | Amount in aokrw (base units, 18 decimals) to forward with the call — becomes msg.value on the target. |
Returns
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.
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.18;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IPcl } from "@maroo-chain/contracts/IPcl.sol";
contract CompliantToken is IERC20 {
IPcl constant PCL = IPcl(0x1000000000000000000000000000000000000005);
// ... 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) {
bytes memory data = abi.encodeWithSelector(
this._transfer.selector,
msg.sender,
to,
amount
);
// Execute via PCL — this contract calls itself through the PCL gate.
PCL.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.
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.18;
import { IPcl } from "@maroo-chain/contracts/IPcl.sol";
contract PclCaller {
IPcl constant PCL = IPcl(0x1000000000000000000000000000000000000005);
function callCompliantService(address service, uint256 value) external {
bytes memory data = abi.encodeWithSignature("deposit(uint256)", value);
try PCL.runOnPcl(service, data, 0) returns (bytes memory) {
// Success path
} catch (bytes memory reason) {
// bytes memory slicing isn't supported in Solidity — read the
// 4-byte error selector via assembly.
bytes4 selector;
assembly { selector := mload(add(reason, 32)) }
if (selector == IPcl.InDenylist.selector) {
// Strip the selector and decode the arg.
bytes memory args = new bytes(reason.length - 4);
for (uint i = 0; i < args.length; i++) args[i] = reason[i + 4];
address sender = abi.decode(args, (address));
revert("Caller is on the denylist");
}
// Bubble up unknown reverts.
revert(string(reason));
}
}
}