IPrivacy.transferWithAuthorization

transferWithAuthorization(
  PrivacyTransferRequest request,
  PrivacyActionAuthorization authorization
) external returns (bool success)

Executes a shielded transfer on behalf of an effectiveSender who signed an EIP-712 PrivacyActionAuthorization. The relayer is msg.sender; the authorization designates the sender whose nonce is spent and whose PCL rules apply. Failures now revert with typed custom errors on IPrivacy (authorization checks, request-expiry, nullifier/commitment collisions, batch limits) or inherited errors on IPrecompile (InvalidNumberOfArgs, RequesterIsNotMsgSender, etc.). PCL policy failures still surface as PCL ReasonCodes from IPcl.

Parameters

Name Type Required Description
request PrivacyTransferRequest The shielded transfer payload. Its expiresAtUnix must be strictly greater than the current block time — a stale or zero deadline reverts with PrivacyRequestExpired(expiresAtUnix).
authorization PrivacyActionAuthorization The EIP-712-signed permission from the effective sender. effectiveSender, executor, nonce, and deadline must all be set (missing fields revert with PrivacyInvalidAuthorizationField(field)); executor must equal msg.sender (mismatch reverts with RequesterIsNotMsgSender); the signature must recover to effectiveSender (mismatch reverts with PrivacyAuthorizationSignerMismatch(expected, got)). See privacy-authorization-eip712-domain for the exact typeHash preimage.

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 is payable. Encoded as PrivacyNonPayable(string methodName).
PrivacyRequestExpired PrivacyRequestExpired Reverts when request.expiresAtUnix is zero or has already passed. Payload carries the offending unix timestamp.
PrivacyInvalidAuthorizationField PrivacyInvalidAuthorizationField Reverts when a required authorization field is missing or invalid — the payload is the field name ("effectiveSender", "executor", "nonce", or "deadline").
PrivacyAuthorizationExpired PrivacyAuthorizationExpired Reverts when the authorization's deadline has passed relative to the current block time.
PrivacyAuthorizationNonceUsed PrivacyAuthorizationNonceUsed Reverts when the nonce has already been consumed for that effective sender. Payload is (sender, nonce).
PrivacyAuthorizationSignerMismatch PrivacyAuthorizationSignerMismatch Reverts when the recovered signer does not match effectiveSender. Payload is (expected, got).
PrivacyAuthorizationRejected PrivacyAuthorizationRejected Reverts when an ERC-1271 or EIP-7702 authorization is rejected by its verifier (bad code shape, verifier revert, or unsupported authorization kind). Payload is the authorization kind.
PrivacyInvalidAuthorizationMagic PrivacyInvalidAuthorizationMagic Reverts when an ERC-1271 smart account returned bytes that are not the 0x1626ba7e magic. Payload is the returned magic bytes.
PrivacyNullifierAlreadySpent PrivacyNullifierAlreadySpent Reverts when a nullifier in the request has already been recorded on-chain.
PrivacyCommitmentAlreadyExists PrivacyCommitmentAlreadyExists Reverts when a new commitment in the request already exists in the Merkle tree.
PrivacyMerkleCapacityExceeded PrivacyMerkleCapacityExceeded Reverts when appending the request's new commitments would exceed the Merkle tree capacity.
PrivacyTxLimitExceeded PrivacyTxLimitExceeded Reverts when the transaction has already exceeded the global stateful-privacy call limit within a single EVM execution.
PrivacySenderTxLimitExceeded PrivacySenderTxLimitExceeded Reverts when the transaction has already exceeded the per-effective-sender stateful-privacy call limit.
RequesterIsNotMsgSender RequesterIsNotMsgSender Inherited from IPrecompile. Reverts when authorization.executor is not the same as msg.sender. Payload is (msgSender, requester).
InvalidNumberOfArgs InvalidNumberOfArgs Inherited from IPrecompile. Reverts when the ABI-encoded call does not carry exactly two arguments.
UnknownMethod UnknownMethod Inherited from IPrecompile. Reverts when the ABI selector does not match any function on the precompile — usually indicates a stale ABI.

Examples

Relayer submits an authorized transfer

The relayer becomes msg.sender; PCL evaluates rules against authorization.effectiveSender. If any check fails the whole call reverts with the corresponding typed error.

import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { privacyAbi } from "@maroo-chain/contracts/abi/IPrivacy";

const PRIVACY = "0x100000000000000000000000000000000000000b" as const;

const relayer = createWalletClient({
  account: privateKeyToAccount(process.env.RELAYER_KEY as `0x${string}`),
  transport: http("https://rpc-testnet.maroo.io"),
});

// `request` and `authorization` are built off-chain; the effective sender
// signs the EIP-712 typeHash preimage (see privacy-authorization-eip712-domain).
// authorization.executor MUST equal the relayer address.
await relayer.writeContract({
  address: PRIVACY,
  abi: privacyAbi,
  functionName: "transferWithAuthorization",
  args: [request, authorization],
});

Decoding authorization / expiry errors

Match on decoded.errorName — each typed error's arg tuple is stable and decodes into typed values (address, bigint) usable directly in your UX.

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

try {
  await relayer.writeContract({
    address: PRIVACY,
    abi: privacyAbi,
    functionName: "transferWithAuthorization",
    args: [request, authorization],
  });
} catch (err: any) {
  if (!err?.data) throw err;
  const decoded = decodeErrorResult({ abi: privacyAbi, data: err.data });
  switch (decoded.errorName) {
    case "PrivacyAuthorizationNonceUsed": {
      const [sender, nonce] = decoded.args as [`0x${string}`, bigint];
      console.warn(`nonce ${nonce} already consumed for ${sender}`);
      break;
    }
    case "PrivacyRequestExpired": {
      const [expiresAtUnix] = decoded.args as [bigint];
      console.warn(`request expired at ${expiresAtUnix}`);
      break;
    }
    case "PrivacyAuthorizationSignerMismatch": {
      const [expected, got] = decoded.args as [`0x${string}`, `0x${string}`];
      console.warn(`bad signature: expected ${expected}, got ${got}`);
      break;
    }
    default:
      throw err;
  }
}
ESC
Type to search