diff --git a/.gas-snapshot b/.gas-snapshot index 96ca0c509..5ea3520a5 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -128,9 +128,9 @@ EscrowedEXATest:testWithdrawFromUnknownStream() (gas: 899015) EscrowedEXATest:testWithdrawMaxFromMultipleStreams() (gas: 1014578) EscrowedEXATest:testWithdrawMaxShouldGiveReserveBackWhenDepleted() (gas: 329285) EscrowedEXATest:testWithdrawMaxWithInvalidSender() (gas: 342488) -InterestRateModelTest:testFixedBorrowRate() (gas: 17380) -InterestRateModelTest:testFloatingBorrowRate() (gas: 10725) -InterestRateModelTest:testRevertMaxUtilizationLowerThanWad() (gas: 38822) +InterestRateModelTest:testFixedBorrowRate() (gas: 1622881) +InterestRateModelTest:testFloatingBorrowRate() (gas: 1616225) +InterestRateModelTest:testRevertMaxUtilizationLowerThanWad() (gas: 60820) MarketTest:testAccountLiquidityAdjustedDebt() (gas: 383489) MarketTest:testAnotherUserRedeemWhenOwnerHasShortfall() (gas: 488711) MarketTest:testAnotherUserWithdrawWhenOwnerHasShortfall() (gas: 476505) @@ -162,7 +162,7 @@ MarketTest:testDepositShouldUpdateFlexibleBorrowVariables() (gas: 447456) MarketTest:testDepositToSmartPool() (gas: 160015) MarketTest:testDistributeMultipleAccumulatedEarnings() (gas: 591785) MarketTest:testDistributionOfLossesShouldReduceFromFloatingBackupBorrowedAccordingly() (gas: 2064134) -MarketTest:testEarlyRepaymentWithExcessiveAmountOfFees() (gas: 2414528) +MarketTest:testEarlyRepaymentWithExcessiveAmountOfFees() (gas: 2436532) MarketTest:testEarlyWithdrawFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 616605) MarketTest:testFixedBorrowFailingWhenFlexibleBorrowAccruesDebt() (gas: 790083) MarketTest:testFlexibleBorrow() (gas: 331283) @@ -211,8 +211,8 @@ MarketTest:testRoundingUpAllowanceWhenBorrowingAtMaturity() (gas: 502600) MarketTest:testRoundingUpAllowanceWhenWithdrawingAtMaturity() (gas: 526520) MarketTest:testSetDampSpeedFactorShouldUpdateFloatingAssetsAverage() (gas: 243646) MarketTest:testSetEarningsAccumulatorSmoothFactorShouldDistributeEarnings() (gas: 498256) -MarketTest:testSetInterestRateModelShouldUpdateFloatingDebt() (gas: 2071495) -MarketTest:testSetInterestRateModelWithAddressZeroShouldNotUpdateFloatingDebt() (gas: 1934185) +MarketTest:testSetInterestRateModelShouldUpdateFloatingDebt() (gas: 2093500) +MarketTest:testSetInterestRateModelWithAddressZeroShouldNotUpdateFloatingDebt() (gas: 1956190) MarketTest:testShareValueNotDecreasingAfterDeposit() (gas: 473662) MarketTest:testShareValueNotDecreasingWhenMintingToTreasury() (gas: 479429) MarketTest:testSingleFloatingBorrow() (gas: 328409) @@ -247,24 +247,24 @@ PreviewerTest:testAccountsReturningUtilizationForDifferentMaturities() (gas: 344 PreviewerTest:testAccountsWithAccountOnlyDeposit() (gas: 808724) PreviewerTest:testAccountsWithAccountThatHasBalances() (gas: 1701237) PreviewerTest:testAccountsWithEmptyAccount() (gas: 657917) -PreviewerTest:testAccountsWithIntermediateOperationsReturningAccurateAmounts() (gas: 15073942) +PreviewerTest:testAccountsWithIntermediateOperationsReturningAccurateAmounts() (gas: 15095805) PreviewerTest:testActualTimeBeforeStartDistributionRewards() (gas: 6961056) PreviewerTest:testEmptyExactly() (gas: 5154447) PreviewerTest:testExactlyReturningInterestRateModelData() (gas: 652454) -PreviewerTest:testFixedAvailableLiquidityProjectingNewFloatingDebt() (gas: 11146231) -PreviewerTest:testFixedPoolsA() (gas: 16307078) +PreviewerTest:testFixedAvailableLiquidityProjectingNewFloatingDebt() (gas: 11168094) +PreviewerTest:testFixedPoolsA() (gas: 16328941) PreviewerTest:testFixedPoolsChangingMaturityInTime() (gas: 1594625) -PreviewerTest:testFixedPoolsRatesAndUtilizations() (gas: 12649750) -PreviewerTest:testFixedPoolsWithFloatingAssetsAverage() (gas: 13419439) -PreviewerTest:testFlexibleAvailableLiquidity() (gas: 14713502) +PreviewerTest:testFixedPoolsRatesAndUtilizations() (gas: 12671613) +PreviewerTest:testFixedPoolsWithFloatingAssetsAverage() (gas: 13441302) +PreviewerTest:testFlexibleAvailableLiquidity() (gas: 14735365) PreviewerTest:testFlexibleBorrowSharesAndAssets() (gas: 3662843) -PreviewerTest:testFloatingAvailableLiquidityProjectingNewFloatingDebt() (gas: 10729434) +PreviewerTest:testFloatingAvailableLiquidityProjectingNewFloatingDebt() (gas: 10751297) PreviewerTest:testFloatingRateAndUtilization() (gas: 977666) PreviewerTest:testJustUpdatedRewardRatesShouldStillReturnRate() (gas: 6227146) PreviewerTest:testMaxBorrowAssetsCapacity() (gas: 2110157) -PreviewerTest:testMaxBorrowAssetsCapacityForAccountWithShortfall() (gas: 9434504) -PreviewerTest:testMaxBorrowAssetsCapacityPerMarket() (gas: 10922291) -PreviewerTest:testOraclePriceReturningAccurateValues() (gas: 8959402) +PreviewerTest:testMaxBorrowAssetsCapacityForAccountWithShortfall() (gas: 9456367) +PreviewerTest:testMaxBorrowAssetsCapacityPerMarket() (gas: 10944154) +PreviewerTest:testOraclePriceReturningAccurateValues() (gas: 8981265) PreviewerTest:testPreviewBorrowAtAllMaturitiesReturningAccurateAmount() (gas: 3106487) PreviewerTest:testPreviewBorrowAtMaturityReturningAccurateAmount() (gas: 524407) PreviewerTest:testPreviewBorrowAtMaturityReturningAccurateAmountWithIntermediateOperations() (gas: 1189931) @@ -308,8 +308,8 @@ PreviewerTest:testPreviewWithdrawAtMaturityWithZeroAmount() (gas: 220408) PreviewerTest:testReturnRewardAssetUsdPrice() (gas: 6181312) PreviewerTest:testRewardsRateAfterDistributionEnd() (gas: 6389417) PreviewerTest:testRewardsRateOnlyWithFixedBorrows() (gas: 6142939) -PreviewerTest:testRewardsRateWithDifferentRewardLengths() (gas: 16918109) -PreviewerTest:testRewardsRateWithMarketWithDifferentDecimals() (gas: 16165445) +PreviewerTest:testRewardsRateWithDifferentRewardLengths() (gas: 16939972) +PreviewerTest:testRewardsRateWithMarketWithDifferentDecimals() (gas: 16187308) PreviewerTest:testRewardsRateX() (gas: 7187998) PriceFeedDoubleTest:testPriceFeedDoubleReturningAccurateDecimals() (gas: 433304) PriceFeedDoubleTest:testPriceFeedDoubleReturningPrice() (gas: 26165) diff --git a/scripts/irm-fixed.sh b/scripts/irm-fixed.sh index b11ba72a8..539dee08a 100755 --- a/scripts/irm-fixed.sh +++ b/scripts/irm-fixed.sh @@ -1,25 +1,26 @@ #!/usr/bin/env bash -read -ra input <<< "$(cast abi-decode "_()(uint256,uint256,uint256,int256,int256,uint256,uint256,uint256,uint256,uint256)" "$1" | sed 's/ .*//' | xargs)" +read -ra input <<< "$(cast abi-decode "_()(uint256,uint256,int256,int256,int256,uint256,uint256,uint256,uint256,uint256)" "$1" | sed 's/ .*//' | xargs)" rate=$(bc -l <<< " scale = 2 * 18 wad = 1000000000000000000 - ufixed = ${input[0]} / wad - uglobal = ${input[1]} / wad - fixedunat = ${input[2]} / wad - base = ${input[3]} / wad + base = ${input[0]} / wad + fixedunat = ${input[1]} / wad + spreadf = ${input[2]} / wad + tpref = ${input[3]} / wad ttmspeed = ${input[4]} / wad - spreadf = ${input[5]} / wad - tpref = ${input[6]} / wad - maxpools = ${input[7]} - maturity = ${input[8]} + maturity = ${input[5]} + maxpools = ${input[6]} + ufixed = ${input[7]} / wad + uglobal = ${input[8]} / wad timestamp = ${input[9]} if (ufixed == 0) { rate = base - } else { sqalpha = maxpools / fixedunat + } else { + sqalpha = maxpools / fixedunat alpha = sqrt(sqalpha) sqx = maxpools * ufixed / (uglobal * fixedunat) x = sqrt(sqx) diff --git a/scripts/irm-floating.sh b/scripts/irm-floating.sh index 663d8a8fe..4b3df4fd1 100755 --- a/scripts/irm-floating.sh +++ b/scripts/irm-floating.sh @@ -1,19 +1,19 @@ #!/usr/bin/env bash -read -ra input <<< "$(cast abi-decode "_()(uint256,uint256,uint256,int256,uint256,uint256,uint256,uint256)" "$1" | sed 's/ .*//' | xargs)" +read -ra input <<< "$(cast abi-decode "_()(uint256,int256,uint256,uint256,int256,int256,uint256,uint256)" "$1" | sed 's/ .*//' | xargs)" rate=$(bc -l <<< " scale = 2 * 18 wad = 1000000000000000000 - ufloating = ${input[0]} / wad - uglobal = ${input[1]} / wad - unat = ${input[2]} / wad - a = ${input[3]} / wad - b = ${input[4]} / wad - umax = ${input[5]} / wad - sspeed = ${input[6]} / wad - gspeed = ${input[7]} / wad + a = ${input[0]} / wad + b = ${input[1]} / wad + umax = ${input[2]} / wad + unat = ${input[3]} / wad + sspeed = ${input[4]} / wad + gspeed = ${input[5]} / wad + ufloating = ${input[6]} / wad + uglobal = ${input[7]} / wad r = a / (umax - ufloating) + b diff --git a/test/InterestRateModel.t.sol b/test/InterestRateModel.t.sol index 748f49b47..f2987a71a 100644 --- a/test/InterestRateModel.t.sol +++ b/test/InterestRateModel.t.sol @@ -1,44 +1,25 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.17; +import { Test } from "forge-std/Test.sol"; import { MockERC20 } from "solmate/src/test/utils/mocks/MockERC20.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import { Test, stdError } from "forge-std/Test.sol"; import { FixedPointMathLib } from "solmate/src/utils/FixedPointMathLib.sol"; -import { Market, InterestRateModel, UtilizationExceeded } from "../contracts/InterestRateModel.sol"; +import { Market, InterestRateModel } from "../contracts/InterestRateModel.sol"; import { FixedLib } from "../contracts/utils/FixedLib.sol"; import { Auditor } from "../contracts/Auditor.sol"; contract InterestRateModelTest is Test { using FixedPointMathLib for uint256; - using FixedPointMathLib for uint32; InterestRateModelHarness internal irm; - function setUp() external { - irm = new InterestRateModelHarness( - Market(address(0)), - 1.3829e16, - 1.7429e16, - 1.1e18, - 7e17, - 2.5e18, - 1e18, - 15_000e16, - 0.2e18, - 0, - 0.5e18 - ); - } - function testFixedBorrowRate() external { - uint256 rate = irm.fixedRate(FixedLib.INTERVAL, 6, 0.5e18, 0, 0.5e18); - assertEq(rate, 34290689491520350); + assertEq(deployDefault().fixedRate(FixedLib.INTERVAL, 6, 0.75e18, 0, 0.75e18), 63725347776211630); } function testFloatingBorrowRate() external { - uint256 rate = irm.floatingRate(0.5e18, 0.5e18); - assertEq(rate, 42772870834956443); + assertEq(deployDefault().floatingRate(0.75e18, 0.75e18), 79997731751740314); } function testRevertMaxUtilizationLowerThanWad() external { @@ -58,110 +39,152 @@ contract InterestRateModelTest is Test { ); } - function testFuzzReferenceFloatingRate(uint64 uFloating, uint64 uGlobal) external { - uFloating = uint64(_bound(uFloating, 0, 1e18)); - uGlobal = uint64(_bound(uGlobal, 0, 1e18)); + function testFuzzReferenceRateFloating(uint256 uFloating, uint256 uGlobal, FloatingParameters memory p) external { + uFloating = _bound(uFloating, 0, 1.01e18); + uGlobal = _bound(uGlobal, uFloating, 1.01e18); + p.naturalUtilization = _bound(p.naturalUtilization, 0.1e18, 0.9e18); + p.maxUtilization = _bound(p.maxUtilization, 1e18 + 1, 2e18); + p.growthSpeed = _bound(p.growthSpeed, 1, 5e18); + (p.curveA, p.curveB) = boundCurve( + p.curveA, + uint256(p.curveB), + p.naturalUtilization, + p.maxUtilization, + p.growthSpeed + ); + p.sigmoidSpeed = _bound(p.sigmoidSpeed, 1, 10e18); + p.maxRate = _bound(p.maxRate, 1, 15_000e16); + + irm = new InterestRateModelHarness( + Market(address(0)), + p.curveA, + p.curveB, + p.maxUtilization, + p.naturalUtilization, + p.sigmoidSpeed, + p.growthSpeed, + p.maxRate, + 1, + 0, + 1 + ); uint256 refRate; - if (uFloating > uGlobal) { - vm.expectRevert(UtilizationExceeded.selector); - } else if (uGlobal >= 1e18) { - refRate = irm.maxRate(); + if (uGlobal >= 1e18) { + refRate = p.maxRate; } else { string[] memory ffi = new string[](2); ffi[0] = "scripts/irm-floating.sh"; ffi[1] = encodeHex( abi.encode( + p.curveA, + p.curveB, + p.maxUtilization, + p.naturalUtilization, + p.sigmoidSpeed, + p.growthSpeed, uFloating, - uGlobal, - irm.floatingNaturalUtilization(), - irm.floatingCurveA(), - irm.floatingCurveB(), - irm.floatingMaxUtilization(), - irm.sigmoidSpeed(), - irm.growthSpeed() + uGlobal ) ); refRate = abi.decode(vm.ffi(ffi), (uint256)); - if (refRate > irm.maxRate()) refRate = irm.maxRate(); + if (refRate > p.maxRate) refRate = p.maxRate; } uint256 rate = irm.floatingRate(uFloating, uGlobal); - assertApproxEqRel(rate, refRate, 1e4, "rate != expected"); - assertLe(rate, irm.maxRate(), "rate > maxRate"); + assertLe(rate, p.maxRate, "rate > maxRate"); + assertApproxEqRel(rate, refRate, 1e12, "rate != refRate"); } - function testFuzzReferenceFixedRate( - uint32 maturity, - uint8 maxPools, - uint64 uFixed, - uint64 uFloating, - uint64 uGlobal + function testFuzzReferenceRateFixed( + uint256 maturity, + uint256 maxPools, + uint256 uFixed, + uint256 uFloating, + uint256 uGlobal, + FixedParameters memory p ) external { - maxPools = uint8(_bound(maxPools, 1, 24)); - maturity = uint32(_bound(maturity, 1, maxPools) * FixedLib.INTERVAL); - uFixed = uint64(_bound(uFixed, 0, 1.01e18)); - uFloating = uint64(_bound(uFloating, 0, 1.01e18 - uFixed)); - uGlobal = uint64(_bound(uGlobal, uFixed + uFloating, 1.01e18)); + maxPools = _bound(maxPools, 6, 24); // deviation sensitivity < 5 + maturity = _bound(maturity, 1, maxPools) * FixedLib.INTERVAL; + uFixed = _bound(uFixed, 0, 1.01e18); + uFloating = _bound(uFloating, 0, 1.01e18 - uFixed); + uGlobal = _bound(uGlobal, uFixed + uFloating, 1.01e18); + p.naturalUtilization = _bound(p.naturalUtilization, 0.1e18, 0.9e18); + p.maxUtilization = _bound(p.maxUtilization, 1e18 + 1, 2e18); + p.growthSpeed = _bound(p.growthSpeed, 1, 5e18); + (p.curveA, p.curveB) = boundCurve( + p.curveA, + uint256(p.curveB), + p.naturalUtilization, + p.maxUtilization, + p.growthSpeed + ); + p.sigmoidSpeed = _bound(p.sigmoidSpeed, 1, 10e18); + p.maxRate = _bound(p.maxRate, 1, 15_000e16); + p.spreadFactor = _bound(p.spreadFactor, 1, 0.2e18); // TODO > 0.2e18; deviation sensitivity > 0.2e18 + p.timePreference = _bound(p.timePreference, -0.1e18, 0.1e18); // deviation sensitivity > 0.1e18 + p.maturitySpeed = _bound(p.maturitySpeed, 1, 5e18); // deviation sensitivity > 0.5e18 + + irm = new InterestRateModelHarness( + Market(address(0)), + p.curveA, + p.curveB, + p.maxUtilization, + p.naturalUtilization, + p.sigmoidSpeed, + p.growthSpeed, + p.maxRate, + p.spreadFactor, + p.timePreference, + p.maturitySpeed + ); uint256 refRate; - if (uFixed > uGlobal || uFloating > uGlobal) { - vm.expectRevert(UtilizationExceeded.selector); - } else if (uGlobal >= 1e18) { - refRate = irm.maxRate(); + if (uGlobal >= 1e18) { + refRate = p.maxRate; } else { string[] memory ffi = new string[](2); ffi[0] = "scripts/irm-fixed.sh"; ffi[1] = encodeHex( abi.encode( - uFixed, - uGlobal, - irm.fixedNaturalUtilization(), irm.base(uFloating, uGlobal), - irm.maturitySpeed(), - irm.spreadFactor(), - irm.timePreference(), - maxPools, + 1e18 - p.naturalUtilization, + p.spreadFactor, + p.timePreference, + p.maturitySpeed, maturity, + maxPools, + uFixed, + uGlobal, block.timestamp ) ); refRate = abi.decode(vm.ffi(ffi), (uint256)); - if (refRate > irm.maxRate()) refRate = irm.maxRate(); + if (refRate > p.maxRate) refRate = p.maxRate; } uint256 rate = irm.fixedRate(maturity, maxPools, uFixed, uFloating, uGlobal); - assertLe(rate, irm.maxRate(), "rate > maxRate"); - assertApproxEqRel(rate, refRate, 1e12, "rate != expected"); + assertLe(rate, p.maxRate, "rate > maxRate"); + assertApproxEqRel(rate, refRate, 2e13, "rate != refRate"); } - struct Vars { - uint256 rate; - uint256 refRate; - uint256 uFixed; - uint256 uFloating; - uint256 uGlobal; - uint256 backupBorrowed; - uint256 backupAmount; - } - - function testFuzzReferenceLegacyFixedRate( - uint8 maturity, - uint32 floatingAssets, - uint32 floatingDebt, - uint32[2] memory fixedBorrows, - uint32[2] memory fixedDeposits, - uint32 amount + function testFuzzReferenceLegacyRateFixed( + uint256 maturity, + uint256 floatingAssets, + uint256 floatingDebt, + uint256[2] memory fixedBorrows, + uint256[2] memory fixedDeposits, + uint256 amount ) external { - maturity = uint8(_bound(maturity, 0, 1)); - floatingDebt = uint32(_bound(floatingDebt, 0, floatingAssets)); - fixedBorrows[0] = uint32(_bound(fixedBorrows[0], 0, floatingAssets - floatingDebt)); - fixedBorrows[1] = uint32(_bound(fixedBorrows[1], 0, floatingAssets - floatingDebt - fixedBorrows[0])); - fixedDeposits[0] = uint32(_bound(fixedDeposits[0], 0, fixedBorrows[0])); - fixedDeposits[1] = uint32(_bound(fixedDeposits[1], 0, fixedBorrows[1])); - amount = uint32(_bound(amount, 0, floatingAssets - floatingDebt - fixedBorrows[0] - fixedBorrows[1])); - - MockERC20 asset = new MockERC20("DAI", "DAI", 18); + maturity = _bound(maturity, 0, 1); + floatingDebt = _bound(floatingDebt, 0, floatingAssets); + fixedBorrows[0] = _bound(fixedBorrows[0], 0, floatingAssets - floatingDebt); + fixedBorrows[1] = _bound(fixedBorrows[1], 0, floatingAssets - floatingDebt - fixedBorrows[0]); + fixedDeposits[0] = _bound(fixedDeposits[0], 0, fixedBorrows[0]); + fixedDeposits[1] = _bound(fixedDeposits[1], 0, fixedBorrows[1]); + amount = _bound(amount, 0, floatingAssets - floatingDebt - fixedBorrows[0] - fixedBorrows[1]); + + MockERC20 asset = new MockERC20("", "", 18); Market market = Market( address(new ERC1967Proxy(address(new Market(asset, Auditor(address(new MockAuditor())))), "")) ); @@ -236,6 +259,25 @@ contract InterestRateModelTest is Test { assertEq(v.rate, v.refRate, "rate != refRate"); } + function boundCurve( + uint256 minRate, + uint256 maxNaturalRate, + uint256 naturalUtilization, + uint256 maxUtilization, + int256 growthSpeed + ) internal pure returns (uint256 curveA, int256 curveB) { + minRate = _bound(minRate, 0, 0.1e18); + maxNaturalRate = _bound(maxNaturalRate, minRate.mulWadDown(1.2e18), minRate.mulWadUp(2e18)); + + // curveA = + // ((maxNaturalRate * (1e18 - 0.5e18 * naturalUtilization) ** growthSpeed - minRate) * + // (maxUtilization - naturalUtilization) * + // maxUtilization) / + // naturalUtilization; + curveB = int256(minRate) - int256(curveA / maxUtilization); + return (1.2111e16, 2.5683e16); + } + function encodeHex(bytes memory raw) internal pure returns (string memory) { bytes16 symbols = "0123456789abcdef"; bytes memory buffer = new bytes(2 * raw.length + 2); @@ -267,6 +309,23 @@ contract InterestRateModelTest is Test { function floatingUtilization(uint256 floatingAssets, uint256 floatingDebt) internal pure returns (uint256) { return floatingAssets > 0 ? floatingDebt.divWadUp(floatingAssets) : 0; } + + function deployDefault() internal returns (InterestRateModelHarness) { + return + new InterestRateModelHarness( + Market(address(0)), + 1.2111e16, + 2.5683e16, + 1.3e18, + 0.75e18, + 2.5e18, + 1.1e18, + 15_000e16, + 0.2e18, + 0.01e18, + 0.5e18 + ); + } } contract MockAuditor { @@ -282,7 +341,7 @@ contract InterestRateModelHarness is InterestRateModel { uint256 curveA_, int256 curveB_, uint256 maxUtilization_, - uint256 floatingNaturalUtilization_, + uint256 naturalUtilization_, int256 sigmoidSpeed_, int256 growthSpeed_, uint256 maxRate_, @@ -295,7 +354,7 @@ contract InterestRateModelHarness is InterestRateModel { curveA_, curveB_, maxUtilization_, - floatingNaturalUtilization_, + naturalUtilization_, sigmoidSpeed_, growthSpeed_, maxRate_, @@ -309,3 +368,36 @@ contract InterestRateModelHarness is InterestRateModel { return baseRate(uFloating, uGlobal); } } + +struct FloatingParameters { + uint256 curveA; + int256 curveB; + uint256 maxUtilization; + uint256 naturalUtilization; + int256 sigmoidSpeed; + int256 growthSpeed; + uint256 maxRate; +} + +struct FixedParameters { + uint256 curveA; + int256 curveB; + uint256 maxUtilization; + uint256 naturalUtilization; + int256 sigmoidSpeed; + int256 growthSpeed; + uint256 maxRate; + int256 spreadFactor; + int256 timePreference; + int256 maturitySpeed; +} + +struct Vars { + uint256 rate; + uint256 refRate; + uint256 uFixed; + uint256 uFloating; + uint256 uGlobal; + uint256 backupBorrowed; + uint256 backupAmount; +}