57425 sc low referral percentage updates are ignored due to append only storage in nftfactory

Submitted on Oct 26th 2025 at 04:34:13 UTC by @Rhaydden for Audit Comp | Belongarrow-up-right

  • Report ID: #57425

  • Report Type: Smart Contract

  • Report severity: Low

  • Target: https://github.com/immunefi-team/audit-comp-belong/blob/feat/cairo/src/nftfactory/nftfactory.cairohttps://github.com/immunefi-team/audit-comp-belong/blob/feat/cairo/src/nftfactory/nftfactory.cairo

  • Impacts: Contract fails to deliver promised returns, but doesn't lose value

Description

Issue description

The _set_referral_percentages function appends new values to used_to_percentage instead of overwriting the existing 5 entries. getReferralRate indexes the vector using the referral usage count (used) which is capped at 4 in _set_referral_user. As a result, it always reads indices 0..4 from the original set and never the newly appended values at indices 5..9, etc.

This renders owner updates to referral percentages ineffective and grows storage unnecessarily.

Relevant code excerpt:

fn _set_referral_percentages(ref self: ContractState, percentages: Span<u16>) {
    assert(percentages.len() == 5, super::Errors::WRONG_PERCENTAGES_LEN);
    for i in 0..percentages.len() {
        self.used_to_percentage.append().write(*percentages.at(i));
    };
}

fn getReferralRate(
    self: @ContractState,
    referral_user: ContractAddress,
    referral_code: felt252,
    amount: u256,
) -> u256 {
    let used = self.used_code.entry(referral_user).entry(referral_code).read();
    assert(used > 0, super::Errors::REFFERAL_CODE_NOT_USED_BY_USER);
    let rate = amount * self.used_to_percentage.at(used.into()).read().into() / SKALING_FACTOR;
    rate
}

fn _set_referral_user(ref self: ContractState, referral_code: felt252, referral_user: ContractAddress) {
    // ...
    let used_code = self.used_code.entry(referral_user).entry(referral_code).read();
    if used_code < 4 {
        self.used_code.entry(referral_user).entry(referral_code).write(used_code + 1);
    }
    // ...
}

How it breaks:

  • Initial Setup: Owner calls initialize or setReferralPercentages with percentages [0, 5000, 3000, 1500, 500]

    • Vec indices: 0->0, 1->5000, 2->3000, 3->1500, 4->500

  • Attempted Update: Owner tries to update percentages to [0, 6000, 4000, 2000, 1000]

    • The function appends instead of replacing

    • Vec now contains: 0->0, 1->5000, 2->3000, 3->1500, 4->500, 5->0, 6->6000, 7->4000, 8->2000, 9->1000

  • Referral rate calculation: getReferralRate uses used (0..4) and therefore always accesses indices 0..4 (the old values). New values at 5..9 are never read.

Impact

Low — Contract fails to deliver promised returns, but doesn't lose value.

  • Owner cannot update referral percentages effectively; updates have no effect.

  • Old percentages persist forever causing users to receive incorrect rewards.

  • The vector grows indefinitely; each update attempt appends 5 more entries and wastes storage.

Overwrite indices 0..4 of used_to_percentage instead of appending.

Suggested approach:

  • If the vector length < 5, append until size == 5.

  • Else, write the new five values to indices 0..4 directly.

Proof of Concept

Attach this PoC to test_nftfactory.cairo:

I made these changes to scarb.toml:

Run test with scarb run test

Logs:


If you want, I can provide a minimal code patch that replaces the append behavior to correctly overwrite indices 0..4 (including handling the <5 length case).

Was this helpful?