# #46747 \[SC-Insight] Self-Referral Vulnerability in Account Referral System

**Submitted on Jun 4th 2025 at 07:21:29 UTC by @Catchme for** [**IOP | Paradex**](https://immunefi.com/audit-competition/iop-paradex)

* **Report ID:** #46747
* **Report Type:** Smart Contract
* **Report severity:** Insight
* **Target:** <https://github.com/tradeparadex/audit-competition-may-2025/tree/main/paraclear>
* **Impacts:**
  * Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

## Description

## Summary

There is a critical vulnerability in the `set_account_referral` function that allows an account to be set as its own referrer, creating a circular referral system that can be exploited to reduce trading fees unfairly.

## Vulnerability Details

In `AccountComponent::set_account_referral`:

```cairo
fn set_account_referral(
    ref self: ComponentState<TContractState>,
    account: ContractAddress,
    referrer: ContractAddress,
    commission: felt252,
    discount: felt252,
) {
    self.assert_only_role(roles::CONFIGURATOR_ROLE);
    self
        .Paraclear_account_referral
        .write(
            account,
            AccountReferral {
                referrer: referrer, fee_commission: commission, fee_discount: discount,
            },
        );
    self
        .emit(
            AccountReferralUpdate {
                account: account,
                referrer: referrer,
                fee_commission: commission,
                fee_discount: discount,
            },
        );
}
```

The function lacks validation to ensure that `account != referrer`. This allows setting up self-referrals where an account can refer itself, creating a circular referral relationship.

## Impact

1. **Fee Manipulation**: An account could receive both fee discounts as a referred account and fee commissions as a referrer for the same trades, allowing users to significantly reduce trading fees.
2. **Economic Exploit**: The system's fee calculation logic in `get_trade_fee_and_referral_commission` would apply both a discount and generate commissions for the same account:

   ```cairo
   if self.referral.fee_discount != @0 {
       let one_minus_discount = math::MULTIPLIER - (*self.referral.fee_discount).try_into().unwrap();
       let fee_after_discount = mul_128(fee, one_minus_discount);
       if self.referral.fee_commission != @0 {
           let fee_commission = mul_128(
               fee_after_discount, (*self.referral.fee_commission).try_into().unwrap(),
           );
           return (fee_after_discount, *self.referral.referrer, fee_commission);
       }
       return (fee_after_discount, *self.referral.referrer, 0);
   }
   ```
3. **Protocol Revenue Loss**: This could result in reduced protocol revenue from trading fees.

## Recommended Fix

Add a validation check to ensure that `account` and `referrer` cannot be the same address:

```cairo
fn set_account_referral(
    ref self: ComponentState<TContractState>,
    account: ContractAddress,
    referrer: ContractAddress,
    commission: felt252,
    discount: felt252,
) {
    self.assert_only_role(roles::CONFIGURATOR_ROLE);
    
    // Prevent self-referral
    assert(account != referrer, 'Self-referral not allowed');
    
    self
        .Paraclear_account_referral
        .write(
            account,
            AccountReferral {
                referrer: referrer, fee_commission: commission, fee_discount: discount,
            },
        );
    self
        .emit(
            AccountReferralUpdate {
                account: account,
                referrer: referrer,
                fee_commission: commission,
                fee_discount: discount,
            },
        );
}
```

## Proof of Concept

## Proof of Concept

1. A user with `CONFIGURATOR_ROLE` calls `set_account_referral` with:
   * `account` = user's address
   * `referrer` = same user's address
   * `commission` =3000
   * `discount` = 2000
2. When the user trades, they receive both a 20% discount on fees and earn 30% commission on these already discounted fees.
