IPrivacy.singleProofBatchTransferWithAuthorization
singleProofBatchTransferWithAuthorization(
PrivacySingleProofBatchTransferRequest request,
PrivacyActionAuthorization authorization
) external returns (bool success) singleProofBatchTransfer의 메타 트랜잭션 변형입니다. 릴레이어가 authorization.effectiveSender를 대신하여 배치를 제출하며, 서명자는 요청 해시를 특정 executor·nonce·deadline에 묶어 오프체인에서 서명해 둡니다. authorization.authorizationKind에 따라 EOA 서명, ERC-1271 스마트 계정 서명, EIP-7702 위임 EOA 서명을 지원합니다. 요청 해시 도메인은 이 단일 증명 배치 메서드 전용이라 다른 프라이버시 호출용 인가와 충돌하지 않습니다. 이 프리컴파일은 커스텀 오류 타입을 선언하지 않으므로 모든 실패는 일반 문자열 revert로 전달됩니다.
파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
request | PrivacySingleProofBatchTransferRequest | ✓ | singleProofBatchTransfer와 동일한 배치 페이로드입니다. 메서드 ID와 ABI 인코딩된 요청 튜플로 계산한 요청 해시가 인가 서명에 함께 묶입니다. |
authorization | PrivacyActionAuthorization | ✓ | 오프체인 인가입니다. 필드는 다음과 같습니다. effectiveSender(정책 엔진이 실제 발신자로 취급하는 계정), executor(msg.sender와 같아야 하며 릴레이어 주소입니다), nonce(effectiveSender별로 고유하며 리플레이 보호가 적용됩니다), deadline(unix 초 단위이며 미래여야 합니다), authorizationKind(0=EOA, 1=ERC-1271, 2=EIP-7702이며 그 외 값은 revert됩니다), signature(bytes이며 EOA는 65바이트 (r, s, v)입니다). |
반환값
타입:
bool 성공 시 true를 반환합니다. 실패는 일반 문자열 revert로 전달됩니다.
에러
| 코드 | 이름 | 설명 |
|---|---|---|
privacy authorization effectiveSender is required | privacy authorization effectiveSender is required | authorization.effectiveSender가 0 주소이면 이 문자열로 revert됩니다. |
privacy authorization executor is required | privacy authorization executor is required | authorization.executor가 0 주소이면 이 문자열로 revert됩니다. |
privacy authorization executor mismatch | privacy authorization executor mismatch | msg.sender가 authorization.executor와 일치하지 않으면 이 문자열로 revert됩니다. |
privacy authorization deadline is required | privacy authorization deadline is required | authorization.deadline이 0이면 이 문자열로 revert됩니다. |
privacy authorization expired | privacy authorization expired | 블록 시간이 authorization.deadline을 지나면 이 문자열로 revert됩니다. |
privacy authorization nonce already used | privacy authorization nonce already used | 동일한 effectiveSender에 대해 이전 프라이버시 호출에서 authorization.nonce가 이미 사용되었을 때 이 문자열로 revert됩니다. |
unsupported privacy authorization kind %d | unsupported privacy authorization kind %d | authorizationKind가 지원 값(0 EOA, 1 ERC-1271, 2 EIP-7702) 중 하나가 아니면 이 문자열로 revert됩니다. |
privacy EOA authorization signer mismatch | privacy EOA authorization signer mismatch | 복원된 EOA 서명자가 authorization.effectiveSender와 일치하지 않으면 이 문자열로 revert됩니다. |
privacy EOA authorization signature must be 65 bytes | privacy EOA authorization signature must be 65 bytes | EOA 서명 blob의 길이가 65바이트가 아니면 이 문자열로 revert됩니다. |
privacy ERC-1271 authorization rejected: %w | privacy ERC-1271 authorization rejected | effectiveSender에 대한 ERC-1271 스마트 계정 서명 검증이 실패하면 이 문자열로 revert됩니다. |
single-proof batch transfer payload has expired | single-proof batch transfer payload has expired | 블록 시간이 이미 request.expiresAtUnix에 도달하면 이 문자열로 revert됩니다. |
privacy single-proof batch nullifier already spent | privacy single-proof batch nullifier already spent | 제출한 nullifier 중 하나가 이전 프라이버시 호출에서 이미 소비된 경우 이 문자열로 revert됩니다. |
privacy single-proof batch output commitment already exists | privacy single-proof batch output commitment already exists | 출력의 commitment이 이미 온체인 commitment 인덱스에 존재하면 이 문자열로 revert됩니다. |
예제
릴레이어가 인가된 단일 증명 배치를 제출
릴레이어 자신의 주소가 authorization.executor와 대조되며, 불일치하면 증명 검증 이전에 revert됩니다. nonce는 오프체인에서 관리하며, effectiveSender별 단조 증가 카운터로도 충분합니다.
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const PRIVACY = "0x100000000000000000000000000000000000000b" as const;
// ABI snippet — reuse the `PrivacySingleProofBatchTransferRequest` shape
// documented on `contract-privacy-single-proof-batch-transfer`.
const privacyAbi = [{
type: "function",
name: "singleProofBatchTransferWithAuthorization",
stateMutability: "nonpayable",
inputs: [
{ name: "request", type: "tuple", components: [ /* … */ ] },
{ name: "authorization", type: "tuple", components: [
{ name: "effectiveSender", type: "address" },
{ name: "executor", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint64" },
{ name: "authorizationKind", type: "uint8" },
{ name: "signature", type: "bytes" },
]},
],
outputs: [{ type: "bool" }],
}] as const;
// The relayer's key. Must equal `authorization.executor`.
// TODO: replace with the real relayer address before production.
const relayer = createWalletClient({
account: privateKeyToAccount(process.env.RELAYER_KEY as `0x${string}`),
transport: http("https://rpc-testnet.maroo.io"),
});
// `request` and `authorization` come from the effective sender's wallet.
// `authorization.signature` is over the EIP-712 domain documented on
// `privacy-authorization-eip712-domain`, using the single-proof batch
// request-hash.
await relayer.writeContract({
address: PRIVACY,
abi: privacyAbi,
functionName: "singleProofBatchTransferWithAuthorization",
args: [request, authorization],
}); 인가 실패 처리
인가 계층은 문자열 revert만 발행합니다. UX 분기는 revert.reason의 부분 문자열로 처리하며, 여기서 decodeErrorResult를 호출해서는 안 됩니다.
import { BaseError, ContractFunctionRevertedError } from "viem";
try {
await relayer.writeContract({
address: PRIVACY,
abi: privacyAbi,
functionName: "singleProofBatchTransferWithAuthorization",
args: [request, authorization],
});
} catch (err) {
if (err instanceof BaseError) {
const revert = err.walk(e => e instanceof ContractFunctionRevertedError);
if (revert instanceof ContractFunctionRevertedError) {
// Match on the verbatim reason substring — the privacy precompile
// does not use typed custom errors.
const r = revert.reason ?? "";
if (r.includes("privacy authorization expired")) {
// ask the effective sender to re-sign with a later deadline
} else if (r.includes("privacy authorization nonce already used")) {
// bump the nonce and re-sign
} else if (r.includes("privacy authorization executor mismatch")) {
// the signature was bound to a different relayer
}
}
}
throw err;
}