IPcl.deployPclProxy
deployPclProxy(PclProxyKind kind, uint256 value, bytes calldata initData) external returns (address proxy) Deploys a PCL-wrapped proxy using the canonical proxy bytecode embedded in the chain binary, registers it in the PCL proxy registry, and binds the deploy caller (msg.sender) as the initial PCL contract admin for the resulting proxy address — all before the call returns. Because the initial admin is bound to msg.sender, deploying via a factory contract makes the factory the admin, not the end user. For PclProxyKind.Transparent, initData is abi.encode(logic, initialOwner, initializer).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
kind | PclProxyKind | ✓ | Proxy variant to deploy. The PclProxyKind enum currently has exactly two members — Unspecified (0, invalid) and Transparent (1). Pass PclProxyKind.Transparent; other proxy kinds are not part of the enum today. |
value | uint256 | ✓ | Native aokrw value forwarded to the proxy constructor / initializer, in base denom (18 decimals). Pass 0 when the proxy does not need funding at deploy. |
initData | bytes | ✓ | ABI-encoded constructor arguments. For Transparent, this is abi.encode(address logic, address initialOwner, bytes initializer) where initializer is the calldata to invoke on logic after deploy (empty bytes to skip initializer). |
Returns
address The address of the deployed proxy. After this call, IPcl.pclProxy(proxy) returns a non-zero PclProxyEntry, and IPcl.contractPolicies(proxy).admin equals msg.sender — even though no PolicySets have been attached yet. Use changeContractPolicies (not registerContractPolicies) to attach the initial policy set.
Errors
| Code | Name | Description |
|---|---|---|
AbiDecodeFailed | AbiDecodeFailed | Reverts when initData cannot be decoded against the expected layout for the given kind. |
PolicyAlreadyRegistered | PolicyAlreadyRegistered | Reverts during the admin-binding step if a PCL admin is already recorded for the deployed proxy address under a different deployer — for example, a CREATE2 pre-registration performed by another address. Same-address pre-registration is idempotent and does not revert. |
Examples
Deploy a Transparent proxy and attach policies
The deploy call auto-binds this contract as PCL admin of the new proxy. The subsequent policy attachment uses changeContractPolicies because the admin slot is already initialized.
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
import {
IPcl, PclProxyKind, ContractPolicyConfig, PolicySet
} from "@maroo-chain/contracts/precompiles/pcl/IPcl.sol";
contract DeployAndConfigure {
IPcl constant PCL = IPcl(0x1000000000000000000000000000000000000005);
// Replace with the actual token logic contract address before production.
address constant LOGIC = 0x000000000000000000000000000000000000dEaD;
function deployAndAttach(PolicySet[] calldata policies)
external
returns (address proxy)
{
bytes memory initializer = abi.encodeWithSignature(
"initialize(address)", msg.sender
);
bytes memory initData = abi.encode(LOGIC, msg.sender, initializer);
// deployPclProxy binds msg.sender (this contract) as PCL admin of `proxy`.
proxy = PCL.deployPclProxy(PclProxyKind.Transparent, 0, initData);
// Slot is already bound → use changeContractPolicies, NOT register.
PCL.changeContractPolicies(ContractPolicyConfig({
_contract: proxy,
admin: address(this), // keep, or replace to hand over
policies: policies
}));
}
} Deploy from an EOA using viem
The EOA that submits deployPclProxy is bound as PCL admin of the resulting proxy. No separate registration step is needed.
import { createWalletClient, encodeAbiParameters, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const PCL = "0x1000000000000000000000000000000000000005" as const;
// Replace with a real testnet key before running.
const account = privateKeyToAccount("0xREPLACE_ME_TESTNET_KEY");
const walletClient = createWalletClient({
account,
transport: http("https://rpc-testnet.maroo.io"),
});
const logic = "0x000000000000000000000000000000000000dEaD"; // replace
const initialOwner = account.address;
const initializer = "0x"; // no initializer call
const initData = encodeAbiParameters(
[
{ type: "address" },
{ type: "address" },
{ type: "bytes" },
],
[logic, initialOwner, initializer]
);
const hash = await walletClient.writeContract({
address: PCL,
abi: pclAbi,
functionName: "deployPclProxy",
args: [1 /* PclProxyKind.Transparent */, 0n, initData],
});
// After the tx is mined:
// pcl.contractPolicies(proxy).admin === account.address
// Follow up with changeContractPolicies to attach policies.