# #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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reports.immunefi.com/iop-paradex/46747-sc-insight-self-referral-vulnerability-in-account-referral-system.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
