IPrivacy.transfer

transfer(PrivacyTransferRequest request) external returns (bool success)

Spends one or more shielded notes and creates new notes under a Groth16 proof. msg.sender is the effective sender — PCL evaluates policies against the caller directly. Failures revert with typed custom errors declared on IPrivacy (request-expiry, nullifier/commitment collisions, Merkle capacity, per-transaction limits) or inherited from IPrecompile. PCL policy failures surface as PCL ReasonCodes from IPcl instead.

Parameters

Name Type Required Description
request PrivacyTransferRequest The shielded transfer payload — proof, Merkle root, nullifiers, new commitments, ciphertexts, view tags, disclosure fields, and expiresAtUnix. expiresAtUnix is required and must be greater than the current block time; a zero or past value reverts with PrivacyRequestExpired(expiresAtUnix).

Returns

Type: bool

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

Errors

Code Name Description
PrivacyNonPayable PrivacyNonPayable Reverts when msg.value is non-zero. Only deposit accepts value. Payload is the method name.
PrivacyRequestExpired PrivacyRequestExpired Reverts when request.expiresAtUnix is zero or has already passed. Payload carries the unix timestamp.
PrivacyNullifierAlreadySpent PrivacyNullifierAlreadySpent Reverts when a nullifier in the request is already recorded on-chain (double-spend attempt).
PrivacyCommitmentAlreadyExists PrivacyCommitmentAlreadyExists Reverts when a new commitment in the request already exists in the Merkle tree.
PrivacyMerkleCapacityExceeded PrivacyMerkleCapacityExceeded Reverts when appending the new commitments would exceed the shielded-pool Merkle tree capacity. Payload is the number of outputs.
PrivacyTxLimitExceeded PrivacyTxLimitExceeded Reverts when the current transaction has already made too many stateful privacy calls (global cap).
PrivacySenderTxLimitExceeded PrivacySenderTxLimitExceeded Reverts when the effective sender has already made too many stateful privacy calls within this transaction. Payload is (sender, limit).
InvalidNumberOfArgs InvalidNumberOfArgs Inherited from IPrecompile. Reverts when the ABI-encoded call does not carry exactly one argument.
UnknownMethod UnknownMethod Inherited from IPrecompile. Reverts when the ABI selector does not match any function on the precompile.

Examples

Direct shielded transfer

msg.sender is treated as the effective sender for PCL and per-sender rate-limit accounting.

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` is prepared off-chain; expiresAtUnix must be in the future.
await wallet.writeContract({
  address: PRIVACY,
  abi: privacyAbi,
  functionName: "transfer",
  args: [request],
});

Decoding a shielded-pool collision

Both errors mean the client's note set is stale relative to the on-chain tree — refresh before retrying.

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

try {
  await wallet.writeContract({
    address: PRIVACY,
    abi: privacyAbi,
    functionName: "transfer",
    args: [request],
  });
} catch (err: any) {
  if (!err?.data) throw err;
  const decoded = decodeErrorResult({ abi: privacyAbi, data: err.data });
  if (decoded.errorName === "PrivacyNullifierAlreadySpent") {
    // The user tried to spend a note that is already gone — refresh their note
    // set from the indexer and rebuild the proof.
    console.warn("note already spent — reload note set");
  } else if (decoded.errorName === "PrivacyMerkleCapacityExceeded") {
    const [count] = decoded.args as [bigint];
    console.warn(`shielded tree is full (${count} outputs requested)`);
  } else {
    throw err;
  }
}
ESC
Type to search