PCL ReasonCode
PCL이 트랜잭션을 거절할 때 발행하는 커스텀 오류 전체와 이제 상속받는 IPrecompile 공유 오류를 함께 다룹니다. 지갑과 SDK는 이 코드들을 기준으로 사용자 경험을 구성합니다.
PCL의 모든 거절은 IPcl에 선언된 커스텀 Solidity 오류 중 하나를 전달합니다. IPcl이 이제 IPrecompile을 상속하므로 공유 경계 오류도 포함됩니다. 지갑과 dApp 코드는 revert 페이로드를 IPcl ABI(전이적으로 IPrecompile 오류를 포함)로 디코드하고, 자유 형식 문자열이 아니라 오류 이름과 인자를 기준으로 사용자 경험을 구성합니다. 코드는 네 그룹으로 나뉩니다. 정책 위반 코드(사용자 트랜잭션이 컴플라이언스 규칙에 실패), 설정 코드(관리자 호출이 잘못되었거나 권한이 없음), 조합/구조 코드(LogicalPolicy 또는 ForEachPolicy 조합기가 거절), 그리고 상속된 경계 오류(잘못된 주소, 인자 개수 불일치, 알 수 없는 메서드, SDK 수준 거절)입니다. 일부 실패는 여전히 평문 문자열 revert이며 커스텀 오류로 디코드할 수 없습니다. 해당 항목은 명시적으로 표시합니다.
정책 위반 ReasonCode
사용자 트랜잭션이 리프 정책에 도달했고 그 정책이 거절할 때 발생합니다. 지갑 UX가 가장 관심 있는 코드들이며, 최종 사용자에게 결제가 차단된 이유를 알려줍니다.
| 오류 | 인자 | 의미 |
|---|---|---|
InDenylist | address sender | 발신자(또는 해석된 주체)가 denylist에 있습니다. |
VolumeBelowMinLimit | uint256 minLimit, uint256 value | 전송 금액이 트랜잭션당 하한 미만입니다. |
VolumeAboveMaxLimit | uint256 maxLimit, uint256 value | 전송 금액이 트랜잭션당 상한을 초과합니다. |
ExceededPeriodicVolume | uint256 maxLimit, uint256 value, uint256 resetAt | 롤링 윈도 상한을 초과합니다. resetAt은 현재 윈도가 재설정되는 시각입니다. |
EasAttestationRequired | address sender | 정책이 요구하는 attestation을 발신자가 보유하지 않습니다. |
EasNoAttestationReceived | address sender | 지정된 스키마로 발신자에 대해 인덱서가 attestation을 반환하지 않았습니다. |
EasAttestationLookupFailed | address sender | 인덱서 조회 자체가 실패했습니다. |
EasAttestationRevoked | address sender | attestation이 존재하지만 취소되었습니다. |
EasAttestationExpired | address sender | attestation이 존재하지만 expirationTime을 지났습니다. |
ExceededAgentTransferLimit | uint256 maxLimit, uint256 value | 에이전트의 온체인 TransferLimit 메타데이터 상한을 초과했습니다. |
AgentTransferLimitMetadataInvalid | string reason | 에이전트 메타데이터가 잘못되었습니다. |
AgentKeeperRequired | — | 에이전트 정책을 평가했으나 에이전트 모듈이 연결되어 있지 않습니다. |
설정 ReasonCode
관리자 작업(
registerPolicyTemplate, changeContractPolicies, setGlobalPolicies 등)에서 페이로드가 잘못되었거나 호출자가 권한이 없을 때 발생합니다.| 오류 | 인자 | 의미 |
|---|---|---|
CannotEmpty | string field | 필수 필드가 비어 있습니다. |
Unauthorized | — | 호출자가 요구되는 admin(정책 admin, 컨트랙트 admin, 보호된 호출자)이 아닙니다. |
InternalError | — | 모듈 내부에서 state 인코딩/디코딩이 실패했습니다. |
InvalidCall | — | 호출 컨텍스트 자체가 잘못되었습니다(예: EVM 없이 deployPclProxy 호출). |
InvalidStructType | string got | ABI struct가 잘못된 형태로 디코드되었습니다. |
AbiDecodeFailed | string reason | 원시 ABI 디코드가 실패했습니다(initData 형식 오류 등). |
InvalidParameter | bytes input | 파라미터 바이트가 템플릿의 예상 struct와 일치하지 않습니다. 중복 selector에도 발생하며, 페이로드는 원시 selector 바이트입니다. |
InvalidSelector | bytes input | selector가 4바이트 값이 아닙니다. |
InvalidPolicyTemplate | string input | 템플릿 ID 문자열이 인식되는 템플릿이 아닙니다. |
DuplicatedPolicyTemplate | string templateId | 이미 존재하는 템플릿을 등록하려고 했습니다. |
PolicyTemplateNotFound | string templateId | 템플릿 ID가 등록되어 있지 않습니다. |
PolicyTemplateInUse | — | 활성 PolicySet이 참조하는 템플릿을 제거하려고 했습니다. |
UnknownPolicyType | string templateId | 이 빌드가 평가할 수 있는 템플릿 ID가 아닙니다. |
UnknownPolicyConfigType | — | 상위 config 타입이 Global도 Contract도 아닙니다. |
PolicyAlreadyRegistered | address contractAddress | 생성 경로에서 이 주소의 ContractPolicyConfig가 이미 존재합니다. |
ContractPolicyNotRegistered | address contractAddress | 갱신/제거 경로에서 이 주소의 ContractPolicyConfig가 없습니다. |
PclProxyNotRegistered | address contractAddress | 이 주소는 등록된 PCL 래핑 프록시가 아닙니다. |
PolicyNotRegistered | string templateId | PolicySet이 참조하는 템플릿이 등록되어 있지 않습니다. |
조합 및 구조 ReasonCode
LogicalPolicy와 ForEachPolicy 조합기(pcl-composite-policies 참고)는 조합기가 어떻게 거절했는지 설명하는 구조적 오류로 실패할 수 있습니다. 특히 중요한 것은 AnyOfRejected이며, 자식 revert들을 재귀적으로 감쌉니다.| 오류 | 인자 | 의미 |
|---|---|---|
AnyOfRejected | bytes[] childReverts | Or 조합기의 자식들이 모두 거절되었습니다. 각 자식 revert가 원시 ABI 바이트로 보존되어 클라이언트가 개별적으로 디코드할 수 있습니다. |
MaxDepthExceeded | uint8 maxDepth | 조합기 중첩이 구조적 깊이 제한을 초과했습니다. |
ChildSelectorNotEmpty | — | 조합기의 자식 PolicySet은 빈 selector여야 하는데 비어 있지 않은 값이 제공되었습니다. |
LogicalPolicyChildrenEmpty | — | LogicalPolicy.children 배열이 비어 있습니다. |
LogicalPolicyChildNil | uint256 index | 주어진 인덱스의 자식이 nil PolicySet으로 디코드되었습니다. |
ForEachChildAbsent | — | ForEachPolicy.child가 설정되지 않았습니다. |
ForEachSubjectUnspecified | — | ForEachPolicy.subject가 Unspecified입니다. |
UnknownForEachSubject | uint8 subject | subject 값이 알려진 ForEachSubject가 아닙니다. |
QuantifierUnspecified | — | 조합기의 quantifier가 Unspecified입니다. |
AnyOfRejected를 디코드할 때는 childReverts 배열을 순회하며 각 항목을 다시 IPcl ABI로 디코드해 기반이 되는 리프 오류를 노출합니다.상속된 IPrecompile 오류
IPcl이 이제 IPrecompile을 상속하므로, 모든 공유 경계 오류를 동일한 ABI로 디코드할 수 있습니다. 이 오류들은 정책 코드가 실행되기 전에 발생하며, 프리컴파일 경계의 잘못된 입력이나 SDK 수준 거절을 나타냅니다.| 오류 | 인자 | 발생 시점 |
|---|---|---|
InvalidAddress | string bad | address 인자가 0 주소이거나 인코딩에 실패합니다. 페이로드가 이전에 로컬로 선언되었던 string input이 아니라 string(16진수로 표현된 주소)이라는 점에 주의합니다. selector는 IPrecompile 쪽 값을 사용합니다. |
InvalidAmount | string amount | 수치 금액이 0이거나 유효하지 않습니다. |
InvalidNumberOfArgs | uint256 expected, uint256 got | 프리컴파일이 잘못된 ABI 인자 개수를 받았습니다. 대개는 오래된 ABI가 원인입니다. |
UnknownMethod | string methodName | selector가 프리컴파일의 어떤 함수와도 일치하지 않습니다. |
InvalidPageRequest | string method, uint256 index, string value | (주기 목록 뷰의) PageRequest 인자가 잘못되었습니다. |
SDKUnauthorized / SDKInvalidAddress / SDKInvalidRequest / SDKNotFound / … | — | 매핑된 SDK 오류 카탈로그를 통해 노출되는 하부 체인 계층 거절입니다. |
이전에 IPcl의 로컬
InvalidAddress(string input) 형태만 매칭하던 클라이언트는 ABI 조각을 IPrecompile의 InvalidAddress(string bad)로 갱신해야 합니다. 인자 이름이 바뀌고 선언 인터페이스가 이동했지만, InvalidAddress(string)의 4바이트 selector는 변하지 않았습니다.import { decodeErrorResult } from "viem";
import IPclAbi from "@maroo-chain/contracts/abi/IPcl.json";
try {
await wallet.writeContract({ /* ... PCL 호출 ... */ });
} catch (err: any) {
if (!err?.data) throw err;
const decoded = decodeErrorResult({ abi: IPclAbi, data: err.data });
// decoded.errorName은 IPcl reason code 중 하나이거나
// 상속된 IPrecompile 오류(InvalidAddress, InvalidNumberOfArgs, UnknownMethod, ...)입니다.
switch (decoded.errorName) {
case "InDenylist":
case "ExceededPeriodicVolume":
case "EasAttestationRevoked":
// 정책 위반 UX
break;
case "Unauthorized":
case "PolicyTemplateNotFound":
// admin / 설정 UX
break;
case "AnyOfRejected":
// decoded.args[0]: bytes[]를 재귀적으로 디코드
break;
case "InvalidAddress":
case "InvalidNumberOfArgs":
case "UnknownMethod":
// 경계 오류입니다. 대개 오래된 ABI 또는 잘못된 인자 타입이 원인입니다.
break;
default:
console.warn("처리되지 않은 PCL revert:", decoded.errorName, decoded.args);
}
} 커스텀 오류가 아닌 실패
일부 PCL 실패는 여전히 평문 문자열 revert이며
이 페이지에 나열된 그 외 모든 실패는 커스텀 오류이며 IPcl ABI로 디코드해야 합니다.
decodeErrorResult로 디코드할 수 없습니다. 문자열 revert로 매칭합니다.duplicate selector: <selector>— 같은 설정 안의 두 PolicySet 항목이 selector를 공유합니다. (현재는 ABI 계층에서InvalidParameterselector를 통해 보고되기도 하지만, 과거 클라이언트는 여전히 문자열 형태로 노출될 수 있으니 두 경로 모두 디코드합니다.)pcl keeper is not initialized— 이 빌드에 모듈이 연결되어 있지 않습니다. 사용자 오류가 아니라 노드 설정 오류입니다.pcl: parse IPcl.json: <err>— 모듈 초기화 실패입니다. 실제 dApp에는 노출되지 않습니다.
이 페이지에 나열된 그 외 모든 실패는 커스텀 오류이며 IPcl ABI로 디코드해야 합니다.