One Shot: Reloaded

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

Zero-Value Bet Enables Economic Griefing

Author Revealed upon completion

Root + Impact

Description

  • Normal behavior: Battles should require meaningful economic stakes to prevent spam and ensure participant commitment.

    The specific issue: The protocol only validates bet equality but not minimum amounts, allowing zero or dust-value bets that cost more in gas than reward value, enabling economic griefing attacks.

// Root cause in the codebase with @> marks to highlight the relevant section
// Root cause - missing minimum bet validation
public entry fun go_on_stage_or_battle(
player: &signer,
rapper_token: Object<Token>,
bet_amount: u64 // @> No validation that bet_amount > minimum threshold
) acquires BattleArena {
// Battle proceeds with any bet amount including zero
let first_bet = coin::withdraw<cred_token::CRED>(player, bet_amount);
}

Risk


Likelihood:

  • Attackers can easily automate zero-bet battle spam with minimal setup cost

  • No economic barriers prevent repeated exploitation

  • Players may accidentally set meaningless bet amounts

Impact:

  • Network congestion from spam battle transactions

  • Economic griefing where gas costs exceed battle rewards

  • Poor user experience with meaningless zero-value battles

  • Blockchain resource waste through pointless transactions

Proof of Concept

#[test]
public fun test_zero_bet_spam_attack() {
let attacker = account::create_account_for_test(@0x123);
// Automated spam with zero economic cost
for (i in 0..1000) {
// Stage zero-bet battles repeatedly
rap_battle::go_on_stage_or_battle(&attacker, nft_a, 0);
rap_battle::go_on_stage_or_battle(&attacker, nft_b, 0);
// Full battle logic executes with no meaningful economic stakes
}
// Result: Network spam, resource waste, DoS through volume
}

Recommended Mitigation

- remove this code
+ add this code
+ const MINIMUM_BET_AMOUNT: u64 = 100; // Set meaningful minimum bet
public entry fun go_on_stage_or_battle(
player: &signer,
rapper_token: Object<Token>,
bet_amount: u64
) acquires BattleArena {
+ // Enforce minimum bet requirement
+ assert!(bet_amount >= MINIMUM_BET_AMOUNT, E_BET_TOO_SMALL);
let player_addr = signer::address_of(player);

Support

FAQs

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