15,000 USDC
View results
Submission Details
Severity: medium
Valid

Tokens can be burned using burnFrom

The function burn in DecentralizedStableCoin.sol has an onlyOwner modifier, so only DSCEngine can use this function. But there is also the function burnFrom in ERC20Burnable that allows burning tokens. Normally, users can only burn their DSCTokens by executing the burnDSC function in the DSCEngine, and in this case, they can only burn as many tokens as were minted:

File: src/DSCEngine.sol
272: function _burnDsc(uint256 amountDscToBurn, address onBehalfOf, address dscFrom) private {
273: s_DSCMinted[onBehalfOf] -= amountDscToBurn; //@audit: When attempting to burn more tokens than the user has minted, there is a revert due to underflow.
274: bool success = i_dsc.transferFrom(dscFrom, address(this), amountDscToBurn);
275: // This conditional is hypothtically unreachable
276: if (!success) {
277: revert DSCEngine__TransferFailed();
278: }
279: i_dsc.burn(amountDscToBurn);
280: }

With burnFrom, users can now burn DSC Tokens that were sent to them and that they did not mint themselves.

Here's a test that can be attached to DSCEngineTest.t.sol, demonstrating that a user can burn more tokens than they have minted.

function testBurnFrom() public {
address user2 = makeAddr("user2");
ERC20Mock(weth).mint(user2, STARTING_USER_BALANCE);
vm.startPrank(user2);
ERC20Mock(weth).approve(address(dsce), amountCollateral);
dsce.depositCollateralAndMintDsc(weth, amountCollateral, amountToMint);
dsc.approve(user, amountToMint);
dsc.transfer(user, amountToMint); //Second user sends DSC tokens to the first user (so that they can be burned by the first user later to prove that he can burn more than he have minted themself).
assertEq(dsc.balanceOf(user2), 0); //Shows that user2 has no more tokens
assertEq(dsc.balanceOf(user), amountToMint); //Shows that the first user now has the tokens
vm.stopPrank();
vm.startPrank(user);
ERC20Mock(weth).approve(address(dsce), amountCollateral);
dsce.depositCollateralAndMintDsc(weth, amountCollateral, amountToMint);
dsc.approve(user, 2*amountToMint);
dsc.burnFrom(user, 2*amountToMint); //Burn the self-minted DSC tokens and those sent by user2 using burnFrom
assertEq(amountToMint, dsce.getDscMinted(user)); //Shows that in the DSC Engine, it is still assumed that the minted tokens exist
assertEq(dsc.balanceOf(user), 0); //Shows that the user actually has no more tokens
vm.stopPrank();
}

The function getDscMinted() needs to be added to DSCEngine, as there is currently no way to access the private variable s_DSCMinted:

function getDscMinted(address user) public view returns(uint256) {
return s_DSCMinted[user];
}

Recommendation

Add the burnFrom function with an onlyOwner modifier in DecentralizedStableCoin.sol so that not everyone can burn more tokens than they have minted themselves:

function burnFrom(address account, uint256 amount) public onlyOwner override {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}

Support

FAQs

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