diff --git a/packages/contracts/contracts/CRLens.sol b/packages/contracts/contracts/CRLens.sol index 978e43a2d..a21c4d5e8 100644 --- a/packages/contracts/contracts/CRLens.sol +++ b/packages/contracts/contracts/CRLens.sol @@ -75,6 +75,16 @@ contract CRLens { return icr; } + function getRealStake(bytes32 cdpId) external returns (uint256) { + cdpManager.syncAccounting(cdpId); + uint256 collShares = cdpManager.getCdpCollShares(cdpId); + return + cdpManager.totalCollateralSnapshot() == 0 + ? collShares + : (collShares * cdpManager.totalStakesSnapshot()) / + cdpManager.totalCollateralSnapshot(); + } + /// @dev Returns 1 if we're in RM function getCheckRecoveryMode(bool revertValue) external returns (uint256) { // Synch State diff --git a/packages/contracts/contracts/CdpManager.sol b/packages/contracts/contracts/CdpManager.sol index ad1bfb766..8d80964a7 100644 --- a/packages/contracts/contracts/CdpManager.sol +++ b/packages/contracts/contracts/CdpManager.sol @@ -192,6 +192,20 @@ contract CdpManager is CdpManagerStorage, ICdpManager, Proxy { collateral.getPooledEthByShares(newColl) < MIN_NET_STETH_BALANCE || newDebt < MIN_CHANGE ) { + _updateStakeAndTotalStakes(_redeemColFromCdp.cdpId); + + emit CdpUpdated( + _redeemColFromCdp.cdpId, + ISortedCdps(sortedCdps).getOwnerAddress(_redeemColFromCdp.cdpId), + msg.sender, + _oldDebtAndColl.debt, + _oldDebtAndColl.collShares, + _oldDebtAndColl.debt, + _oldDebtAndColl.collShares, + Cdps[_redeemColFromCdp.cdpId].stake, + CdpOperation.failedPartialRedemption + ); + singleRedemption.cancelledPartial = true; return singleRedemption; } diff --git a/packages/contracts/contracts/Interfaces/ICdpManagerData.sol b/packages/contracts/contracts/Interfaces/ICdpManagerData.sol index 978477c67..38dde3e79 100644 --- a/packages/contracts/contracts/Interfaces/ICdpManagerData.sol +++ b/packages/contracts/contracts/Interfaces/ICdpManagerData.sol @@ -81,7 +81,8 @@ interface ICdpManagerData is IRecoveryModeGracePeriod { liquidateInNormalMode, liquidateInRecoveryMode, redeemCollateral, - partiallyLiquidate + partiallyLiquidate, + failedPartialRedemption } enum Status { @@ -265,4 +266,8 @@ interface ICdpManagerData is IRecoveryModeGracePeriod { ) external view returns (uint256 debt, uint256 collShares); function canLiquidateRecoveryMode(uint256 icr, uint256 tcr) external view returns (bool); + + function totalCollateralSnapshot() external view returns (uint256); + + function totalStakesSnapshot() external view returns (uint256); } diff --git a/packages/contracts/contracts/TestContracts/invariants/BeforeAfter.sol b/packages/contracts/contracts/TestContracts/invariants/BeforeAfter.sol index 631e2d14a..a1202005e 100644 --- a/packages/contracts/contracts/TestContracts/invariants/BeforeAfter.sol +++ b/packages/contracts/contracts/TestContracts/invariants/BeforeAfter.sol @@ -36,6 +36,8 @@ abstract contract BeforeAfter is BaseStorageVariables { uint256 cdpCollAfter; uint256 cdpDebtBefore; uint256 cdpDebtAfter; + uint256 cdpStakeBefore; + uint256 cdpStakeAfter; uint256 liquidatorRewardSharesBefore; uint256 liquidatorRewardSharesAfter; uint256 sortedCdpsSizeBefore; @@ -73,6 +75,12 @@ abstract contract BeforeAfter is BaseStorageVariables { uint256 cumulativeCdpsAtTimeOfRebase; uint256 prevStEthFeeIndex; uint256 afterStEthFeeIndex; + uint256 totalStakesBefore; + uint256 totalStakesAfter; + uint256 totalStakesSnapshotBefore; + uint256 totalStakesSnapshotAfter; + uint256 totalCollateralSnapshotBefore; + uint256 totalCollateralSnapshotAfter; } Vars vars; @@ -87,14 +95,15 @@ abstract contract BeforeAfter is BaseStorageVariables { address ownerToCheck = sortedCdps.getOwnerAddress(_cdpId); vars.userSurplusBefore = collSurplusPool.getSurplusCollShares(ownerToCheck); - (uint256 debtBefore, ) = cdpManager.getSyncedDebtAndCollShares(_cdpId); + (uint256 debtBefore, uint256 collBefore) = cdpManager.getSyncedDebtAndCollShares(_cdpId); vars.nicrBefore = _cdpId != bytes32(0) ? crLens.quoteRealNICR(_cdpId) : 0; vars.icrBefore = _cdpId != bytes32(0) ? cdpManager.getCachedICR(_cdpId, vars.priceBefore) : 0; - vars.cdpCollBefore = _cdpId != bytes32(0) ? cdpManager.getCdpCollShares(_cdpId) : 0; + vars.cdpCollBefore = _cdpId != bytes32(0) ? collBefore : 0; vars.cdpDebtBefore = _cdpId != bytes32(0) ? debtBefore : 0; + vars.cdpStakeBefore = _cdpId != bytes32(0) ? crLens.getRealStake(_cdpId) : 0; vars.liquidatorRewardSharesBefore = _cdpId != bytes32(0) ? cdpManager.getCdpLiquidatorRewardShares(_cdpId) : 0; @@ -142,6 +151,10 @@ abstract contract BeforeAfter is BaseStorageVariables { 1e18 - vars.activePoolDebtBefore; vars.prevStEthFeeIndex = cdpManager.systemStEthFeePerUnitIndex(); + + vars.totalStakesBefore = cdpManager.totalStakes(); + vars.totalStakesSnapshotBefore = cdpManager.totalStakesSnapshot(); + vars.totalCollateralSnapshotBefore = cdpManager.totalCollateralSnapshot(); } function _after(bytes32 _cdpId) internal { @@ -150,10 +163,13 @@ abstract contract BeforeAfter is BaseStorageVariables { vars.priceAfter = priceFeedMock.fetchPrice(); + (, uint256 collAfter) = cdpManager.getSyncedDebtAndCollShares(_cdpId); + vars.nicrAfter = _cdpId != bytes32(0) ? crLens.quoteRealNICR(_cdpId) : 0; vars.icrAfter = _cdpId != bytes32(0) ? cdpManager.getCachedICR(_cdpId, vars.priceAfter) : 0; - vars.cdpCollAfter = _cdpId != bytes32(0) ? cdpManager.getCdpCollShares(_cdpId) : 0; + vars.cdpCollAfter = _cdpId != bytes32(0) ? collAfter : 0; vars.cdpDebtAfter = _cdpId != bytes32(0) ? cdpManager.getCdpDebt(_cdpId) : 0; + vars.cdpStakeAfter = _cdpId != bytes32(0) ? crLens.getRealStake(_cdpId) : 0; vars.liquidatorRewardSharesAfter = _cdpId != bytes32(0) ? cdpManager.getCdpLiquidatorRewardShares(_cdpId) : 0; @@ -208,6 +224,10 @@ abstract contract BeforeAfter is BaseStorageVariables { if (vars.afterStEthFeeIndex > vars.prevStEthFeeIndex) { vars.cumulativeCdpsAtTimeOfRebase += cdpManager.getActiveCdpsCount(); } + + vars.totalStakesAfter = cdpManager.totalStakes(); + vars.totalStakesSnapshotAfter = cdpManager.totalStakesSnapshot(); + vars.totalCollateralSnapshotAfter = cdpManager.totalCollateralSnapshot(); } function _diff() internal view returns (string memory log) { diff --git a/packages/contracts/contracts/TestContracts/invariants/Properties.sol b/packages/contracts/contracts/TestContracts/invariants/Properties.sol index ef70400dd..b5e0d75d5 100644 --- a/packages/contracts/contracts/TestContracts/invariants/Properties.sol +++ b/packages/contracts/contracts/TestContracts/invariants/Properties.sol @@ -117,6 +117,35 @@ abstract contract Properties is BeforeAfter, PropertiesDescriptions, Asserts, Pr isApproximateEq(vars.valueInSystemAfter, vars.valueInSystemBefore, 0.01e18); } + function invariant_CDPM_10(CdpManager cdpManager) internal view returns (bool) { + if (vars.afterStEthFeeIndex > vars.prevStEthFeeIndex) { + return cdpManager.totalStakesSnapshot() == cdpManager.totalStakes(); + } + return true; + } + + function invariant_CDPM_11(CdpManager cdpManager) internal view returns (bool) { + if (vars.afterStEthFeeIndex > vars.prevStEthFeeIndex) { + return cdpManager.totalCollateralSnapshot() == cdpManager.getSystemCollShares(); + } + return true; + } + + function invariant_CDPM_12( + SortedCdps sortedCdps, + Vars memory vars + ) internal view returns (bool) { + bytes32 currentCdp = sortedCdps.getFirst(); + + uint256 sumStakes; + while (currentCdp != bytes32(0)) { + sumStakes += cdpManager.getCdpStake(currentCdp); + currentCdp = sortedCdps.getNext(currentCdp); + } + + return sumStakes == vars.totalStakesAfter; + } + function invariant_CSP_01( ICollateralToken collateral, CollSurplusPool collSurplusPool diff --git a/packages/contracts/contracts/TestContracts/invariants/PropertiesDescriptions.sol b/packages/contracts/contracts/TestContracts/invariants/PropertiesDescriptions.sol index d3bb4e1a0..f53564af6 100644 --- a/packages/contracts/contracts/TestContracts/invariants/PropertiesDescriptions.sol +++ b/packages/contracts/contracts/TestContracts/invariants/PropertiesDescriptions.sol @@ -28,6 +28,16 @@ abstract contract PropertiesDescriptions { string constant CDPM_04 = "CDPM-04: The total system value does not decrease during redemptions"; string constant CDPM_05 = "CDPM-05: Redemptions do not increase the total system debt"; string constant CDPM_06 = "CDPM-06: Redemptions do not increase the total system debt"; + string constant CDPM_07 = "CDPM-07: Stake decreases when collShares decreases for a CDP"; + string constant CDPM_08 = "CDPM-08: Stake increases when collShares increases for a CDP"; + string constant CDPM_09 = + "CDPM-09: expectedStake = coll * totalStakesSnapshot / totalCollateralSnapshot after every operation involving a CDP"; + string constant CDPM_10 = + "CDPM-10: totalStakesSnapshot matches totalStakes after an operation, if rebase index changed during the OP"; + string constant CDPM_11 = + "CDPM-11: totalCollateralSnapshot matches activePool.systemCollShares after an operation, if rebase index changed during the OP"; + string constant CDPM_12 = + "CDPM-12: Sum of all individual CDP stakes should equal to totalStakes"; /////////////////////////////////////////////////////// // Collateral Surplus Pool diff --git a/packages/contracts/contracts/TestContracts/invariants/TargetFunctions.sol b/packages/contracts/contracts/TestContracts/invariants/TargetFunctions.sol index 827daec45..b3136a000 100644 --- a/packages/contracts/contracts/TestContracts/invariants/TargetFunctions.sol +++ b/packages/contracts/contracts/TestContracts/invariants/TargetFunctions.sol @@ -287,6 +287,8 @@ abstract contract TargetFunctions is Properties { lt(cdpManager.lastEBTCDebtErrorRedistribution(), cdpManager.totalStakes(), L_17); totalCdpDustMaxCap += cdpManager.getActiveCdpsCount(); } + + _checkStakeInvariants(); } else if (vars.sortedCdpsSizeBefore > _i) { assertRevertReasonNotEqual(returnData, "Panic(17)"); } @@ -379,6 +381,27 @@ abstract contract TargetFunctions is Properties { } } + function _checkStakeInvariants() private { + if (vars.cdpCollAfter < vars.cdpCollBefore) { + lt(vars.cdpStakeAfter, vars.cdpStakeBefore, CDPM_07); + } + + if (vars.cdpCollAfter > vars.cdpCollBefore) { + gt(vars.cdpStakeAfter, vars.cdpStakeBefore, CDPM_08); + } + + if (vars.totalCollateralSnapshotAfter > 0) { + eq( + vars.cdpStakeAfter, + (vars.cdpCollAfter * vars.totalStakesSnapshotAfter) / + vars.totalCollateralSnapshotAfter, + CDPM_09 + ); + } else { + eq(vars.cdpStakeAfter, vars.cdpCollAfter, CDPM_09); + } + } + /** Active Pool TWAP Revert Checks */ function observe() public { // We verify that any observation will never revert @@ -473,6 +496,8 @@ abstract contract TargetFunctions is Properties { lt(cdpManager.lastEBTCDebtErrorRedistribution(), cdpManager.totalStakes(), L_17); totalCdpDustMaxCap += cdpManager.getActiveCdpsCount(); } + + _checkStakeInvariants(); } else if (vars.sortedCdpsSizeBefore > _n) { if (_atLeastOneCdpIsLiquidatable(cdpsBefore, vars.isRecoveryModeBefore)) { assertRevertReasonNotEqual(returnData, "Panic(17)"); @@ -492,6 +517,7 @@ abstract contract TargetFunctions is Properties { _partialRedemptionHintNICR, false, false, + false, _maxFeePercentage, _maxIterations ); @@ -503,6 +529,7 @@ abstract contract TargetFunctions is Properties { uint256 _partialRedemptionHintNICRFromMedusa, bool useProperFirstHint, bool useProperPartialHint, + bool failPartialRedemption, uint _maxFeePercentage, uint _maxIterations ) public setup { @@ -512,6 +539,7 @@ abstract contract TargetFunctions is Properties { _partialRedemptionHintNICRFromMedusa, useProperFirstHint, useProperPartialHint, + failPartialRedemption, _maxFeePercentage, _maxIterations ); @@ -523,9 +551,12 @@ abstract contract TargetFunctions is Properties { uint256 _partialRedemptionHintNICRFromMedusa, bool useProperFirstHint, bool useProperPartialHint, + bool failPartialRedemption, uint _maxFeePercentage, uint _maxIterations ) internal { + require(cdpManager.getActiveCdpsCount() > 1, "Cannot redeem last CDP"); + _EBTCAmount = between(_EBTCAmount, 0, eBTCToken.balanceOf(address(actor))); _maxIterations = between(_maxIterations, 0, 10); @@ -568,7 +599,7 @@ abstract contract TargetFunctions is Properties { _firstRedemptionHintFromMedusa, bytes32(0), bytes32(0), - _partialRedemptionHintNICRFromMedusa, + failPartialRedemption ? 0 : _partialRedemptionHintNICRFromMedusa, _maxIterations, _maxFeePercentage ) @@ -614,6 +645,8 @@ abstract contract TargetFunctions is Properties { if (vars.isRecoveryModeBefore && !vars.isRecoveryModeAfter) { t(!vars.lastGracePeriodStartTimestampIsSetAfter, L_16); } + + _checkStakeInvariants(); } /////////////////////////////////////////////////////// @@ -671,6 +704,8 @@ abstract contract TargetFunctions is Properties { if (vars.isRecoveryModeBefore && !vars.isRecoveryModeAfter) { t(!vars.lastGracePeriodStartTimestampIsSetAfter, L_16); } + + _checkStakeInvariants(); } /////////////////////////////////////////////////////// @@ -725,6 +760,8 @@ abstract contract TargetFunctions is Properties { if (vars.isRecoveryModeBefore && !vars.isRecoveryModeAfter) { t(!vars.lastGracePeriodStartTimestampIsSetAfter, L_16); } + + _checkStakeInvariants(); } function openCdp(uint256 _col, uint256 _EBTCAmount) public setup returns (bytes32 _cdpId) { @@ -808,6 +845,8 @@ abstract contract TargetFunctions is Properties { gte(_EBTCAmount, borrowerOperations.MIN_CHANGE(), GENERAL_16); gte(vars.cdpDebtAfter, borrowerOperations.MIN_CHANGE(), GENERAL_15); require(invariant_BO_09(cdpManager, priceFeedMock.getPrice(), _cdpId), BO_09); + + _checkStakeInvariants(); } else { assertRevertReasonNotEqual(returnData, "Panic(17)"); /// Done } @@ -908,6 +947,8 @@ abstract contract TargetFunctions is Properties { } gte(_coll, borrowerOperations.MIN_CHANGE(), GENERAL_16); + + _checkStakeInvariants(); } else { assertRevertReasonNotEqual(returnData, "Panic(17)"); } @@ -978,6 +1019,8 @@ abstract contract TargetFunctions is Properties { } gte(_amount, borrowerOperations.MIN_CHANGE(), GENERAL_16); + + _checkStakeInvariants(); } else { assertRevertReasonNotEqual(returnData, "Panic(17)"); } @@ -1050,6 +1093,8 @@ abstract contract TargetFunctions is Properties { gte(_amount, borrowerOperations.MIN_CHANGE(), GENERAL_16); gte(vars.cdpDebtAfter, borrowerOperations.MIN_CHANGE(), GENERAL_15); + + _checkStakeInvariants(); } else { assertRevertReasonNotEqual(returnData, "Panic(17)"); } @@ -1122,6 +1167,8 @@ abstract contract TargetFunctions is Properties { gte(_amount, borrowerOperations.MIN_CHANGE(), GENERAL_16); gte(vars.cdpDebtAfter, borrowerOperations.MIN_CHANGE(), GENERAL_15); + + _checkStakeInvariants(); } else { assertRevertReasonNotEqual(returnData, "Panic(17)"); } @@ -1198,6 +1245,8 @@ abstract contract TargetFunctions is Properties { if (vars.isRecoveryModeBefore && !vars.isRecoveryModeAfter) { t(!vars.lastGracePeriodStartTimestampIsSetAfter, L_16); } + + _checkStakeInvariants(); } else { assertRevertReasonNotEqual(returnData, "Panic(17)"); } @@ -1285,6 +1334,8 @@ abstract contract TargetFunctions is Properties { } } gte(vars.cdpDebtAfter, borrowerOperations.MIN_CHANGE(), GENERAL_15); + + _checkStakeInvariants(); } /////////////////////////////////////////////////////// diff --git a/packages/contracts/contracts/TestContracts/invariants/echidna/EchidnaProperties.sol b/packages/contracts/contracts/TestContracts/invariants/echidna/EchidnaProperties.sol index a8b0de4c1..8b930bb39 100644 --- a/packages/contracts/contracts/TestContracts/invariants/echidna/EchidnaProperties.sol +++ b/packages/contracts/contracts/TestContracts/invariants/echidna/EchidnaProperties.sol @@ -40,6 +40,18 @@ abstract contract EchidnaProperties is TargetContractSetup, Properties { return invariant_CDPM_03(cdpManager); } + function echidna_cdp_manager_invariant_10() public returns (bool) { + return invariant_CDPM_10(cdpManager); + } + + function echidna_cdp_manager_invariant_11() public returns (bool) { + return invariant_CDPM_11(cdpManager); + } + + function echidna_cdp_manager_invariant_12() public returns (bool) { + return invariant_CDPM_12(sortedCdps, vars); + } + // CDPM_04 is a vars invariant function echidna_coll_surplus_pool_invariant_1() public returns (bool) { diff --git a/packages/contracts/foundry.toml b/packages/contracts/foundry.toml index 8554675e2..ff558e3b1 100644 --- a/packages/contracts/foundry.toml +++ b/packages/contracts/foundry.toml @@ -6,6 +6,7 @@ test = 'foundry_test' cache_path = 'forge-cache' gas_reports = ["*"] no_match_contract = 'Echidna' +solc_version= '0.8.17' evm_version = 'london' diff --git a/packages/contracts/foundry_test/CDPManager.redemptions.t.sol b/packages/contracts/foundry_test/CDPManager.redemptions.t.sol index 66060bad0..7abe5c9f3 100644 --- a/packages/contracts/foundry_test/CDPManager.redemptions.t.sol +++ b/packages/contracts/foundry_test/CDPManager.redemptions.t.sol @@ -642,4 +642,98 @@ contract CDPManagerRedemptionsTest is eBTCBaseInvariants { cdpManager.redeemCollateral(debt, userCdpId, bytes32(0), bytes32(0), 0, 0, 1e18); vm.stopPrank(); } + + function testStakeUpdateForPartialRedemption() public { + address wallet = _utils.getNextUserAddress(); + uint256 _yearlyInc = 36000000000000000; + uint256 _dailyInc = _yearlyInc / 365; + + // fetch the price + uint256 price = priceFeedMock.fetchPrice(); + + // calculate the net coll + liquidator reward + uint256 grossColl = 10e18 + cdpManager.LIQUIDATOR_REWARD(); + + // calculate the debt allowed with collateral ratio of 110% + uint256 debtMCR = _utils.calculateBorrowAmount(10e18, price, MINIMAL_COLLATERAL_RATIO); + + // calculate the debt allowed with collateral ratio of 125% + uint256 debtColl = _utils.calculateBorrowAmount(10e18, price, COLLATERAL_RATIO); + + // open 5 cdps in the system + _openTestCDP(wallet, grossColl, debtColl - 20000); + _openTestCDP(wallet, grossColl, debtColl - 10000); + _openTestCDP(wallet, grossColl, debtColl - 7500); + bytes32 partialCdp = _openTestCDP(wallet, grossColl, debtColl); + _openTestCDP(wallet, grossColl, debtMCR); + + vm.startPrank(wallet); + + // approve ebtc token to cdp manager + eBTCToken.approve(address(cdpManager), eBTCToken.balanceOf(wallet)); + + // increase the eth per share with one day split fee + collateral.setEthPerShare(1e18 + _dailyInc); + + // get the last Cdp in the system + bytes32 last = sortedCdps.getLast(); + + // record the old coll and stake of the partial redeemed cdp + uint256 oldCollBefore = cdpManager.getCdpCollShares(partialCdp); + uint256 oldStakeBefore = cdpManager.getCdpStake(partialCdp); + + // sync the debt twap spot value + _syncSystemDebtTwapToSpotValue(); + + uint256 _syncedCollAfter = cdpManager.getSyncedCdpCollShares(partialCdp); + + // fully redeem the last cdp and try to partial redeem but cancel with wrong NICR + cdpManager.redeemCollateral( + debtMCR + (debtColl / 2), + last, + bytes32(0), + bytes32(0), + 0, + 0, + 1e18 + ); + + vm.stopPrank(); + + // record the new coll and stake after the canceled partial redeeming + uint256 newCollAfter = cdpManager.getCdpCollShares(partialCdp); + uint256 newStakeAfter = cdpManager.getCdpStake(partialCdp); + require( + _syncedCollAfter == newCollAfter, + "!collateral share for cancelled partially-redeemed CDP should be synced" + ); + + // ensure that the last cdp was fully redeemed + assert(!sortedCdps.contains(last)); + + // console log the old and new stats of the canceled cdp + console.log("================Before"); + console.log("CollBefore :", oldCollBefore); + console.log("StakeBefore :", oldStakeBefore); + console.log("================After"); + console.log("CollAfter :", newCollAfter); + console.log("StakeAfter :", newStakeAfter); + + // fetch the stake and coll snapshots + uint256 stakeSnapshot = cdpManager.totalStakesSnapshot(); + uint256 collSnapshot = cdpManager.totalCollateralSnapshot(); + + // calculate the correct stake based on the cdp new coll and latest stake ratio + uint256 correctStake = (newCollAfter * stakeSnapshot) / collSnapshot; + + // console log the correct stake + console.log("====================="); + console.log("CorrectStake :", correctStake); + + // The diference is based on 5 cdps in the system and only one daily increase. + require( + correctStake == newStakeAfter, + "!stake for cancelled partially-redeemed CDP should be updated" + ); + } } diff --git a/packages/contracts/foundry_test/EchidnaToFoundry.t.sol b/packages/contracts/foundry_test/EchidnaToFoundry.t.sol index 1e51c1b00..2df3aff66 100644 --- a/packages/contracts/foundry_test/EchidnaToFoundry.t.sol +++ b/packages/contracts/foundry_test/EchidnaToFoundry.t.sol @@ -1829,26 +1829,6 @@ contract EToFoundry is liquidate(18067694189672298071702989353445355683192970783067545306964966803281163906085); } - function test_echidna_GENERAL_19_f013137f() public { - openCdp( - 11301203448604662049811798001092512619773943788999911527796838786906727231319, - 395072 - ); - redeemCollateral( - 19231123011496444867956171762517437951540782746670644738290741663254238928897, - 0x4316a3d8d6ac4a5b79d46935aa54cd4414c169c2c567c1a321851cb97921639c, - 115792089237316195423570985007720775198317476882341396735424792728422849019608, - false, - true, - 45293226562680564985708122691552976547680258329418575378480455888078590502957, - 73848382371233259027908259540810899867775610968899633645073264036267903406851 - ); - repayDebt( - 115792089237316195423570985008687907853269984665640564039456584007913129639937, - 65027340797815045438359672520157886948156592359820798948574367617754750402490 - ); - } - function test_observe_f013137f() public { vm.roll(1011); vm.warp(163975); @@ -1866,6 +1846,7 @@ contract EToFoundry is 115792089237316195423570985008687907853269984665640564039457584007913129639840, false, true, + false, 68776281145185225593163376753987289182251561033872594443954035002017028108451, 30567697059120246049752387472096319267260164692360041538630585861516003558881 ); diff --git a/packages/contracts/remappings.txt b/packages/contracts/remappings.txt index 0c0cd3440..77c39f7a4 100644 --- a/packages/contracts/remappings.txt +++ b/packages/contracts/remappings.txt @@ -1,4 +1,4 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ @crytic/properties/=node_modules/@crytic/properties/ -@openzeppelin/=node_modules/@openzeppelin/ \ No newline at end of file +@openzeppelin/=node_modules/@openzeppelin/