Beginner FriendlyGameFi
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Inconsistent Balance Tracking Leads to Potential DoS

Vulnerability : Inconsistent Balance Tracking Leads to Potential DoS

Root + Impact

Description

The contract uses a state variable balance to manually track the funds in the resource account. This internal counter can become out of sync with the true APT balance, and the claim_pizza_slice function relies on this potentially inaccurate balance for its E_INSUFFICIENT_FUND check.

// sources/pizza_drop.move
public entry fun fund_pizza_drop(owner: &signer, amount: u64) acquires ModuleData, State {
// ...
coin::transfer<AptosCoin>(owner, resource_addr, amount);
@> state.balance = state.balance + amount;
}
public entry fun claim_pizza_slice(user: &signer) acquires ModuleData, State {
// ...
@> assert!(state.balance >= amount, E_INSUFFICIENT_FUND);
// ...
@> state.balance = state.balance - amount;
}

Risk

Likelihood:

  • This will occur when funds are transferred directly to the resource account's address, bypassing the fund_pizza_drop function.

  • The contract's internal state will not reflect the new funds, leading to incorrect validation.

Impact:

  • Denial of Service (DoS): If the internal balance is incorrectly lower than the actual balance, legitimate users will be blocked from claiming their airdrop, as the assertion state.balance >= amount will fail even when enough funds are present.

Proof of Concept

A mismatch between the tracked and actual balance can lock legitimate users out of their funds.

public entry fun claim_pizza_slice(user: &signer) acquires ModuleData, State {
let user_addr = signer::address_of(user);
let state = borrow_global_mut<State>(get_resource_address());
- // ...
- // Check if contract has sufficient balance
- assert!(state.balance >= amount, E_INSUFFICIENT_FUND);
+ // POC:
+ // 1. Internal state.balance is 0.
+ // 2. 10,000 APT is sent directly to the resource account. Actual balance is 10,000.
+ // 3. User tries to claim. The check `assert!(0 >= 300, ...)` fails, blocking the claim.
}

Recommended Mitigation

Remove the manual balance variable and check the real-time balance of the resource account directly.

- // In the `State` struct:
- balance: u64,
- // In `fund_pizza_drop`:
- state.balance = state.balance + amount;
- // In `claim_pizza_slice`:
- assert!(state.balance >= amount, E_INSUFFICIENT_FUND);
- state.balance = state.balance - amount;
+ // In `claim_pizza_slice`:
+ assert!(get_actual_apt_balance() >= amount, E_INSUFFICIENT_FUND);

Updates

Appeal created

bube Lead Judge 9 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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