IPrivacy.batchTransfer
batchTransfer(
bytes32 batchId,
PrivacyTransferRequest[] requests
) external returns (bool success) 여러 개의 독립적으로 증명된 셸디드 transfer를 단일 트랜잭션 내에서 원자적으로 적용하며, 모두 batchId로 묶입니다. 각 항목은 배치 전체를 기준으로 중복 nullifier, 중복 커밋먼트, Merkle 용량이 사전 점검됩니다. 어느 하나라도 실패하면 호출 전체가 revert됩니다. 실패는 IPrivacy의 타입 지정 커스텀 오류 또는 IPrecompile의 상속 오류로 노출됩니다.
파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
batchId | bytes32 | ✓ | 호출자가 정하는 배치 식별자입니다. 0이 아니어야 하며 트랜잭션 내에서 유일해야 합니다. 각 배치 항목 이벤트와 로그가 이 값을 담아 오프체인 인덱서가 그룹화할 수 있습니다. |
requests | PrivacyTransferRequest[] | ✓ | 배치 항목들입니다. 길이는 1..MaxPrivacyMultiProofBatchItems 범위여야 하며, 비어 있거나 초과하면 PrivacyBatchSizeOutOfRange(count, max)로 revert됩니다. 모든 nullifier와 모든 새 커밋먼트는 배치 전체에서 유일해야 하고 이미 온체인에 기록되어 있어서는 안 됩니다. |
반환값
타입:
bool 모든 항목이 커밋되면 true를 반환합니다. 어떤 실패든 배치 전체가 revert됩니다.
에러
| 코드 | 이름 | 설명 |
|---|---|---|
PrivacyBatchSizeOutOfRange | PrivacyBatchSizeOutOfRange | requests가 비어 있거나 프로토콜 최대치를 초과할 때 revert됩니다. 페이로드는 (count, max)입니다. |
PrivacyDuplicateNullifier | PrivacyDuplicateNullifier | 같은 nullifier가 배치의 여러 항목에 나타날 때 revert됩니다. |
PrivacyNullifierAlreadySpent | PrivacyNullifierAlreadySpent | 배치의 nullifier 중 하나라도 이미 온체인에 기록되어 있을 때 revert됩니다. |
PrivacyDuplicateCommitment | PrivacyDuplicateCommitment | 같은 새 커밋먼트가 배치의 여러 항목에 나타날 때 revert됩니다. |
PrivacyCommitmentAlreadyExists | PrivacyCommitmentAlreadyExists | 배치의 새 커밋먼트 중 하나라도 이미 Merkle 트리에 존재할 때 revert됩니다. |
PrivacyMerkleCapacityExceeded | PrivacyMerkleCapacityExceeded | 배치의 출력을 추가하면 셸디드 풀 Merkle 트리 용량을 초과할 때 revert됩니다. |
PrivacyRequestExpired | PrivacyRequestExpired | 어떤 항목의 expiresAtUnix가 0이거나 이미 지났을 때 revert됩니다. |
PrivacyTxLimitExceeded | PrivacyTxLimitExceeded | 트랜잭션이 이미 전역 stateful-privacy 호출 한도를 초과했을 때 revert됩니다. |
InvalidNumberOfArgs | InvalidNumberOfArgs | IPrecompile에서 상속됩니다. 호출이 정확히 두 개의 인자를 담고 있지 않을 때 revert됩니다. |
예제
다섯 개의 셸디드 transfer 묶기
5개 항목이 각각 독립적으로 증명되지만 함께 커밋되거나 함께 revert됩니다. 트랜잭션마다 새 batchId를 사용해 PrivacyBatchTransferItem 이벤트가 깔끔하게 묶이도록 합니다.
import { createWalletClient, http, keccak256, toHex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { privacyAbi } from "@maroo-chain/contracts/abi/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"),
});
const batchId = keccak256(toHex(`payroll-${Date.now()}`));
await wallet.writeContract({
address: PRIVACY,
abi: privacyAbi,
functionName: "batchTransfer",
args: [batchId, requests], // requests.length in 1..MaxPrivacyMultiProofBatchItems
}); 어떤 항목이 배치를 실패시켰는지 진단
배치 preflight 오류는 형태 수준(중복 제거, 용량)입니다. 실패한 한 항목이 배치 전체를 오염시킵니다. 필요하면 배치를 분할해 재시도하여 항목을 격리합니다.
import { decodeErrorResult } from "viem";
import { privacyAbi } from "@maroo-chain/contracts/abi/IPrivacy";
try {
await wallet.writeContract({
address: PRIVACY,
abi: privacyAbi,
functionName: "batchTransfer",
args: [batchId, requests],
});
} catch (err: any) {
if (!err?.data) throw err;
const decoded = decodeErrorResult({ abi: privacyAbi, data: err.data });
// The typed error identifies WHICH invariant failed. Recompute preflight
// client-side (dedupe nullifiers/commitments, refresh note set) and retry.
console.warn("batch preflight failed:", decoded.errorName, decoded.args);
}