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

Invalid State Management - get_claimed_amount Returns Wrong State Information

Description

The protocol has fundamentally incorrect state variable naming and getter implementation that returns invalid state information. The get_claimed_amount() function returns values from a misnamed users_claimed_amount table which actually stores assigned amounts, not claimed amounts. This creates a critical disconnect between function behavior and state representation, as users can have "claimed amounts" returned even when they haven't claimed anything.

Root Cause

Multiple layers of incorrect state management:

  1. Misnamed state variable: users_claimed_amount actually stores assigned amounts, not claimed amounts

  2. Invalid getter logic: get_claimed_amount() returns assigned amounts regardless of actual claim status

  3. State inconsistency: The actual claim status in claimed_users table is completely ignored by the getter

struct State has key {
users_claimed_amount: Table<address, u64>, // WRONG: Should be "users_assigned_amount"
claimed_users: Table<address, bool>, // This tracks actual claims but getter ignores it
// ...
}
#[view]
public fun get_claimed_amount(user: address): u64 acquires ModuleData, State {
let state = borrow_global<State>(get_resource_address());
if (!table::contains(&state.users_claimed_amount, user)) {
return 0
};
let amount = table::borrow(&state.users_claimed_amount, user);
*amount // WRONG: Returns assigned amount, not claimed amount!
}

The actual claim happens here but the getter doesn't check this:

// In claim_pizza_slice:
table::add(&mut state.claimed_users, user_addr, true); // Real claim status ignored by getter

Impact

  • Invalid state reporting: Protocol reports wrong claim information to external systems

  • Protocol integrity compromise: Core state management violates basic naming and getter conventions

Proof of Concept

// User is registered and assigned 300 APT
register_pizza_lover(owner, user);
// get_claimed_amount returns 300 even though user hasn't claimed
assert!(get_claimed_amount(user) == 300, 1);
// User hasn't actually claimed yet
assert!(!table::contains(&state.claimed_users, user), 2);
// After claiming
claim_pizza_slice(user);
// Now user has actually claimed, but get_claimed_amount returns same value
assert!(get_claimed_amount(user) == 300, 3);
assert!(table::contains(&state.claimed_users, user), 4);

Recommended Mitigation

Fix the state variable naming and implement correct getter logic:

struct State has key {
- users_claimed_amount: Table<address, u64>,
+ users_assigned_amount: Table<address, u64>, // Correct naming
claimed_users: Table<address, bool>,
// ...
}
#[view]
- public fun get_claimed_amount(user: address): u64 acquires ModuleData, State {
+ public fun get_assigned_amount(user: address): u64 acquires ModuleData, State {
let state = borrow_global<State>(get_resource_address());
- if (!table::contains(&state.users_claimed_amount, user)) {
+ if (!table::contains(&state.users_assigned_amount, user)) {
return 0
};
- let amount = table::borrow(&state.users_claimed_amount, user);
+ let amount = table::borrow(&state.users_assigned_amount, user);
*amount
}
+ // Correctly implement get_claimed_amount that returns actual claimed amounts
+ #[view]
+ public fun get_claimed_amount(user: address): u64 acquires ModuleData, State {
+ let state = borrow_global<State>(get_resource_address());
+ // Only return amount if user has actually claimed (use existing has_claimed_slice)
+ if (table::contains(&state.claimed_users, user)) {
+ if (table::contains(&state.users_assigned_amount, user)) {
+ return *table::borrow(&state.users_assigned_amount, user)
+ }
+ };
+ 0 // Return 0 if not claimed
+ }

This ensures:

  1. State variables have correct, descriptive names

  2. Getters return accurate state information

  3. Clear distinction between assigned and claimed amounts

Updates

Appeal created

bube Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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