One Shot: Reloaded

First Flight #47
Beginner FriendlyNFT
100 EXP
Submission Details
Impact: high
Likelihood: medium

Unstake Aborts if User Not Registered for CRED

Author Revealed upon completion

Root + Impact

Description

  • Unstaking should succeed and reward CRED if applicable, assuming the user can receive coins.

  • The specific issue is that if the user isn't registered for CRED, mint attempts to destroy non-zero coins with destroy_zero, causing an abort and blocking unstake.

// In cred_token.move
public(friend) fun mint(
// ...
let coins = coin::mint<CRED>(amount, &caps.mint_cap);
if (coin::is_account_registered<CRED>(to)) {
coin::deposit(to, coins);
} else {
//@> coin::destroy_zero(coins); // Aborts if amount > 0
};
}

Risk

Likelihood:

Medium

  • User unstakes after staking >=1 day.

  • User has not called register for CRED.

Impact: High

  • Unstake transaction aborts entirely.

  • User cannot retrieve staked NFT without prior registration.

Proof of Concept

  • Stake for >1 day without registering for CRED.

  • Call unstake: aborts at destroy_zero with non-zero coins.

#[test(module_owner = @battle_addr, staker = @0x123)]
#[expected_failure] // Expect abort on destroy_zero with non-zero coins
fun test_unstake_aborts_unregistered_cred(module_owner: &signer, staker: &signer) acquires battle_addr::streets::StakeInfo {
// Setup: Initialize modules
battle_addr::cred_token::init_module(module_owner);
battle_addr::one_shot::init_module(module_owner);
// Mint and stake rapper
battle_addr::one_shot::mint_rapper(module_owner, signer::address_of(staker));
let rapper = /* assume fetched Object<Token> */;
battle_addr::streets::stake(staker, rapper);
// Simulate time passage for days_staked > 0
aptos_framework::timestamp::set_time_has_started_for_testing(module_owner);
aptos_framework::timestamp::fast_forward_seconds(86400 * 2); // 2 days
// Do NOT register staker for CRED
// Attempt unstake: mint calls destroy_zero on non-zero, aborts
battle_addr::streets::unstake(staker, module_owner, rapper);
}

Recommended Mitigation

  • Optimized and recommended for the observed vulnerability.

- coin::destroy_zero(coins);
+ // Remove or replace with: if (!coin::is_account_registered<CRED>(to)) { coin::register_for_test(to); } then deposit // Or force registration in unstake

Support

FAQs

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