IPrivacy.withdraw

withdraw(PrivacyWithdrawRequest request) external returns (bool success)

Spends a shielded note and pays the underlying transparent OKRW to request.recipient. msg.sender is the effective sender for PCL policy purposes. Failures revert with typed custom errors declared on IPrivacy (request-expiry, native-denom mismatch, nullifier collision) or inherited from IPrecompile (InvalidAmount, InvalidAddress, etc.).

Parameters

Name Type Required Description
request PrivacyWithdrawRequest The withdraw payload — proof, Merkle root, nullifier, amount (SDK coin string, e.g. "15000000000000000000aokrw" for 15 OKRW-worth of aokrw — must use the native denom or revert with PrivacyNativeDenomMismatch), recipient, chain id, and 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. withdraw is not payable.
PrivacyRequestExpired PrivacyRequestExpired Reverts when request.expiresAtUnix is zero or has already passed.
PrivacyNativeDenomMismatch PrivacyNativeDenomMismatch Reverts when request.amount uses a denom other than the network's native denom (aokrw). Payload is (got, expected).
PrivacyNullifierAlreadySpent PrivacyNullifierAlreadySpent Reverts when the nullifier is already recorded on-chain.
InvalidAmount InvalidAmount Inherited from IPrecompile. Reverts when request.amount fails to parse as a coin string. Encoded as InvalidAmount(string amount) where the payload is the raw amount string.
InvalidAddress InvalidAddress Inherited from IPrecompile. Reverts when the caller cannot be converted to a valid account address. Encoded as InvalidAddress(string bad).
PrivacyTxLimitExceeded PrivacyTxLimitExceeded Reverts when the transaction has already exceeded the global stateful-privacy call limit.
PrivacySenderTxLimitExceeded PrivacySenderTxLimitExceeded Reverts when the effective sender has already exceeded the per-sender stateful-privacy call limit.
InvalidNumberOfArgs InvalidNumberOfArgs Inherited from IPrecompile. Reverts when the call does not carry exactly one argument.

Examples

Basic withdraw

The amount string must use aokrw — the network's native denom. Passing another denom reverts with PrivacyNativeDenomMismatch.

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.USER_KEY as `0x${string}`),
  transport: http("https://rpc-testnet.maroo.io"),
});

// The single withdraw amount fits inside the 64-bit shielded cap.
// TODO: replace with the real payout recipient before production.
const request = {
  proof: proofBytes,
  root: merkleRoot,
  nullifier: noteNullifier,
  amount: "15000000000000000000aokrw", // 15 OKRW
  recipient: "0x8f3aC2b1D9e74C05a6B18Fe27dC4913E5A0f7b62",
  chainId: "maroo-testnet",
  expiresAtUnix: BigInt(Math.floor(Date.now() / 1000) + 300),
};

await wallet.writeContract({
  address: PRIVACY,
  abi: privacyAbi,
  functionName: "withdraw",
  args: [request],
});

Handling denom / expiry rejections

Rebuild the request with the correct aokrw denom string or a fresh deadline; the proof itself remains valid.

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

try {
  await wallet.writeContract({
    address: PRIVACY,
    abi: privacyAbi,
    functionName: "withdraw",
    args: [request],
  });
} catch (err: any) {
  if (!err?.data) throw err;
  const decoded = decodeErrorResult({ abi: privacyAbi, data: err.data });
  if (decoded.errorName === "PrivacyNativeDenomMismatch") {
    const [got, expected] = decoded.args as [string, string];
    console.warn(`use denom ${expected}; got ${got}`);
  } else if (decoded.errorName === "PrivacyRequestExpired") {
    const [expiresAtUnix] = decoded.args as [bigint];
    console.warn(`withdraw request expired at ${expiresAtUnix} — rebuild with a fresh deadline`);
  } else {
    throw err;
  }
}
ESC
Type to search