#46326 [SC-Medium] Incorrect Minting Cap Check in Minting Process
Submitted on May 28th 2025 at 12:17:07 UTC by @aman for Audit Comp | Flare | FAssets
Report ID: #46326
Report Type: Smart Contract
Report severity: Medium
Target: https://github.com/flare-foundation/fassets/blob/main/contracts/assetManager/library/Minting.sol
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
The checkMintingCap function in Minting.sol only checks the valueAMG against the minting cap, but the actual minting process adds both valueAMG and poolFeeAMG to the agent's minted amount. This discrepancy allows the total minted amount to exceed the intended minting cap.
Vulnerability Details
The vulnerable code is in Minting.sol:
/fassets/contracts/assetManager/library/Minting.sol:75
75: function selfMint(
76: IPayment.Proof calldata _payment,
77: address _agentVault,
78: uint64 _lots
79: )
80: internal
81: {
.....
90: require(collateralData.freeCollateralLots(agent) >= _lots, "not enough free collateral");
91: uint64 valueAMG = _lots * Globals.getSettings().lotSizeAMG;
92: checkMintingCap(valueAMG); <----@
107: if (_lots > 0) {
108: _performMinting(agent, MintingType.SELF_MINT, 0, msg.sender, valueAMG, receivedAmount, poolFeeUBA);
109: } else {
_performMinting function :
/fassets/contracts/assetManager/library/Minting.sol:189
189: function _performMinting(
190: Agent.State storage _agent,
191: MintingType _mintingType,
192: uint64 _crtId,
193: address _minter,
194: uint64 _mintValueAMG,
195: uint256 _receivedAmountUBA,
196: uint256 _poolFeeUBA
197: )
198: private
199: {
200: uint64 poolFeeAMG = Conversion.convertUBAToAmg(_poolFeeUBA);
201: Agents.createNewMinting(_agent, _mintValueAMG + poolFeeAMG); <----@ we add both valueHere we only check the valueAMG not the Fee which will also be added to agent.mintedAMG
/fassets/contracts/assetManager/library/Minting.sol:139
139: function checkMintingCap(
140: uint64 _increaseAMG
141: )
142: internal view
143: {
144: AssetManagerState.State storage state = AssetManagerState.get();
145: AssetManagerSettings.Data storage settings = Globals.getSettings();
146: uint256 mintingCapAMG = settings.mintingCapAMG;
147: if (mintingCapAMG == 0) return; // minting cap disabled
148: uint256 totalMintedUBA = IERC20(settings.fAsset).totalSupply();
149: uint256 totalAMG = state.totalReservedCollateralAMG + Conversion.convertUBAToAmg(totalMintedUBA);
150: require(totalAMG + _increaseAMG <= mintingCapAMG, "minting cap exceeded");
151: }
152:Impact Details
Severity: Low
Impact: Allows total minted amount to exceed the minting cap by the amount of pool fees
Scope: Affects all minting operations in the system either via
reserveCollateral,mintFromFreeUnderlyingandselfMint
References
File:
contracts/assetManager/library/Minting.solFunction:
checkMintingCapFunction:
_performMinting
Fix the minting cap check to include pool fees:
function checkMintingCap(
uint64 _valueAMG,
uint64 _poolFeeAMG
)
internal view
{
AssetManagerState.State storage state = AssetManagerState.get();
AssetManagerSettings.Data storage settings = Globals.getSettings();
uint256 mintingCapAMG = settings.mintingCapAMG;
if (mintingCapAMG == 0) return; // minting cap disabled
uint256 totalMintedUBA = IERC20(settings.fAsset).totalSupply();
uint256 totalAMG = state.totalReservedCollateralAMG + Conversion.convertUBAToAmg(totalMintedUBA);
require(totalAMG + _valueAMG + _poolFeeAMG <= mintingCapAMG, "minting cap exceeded");
}Update all calls to
checkMintingCapto include pool fees
Proof of Concept
Proof of Concept
The
MintingCapAMGis set to 1000.An agent attempts to mint
valueAMGof 1000.The
PoolFeeAMGis calculated as 100 (10% of the minting amount).The code only checks the
valueAMGagainst the minting cap, so the checkrequire(totalAMG + _increaseAMG <= mintingCapAMG, "minting cap exceeded")passes because0 + 1000 <= 1000.After the check, the system increases the agent's
mintedAMGby_valueAMG, where_valueAMG = valueAMG + feeAMG.The final value of
agent.MintedAMGbecomes 1100 (1000 + 100).
This demonstrates that despite the minting cap being set to 1000, the agent successfully minted 1100 AMG, exceeding the intended cap by 100 AMG. For simplicity, decimal and dust calculations have been ignored in this example.
Was this helpful?