Blackhan Software Disclosed Report

mint function problem

Created date
May 10 2025

Target

https://github.com/blackhan-software/xpower-banq/tree/5f734cc14fd05afcf7ee221de0bb600f8ca2339c

Vulnerability Details

mint function in the Position contract has a flaw: it checks supply limits before accruing interest, allowing the owner to mint tokens beyond the cap. github : https://github.com/blackhan-software/xpower-banq/blob/5f734cc14fd05afcf7ee221de0bb600f8ca2339c/source/contract/Position.sol#L218 code :

function mint(address user, uint256 amount, bool lock) public virtual onlyOwner {
    (uint256 abs_limit, ) = _absLimit();
    if (amount + totalSupply() > abs_limit) {
        revert AbsExceeded(abs_limit);
    }
    (uint256 rel_limit, ) = _capOf(user);
    if (amount > rel_limit) {
        revert RelExceeded(rel_limit);
    }
    if (lock) {
        _lockMore(user, amount);
    }
    _accrueInterest(user);  // bug: this should happen before checks
    _mint(user, amount);
}

call to _accrueInterest(user) comes after the supply cap checks. since _accrueInterest increases the total supply (by minting interest tokens), the checks don’t account for this increase. for example, if totalSupply() is 1000, abs_limit is 1100, and interest adds 50 tokens, minting 60 tokens passes the check (1000 + 60 = 1060 <= 1100), but after interest, the supply hits 1110, exceeding the cap. this breaks the contract’s invariant that the total supply must stay under abs_limit. over minting could inflate the token supply, undermining its value or governance rules.

fix : move _accrueInterest before the cap checks to ensure they use updated values.

function mint(address user, uint256 amount, bool lock) public virtual onlyOwner {
    _accrueInterest(user);  // fix: accrue interest first
    (uint256 abs_limit, ) = _absLimit();
    if (amount + totalSupply() > abs_limit) {
        revert AbsExceeded(abs_limit);
    }
    (uint256 rel_limit, ) = _capOf(user);
    if (amount > rel_limit) {
        revert RelExceeded(rel_limit);
    }
    if (lock) {
        _lockMore(user, amount);
    }
    _mint(user, amount);
}

Validation steps

test :

function test_mint_exceeds_cap() public {
    // setup: total supply at 1000, cap at 1100, interest of 50
    uint256 abs_limit = 1100;
    uint256 supply = 1000;
    uint256 interest = 50;
    uint256 amount = 60;

    // check cap with current supply
    require(amount + supply <= abs_limit, "cap check fails");  // passes: 1060 <= 1100

    // accrue interest, then mint
    supply += interest;  // supply becomes 1050
    supply += amount;    // supply becomes 1110

    // confirm supply exceeds cap
    assert(supply > abs_limit);  // 1110 > 1100, bug proven
}
CommentsReport History
Comments on this report are hidden
Details
Statedisclosed
Severity
Low
Bounty$500
Visibilitypartially
VulnerabilityOther
Participants (3)
company admin
company admin
triage team