The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

User cannot burn entire minted amount

Summary

User deposits 1 ETH collateral so they can mint some EUROs. They mint 100 EUROs. If they try to repay and burn the 100 EUROs, the transaction fails because it will first burn the entire 100e18 EUROs balance, and then attempt to send the 0.5e18 fee, but there is no balance left to send, so SmartVaultV3::burn() reverts anytime _amount = minted. Burning the full EUROs amount should be a valid and quick way to repay all debt, but it reverts.

PoC

Put in smartVault.js inside burning test. Run with npx hardhat test --grep "cannot burn mintedValue"

describe('burning', async () => {
it('cannot burn mintedValue', async () => {
// send 1 eth collateral to vault
const collateral = ethers.utils.parseEther('1');
await user.sendTransaction({to: Vault.address, value: collateral});
// Amount to burn = 50 EUROs
const burnedValue = ethers.utils.parseEther('50');
let burn = Vault.connect(user).burn(burnedValue);
// Amount to mint = 100 EUROs (1 ETH collateral is plenty).
const mintedValue = ethers.utils.parseEther('100');
await Vault.connect(user).mint(user.address, mintedValue);
// Fee = 0.5e18 (100e18 * (500 / 100000)
const mintingFee = mintedValue.mul(PROTOCOL_FEE_RATE).div(HUNDRED_PC);
await EUROs.connect(user).approve(Vault.address, mintingFee); // must approve
// Attempting to burn 100 out of their 100 EUROs minted, it fails because after burning 100e18
// it still needs to send the 0.5e18 fee to the protocol, but no longer has any balance left.
// So the transaction reverts.
burn = Vault.connect(user).burn(mintedValue);
await expect(burn).to.be.reverted;
// Attempting to burn 99 out of their 100 EUROs minted works fine because fee is only 0.5e18.
burn = Vault.connect(user).burn(ethers.utils.parseEther('99'));
await expect(burn).not.to.be.reverted;
});
});

Recommendations

Remove the ifMinted modifier and replace with a require statement for the _amount + fee passed in to be less than or equal to minted.

- modifier ifMinted(uint256 _amount) {
- require(minted >= _amount, "err-insuff-minted");
- _;
- }
- function burn(uint256 _amount) external ifMinted(_amount) {
+ function burn(uint256 _amount) external {
uint256 fee = _amount * ISmartVaultManagerV3(manager).burnFeeRate() / ISmartVaultManagerV3(manager).HUNDRED_PC();
+ require(_amount + fee <= minted, "err-too-much-burn");
minted = minted - _amount;
EUROs.burn(msg.sender, _amount);
IERC20(address(EUROs)).safeTransferFrom(msg.sender, ISmartVaultManagerV3(manager).protocol(), fee);
emit EUROsBurned(_amount, fee);
}
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
Invalidated
Reason: Design choice
Assigned finding tags:

fee-loss

Support

FAQs

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