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 11 days ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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