Beginner FriendlyGameFi
100 EXP
View results
Submission Details
Severity: high
Valid

Pizza lovers lose money when they claim their slices

Pizza lovers lose money when they claim their slices

Description

When the owner register Pizza lovers through airdrop::register_pizza_lover, they get a random slice with airdrop::get_random_slice, it is between 100-500 APT.
But in fact it is 100-500 Octas, which is the smallest unit, 1 APT = 10⁸ octas.
see code below :

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; // 100-500 APT (in Octas: 10^8 smallest unit)
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}

The problem is reside in the fact that for the pizza lovers to claim his/her slice, he/she needs to create a transaction that cost a certain amount of APT(actually Octas) for the gas, and the gas cost is higher than the reward.

Risk

The risk is that everytime a pizza lover will claim his/her slice, they will lose money.
The game is not profitable.

Proof of Concept

The following test scenario, shows the problem :

Start a local node in a seperate terminal

aptos node run-local-testnet --with-faucet

initialize an account

aptos init --profile local --network local

Create an another user

aptos init --profile user --network local

See the aptos balance

aptos account balance --account user --url http://localhost:8080
{
"Result": [
{
"asset_type": "coin",
"coin_type": "0x1::aptos_coin::AptosCoin",
"balance": 100000000
}
]
}

Deploy

aptos move publish --profile local --named-addresses pizza_drop=local

NOTE: local_account, user_account and local_private_key are found in .aptos/config.yaml.

Fund the pizza drop

aptos move run --function-id LOCAL_ACCOUNT::airdrop::fund_pizza_drop --args u64:10000 --url http://localhost:8080 --private-key LOCAL_PRIVATE_KEY

Get the APT balance

aptos move view --function-id LOCAL_ACCOUNT::airdrop::get_actual_apt_balance --url http://localhost:8080 --private-key LOCAL_PRIVATE_KEY
{
"Result": [
"10000"
]
}

Register the user as a pizza lover

aptos move run --function-id LOCAL_ACCOUNT::airdrop::register_pizza_lover --args address:USER_ACCOUNT --url http://localhost:8080 --private-key LOCAL_PRIVATE_KEY

See if user register correctly

aptos move view --function-id LOCAL_ACCOUNT::airdrop::is_registered --args address:USER_ACCOUNT --url http://localhost:8080 --private-key LOCAL_PRIVATE_KEY
{
"Result": [
true
]
}

See the amount to claim

aptos move view --function-id LOCAL_ACCOUNT::airdrop::get_claimed_amount --args address:USER_ACCOUNT --url http://localhost:8080 --private-key LOCAL_PRIVATE_KEY
{
"Result": [
"363"
]
}

The user claim the slice

aptos move run --function-id LOCAL_ACCOUNT::airdrop::claim_pizza_slice --url http://localhost:8080 --private-key USER_PRIVATE_KEY
{
"Result": {
"transaction_hash": "0xff7c8ffff2233ffde2ebd3704ec218489547c428a62e48b5c129f75a56810cfd",
"gas_used": 939,
"gas_unit_price": 100,
"sender": "acc574bf21fb6d64c454f056c80b1b5d2c95839af2ada72bbf827e38c55754c9",
"sequence_number": 0,
"replay_protector": {
"SequenceNumber": 0
},
"success": true,
"timestamp_us": 1756629490224223,
"version": 3230,
"vm_status": "Executed successfully"
}
}

The gas unit price is already 100 octas and the actual gas used is 939, so 939 x 100 = 93 900 octas.
note: The mainnet fee is even higher, see: https://www.theblock.co/data/on-chain-metrics/aptos/average-transaction-fee-on-aptos-7dma-in-apt.

Get the smart contract APT balance

aptos move view --function-id LOCAL_ACCOUNT::airdrop::get_actual_apt_balance --url http://localhost:8080 --private-key LOCAL_PRIVATE_KEY
{
"Result": [
"9637"
]
}

The smart contract apt balance is correct.

The user apt balance

aptos account balance --account user --url http://127.0.0.1:8080
{
"Result": [
{
"asset_type": "coin",
"coin_type": "0x1::aptos_coin::AptosCoin",
"balance": 99906463
}
]
}

The initial balance of user was : 100000000(see part 4), and now is reduce to 99906463, because gas fee was higher than the reward.
The user loses money.

Recommended Mitigation

The recommended mitigation is to multiple the reward by at least 1000 for local environment and even higher for the mainnet.

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; // 100-500 APT (in Octas: 10^8 smallest unit)
+ let random_val = (time % 401) * 1000;
+ let random_amount = 100000 + random_val; // 100000-500000 (in Octas: 10^8 smallest unit)
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}
Updates

Appeal created

bube Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

Incorrect APT value

Support

FAQs

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