IPrivacy.singleProofBatchTransfer

singleProofBatchTransfer(
  PrivacySingleProofBatchTransferRequest request
) external returns (bool success)

Applies a batch join-split (multiple inputs, multiple outputs) validated by a single Groth16 proof. Input count must be in 1..BatchJoinSplitV1MaxInputs, output count must be in 1..BatchJoinSplitV1MaxOutputs; out-of-range counts revert with PrivacyBatchSizeOutOfRange(count, max). Failures surface as typed custom errors on IPrivacy or inherited on IPrecompile.

Parameters

Name Type Required Description
request PrivacySingleProofBatchTransferRequest The single-proof batch payload — one proof, one root, an array of input nullifiers, an array of output entries (each with commitment + ciphertext + disclosure fields), audit-key metadata, audit disclosure target pubkey, and expiresAtUnix.

Returns

Type: bool

Returns true on success. Failures revert with a typed custom error rather than returning false.

Errors

Code Name Description
PrivacyBatchSizeOutOfRange PrivacyBatchSizeOutOfRange Reverts when input count is outside 1..BatchJoinSplitV1MaxInputs or output count is outside 1..BatchJoinSplitV1MaxOutputs. Payload is (count, max).
PrivacyRequestExpired PrivacyRequestExpired Reverts when request.expiresAtUnix is zero or has already passed.
PrivacyDuplicateNullifier PrivacyDuplicateNullifier Reverts when the same nullifier appears more than once in request.nullifiers.
PrivacyNullifierAlreadySpent PrivacyNullifierAlreadySpent Reverts when any nullifier is already recorded on-chain.
PrivacyDuplicateCommitment PrivacyDuplicateCommitment Reverts when the same output commitment appears more than once in request.outputs.
PrivacyCommitmentAlreadyExists PrivacyCommitmentAlreadyExists Reverts when any output commitment already exists in the Merkle tree.
PrivacyMerkleCapacityExceeded PrivacyMerkleCapacityExceeded Reverts when appending the outputs would exceed the shielded-pool Merkle tree capacity.
PrivacyTxLimitExceeded PrivacyTxLimitExceeded Reverts when the transaction has already exceeded the global stateful-privacy call limit.
InvalidNumberOfArgs InvalidNumberOfArgs Inherited from IPrecompile. Reverts when the call does not carry exactly one argument.

Examples

Merge two notes into one

A single proof authorises the whole join-split — cheaper than a multi-proof batchTransfer when all inputs belong to the same sender.

import { createWalletClient, http } 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"),
});

// request.nullifiers.length in 1..BatchJoinSplitV1MaxInputs;
// request.outputs.length   in 1..BatchJoinSplitV1MaxOutputs.
await wallet.writeContract({
  address: PRIVACY,
  abi: privacyAbi,
  functionName: "singleProofBatchTransfer",
  args: [request],
});

Reacting to a size violation

PrivacyBatchSizeOutOfRange fires for both inputs and outputs; log the offending value against the interface's BatchJoinSplitV1MaxInputs / BatchJoinSplitV1MaxOutputs limits.

import { decodeErrorResult } from "viem";
import { privacyAbi } from "@maroo-chain/contracts/abi/IPrivacy";

try {
  await wallet.writeContract({
    address: PRIVACY,
    abi: privacyAbi,
    functionName: "singleProofBatchTransfer",
    args: [request],
  });
} catch (err: any) {
  if (!err?.data) throw err;
  const decoded = decodeErrorResult({ abi: privacyAbi, data: err.data });
  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;
  }
}
ESC
Type to search