Fee & Treasury Patterns
Fee patterns skim basis points on actions, route to treasury PDAs or ATAs, and document rounding. Use checked math and cap configurable fee bps in admin config.
Search across all documentation pages
Fee patterns skim basis points on actions, route to treasury PDAs or ATAs, and document rounding. Use checked math and cap configurable fee bps in admin config.
let fee = amount.checked_mul(config.fee_bps)?.checked_div(10_000)?;
let net = amount.checked_sub(fee)?;When to reach for this:
pub fn split_fee(amount: u64, bps: u16) -> Result<(u64, u64), ProgramError> {
if bps as u64 > 10_000 { return Err(ProgramError::InvalidArgument); }
let fee = amount.checked_mul(bps as u64)?.checked_div(10_000)?;
let net = amount.checked_sub(fee)?;
Ok((net, fee))
}What this demonstrates:
Floor fee favor protocol or user - document choice.
Dedicated PDA ATA per mint.
// u128 intermediate for large mul if needed.| Alternative | Use When | Don't Use When |
|---|---|---|
| Off-chain fee | Not enforceable | On-chain only |
| Jupiter referral | Partner fees | Your program fee separate |
| Donation optional | UX choice | Revenue |
1 bp = 0.01%.
Lamports same math.
Additional layer.
Admin ix.
Fixed in program.
Config account.
Common favor user.
Split fee vector - cap len.
Emit fee taken.
Math + treasury access.
Assert fee amounts.
Same math in handler.
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.
Reviewed by Chris St. John·Last updated Jul 19, 2026