IPcl.preCall
preCall(
address contractAddress,
address principal,
bytes calldata data,
uint256 value
) external returns (bytes32 sessionId) 프록시 호출을 위한 PCL 정책 평가 세션을 엽니다. PCL로 래핑된 프록시 훅이 구현체를 delegatecall하기 전에 호출합니다. preCall은 contractAddress에 등록된 ContractPolicyConfig(존재하는 경우)와 체인 전역 GlobalPolicyConfig를 전달된 principal / data / value에 대해 평가하고, 주기 거래량 등 누적형 정책에 필요한 사전 상태를 스냅숏한 뒤, 짝이 되는 postCall이 제시해야 할 sessionId를 반환합니다. 정책 중 하나라도 거절하면 해당 PCL ReasonCode로 revert됩니다.
파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
contractAddress | address | ✓ | PCL 정책 조회 키로 사용되는 프록시 주소입니다. 등록된 ContractPolicyConfig 항목이 이 주소로 매칭됩니다. |
principal | address | ✓ | 프록시가 관찰한 호출의 실제 발신자(EOA 또는 AA 스마트 계정)입니다. PCL이 보는 직접 호출자(msg.sender)는 프록시이므로, 이 인자를 명시적으로 전달하지 않으면 sender / principal denylist 검사가 발동하지 않습니다. |
data | bytes | ✓ | 구현체로 전달되는 원본 내부 calldata입니다. 앞 4바이트(존재하는 경우)를 함수 selector로 추출하여 selector 범위 PolicySet과 매칭합니다. |
value | uint256 | ✓ | 호출에 첨부된 네이티브 값(aokrw 단위)입니다. 거래량 기반 정책의 입력으로 사용됩니다. |
반환값
타입:
bytes32 delegatecall이 끝난 뒤 postCall로 다시 전달해야 하는 세션 식별자입니다. 세션은 post-call 시점의 누적형 정책 평가에 필요한 사전 로그 오프셋과 네이티브 전송 수집기를 보관합니다.
에러
| 코드 | 이름 | 설명 |
|---|---|---|
Unauthorized | Unauthorized | 직접 호출자가 허용된 PCL 프록시 진입점이 아니면 revert됩니다(PCL에 등록된 프록시만 세션을 열 수 있습니다). |
InDenylist | InDenylist | principal(또는 대상 컨트랙트)이 전역 또는 컨트랙트 범위의 활성 DENYLIST_POLICY와 일치하면 revert됩니다. |
EasAttestationRequired | EasAttestationRequired | EAS_POLICY가 요구하는 attestation을 principal이 보유하지 않으면 revert됩니다. |
ExceededPeriodicVolume | ExceededPeriodicVolume | 주기 거래량 정책의 누적 금액과 value의 합이 설정된 상한을 초과하면 revert됩니다. |
예제
PCL 프록시 훅에서 호출
preCall의 표준 호출자는 PCL 프록시 훅입니다. 반환된 sessionId를 저장했다가 delegatecall 종료 후 짝이 되는 postCall과 함께 사용합니다. 외부 dApp은 프록시를 직접 호출하며, 훅 배선은 노출되지 않습니다.
// 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)) }
} 프록시 호출에서 PCL revert 디코딩
preCall이 거절하면 revert가 프록시를 통해 클라이언트까지 전파됩니다. IPcl ABI로 디코드하여 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... }
}