The following code is from a Stablecoin project.
To gain more context, please refer to this Github repo.
I've added a new function to the DSCEngine.sol name collapseDsc() which is responsible for returning the collateral deposited by all users of the protocol, in case of a dramatic collapse of the underlying asset (wEth, wBtc) values.
DSCEngine.sol
function collapseDsc() public {
uint256 totalCollateralValueOfProtocol = _getTotalCollateralValueOfProtocol();
uint256 totalDSCMinted = i_dsc.totalSupply();
if(totalCollateralValueOfProtocol > totalDSCMinted) {
revert DSCEngine__DSCNotCollapsed(totalDSCMinted, totalCollateralValueOfProtocol);
}
emit DSCProtocolCollapsed();
console.log('Users in the protocol: ', s_users.length);
for(uint256 u = 0; u < s_users.length; u++){
address user = s_users[u];
uint256 userDscMinted = s_DSCMinted[user];
if(userDscMinted > 0) {
_burnDSC(userDscMinted, user);
}
for(uint256 t = 0 ; t < s_collateralTokens.length; t++) {
address collateralToken = s_collateralTokens[t];
uint256 collateralTokenAmount = s_collateralDeposited[user][collateralToken];
if(collateralTokenAmount > 0 ) {
_redeemCollateral(collateralToken, collateralTokenAmount, user, user);
}
}
}
}
I have the following Fuzz tests:
Minting DSC: This will mint the DSC and approve it to the DSCEngine at the same time.
Handler.t.sol
function mintDsc(uint256 mintAmount, uint256 userWithCollateralSeed) public {
if(usersWithDepositedCollateral.length == 0){
return;
}
// Arrange
address sender = _getRandomUserWithCollateral(userWithCollateralSeed);
// mintAmount should not be 0 and should not be more than (half the collateral value deposited - dscMinted)
(uint256 dscMinted, uint256 collateralDepositedValue) = dscEngine
.getAccountInformation(sender);
int256 maxMintAmount = (int256(collateralDepositedValue) / 2) - int256(dscMinted);
if (maxMintAmount < 0) {
// this can happen if the collateral value drops after a token crash
return;
}
mintAmount = bound(mintAmount, 0, uint256(maxMintAmount));
if (mintAmount == 0) {
// can be 0 if no collateral is deposited
return;
}
vm.startPrank(sender);
dscEngine.mintDSC(mintAmount);
dsc.approve(address(dscEngine), mintAmount); // dsc approval given to dscEngine by the user, incase burning of these tokens required.
vm.stopPrank();
mintDscCalls++;
}
The second is the test which crashes the value of the underlying asset and triggers the collapseDsc()
function.
Handler.t.sol
// TODO This breaks our system and should be taken care of.
function updateEthUsdPriceFeed(uint96 newEthUsdPrice) public {
// Arrange
MockV3Aggregator ethUsdPriceFeed = MockV3Aggregator(dscEngine.getCollateralTokenPriceFeed(wEth));
int256 newPrice = int256(uint256(newEthUsdPrice));
ethUsdPriceFeed.updateAnswer(newPrice);
uint256 totalDscMinted = dsc.totalSupply();
uint256 wEthDeposited = ERC20Mock(wEth).balanceOf(address(dscEngine));
uint256 wBtcDeposited = ERC20Mock(wBtc).balanceOf(address(dscEngine));
uint256 totalCollateralValue = dscEngine.getUsdValue(wEth, wEthDeposited) + dscEngine.getUsdValue(wBtc, wBtcDeposited);
// Act
if(totalCollateralValue < totalDscMinted){
dscEngine.collapseDsc();
}
}
Now on running this test, I get a revert. Here are the error logs:
[234902] Handler::updateEthUsdPriceFeed(4463)
├─ [2613] DSCEngine::getCollateralTokenPriceFeed(ERC20Mock: [0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76]) [staticcall]
│ └─ ← MockV3Aggregator: [0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496]
├─ [79404] MockV3Aggregator::updateAnswer(4463)
│ └─ ← ()
├─ [2349] DecentralizedStableCoin::totalSupply() [staticcall]
│ └─ ← 36867680678869884643073056819756867246485946828 [3.686e46]
├─ [2585] ERC20Mock::balanceOf(DSCEngine: [0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512]) [staticcall]
│ └─ ← 147181787746094153296977941449 [1.471e29]
├─ [2585] ERC20Mock::balanceOf(DSCEngine: [0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512]) [staticcall]
│ └─ ← 118501289238336304 [1.185e17]
├─ [18666] DSCEngine::getUsdValue(ERC20Mock: [0x50EEf481cae4250d252Ae577A09bF514f224C6C4], 118501289238336304 [1.185e17]) [staticcall]
│ ├─ [12650] OracleLib::2ba1c96a(000000000000000000000000bb2180ebd78ce97360503434ed37fcf4a1df61c3) [delegatecall]
│ │ ├─ [8993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 1, 100000000000, 1, 1, 1
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001
│ └─ ← 118501289238336304000 [1.185e20]
├─ [3666] DSCEngine::getUsdValue(ERC20Mock: [0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76], 147181787746094153296977941449 [1.471e29]) [staticcall]
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000a8452ec99ce0c64f20701db7dd3abdb607c00496) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 6, 4463, 1, 1, 6
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000116f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006
│ └─ ← 6568723187108182061644125 [6.568e24]
├─ [92888] DSCEngine::collapseDsc()
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000a8452ec99ce0c64f20701db7dd3abdb607c00496) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 6, 4463, 1, 1, 6
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000116f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000bb2180ebd78ce97360503434ed37fcf4a1df61c3) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 1, 100000000000, 1, 1, 1
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000a8452ec99ce0c64f20701db7dd3abdb607c00496) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 6, 4463, 1, 1, 6
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000116f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000bb2180ebd78ce97360503434ed37fcf4a1df61c3) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 1, 100000000000, 1, 1, 1
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000a8452ec99ce0c64f20701db7dd3abdb607c00496) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 6, 4463, 1, 1, 6
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000116f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000bb2180ebd78ce97360503434ed37fcf4a1df61c3) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 1, 100000000000, 1, 1, 1
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000a8452ec99ce0c64f20701db7dd3abdb607c00496) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 6, 4463, 1, 1, 6
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000116f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000bb2180ebd78ce97360503434ed37fcf4a1df61c3) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 1, 100000000000, 1, 1, 1
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000a8452ec99ce0c64f20701db7dd3abdb607c00496) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 6, 4463, 1, 1, 6
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000116f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006
│ ├─ [2150] OracleLib::2ba1c96a(000000000000000000000000bb2180ebd78ce97360503434ed37fcf4a1df61c3) [delegatecall]
│ │ ├─ [993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 1, 100000000000, 1, 1, 1
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001
│ ├─ [349] DecentralizedStableCoin::totalSupply() [staticcall]
│ │ └─ ← 36867680678869884643073056819756867246485946828 [3.686e46]
│ ├─ emit DSCProtocolCollapsed()
│ ├─ [0] console::9710a9d0(000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000017557365727320696e207468652070726f746f636f6c3a20000000000000000000) [staticcall]
│ │ └─ ← ()
│ ├─ [3016] DecentralizedStableCoin::transferFrom(0x8499B29da102AD9F9C76c0380A190408a49e2326, DSCEngine: [0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512], 59250644619166520499 [5.925e19])
│ │ └─ ← "ERC20: insufficient allowance"
│ └─ ← "ERC20: insufficient allowance"
└─ ← "ERC20: insufficient allowance"
[75427] InvariantsTest::invariant_protocolMustAlwaysHaveMoreCollateralValueThanMintedDSCValue()
├─ [2349] DecentralizedStableCoin::totalSupply() [staticcall]
│ └─ ← 36867680678869884643073056819756867246485946828 [3.686e46]
├─ [2585] ERC20Mock::balanceOf(DSCEngine: [0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512]) [staticcall]
│ └─ ← 147181787746094153296977941449 [1.471e29]
├─ [2585] ERC20Mock::balanceOf(DSCEngine: [0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512]) [staticcall]
│ └─ ← 118501289238336304 [1.185e17]
├─ [18666] DSCEngine::getUsdValue(ERC20Mock: [0x50EEf481cae4250d252Ae577A09bF514f224C6C4], 118501289238336304 [1.185e17]) [staticcall]
│ ├─ [12650] OracleLib::2ba1c96a(000000000000000000000000bb2180ebd78ce97360503434ed37fcf4a1df61c3) [delegatecall]
│ │ ├─ [8993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 1, 100000000000, 1, 1, 1
│ │ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001
│ └─ ← 118501289238336304000 [1.185e20]
├─ [16166] DSCEngine::getUsdValue(ERC20Mock: [0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76], 147181787746094153296977941449 [1.471e29]) [staticcall]
│ ├─ [12650] OracleLib::2ba1c96a(000000000000000000000000a8452ec99ce0c64f20701db7dd3abdb607c00496) [delegatecall]
│ │ ├─ [8993] MockV3Aggregator::latestRoundData() [staticcall]
│ │ │ └─ ← 5, 93067110251944013583873118, 1, 1, 5
│ │ └─ ← 0x00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000004cfbba27f6840d86cbf45e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000005
│ └─ ← 136977836672439669664473841260116313382165854880 [1.369e47]
├─ [0] console::9710a9d0(000000000000000000000000000000000000000000000000000000000000004000000000000000000000000006753437ea323983eb5b8b193a9445be01d841cc0000000000000000000000000000000000000000000000000000000000000012546f74616c2044534320537570706c793a200000000000000000000000000000) [staticcall]
│ └─ ← ()
├─ [0] console::9710a9d0(000000000000000000000000000000000000000000000000000000000000004000000000000000000000000017fe4c72fda38f95a91c31d6fd5a12f4a5a9ae200000000000000000000000000000000000000000000000000000000000000018546f74616c20436f6c6c61746572616c2076616c75653a200000000000000000) [staticcall]
│ └─ ← ()
├─ [2308] Handler::mintDscCalls() [staticcall]
│ └─ ← 3
├─ [0] console::9710a9d0(00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000c4d696e742063616c6c733a200000000000000000000000000000000000000000) [staticcall]
│ └─ ← ()
└─ ← ()
I don't understand why I'm getting a ERC20: insufficient allowance
error when the user is allowing the DSCEngine the DSCs they mint, right after the minting in the mintDsc(..)
test.
Source code: https://github.com/alfheimrShiven/defi-stablecoin
Github discussion thread: https://github.com/Cyfrin/foundry-full-course-f23/discussions/871#discussioncomment-7012541
Any help would be appreciated. Thanks!