The Standard

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

User can't withdraw max collateral if have `minted euro`

Summary

User can't withdraw max collateral from vault if have any minted euros because wrong calculations in canRemoveCollateral function

Vulnerability Details

A user should withdraw all the collateral, except enough collateral require to back the minted euros, but with current implementation its not possible because canRemoveCollateral function is using maxMintable in calculation instead of euroCollateral

function canRemoveCollateral(ITokenManager.Token memory _token, uint256 _amount) private view returns (bool) {
if (minted == 0) return true;
@> uint256 currentMintable = maxMintable();
uint256 eurValueToRemove = calculator.tokenToEurAvg(_token, _amount);
return currentMintable >= eurValueToRemove &&
minted <= currentMintable - eurValueToRemove;
}

How this will work:-

  1. User deposited 6000 euro worth of collateral in the vault

  2. maxMintable at 120% collateral rate will be 5000 euro ie:- ((6000 * 100000)/120000) = 5000

function maxMintable() private view returns (uint256) {
return euroCollateral() * ISmartVaultManagerV3(manager).HUNDRED_PC() / ISmartVaultManagerV3(manager).collateralRate();
}
  1. User minted 2000 euros

  2. Total collateral required to back 2000 minted euros is 2400 euro worth collateral ie:- ( mintedEuros * collateralRate)/ HUNDRED_PC, (2000 * 120000)/100000

  3. Available collateral to withdraw is 6000 - 2400 = 3600 euro worth collateral, but with current implementation user can only withdraw 3000 worth of collateral, which is 600 euro less worth of collateral

  4. This 600 can go up depending upon the size of collateral and minted euros

Here is the POC( run this test in smartVault.js test file )

it("user can not withdraw max collateral", async () => {
// depositing tether as collateral
const Tether = await (
await ethers.getContractFactory("ERC20Mock")
).deploy("Tether", "USDT", 6);
const USDTBytes = ethers.utils.formatBytes32String("USDT");
const clUsdUsdPrice = 100000000; //$1
const ClUsdUsd = await (
await ethers.getContractFactory("ChainlinkMock")
).deploy("USD / USD");
await ClUsdUsd.setPrice(clUsdUsdPrice);
await TokenManager.addAcceptedToken(Tether.address, ClUsdUsd.address);
// 6360 USDT will worth 6000 euro at 1.06 conversion rate
const value = 6360000000;
await Tether.mint(Vault.address, value);
// minting 1990 euro which will make minted = 2000 euro after adding mintFee
const mintvalue = ethers.utils.parseEther("1990");
await Vault.connect(user).mint(user.address, mintvalue);
let { minted, maxMintable, totalCollateralValue } = await Vault.status();
console.log(totalCollateralValue);
console.log(maxMintable);
console.log(minted);
// 3816 USDT will worth 3600 euro at 1.06 conversion rate
// 3180 USDT will worth 3000 euro, anything below this will be removed as collateral
const removeValue = 3816000000;
let removeCollateral = Vault.connect(user).removeCollateral(
USDTBytes,
removeValue,
user.address
);
await expect(removeCollateral).to.be.revertedWith("err-under-coll");
});

Impact

No loss of funds but will grief user

Tools Used

Manual Review

Recommendations

Use euroCollateral instead of maxMintable in calculation

function canRemoveCollateral(ITokenManager.Token memory _token, uint256 _amount) private view returns (bool) {
if (minted == 0) return true;
- uint256 currentMintable = maxMintable();
uint256 eurValueToRemove = calculator.tokenToEurAvg(_token, _amount);
- return currentMintable >= eurValueToRemove &&
- minted <= currentMintable - eurValueToRemove;
}
function canRemoveCollateral(ITokenManager.Token memory _token, uint256 _amount) private view returns (bool) {
if (minted == 0) return true;
+ uint256 currentEuroCollateral = euroCollateral();
uint256 eurValueToRemove = calculator.tokenToEurAvg(_token, _amount);
+ uint256 requiredCollateralValue =
+ minted * ISmartVaultManagerV3(manager).collateralRate() / ISmartVaultManagerV3(manager).HUNDRED_PC();
+ return currentEuroCollateral >= eurValueToRemove
+ && requiredCollateralValue <= currentEuroCollateral - eurValueToRemove;
}
Updates

Lead Judging Commences

hrishibhat Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

canRemoveCollateral

Support

FAQs

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