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.
@> 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:
let module_owner = account::create_account_for_test(@battle_addr);
let attacker_address = @0x1234567890abcdef;
cred_token::mint(&module_owner, attacker_address, 1000000000000);
let target_user = @0xabcdef1234567890;
cred_token::mint(&module_owner, target_user, 1000000);
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.