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

No Maximum Registration Limit

Root + Impact

Description

  • The contract should ensure that the total potential claims do not exceed the available funds to guarantee all registered users can claim their allocated amounts.

  • The owner can register unlimited users without checking if there are sufficient funds to cover all potential claims, leading to a situation where later users cannot claim their allocated amounts.

public entry fun register_pizza_lover(owner: &signer, user: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
assert!(signer::address_of(owner) == state.owner, E_NOT_OWNER);
get_random_slice(user); // @> No check for available funds vs potential claims
event::emit(PizzaLoverRegistered {
user: user,
});
}
#[randomness]
entry fun get_random_slice(user_addr: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
let time = timestamp::now_microseconds();
let random_val = time % 401;
let random_amount = 100 + random_val; // @> Each user gets 100-500 APT
table::add(&mut state.users_claimed_amount, user_addr, random_amount); // @> No limit check
}

Risk

Likelihood:

  • Owner can easily register more users than the contract can afford to pay

  • No mechanism prevents over-registration relative to available funds

  • Registration happens before funding, making it easy to create unfulfillable obligations

Impact:

  • Later users will be unable to claim their allocated amounts due to insufficient funds

  • Unfair distribution where early claimers get their full amounts while others get nothing

  • User frustration and loss of trust in the project

  • Potential legal issues if users expect guaranteed payouts

Proof of Concept

#[test]
fun test_over_registration() acquires ModuleData, State {
// Setup contract with limited funds
init_module(deployer);
fund_pizza_drop(deployer, 1000); // Only 1000 APT available
// Register 10 users (each could get up to 500 APT = 5000 APT total needed)
let i = 0;
while (i < 10) {
let user_addr = @0x100 + i;
register_pizza_lover(deployer, user_addr);
i = i + 1;
};
// Check total potential claims
let total_potential = 0;
let j = 0;
while (j < 10) {
let user_addr = @0x100 + j;
total_potential = total_potential + get_claimed_amount(user_addr);
j = j + 1;
};
// Total potential claims likely exceed available funds
assert!(total_potential > 1000, 1); // This will likely pass
// Some users won't be able to claim
// First few users claim successfully, but later ones will fail
}

Recommended Mitigation

struct State has key {
users_claimed_amount: Table<address, u64>,
claimed_users: Table<address, bool>,
owner: address,
balance: u64,
+ max_users: u64, // Maximum number of users that can be registered
+ registered_count: u64, // Current number of registered users
+ total_allocated: u64, // Total amount allocated to all users
}
fun init_module(deployer: &signer) {
// ... existing code ...
let state = State {
users_claimed_amount: table::new(),
claimed_users: table::new(),
owner: signer::address_of(deployer),
balance: 0,
+ max_users: 0, // Initially no registrations allowed
+ registered_count: 0,
+ total_allocated: 0,
};
// ... rest of function
}
+ // Owner must set registration limits based on available funds
+ public entry fun set_registration_limit(owner: &signer, max_users: u64) acquires ModuleData, State {
+ let state = borrow_global_mut<State>(get_resource_address());
+ assert!(signer::address_of(owner) == state.owner, E_NOT_OWNER);
+
+ // Ensure we have enough funds for worst case (all users get 500 APT)
+ let worst_case_total = max_users * 500;
+ assert!(state.balance >= worst_case_total, E_INSUFFICIENT_FUND);
+
+ state.max_users = max_users;
+ }
public entry fun register_pizza_lover(owner: &signer, user: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
assert!(signer::address_of(owner) == state.owner, E_NOT_OWNER);
+ // Check if already registered
+ assert!(!table::contains(&state.users_claimed_amount, user), E_ALREADY_REGISTERED);
+
+ // Check registration limits
+ assert!(state.registered_count < state.max_users, E_REGISTRATION_LIMIT_REACHED);
get_random_slice(user);
+ state.registered_count = state.registered_count + 1;
event::emit(PizzaLoverRegistered {
user: user,
});
}
#[randomness]
entry fun get_random_slice(user_addr: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
let time = timestamp::now_microseconds();
let random_val = time % 401;
let random_amount = 100 + random_val;
+ // Update total allocated amount
+ state.total_allocated = state.total_allocated + random_amount;
+
+ // Ensure we still have enough funds for this allocation
+ assert!(state.balance >= state.total_allocated, E_INSUFFICIENT_FUND);
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}
+ // Add new error codes
+ const E_ALREADY_REGISTERED: u64 = 5;
+ const E_REGISTRATION_LIMIT_REACHED: u64 = 6;
Updates

Appeal created

bube Lead Judge 10 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.