Stake reward calculation is broken
Description
Staking rewards should mint whole CRED token per day and deposit them to the recipient's CoinStore.
However, the current implementation does not take into account the decimals of the token and mints 1 subunit of CRED per day.
Risk
Likelihood: High
This issue affects any user who unstakes.
Impact: High
Stakers receive an unexpected reward breaking incentives and economics.
Proof of Concept
#[test]
public fun test_staking_rewards_calculation () {
let module_owner = account::create_account_for_test(@battle_addr);
let framework = account::create_account_for_test(@0x1);
let recipient = account::create_account_for_test(@0xA);
timestamp::set_time_has_started_for_testing(&framework);
cred_token::initialize_for_test(&module_owner, &framework);
cred_token::register(&recipient);
one_shot::mint_rapper(&module_owner, signer::address_of(&recipient));
let tokens = one_shot::get_tokens();
assert!(vector::length(&tokens) == 1);
let token_address = vector::borrow(&tokens, 0);
let token = object::address_to_object<token::Token>(*token_address);
streets::stake(&recipient, token);
timestamp::fast_forward_seconds(4 * 24 * 60 * 60);
streets::unstake(&recipient, &module_owner, token);
assert!(cred_token::balance_of(signer::address_of(&recipient)) == 4)
}
The test asserts that the current reward calculation distributes 4 CRED subunits for 4 days of staking. Instead, 4 * 10^8 CRED should be minted.
Recommended Mitigation
Convert the amount to main unit by multiplying by 10^8 before calling cred_token::mint
.
- if (days_staked >= 1) { cred_token::mint(module_owner, staker_addr, 1); };
- if (days_staked >= 2) { cred_token::mint(module_owner, staker_addr, 1); };
- if (days_staked >= 3) { cred_token::mint(module_owner, staker_addr, 1); };
- if (days_staked >= 4) { cred_token::mint(module_owner, staker_addr, 1); };
+ if (days_staked >= 1) { cred_token::mint(module_owner, staker_addr, 1 * 10 ** 8); };
+ if (days_staked >= 2) { cred_token::mint(module_owner, staker_addr, 1 * 10 ** 8); };
+ if (days_staked >= 3) { cred_token::mint(module_owner, staker_addr, 1 * 10 ** 8); };
+ if (days_staked >= 4) { cred_token::mint(module_owner, staker_addr, 1 * 10 ** 8); };