IPrivacy.singleProofBatchTransfer
singleProofBatchTransfer(
PrivacySingleProofBatchTransferRequest request
) external returns (bool success) 다중 입력·다중 출력의 배치 join-split을 단일 Groth16 증명으로 검증하여 적용합니다. 입력 개수는 1..BatchJoinSplitV1MaxInputs, 출력 개수는 1..BatchJoinSplitV1MaxOutputs 범위여야 합니다. 범위를 벗어나면 PrivacyBatchSizeOutOfRange(count, max)로 revert됩니다. 실패는 IPrivacy의 타입 지정 커스텀 오류 또는 IPrecompile의 상속 오류로 노출됩니다.
파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
request | PrivacySingleProofBatchTransferRequest | ✓ | 단일 증명 배치 페이로드입니다. 하나의 proof, 하나의 root, 입력 nullifier 배열, 출력 항목 배열(각 항목은 커밋먼트 + 암호문 + 공개 필드), audit 키 메타데이터, audit 공개 대상 pubkey, expiresAtUnix를 포함합니다. |
반환값
타입:
bool 성공 시 true를 반환합니다. 실패는 false 반환이 아니라 타입 지정 커스텀 오류로 revert됩니다.
에러
| 코드 | 이름 | 설명 |
|---|---|---|
PrivacyBatchSizeOutOfRange | PrivacyBatchSizeOutOfRange | 입력 개수가 1..BatchJoinSplitV1MaxInputs 범위를 벗어나거나 출력 개수가 1..BatchJoinSplitV1MaxOutputs 범위를 벗어날 때 revert됩니다. 페이로드는 (count, max)입니다. |
PrivacyRequestExpired | PrivacyRequestExpired | request.expiresAtUnix가 0이거나 이미 지났을 때 revert됩니다. |
PrivacyDuplicateNullifier | PrivacyDuplicateNullifier | 같은 nullifier가 request.nullifiers에 두 번 이상 나타날 때 revert됩니다. |
PrivacyNullifierAlreadySpent | PrivacyNullifierAlreadySpent | 어떤 nullifier가 이미 온체인에 기록되어 있을 때 revert됩니다. |
PrivacyDuplicateCommitment | PrivacyDuplicateCommitment | 같은 출력 커밋먼트가 request.outputs에 두 번 이상 나타날 때 revert됩니다. |
PrivacyCommitmentAlreadyExists | PrivacyCommitmentAlreadyExists | 어떤 출력 커밋먼트가 이미 Merkle 트리에 존재할 때 revert됩니다. |
PrivacyMerkleCapacityExceeded | PrivacyMerkleCapacityExceeded | 출력을 추가하면 셸디드 풀 Merkle 트리 용량을 초과할 때 revert됩니다. |
PrivacyTxLimitExceeded | PrivacyTxLimitExceeded | 트랜잭션이 이미 전역 stateful-privacy 호출 한도를 초과했을 때 revert됩니다. |
InvalidNumberOfArgs | InvalidNumberOfArgs | 공유 IPrecompile 오류 집합에 InvalidNumberOfArgs(uint256 expected, uint256 got)로 선언되어 있지만, 이 함수는 인자 개수가 틀려도 이 오류로 revert되지 않습니다. calldata가 요청을 담기에 짧으면 ABISetupFailed로 revert되고, 요청 뒤에 붙은 바이트는 무시됩니다. |
ABISetupFailed | ABISetupFailed | IPrecompile의 ABISetupFailed(string reason)을 상속합니다. calldata를 프리컴파일 ABI로 디코딩할 수 없을 때 revert됩니다. 알 수 없는 4바이트 selector이면 사유가 no method with id: 0x…이고(보통 ABI가 오래된 경우), calldata가 인자를 담기에 짧으면 디코더의 메시지(예: abi: cannot marshal in to go type: length insufficient 32 require 64)가 사유가 됩니다. 프리컴파일에 UnknownMethod도 선언되어 있지만, 알 수 없는 selector는 이 오류로 revert되지 않습니다. |
예제
두 노트를 하나로 병합
단일 증명이 join-split 전체를 승인합니다. 모든 입력이 같은 발신자의 것이라면 다중 증명 batchTransfer보다 비용이 저렴합니다.
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { iPrivacyAbi } from "@maroo-chain/contracts/abi/precompiles/privacy/IPrivacy";
const PRIVACY = "0x100000000000000000000000000000000000000b" as const;
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.SENDER_KEY as `0x${string}`),
transport: http("https://rpc-testnet.maroo.io"),
});
// request.nullifiers.length in 1..BatchJoinSplitV1MaxInputs;
// request.outputs.length in 1..BatchJoinSplitV1MaxOutputs.
await wallet.writeContract({
address: PRIVACY,
abi: iPrivacyAbi,
functionName: "singleProofBatchTransfer",
args: [request],
}); 크기 위반 처리
PrivacyBatchSizeOutOfRange는 입력과 출력 모두에 대해 발생합니다. 인터페이스의 BatchJoinSplitV1MaxInputs / BatchJoinSplitV1MaxOutputs 한도와 대조해 문제가 된 값을 로깅합니다.
import { BaseError, ContractFunctionRevertedError, decodeErrorResult } from "viem";
import { iPrivacyAbi } from "@maroo-chain/contracts/abi/precompiles/privacy/IPrivacy";
try {
await wallet.writeContract({
address: PRIVACY,
abi: iPrivacyAbi,
functionName: "singleProofBatchTransfer",
args: [request],
});
} catch (err) {
// viem keeps the revert data on the ContractFunctionRevertedError in the cause chain, not on err.
const revert = err instanceof BaseError ? err.walk((e) => e instanceof ContractFunctionRevertedError) : null;
if (!(revert instanceof ContractFunctionRevertedError) || !revert.raw) throw err;
const decoded = decodeErrorResult({ abi: iPrivacyAbi, data: revert.raw });
if (decoded.errorName === "PrivacyBatchSizeOutOfRange") {
const [count, max] = decoded.args as [bigint, bigint];
console.warn(`batch input or output count ${count} exceeds max ${max}`);
} else {
throw err;
}
}