The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: low
Valid

Smaller mint and burn amounts cause rounding issues in Smart Vault V3, which can lead to no fees minted to the protocol, griefing the procotol of fee

Summary

In SmartVaultV3.sol contract, a user can mint and burn EUROs. Every mint and burn action accrues a fee to the protocol. For smaller amounts for mint and burn, the fee becomes 0, and the user can hence grief the protocol of any fees.

Vulnerability Details

Below are the mint() and burn() functions in SmartVaultV3:

function mint(address _to, uint256 _amount) external onlyOwner ifNotLiquidated {
@> uint256 fee = _amount * ISmartVaultManagerV3(manager).mintFeeRate() / ISmartVaultManagerV3(manager).HUNDRED_PC();
require(fullyCollateralised(_amount + fee), UNDER_COLL);
minted = minted + _amount + fee;
EUROs.mint(_to, _amount);
EUROs.mint(ISmartVaultManagerV3(manager).protocol(), fee);
emit EUROsMinted(_to, _amount, fee);
}
function burn(uint256 _amount) external ifMinted(_amount) {
@> uint256 fee = _amount * ISmartVaultManagerV3(manager).burnFeeRate() / ISmartVaultManagerV3(manager).HUNDRED_PC();
minted = minted - _amount;
EUROs.burn(msg.sender, _amount);
IERC20(address(EUROs)).safeTransferFrom(msg.sender, ISmartVaultManagerV3(manager).protocol(), fee);
emit EUROsBurned(_amount, fee);
}

The fee is calculated as:

(amount * feeRate) / HUNDRED_PERCENT

Now, if (amount * feeRate) is less than HUNDRED_PERCENT, then the above expression evaluates to 0 because of rounding in Solidity, which then makes fee as 0.

Proof of Concept

The below test can be used in the test file test/smartVault.js. The test assumes that the mint and burn fee rates are PROTOCOL_FEE_RATE for proof of concept.

describe('Proof of Concept', async () => {
it('Minting and Burning without fee', async () => {
const mintAndBurnValue = BigNumber.from(Math.ceil(HUNDRED_PC / PROTOCOL_FEE_RATE) - 1);
expect(mintAndBurnValue).to.equal(199);
const collateral = ethers.utils.parseEther('1');
await user.sendTransaction({ to: Vault.address, value: collateral });
await Vault.connect(user).mint(user.address, mintAndBurnValue);
expect((await Vault.status()).minted).to.equal(mintAndBurnValue);
expect((await EUROs.balanceOf(protocol.address))).to.equal(0);
await Vault.connect(user).burn(mintAndBurnValue);
expect((await Vault.status()).minted).to.equal(0);
expect((await EUROs.balanceOf(protocol.address))).to.equal(0);
});
});

Tools Used

Manual Review

Recommendations

There are different ways to solve this problem, depending on what protocol thinks is fair:

  • Enforce a minimum amount to be minted and burnt, so that the fee is not zero

  • Introduce a base fee so that the fee is never zero.

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

fee-loss

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

mint-precision

Support

FAQs

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