Mismatch between calculation styles of minted
variable inside mint() and inside burn() results in the following issues. These occur because fee
is not taken into account in a consistent manner while calculating minted
-
minted
is not reset to zero even when the user burns the amount he minted. He has to pay more fee in case he wants to reset this to zero.
If he does not pay more fee during burn()
, results in a reduced ability for the user to mint()
now. He is not able to mint the max amount.
If he does not pay more fee during burn()
, he is not able to remove all of his collateral since canRemoveCollateral() does not allow him to do so by returning false
.
Let us see an example:
Few initial suppositions first for easier calculation (you can keep the default values too if you wish to, just that the arrived figures will be a bit different):
Let ETH_USD_PRICE = $1
Let EUR_USD_PRICE = $1
Let COLLATERAL_RATE = 120%
Let mint & burn FEE_RATE = 0.5%
User deposits ETH as collateral.
This means he can receive EUROs in his account upon minting. This is because . Hence, minted
value as per L163 is . With a collateralRate
of , this is his max limit since
User goes ahead and calls mint(1e18) to receive 1 EUROs in his account. Also, since fee
is added while calculating it.
User wants to burn all his minted EUROs. He calls burn(1e18)
. He is charged the burnFee of on L173. However, L171 incorrectly reduces minted
to , not resetting it to zero since fee
has been excluded from this calculation.
User is now constrained unfairly and faces the following issues -
He can not now mint again the value (or including fee) and his minting capacity is reduced to
He can't mint because fullyCollateralized() which is called internally by mint() returns false
since
His reduced value is because and any value greater than calculates to higher than , the maxMintable()
.
He can not now remove his full collateral since canRemoveCollateral() returns false
for _amount
equalling
If he wants to regain his full powers, he needs to agree to pass an _amount
figure of to the burn() function by calling burn(1.005e18)
which will reset minted
to , but he will have to pay more fee due to this -
, thus paying this as an unfair additional fee.
Add the following inside test/smartVault.js
and run via npx hardhat test --grep 'incorrect burning calculation' test/smartVault.js
. The tests will pass all assertions.
User is forced to pay extra fee otherwise his capability to call other functions like removeCollateral()
& mint()
is unfairly limited.
There is also some degree of risk that if the user does not notice the ever increasing value of minted
after each mint() & burn() cycle of exact same amounts, then he may eventually get under-collateralized and be liquidated.
Hardhat
Multiple approaches can be taken to correct this, one of which could be:
Note that the in the above approach, mintFeeRate and burnFeeRate need to be equal -
If mintFeeRate > burnFeeRate
, then the same problem occurs as above despite the fix, albeit at a slower pace.
If mintFeeRate < burnFeeRate
, then the above fix's require
statement will revert and the user won't be able to burn all of his minted amount.
There are other ways to handle the scenario if the protocol wishes to have the liberty of setting mintFeeRate & burnFeeRate as different numbers. You may then exclude fee
from the minted
calculation inside the mint()
function but it has repurcussions across the protocol, such as while liquidating an underwater vault. These will have to be further examined.
Constraining mintFeeRate & burnFeeRate to be equal seems to be the simpler solution.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.