Вся документация
Protocol·Обновлено 2026-05-31

Fee model

Free deposits. The protocol fee — a percentage in the withdrawn token plus a flat 0.003 SOL — is charged at withdraw time. Worked examples, where the fees go, and how sweep works.

Deposits are free. SolMask charges its protocol fee at withdraw time, in two parts. Both are enforced on-chain, both are admin-tunable without a redeploy, and both are deliberately simple.

The fees

FeeWhereAmount
DepositFree
Withdraw — percentageAt withdraw time, in the withdrawn tokenConfig.withdraw_fee_bps (default 0, recommended 23 bps = 0.23%; capped at 100 bps)
Withdraw — flatAt withdraw time, in SOLConfig.withdraw_fee_lamports (default 0.003 SOL; capped at 0.1 SOL)

The full amount you deposit enters the shielded pool — nothing is skimmed on the way in. When you withdraw, the percentage fee is taken in the asset you are withdrawing and accrues to the pool's token fee vault; the flat fee is paid in SOL to the fee destination. Both rates live in the global Config account and are tunable by the admin (the percentage via set_withdraw_fee_bps, the flat fee via set_withdraw_fee) within hard on-chain caps.

The percentage fee is bound inside the withdraw zero-knowledge proof: the circuit proves Σ inputs = Σ outputs + fee_amount + change, and the on-chain handler recomputes the expected fee from Config.withdraw_fee_bps and rejects any proof whose bound fee_amount doesn't match. The relayer cannot tamper with it.

The flat SOL fee works as before: the relayer pays it to the fee destination on every withdraw and recoups exactly it by keeping a small tip slice in the withdrawn asset; the network gas it pays to land the transaction is a separate cost the relayer absorbs. If you self-relay, you pay the current fees yourself — the program collects them either way.

There is no minimum, no maximum beyond the caps, no slab structure, and no surge pricing.

Worked example: 1 SOL deposit

You deposit 1 SOL:

amount        = 1_000_000_000 lamports
fee on deposit = 0
credited      = 1_000_000_000 lamports (the full 1 SOL)

Two on-chain effects:

  1. The full 1,000,000,000 lamports moves from your token account to the shielded vault.
  2. Your commitment is appended to the Merkle tree; the new root goes into the recent-root history.

Your note's amount field is 1,000,000,000 lamports — the full deposit.

Later you withdraw the 1 SOL to a fresh wallet, with the percentage rate set to 23 bps:

percentage fee = released * 23 / 10_000   (in the withdrawn token, SOL here)
flat fee       = 0.003 SOL

The circuit's conservation check enforces Σ inputs = Σ outputs + fee_amount + change over your note, so the fee is part of what your note pays for. On a 1 SOL withdraw that's ~0.0023 SOL of percentage fee plus the 0.003 SOL flat fee — roughly 0.0053 SOL total, or about 0.53% on a 1 SOL round-trip. The difference versus the old model: you pay nothing until you exit.

Worked example: 100 USDC deposit

You deposit 100 USDC (decimals = 6, so 100,000,000 base units):

amount    = 100_000_000  base units = 100 USDC
credited  = 100_000_000  base units = 100 USDC   (deposit is free)

The full 100 USDC enters the pool. When you withdraw, the percentage fee is taken in USDC (e.g. 0.23 USDC at 23 bps) and accrues to the USDC pool's token fee vault. The flat 0.003 SOL fee is still paid in SOL: the relayer pays it from its own wallet and recoups it by keeping a small USDC tip slice out of the withdrawn amount, so the recipient does not need to hold any SOL beforehand. The recipient receives their USDC net of the percentage fee and that tip.

Where the fees go

Both fees flow to a single configurable fee destination, set at initialization and rotatable by admin. The flat SOL fee transfers directly to it as a lamport transfer inside the withdraw handler. The percentage fee accumulates in each pool's token fee vault and only moves to the fee destination when a sweep is triggered.

Sweep mechanics

Sweeping is a user-facing instruction. It checks the fee vault balance against the pool's configured sweep threshold; if the threshold is met, the full balance transfers to the fee destination's token account via a pool-PDA-signed transfer. If not, it errors. The threshold is per-pool and admin-adjustable.

Sweep is not automated by the program — there is no built-in cron or keeper. The intended workflow is an off-chain job that checks each pool's fee vault and calls sweep when the threshold is met, paying gas for the sweep transaction. If that job stops, anyone can call sweep and accomplish the same thing; the funds always go to the configured fee destination regardless of who triggers the call. There is no path by which fees stay stuck in a fee vault indefinitely.

What the fee model is not

It is not a yield-sharing mechanism. Depositors do not receive a portion of fees. They are protocol revenue.

It is not progressive. A 0.05 SOL withdraw pays the same 23 bps as a 5,000 SOL withdraw. The flat-percentage model was chosen for legibility — you can predict your fee in your head, and the math is identical across assets. Charging it on exit means deposits cost nothing and you only pay when value actually leaves the pool.

Fee model · SolMask