https://github.com/blackhan-software/xpower-banq/tree/5f734cc14fd05afcf7ee221de0bb600f8ca2339c
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);
}
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
}