One Shot: Reloaded

First Flight #48
Beginner FriendlyNFT
100 EXP
View results
Submission Details
Severity: low
Valid

Minting non zero CRED to unregistered users aborts with EDESTRUCTION_OF_NONZERO_TOKEN

Minting non zero CRED to unregistered users aborts with EDESTRUCTION_OF_NONZERO_TOKEN

Description

Minting CRED to a recipient should either deposit tokens to that account if registered, or safely handle the case when the recipient is unregistered without causing unexpected reverts. However, minting non zero amount to an unregistered account triggers an abort due to clean up because the code mints coins and then calls coin::destroy_zero which aborts for non zero amounts.

Risk

Likelihood: Low

The issue impacts all unregistered users who try to unstake.

Impact: Low

The transaction aborts, however, the user can registered and the issue resolves.

Proof of Concept

#[test]
#[expected_failure(abort_code = 65543, location = 0x1::coin)] // EDESTRUCTION_OF_NONZERO_TOKEN
public fun test_owner_mint_cred_to_unregistered_destroys_minted () {
let module_owner = account::create_account_for_test(@battle_addr);
let framework = account::create_account_for_test(@0x1);
let recipient = @0x1234;
cred_token::initialize_for_test(&module_owner, &framework);
assert!(cred_token::is_account_registered(recipient) == false);
cred_token::mint(&module_owner, recipient, 1000);
assert!(cred_token::balance_of(recipient) == 0);
}

The test proves that transaction aborts when the module owner tries to mint coins to an unregistered account.

Recommended Mitigation

It is recommended to update the flow as:

  • mint only if the amount is greater than zero

  • if the user is not registered, register them

public(friend) fun mint(
module_owner: &signer,
- to: address,
+ to: &signer,
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);
- };
+ if(amount > 0) {
+ let to_addr = signer::address_of(to);
+ if (!coin::is_account_registered<CRED>(to_addr)) {
+ coin::register<CRED>(to);
+ }
+ let coins = coin::mint<CRED>(amount, &caps.mint_cap);
+ coin::deposit(to_addr, coins);
+ }
}
Updates

Lead Judging Commences

bube Lead Judge 20 days ago
Submission Judgement Published
Validated
Assigned finding tags:

The `cred_token::mint` function fails when the user is not registered

Support

FAQs

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