SSSwap

First Flight #41
Beginner FriendlyRust
100 EXP
View results
Submission Details
Severity: low
Valid

Missing Token-2022 Transfer Fee Handling

Root + Impact

Description

  • Token transfers should account for Token-2022 transfer fees to ensure vault balances reflect actual amounts received, maintaining the x * y = k invariant.

  • Specific Issue: The codebase assumes transfers in transfer_tokens (transfer.rs) and related operations (liquidity_operations.rs, swap_operations.rs) occur without fees, ignoring Token-2022’s transfer fee extension. This results in vault balances being lower than expected, breaking pool calculations and pricing.

// Root cause in the codebase with @> marks to highlight the relevant sectionRoot + Impact:
```rust
// transfer.rs
pub fn transfer_tokens<'info>(
from: &InterfaceAccount<'info, TokenAccount>,
to: &InterfaceAccount<'info, TokenAccount>,
amount: &u64,
...
) -> Result<()> {
transfer_checked(cpi_context, *amount, mint.decimals())?; // @> No fee adjustment
...
}
// liquidity_operations.rs
txr!(transfer_tokens(
&context.accounts.user_token_a,
&mut context.accounts.token_a,
&amount_token_a, // @> No fee handling
...
);
```

Risk

Likelihood:Medium

  • Reason 1: Token-2022 tokens (e.g., with 1% fees) are increasingly used in Solana AMMs.

  • Reason 2: Users deposit or swap without fee checks, making this issue prevalent in affected pools.

  • Impact: High

    • Vault balances misalign with expected amounts, leading to underfunded swaps or liquidity provisions, causing user losses.

    • Breaks x * y = k, enabling arbitrage or pool manipulation.

Proof of Concept

```rust
// Assume token_a has 1% transfer fee
let amount_in = 100_000; // User transfers 100,000 tokens
// transfer.rs: transfer_tokens
transfer_checked(cpi_context, amount_in, mint.decimals())?; // Vault receives 99,000 after 1% fee
// swap_operations.rs: swap_exact_in
let reserve_a = token_vault_a.amount; // Uses 99,000, but assumes 100,000
// Results in incorrect amount_out, breaking pricing
```

Recommended Mitigation

```diff
// transfer.rs
pub fn transfer_tokens<'info>(
from: &InterfaceAccount<'info, TokenAccount>,
to: &InterfaceAccount<'info, TokenAccount>,
amount: &u64,
mint: &InterfaceAccount<'info, Mint>,
...
) -> Result<()> {
+ let fee = mint.get_transfer_fee().unwrap_or(0);
+ require!(*amount > fee, AmmError::InsufficientAfterFee);
transfer_checked(cpi_context, *amount, mint.decimals())?;
+ to.reload()?;
+ require!(to.amount >= *amount - fee, AmmError::TransferFeeMismatch);
...
}
```
Updates

Lead Judging Commences

0xtimefliez Lead Judge 10 days ago
Submission Judgement Published
Validated
Assigned finding tags:

weird T22s

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.