One Shot: Reloaded

First Flight #47
Beginner FriendlyNFT
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

[H-01] Centralized Mint Authority Can Manipulate Token Supply

Root + Impact

Description

The CRED token contract allows the module owner to mint unlimited tokens to any address without restriction. This centralization risk creates a single point of failure where the owner could manipulate the token supply and economic balance of the protocol.

The mint function lacks any supply cap or rate limiting mechanism, enabling the module owner to mint arbitrary amounts of CRED tokens to any address, which could be used to manipulate battle outcomes or devalue existing token holdings.

// In cred_token.move
@> public(friend) fun mint(
@> module_owner: &signer,
@> to: address,
@> amount: u64
@> ) acquires CredCapabilities {
@> let caps = borrow_global<CredCapabilities>(signer::address_of(module_owner));
@> 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);
@> };
@> }

Risk

Likelihood:

  • The module owner can mint tokens at any time since there are no restrictions on the mint function

  • The owner address is hardcoded as @battle_addr, creating a permanent centralization point

Impact:

  • Token supply inflation leading to devaluation of existing holdings

  • Manipulation of battle outcomes by minting tokens to specific users

  • Loss of trust in the protocol due to centralization risks

Proof of Concept

This PoC demonstrates how the module owner can mint unlimited tokens to manipulate the economy:

// Module owner can mint unlimited tokens to any address
let module_owner = account::create_account_for_test(@battle_addr);
let attacker_address = @0x1234567890abcdef;
// Attack scenario 1: Mint massive amounts to manipulate battles
cred_token::mint(&module_owner, attacker_address, 1000000000000); // 1 trillion tokens
// Attack scenario 2: Mint to specific users to guarantee battle wins
let target_user = @0xabcdef1234567890;
cred_token::mint(&module_owner, target_user, 1000000); // Enough to win any battle
// Result: Token supply inflates, existing holders lose value
// The attacker now has enough CRED to guarantee victory in any battle

Recommended Mitigation

The mitigation adds a supply cap mechanism to prevent unlimited minting:

+ struct SupplyCap has key {
+ max_supply: u64,
+ current_supply: u64,
+ }
+
+ fun init_module(sender: &signer) {
+ let (burn_cap, freeze_cap, mint_cap) = coin::initialize<CRED>(
+ sender,
+ string::utf8(b"Credibility"),
+ string::utf8(b"CRED"),
+ 8,
+ false,
+ );
+ move_to(sender, CredCapabilities { mint_cap, burn_cap });
+ move_to(sender, SupplyCap { max_supply: 1000000000, current_supply: 0 });
+ coin::destroy_freeze_cap(freeze_cap);
+ }
+
public(friend) fun mint(
module_owner: &signer,
to: address,
amount: u64
- ) acquires CredCapabilities {
+ ) acquires CredCapabilities, SupplyCap {
+ let supply = borrow_global_mut<SupplyCap>(@battle_addr);
+ assert!(supply.current_supply + amount <= supply.max_supply, E_SUPPLY_EXCEEDED);
+ supply.current_supply = supply.current_supply + amount;
+
let caps = borrow_global<CredCapabilities>(signer::address_of(module_owner));
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);
};
}

This mitigation adds a supply cap that prevents unlimited token minting by tracking the current supply against a maximum limit. The cap ensures the token economy remains controlled and prevents centralization abuse.

Updates

Lead Judging Commences

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