IPcl.postCall
postCall(bytes32 sessionId, address contractAddress, address principal, bytes calldata data, uint256 value, bool workable) external returns (bytes memory) 프록시가 구현체에 대한 delegatecall을 마친 뒤, 짝이 되는 preCall이 연 PCL 세션을 닫습니다. postCall은 구현체 내부에서 발생한 전송 로그 델타를 검사해 주기 거래량 누적을 기록하고, AGENT_OKRW_TRANSFER_LIMIT_POLICY, OKRW_EAS_TRANSFER_LIMIT_POLICY 같은 사후 정책을 평가합니다. workable이 false이면(구현체가 이미 revert된 경우) 사후 정책 평가, 거래량 기록, 로그 마킹, 이벤트 발행을 모두 건너뜁니다.
파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
sessionId | bytes32 | ✓ | 짝이 되는 preCall이 반환한 식별자입니다. 사전 로그 오프셋과 네이티브 전송 수집기 스냅숏을 조회하는 데 사용합니다. |
contractAddress | address | ✓ | PCL 정책 조회 키로 사용되는 프록시 주소입니다. preCall에 전달한 값과 일치해야 합니다. |
principal | address | ✓ | preCall에서 전파된 호출 발신자입니다. denylist, 에이전트 전송 한도 등 principal 범위 정책에 사용합니다. |
data | bytes | ✓ | preCall에 전달한 값과 동일한, 구현체로 전달된 원본 내부 calldata입니다. |
value | uint256 | ✓ | 호출에 첨부된 네이티브 값(aokrw 단위)입니다. 호출이 성공했을 때 주기 네이티브 거래량 기록에 사용합니다. |
workable | bool | ✓ | 구현체 delegatecall이 성공적으로 반환되면 true, revert되면 false입니다. false이면 PCL이 사후 처리(부기)를 모두 건너뛰어 revert된 호출의 부작용이 남지 않습니다. |
반환값
타입:
bytes 빈 bytes를 반환합니다. 호출자(프록시 훅)는 delegatecall의 원본 반환 데이터 또는 revert 데이터를 전파하며, 이 반환값은 사용하지 않습니다.
에러
| 코드 | 이름 | 설명 |
|---|---|---|
Unauthorized | Unauthorized | 직접 호출자가 허용된 PCL 프록시 진입점이 아니면 revert됩니다. |
ExceededPeriodicVolume | ExceededPeriodicVolume | 사후 전송 누적으로 주기 거래량 정책이 상한을 초과하면 revert됩니다. |
ExceededAgentTransferLimit | ExceededAgentTransferLimit | 호출 내부에서 관찰된 OKRW 전송이 AGENT_OKRW_TRANSFER_LIMIT_POLICY의 단일 전송 상한을 초과하면 revert됩니다. |
VolumeAboveMaxLimit | VolumeAboveMaxLimit | 호출 내부 전송이 OKRW_EAS_TRANSFER_LIMIT_POLICY 또는 VOLUME_POLICY의 최댓값을 초과하면 revert됩니다. |
예제
PCL 프록시 훅에서 호출
프록시 훅은 구현체가 revert된 경우에도 반드시 postCall을 호출해 세션을 정상 종료합니다. workable 플래그는 사후 정책을 실행할지 단축 평가할지를 PCL에 알려줍니다.
// Excerpt from PclProxyHook.sol — external dApps do NOT call postCall directly.
address constant PCL = 0x1000000000000000000000000000000000000005;
function hookedDelegate(address addrForPolicy, address impl) internal {
address principal = msg.sender;
bytes32 sessionId = IPcl(PCL).preCall(addrForPolicy, principal, msg.data, msg.value);
(bool ok, bytes memory ret) = impl.delegatecall(msg.data);
// `ok` is forwarded as `workable`; when false, PCL skips post-call bookkeeping.
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)) }
} 사후 revert 디코딩
PCL은 사후 거절도 사전 거절과 동일한 revert 경로로 노출합니다. 어느 쪽에서 발동했는지와 무관하게 클라이언트는 IPcl ABI로 디코드합니다.
import { decodeErrorResult } from "viem";
// A transfer that fits pre-call policy but pushes the daily accumulator over its cap
// will succeed inside the implementation and then revert in postCall.
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(decoded.errorName, decoded.args);
// e.g. ExceededPeriodicVolume { maxLimit, value, resetAt }
}