Skip to main content
Fees are collected strictly on-chain by the shield-pool program into the mint-scoped treasury PDA. No other component assesses or deducts fees. Fees apply only to flows that move value out of the pool (publicAmount < 0: withdrawals and swaps). Deposits and shield-to-shield transfers charge no fee.

Constants

  • SOL pool fixed fee: 5_000_000 lamports (0.005 SOL)
  • Variable fee (all pools): gross * 3 / 1000 (0.30%)
  • SPL pools (USDC/USDT): variable fee only — no fixed component, charged in the withdrawn token
  • Minimum deposit (SOL pool only): 10_000_000 lamports (0.01 SOL); SPL deposits have no program-enforced minimum

Per-flow behavior

  • Deposit (publicAmount > 0): no fee. The full deposit amount moves into the pool and the output commitments encode the full amount.
  • Shield-to-shield transfer (publicAmount = 0): no fee. No external value moves; the output commitments sum to the inputs.
  • Withdrawal (publicAmount < 0): the destination receives net = gross - fee; the treasury PDA receives fee.
  • Swap (TransactSwap, SOL pool only): fee = fixed + variable is taken from the gross SOL at the first transaction; the remainder (gross - fee) is what gets swapped.

Fee math

SOL pool (withdraw/swap):
SPL pools (withdraw):

Worked examples

Withdraw 1 SOL (1_000_000_000 lamports) gross:
  • Fixed: 5_000_000 lamports
  • Variable: 1_000_000_000 * 3 / 1000 = 3_000_000 lamports
  • Fee: 8_000_000 lamports — net to recipient: 992_000_000 lamports
Withdraw 100 USDC (100_000_000 base units) gross:
  • Variable: 100_000_000 * 3 / 1000 = 300_000 base units (0.30 USDC)
  • Fee: 300_000 base units — net to recipient: 99_700_000 base units
The Cloak app presents this in reverse: the user enters what the recipient should receive, and the app grosses up so net equals the entered amount. At the program level the invariant is always net = gross - fee.
For a user-facing summary with a who-pays table, see Fees.