diff --git a/contracts/src/BoldToken.sol b/contracts/src/BoldToken.sol index 3227cf6f..8467cc65 100644 --- a/contracts/src/BoldToken.sol +++ b/contracts/src/BoldToken.sol @@ -46,6 +46,8 @@ contract BoldToken is CheckContract, IBoldToken { mapping (address => uint256) private _nonces; + uint256 public deploymentStartTime; + // User data for Bold token mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; @@ -88,6 +90,8 @@ contract BoldToken is CheckContract, IBoldToken { _HASHED_VERSION = hashedVersion; _CACHED_CHAIN_ID = _chainID(); _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(_TYPE_HASH, hashedName, hashedVersion); + + deploymentStartTime = block.timestamp; } // --- Functions for intra-Liquity calls --- diff --git a/contracts/src/BorrowerOperations.sol b/contracts/src/BorrowerOperations.sol index 26c49b72..204e45c5 100644 --- a/contracts/src/BorrowerOperations.sol +++ b/contracts/src/BorrowerOperations.sol @@ -168,8 +168,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe vars.netDebt = _boldAmount; if (!isRecoveryMode) { - vars.BoldFee = _triggerBorrowingFee(contractsCache.troveManager, contractsCache.boldToken, _boldAmount, _maxFeePercentage); - vars.netDebt = vars.netDebt + vars.BoldFee; + // TODO: implement interest rate charges } _requireAtLeastMinNetDebt(vars.netDebt); @@ -274,8 +273,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe // If the adjustment incorporates a debt increase and system is in Normal Mode, then trigger a borrowing fee if (_isDebtIncrease && !isRecoveryMode) { - vars.BoldFee = _triggerBorrowingFee(contractsCache.troveManager, contractsCache.boldToken, _boldChange, _maxFeePercentage); - vars.netDebtChange = vars.netDebtChange + vars.BoldFee; // The raw debt change includes the fee + // TODO: implement interest rate charges } vars.debt = contractsCache.troveManager.getTroveDebt(_borrower); @@ -361,19 +359,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe // --- Helper functions --- - function _triggerBorrowingFee(ITroveManager _troveManager, IBoldToken _boldToken, uint _boldAmount, uint _maxFeePercentage) internal returns (uint) { - _troveManager.decayBaseRateFromBorrowing(); // decay the baseRate state variable - uint BoldFee = _troveManager.getBorrowingFee(_boldAmount); - - _requireUserAcceptsFee(BoldFee, _boldAmount, _maxFeePercentage); - - // Send fee to LQTY staking contract - lqtyStaking.increaseF_bold(BoldFee); - _boldToken.mint(lqtyStakingAddress, BoldFee); - - return BoldFee; - } - function _getUSDValue(uint _coll, uint _price) internal pure returns (uint) { uint usdValue = _price * _coll / DECIMAL_PRECISION; diff --git a/contracts/src/Interfaces/IBoldToken.sol b/contracts/src/Interfaces/IBoldToken.sol index 64127c10..08d6116a 100644 --- a/contracts/src/Interfaces/IBoldToken.sol +++ b/contracts/src/Interfaces/IBoldToken.sol @@ -6,6 +6,8 @@ import "../Dependencies/IERC20.sol"; import "../Dependencies/IERC2612.sol"; interface IBoldToken is IERC20, IERC2612 { + function deploymentStartTime() external view returns (uint256); + function mint(address _account, uint256 _amount) external; function burn(address _account, uint256 _amount) external; diff --git a/contracts/src/Interfaces/IStabilityPool.sol b/contracts/src/Interfaces/IStabilityPool.sol index 2f5dc63d..96c6f413 100644 --- a/contracts/src/Interfaces/IStabilityPool.sol +++ b/contracts/src/Interfaces/IStabilityPool.sol @@ -27,18 +27,7 @@ import "./ITroveManager.sol"; * Please see the implementation spec in the proof document, which closely follows on from the compounded deposit / ETH gain derivations: * https://github.com/liquity/liquity/blob/master/papers/Scalable_Reward_Distribution_with_Compounding_Stakes.pdf * - * --- LQTY ISSUANCE TO STABILITY POOL DEPOSITORS --- - * - * An LQTY issuance event occurs at every deposit operation, and every liquidation. - * - * Each deposit is tagged with the address of the front end through which it was made. - * - * All deposits earn a share of the issued LQTY in proportion to the deposit as a share of total deposits. The LQTY earned - * by a given deposit, is split between the depositor and the front end through which the deposit was made, based on the front end's kickbackRate. - * - * Please see the system Readme for an overview: - * https://github.com/liquity/dev/blob/main/README.md#lqty-issuance-to-stability-providers - */ +*/ interface IStabilityPool is ILiquityBase { function borrowerOperations() external view returns (IBorrowerOperations); function boldToken() external view returns (IBoldToken); @@ -54,64 +43,34 @@ interface IStabilityPool is ILiquityBase { address _activePoolAddress, address _boldTokenAddress, address _sortedTrovesAddress, - address _priceFeedAddress, - address _communityIssuanceAddress + address _priceFeedAddress ) external; - - /* - * Initial checks: - * - Frontend is registered or zero address - * - Sender is not a registered frontend - * - _amount is not zero - * --- - * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends - * - Tags the deposit with the provided front end tag param, if it's a new deposit - * - Sends depositor's accumulated gains (LQTY, ETH) to depositor - * - Sends the tagged front end's accumulated LQTY gains to the tagged front end - * - Increases deposit and tagged front end's stake, and takes new snapshots for each. - */ - function provideToSP(uint _amount, address _frontEndTag) external; - - /* - * Initial checks: - * - _amount is zero or there are no under collateralized troves left in the system - * - User has a non zero deposit - * --- - * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends - * - Removes the deposit's front end tag if it is a full withdrawal - * - Sends all depositor's accumulated gains (LQTY, ETH) to depositor - * - Sends the tagged front end's accumulated LQTY gains to the tagged front end - * - Decreases deposit and tagged front end's stake, and takes new snapshots for each. - * - * If _amount > userDeposit, the user withdraws all of their compounded deposit. - */ + + /* provideToSP(): + * - Calculates depositor's ETH gain + * - Calculates the compounded deposit + * - Increases deposit, and takes new snapshots of accumulators P and S + * - Sends depositor's accumulated ETH gains to depositor + */ + function provideToSP(uint _amount) external; + + + /* withdrawFromSP(): + * - Calculates depositor's ETH gain + * - Calculates the compounded deposit + * - Sends the requested BOLD withdrawal to depositor + * - (If _amount > userDeposit, the user withdraws all of their compounded deposit) + * - Decreases deposit by withdrawn amount and takes new snapshots of accumulators P and S + */ function withdrawFromSP(uint _amount) external; - /* - * Initial checks: - * - User has a non zero deposit - * - User has an open trove - * - User has some ETH gain - * --- - * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends - * - Sends all depositor's LQTY gain to depositor - * - Sends all tagged front end's LQTY gain to the tagged front end - * - Transfers the depositor's entire ETH gain from the Stability Pool to the caller's trove - * - Leaves their compounded deposit in the Stability Pool - * - Updates snapshots for deposit and tagged front end stake - */ + /* withdrawETHGainToTrove(): + * - Transfers the depositor's entire ETH gain from the Stability Pool to the caller's trove + * - Leaves their compounded deposit in the Stability Pool + * - Takes new snapshots of accumulators P and S + */ function withdrawETHGainToTrove(address _upperHint, address _lowerHint) external; - /* - * Initial checks: - * - Frontend (sender) not already registered - * - User (sender) has no deposit - * - _kickbackRate is in the range [0, 100%] - * --- - * Front end makes a one-time selection of kickback rate upon registering - */ - function registerFrontEnd(uint _kickbackRate) external; - /* * Initial checks: * - Caller is TroveManager @@ -138,31 +97,11 @@ interface IStabilityPool is ILiquityBase { */ function getDepositorETHGain(address _depositor) external view returns (uint); - /* - * Calculate the LQTY gain earned by a deposit since its last snapshots were taken. - * If not tagged with a front end, the depositor gets a 100% cut of what their deposit earned. - * Otherwise, their cut of the deposit's earnings is equal to the kickbackRate, set by the front end through - * which they made their deposit. - */ - function getDepositorLQTYGain(address _depositor) external view returns (uint); - - /* - * Return the LQTY gain earned by the front end. - */ - function getFrontEndLQTYGain(address _frontEnd) external view returns (uint); - /* * Return the user's compounded deposit. */ function getCompoundedBoldDeposit(address _depositor) external view returns (uint); - /* - * Return the front end's compounded stake. - * - * The front end's compounded stake is equal to the sum of its depositors' compounded deposits. - */ - function getCompoundedFrontEndStake(address _frontEnd) external view returns (uint); - /* * Fallback function * Only callable by Active Pool, it just accounts for ETH received diff --git a/contracts/src/Interfaces/ITroveManager.sol b/contracts/src/Interfaces/ITroveManager.sol index f5ed1f3c..e17dc849 100644 --- a/contracts/src/Interfaces/ITroveManager.sol +++ b/contracts/src/Interfaces/ITroveManager.sol @@ -33,6 +33,8 @@ interface ITroveManager is ILiquityBase { function sortedTroves() external view returns(ISortedTroves); function borrowerOperationsAddress() external view returns (address); + function BOOTSTRAP_PERIOD() external view returns (uint256); + function getTroveOwnersCount() external view returns (uint); function getTroveFromTroveOwnersArray(uint _index) external view returns (address); diff --git a/contracts/src/OldTestContracts/ActivePoolTester.sol b/contracts/src/OldTestContracts/ActivePoolTester.sol deleted file mode 100644 index 75cee9b5..00000000 --- a/contracts/src/OldTestContracts/ActivePoolTester.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../ActivePool.sol"; - -contract ActivePoolTester is ActivePool { - - function unprotectedIncreaseBoldDebt(uint _amount) external { - boldDebt = boldDebt + _amount; - } - - function unprotectedPayable() external payable { - ETH = ETH + msg.value; - } -} diff --git a/contracts/src/OldTestContracts/BoldTokenCaller.sol b/contracts/src/OldTestContracts/BoldTokenCaller.sol deleted file mode 100644 index 613b41a3..00000000 --- a/contracts/src/OldTestContracts/BoldTokenCaller.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../Interfaces/IBoldToken.sol"; - -contract BoldTokenCaller { - IBoldToken Bold; - - function setBold(IBoldToken _bold) external { - Bold = _bold; - } - - function boldMint(address _account, uint _amount) external { - Bold.mint(_account, _amount); - } - - function boldBurn(address _account, uint _amount) external { - Bold.burn(_account, _amount); - } - - function boldSendToPool(address _sender, address _poolAddress, uint256 _amount) external { - Bold.sendToPool(_sender, _poolAddress, _amount); - } - - function boldReturnFromPool(address _poolAddress, address _receiver, uint256 _amount ) external { - Bold.returnFromPool(_poolAddress, _receiver, _amount); - } -} diff --git a/contracts/src/OldTestContracts/BoldTokenTester.sol b/contracts/src/OldTestContracts/BoldTokenTester.sol deleted file mode 100644 index 446299e6..00000000 --- a/contracts/src/OldTestContracts/BoldTokenTester.sol +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../BoldToken.sol"; - -contract BoldTokenTester is BoldToken { - - bytes32 private immutable _PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; - - constructor( - address _troveManagerAddress, - address _stabilityPoolAddress, - address _borrowerOperationsAddress - ) public BoldToken(_troveManagerAddress, - _stabilityPoolAddress, - _borrowerOperationsAddress) {} - - function unprotectedMint(address _account, uint256 _amount) external { - // No check on caller here - - _mint(_account, _amount); - } - - function unprotectedBurn(address _account, uint _amount) external { - // No check on caller here - - _burn(_account, _amount); - } - - function unprotectedSendToPool(address _sender, address _poolAddress, uint256 _amount) external { - // No check on caller here - - _transfer(_sender, _poolAddress, _amount); - } - - function unprotectedReturnFromPool(address _poolAddress, address _receiver, uint256 _amount ) external { - // No check on caller here - - _transfer(_poolAddress, _receiver, _amount); - } - - function callInternalApprove(address owner, address spender, uint256 amount) external returns (bool) { - _approve(owner, spender, amount); - } - - function getChainId() external view returns (uint256 chainID) { - //return _chainID(); // it’s private - assembly { - chainID := chainid() - } - } - - function getDigest(address owner, address spender, uint amount, uint nonce, uint deadline) external view returns (bytes32) { - return keccak256(abi.encodePacked( - uint16(0x1901), - domainSeparator(), - keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, amount, nonce, deadline)) - ) - ); - } - - function recoverAddress(bytes32 digest, uint8 v, bytes32 r, bytes32 s) external pure returns (address) { - return ecrecover(digest, v, r, s); - } -} diff --git a/contracts/src/OldTestContracts/BorrowerOperationsTester.sol b/contracts/src/OldTestContracts/BorrowerOperationsTester.sol deleted file mode 100644 index 142cbdad..00000000 --- a/contracts/src/OldTestContracts/BorrowerOperationsTester.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../BorrowerOperations.sol"; - -/* Tester contract inherits from BorrowerOperations, and provides external functions -for testing the parent's internal functions. */ -contract BorrowerOperationsTester is BorrowerOperations { - - function getNewICRFromTroveChange - ( - uint _coll, - uint _debt, - uint _collChange, - bool isCollIncrease, - uint _debtChange, - bool isDebtIncrease, - uint _price - ) - external - pure - returns (uint) - { - return _getNewICRFromTroveChange(_coll, _debt, _collChange, isCollIncrease, _debtChange, isDebtIncrease, _price); - } - - function getNewTCRFromTroveChange - ( - uint _collChange, - bool isCollIncrease, - uint _debtChange, - bool isDebtIncrease, - uint _price - ) - external - view - returns (uint) - { - return _getNewTCRFromTroveChange(_collChange, isCollIncrease, _debtChange, isDebtIncrease, _price); - } - - function getUSDValue(uint _coll, uint _price) external pure returns (uint) { - return _getUSDValue(_coll, _price); - } - - function callInternalAdjustLoan - ( - address _borrower, - uint _collWithdrawal, - uint _debtChange, - bool _isDebtIncrease, - address _upperHint, - address _lowerHint) - external - { - _adjustTrove(_borrower, _collWithdrawal, _debtChange, _isDebtIncrease, _upperHint, _lowerHint, 0); - } - - - // Payable fallback function - receive() external payable { } -} diff --git a/contracts/src/OldTestContracts/CDPManagerTester.sol b/contracts/src/OldTestContracts/CDPManagerTester.sol deleted file mode 100644 index 02eee294..00000000 --- a/contracts/src/OldTestContracts/CDPManagerTester.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../TroveManager.sol"; - -/* Tester contract inherits from TroveManager, and provides external functions -for testing the parent's internal functions. */ - -contract TroveManagerTester is TroveManager { - - function computeICR(uint _coll, uint _debt, uint _price) external pure returns (uint) { - return LiquityMath._computeCR(_coll, _debt, _price); - } - - function getCollGasCompensation(uint _coll) external pure returns (uint) { - return _getCollGasCompensation(_coll); - } - - function getBoldGasCompensation() external pure returns (uint) { - return BOLD_GAS_COMPENSATION; - } - - function getCompositeDebt(uint _debt) external pure returns (uint) { - return _getCompositeDebt(_debt); - } - - function unprotectedDecayBaseRateFromBorrowing() external returns (uint) { - baseRate = _calcDecayedBaseRate(); - assert(baseRate >= 0 && baseRate <= DECIMAL_PRECISION); - - _updateLastFeeOpTime(); - return baseRate; - } - - function minutesPassedSinceLastFeeOp() external view returns (uint) { - return _minutesPassedSinceLastFeeOp(); - } - - function setLastFeeOpTimeToNow() external { - lastFeeOperationTime = block.timestamp; - } - - function setBaseRate(uint _baseRate) external { - baseRate = _baseRate; - } - - function callGetRedemptionFee(uint _ETHDrawn) external view returns (uint) { - _getRedemptionFee(_ETHDrawn); - } - - function getActualDebtFromComposite(uint _debtVal) external pure returns (uint) { - return _getNetDebt(_debtVal); - } - - function callInternalRemoveTroveOwner(address _troveOwner) external { - uint troveOwnersArrayLength = TroveOwners.length; - _removeTroveOwner(_troveOwner, troveOwnersArrayLength); - } -} diff --git a/contracts/src/OldTestContracts/DefaultPoolTester.sol b/contracts/src/OldTestContracts/DefaultPoolTester.sol deleted file mode 100644 index a81c3f47..00000000 --- a/contracts/src/OldTestContracts/DefaultPoolTester.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../DefaultPool.sol"; - -contract DefaultPoolTester is DefaultPool { - - function unprotectedIncreaseBoldDebt(uint _amount) external { - BoldDebt = BoldDebt + _amount; - } - - function unprotectedPayable() external payable { - ETH = ETH + msg.value; - } -} diff --git a/contracts/src/OldTestContracts/Destructible.sol b/contracts/src/OldTestContracts/Destructible.sol deleted file mode 100644 index 2146a9ff..00000000 --- a/contracts/src/OldTestContracts/Destructible.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -contract Destructible { - - receive() external payable {} - - function destruct(address payable _receiver) external { - selfdestruct(_receiver); - } -} diff --git a/contracts/src/OldTestContracts/FunctionCaller.sol b/contracts/src/OldTestContracts/FunctionCaller.sol deleted file mode 100644 index 04929ae6..00000000 --- a/contracts/src/OldTestContracts/FunctionCaller.sol +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import '../Interfaces/ITroveManager.sol'; -import '../Interfaces/ISortedTroves.sol'; -import '../Interfaces/IPriceFeed.sol'; -import '../Dependencies/LiquityMath.sol'; - -/* Wrapper contract - used for calculating gas of read-only and internal functions. -Not part of the Liquity application. */ -contract FunctionCaller { - - ITroveManager troveManager; - address public troveManagerAddress; - - ISortedTroves sortedTroves; - address public sortedTrovesAddress; - - IPriceFeed priceFeed; - address public priceFeedAddress; - - // --- Dependency setters --- - - function setTroveManagerAddress(address _troveManagerAddress) external { - troveManagerAddress = _troveManagerAddress; - troveManager = ITroveManager(_troveManagerAddress); - } - - function setSortedTrovesAddress(address _sortedTrovesAddress) external { - troveManagerAddress = _sortedTrovesAddress; - sortedTroves = ISortedTroves(_sortedTrovesAddress); - } - - function setPriceFeedAddress(address _priceFeedAddress) external { - priceFeedAddress = _priceFeedAddress; - priceFeed = IPriceFeed(_priceFeedAddress); - } - - // --- Non-view wrapper functions used for calculating gas --- - - function troveManager_getCurrentICR(address _address, uint _price) external returns (uint) { - return troveManager.getCurrentICR(_address, _price); - } - - function sortedTroves_findInsertPosition(uint _NICR, address _prevId, address _nextId) external returns (address, address) { - return sortedTroves.findInsertPosition(_NICR, _prevId, _nextId); - } -} diff --git a/contracts/src/OldTestContracts/LiquityMathTester.sol b/contracts/src/OldTestContracts/LiquityMathTester.sol deleted file mode 100644 index 627bf788..00000000 --- a/contracts/src/OldTestContracts/LiquityMathTester.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../Dependencies/LiquityMath.sol"; - -/* Tester contract for math functions in Math.sol library. */ - -contract LiquityMathTester { - - function callMax(uint _a, uint _b) external pure returns (uint) { - return LiquityMath._max(_a, _b); - } - - // Non-view wrapper for gas test - function callDecPowTx(uint _base, uint _n) external returns (uint) { - return LiquityMath._decPow(_base, _n); - } - - // External wrapper - function callDecPow(uint _base, uint _n) external pure returns (uint) { - return LiquityMath._decPow(_base, _n); - } -} diff --git a/contracts/src/OldTestContracts/MockTellor.sol b/contracts/src/OldTestContracts/MockTellor.sol deleted file mode 100644 index be039b47..00000000 --- a/contracts/src/OldTestContracts/MockTellor.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - - -contract MockTellor { - - // --- Mock price data --- - - bool didRetrieve = true; // default to a positive retrieval - uint private price; - uint private updateTime; - - bool private revertRequest; - - // --- Setters for mock price data --- - - function setPrice(uint _price) external { - price = _price; - } - - function setDidRetrieve(bool _didRetrieve) external { - didRetrieve = _didRetrieve; - } - - function setUpdateTime(uint _updateTime) external { - updateTime = _updateTime; - } - - function setRevertRequest() external { - revertRequest = !revertRequest; - } - - // --- Mock data reporting functions --- - - function getTimestampbyRequestIDandIndex(uint, uint) external view returns (uint) { - return updateTime; - } - - function getNewValueCountbyRequestId(uint) external view returns (uint) { - if (revertRequest) {require (1 == 0, "Tellor request reverted");} - return 1; - } - - function retrieveData(uint256, uint256) external view returns (uint256) { - return price; - } - - - -} diff --git a/contracts/src/OldTestContracts/NonPayable.sol b/contracts/src/OldTestContracts/NonPayable.sol deleted file mode 100644 index 6539427c..00000000 --- a/contracts/src/OldTestContracts/NonPayable.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -//import "../Dependencies/console.sol"; - - -contract NonPayable { - bool isPayable; - - function setPayable(bool _isPayable) external { - isPayable = _isPayable; - } - - function forward(address _dest, bytes calldata _data) external payable { - (bool success, bytes memory returnData) = _dest.call{ value: msg.value }(_data); - //console.logBytes(returnData); - require(success, string(returnData)); - } - - receive() external payable { - require(isPayable); - } -} diff --git a/contracts/src/OldTestContracts/PriceFeedTester.sol b/contracts/src/OldTestContracts/PriceFeedTester.sol deleted file mode 100644 index aec017ef..00000000 --- a/contracts/src/OldTestContracts/PriceFeedTester.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../PriceFeed.sol"; - -contract PriceFeedTester is PriceFeed { - - function setLastGoodPrice(uint _lastGoodPrice) external { - lastGoodPrice = _lastGoodPrice; - } - - function setStatus(Status _status) external { - status = _status; - } -} \ No newline at end of file diff --git a/contracts/src/OldTestContracts/PriceFeedTestnet.sol b/contracts/src/OldTestContracts/PriceFeedTestnet.sol deleted file mode 100644 index 2f220196..00000000 --- a/contracts/src/OldTestContracts/PriceFeedTestnet.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../Interfaces/IPriceFeed.sol"; - -/* -* PriceFeed placeholder for testnet and development. The price is simply set manually and saved in a state -* variable. The contract does not connect to a live Chainlink price feed. -*/ -contract PriceFeedTestnet is IPriceFeed { - event LastGoodPriceUpdated(uint _lastGoodPrice); - - uint256 private _price = 200 * 1e18; - - // --- Functions --- - - // View price getter for simplicity in tests - function getPrice() external view returns (uint256) { - return _price; - } - - function fetchPrice() external override returns (uint256) { - // Fire an event just like the mainnet version would. - // This lets the subgraph rely on events to get the latest price even when developing locally. - emit LastGoodPriceUpdated(_price); - return _price; - } - - // Manual external price setter. - function setPrice(uint256 price) external returns (bool) { - _price = price; - return true; - } -} diff --git a/contracts/src/OldTestContracts/SortedTrovesTester.sol b/contracts/src/OldTestContracts/SortedTrovesTester.sol deleted file mode 100644 index 3a373d16..00000000 --- a/contracts/src/OldTestContracts/SortedTrovesTester.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../Interfaces/ISortedTroves.sol"; - - -contract SortedTrovesTester { - ISortedTroves sortedTroves; - - function setSortedTroves(address _sortedTrovesAddress) external { - sortedTroves = ISortedTroves(_sortedTrovesAddress); - } - - function insert(address _id, uint256 _NICR, address _prevId, address _nextId) external { - sortedTroves.insert(_id, _NICR, _prevId, _nextId); - } - - function remove(address _id) external { - sortedTroves.remove(_id); - } - - function reInsert(address _id, uint256 _newNICR, address _prevId, address _nextId) external { - sortedTroves.reInsert(_id, _newNICR, _prevId, _nextId); - } - - function getNominalICR(address) external pure returns (uint) { - return 1; - } - - function getCurrentICR(address, uint) external pure returns (uint) { - return 1; - } -} diff --git a/contracts/src/OldTestContracts/StabilityPoolTester.sol b/contracts/src/OldTestContracts/StabilityPoolTester.sol deleted file mode 100644 index 57f503ce..00000000 --- a/contracts/src/OldTestContracts/StabilityPoolTester.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.18; - -import "../StabilityPool.sol"; - -contract StabilityPoolTester is StabilityPool { - - function unprotectedPayable() external payable { - ETH = ETH + msg.value; - } - - function setCurrentScale(uint128 _currentScale) external { - currentScale = _currentScale; - } - - function setTotalDeposits(uint _totalBoldDeposits) external { - totalBoldDeposits = _totalBoldDeposits; - } -} diff --git a/contracts/src/StabilityPool.sol b/contracts/src/StabilityPool.sol index aa90762c..550ff4e5 100644 --- a/contracts/src/StabilityPool.sol +++ b/contracts/src/StabilityPool.sol @@ -8,7 +8,6 @@ import './Interfaces/IBorrowerOperations.sol'; import './Interfaces/ITroveManager.sol'; import './Interfaces/IBoldToken.sol'; import './Interfaces/ISortedTroves.sol'; -import "./Interfaces/ICommunityIssuance.sol"; import "./Dependencies/LiquityBase.sol"; import "./Dependencies/Ownable.sol"; import "./Dependencies/CheckContract.sol"; @@ -126,21 +125,6 @@ import "./Dependencies/CheckContract.sol"; * https://github.com/liquity/liquity/blob/master/papers/Scalable_Reward_Distribution_with_Compounding_Stakes.pdf * * - * --- LQTY ISSUANCE TO STABILITY POOL DEPOSITORS --- - * - * An LQTY issuance event occurs at every deposit operation, and every liquidation. - * - * Each deposit is tagged with the address of the front end through which it was made. - * - * All deposits earn a share of the issued LQTY in proportion to the deposit as a share of total deposits. The LQTY earned - * by a given deposit, is split between the depositor and the front end through which the deposit was made, based on the front end's kickbackRate. - * - * Please see the system Readme for an overview: - * https://github.com/liquity/dev/blob/main/README.md#lqty-issuance-to-stability-providers - * - * We use the same mathematical product-sum approach to track LQTY gains for depositors, where 'G' is the sum corresponding to LQTY gains. - * The product P (and snapshot P_t) is re-used, as the ratio P/P_t tracks a deposit's depletion due to liquidations. - * */ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { string constant public NAME = "StabilityPool"; @@ -154,8 +138,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { // Needed to check if there are pending liquidations ISortedTroves public sortedTroves; - ICommunityIssuance public communityIssuance; - uint256 internal ETH; // deposited ether tracker // Tracker for Bold held in the pool. Changes when users deposit/withdraw, and when Trove debt is offset. @@ -163,14 +145,8 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { // --- Data structures --- - struct FrontEnd { - uint kickbackRate; - bool registered; - } - struct Deposit { uint initialValue; - address frontEndTag; } struct Snapshots { @@ -184,10 +160,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { mapping (address => Deposit) public deposits; // depositor address -> Deposit struct mapping (address => Snapshots) public depositSnapshots; // depositor address -> snapshots struct - mapping (address => FrontEnd) public frontEnds; // front end address -> FrontEnd struct - mapping (address => uint) public frontEndStakes; // front end address -> last recorded total deposits, tagged with that front end - mapping (address => Snapshots) public frontEndSnapshots; // front end address -> snapshots struct - /* Product 'P': Running product by which to multiply an initial deposit, in order to find the current compounded deposit, * after a series of liquidations have occurred, each of which cancel some Bold debt with the deposit. * @@ -214,17 +186,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { */ mapping (uint128 => mapping(uint128 => uint)) public epochToScaleToSum; - /* - * Similarly, the sum 'G' is used to calculate LQTY gains. During it's lifetime, each deposit d_t earns a LQTY gain of - * ( d_t * [G - G_t] )/P_t, where G_t is the depositor's snapshot of G taken at time t when the deposit was made. - * - * LQTY reward events occur are triggered by depositor operations (new deposit, topup, withdrawal), and liquidations. - * In each case, the LQTY reward is issued (i.e. G is updated), before other state changes are made. - */ - mapping (uint128 => mapping(uint128 => uint)) public epochToScaleToG; - - // Error tracker for the error correction in the LQTY issuance calculation - uint public lastLQTYError; // Error trackers for the error correction in the offset calculation uint public lastETHError_Offset; uint public lastBoldLossError_Offset; @@ -241,7 +202,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { event BoldTokenAddressChanged(address _newBoldTokenAddress); event SortedTrovesAddressChanged(address _newSortedTrovesAddress); event PriceFeedAddressChanged(address _newPriceFeedAddress); - event CommunityIssuanceAddressChanged(address _newCommunityIssuanceAddress); event P_Updated(uint _P); event S_Updated(uint _S, uint128 _epoch, uint128 _scale); @@ -249,17 +209,10 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { event EpochUpdated(uint128 _currentEpoch); event ScaleUpdated(uint128 _currentScale); - event FrontEndRegistered(address indexed _frontEnd, uint _kickbackRate); - event FrontEndTagSet(address indexed _depositor, address indexed _frontEnd); - - event DepositSnapshotUpdated(address indexed _depositor, uint _P, uint _S, uint _G); - event FrontEndSnapshotUpdated(address indexed _frontEnd, uint _P, uint _G); + event DepositSnapshotUpdated(address indexed _depositor, uint _P, uint _S); event UserDepositChanged(address indexed _depositor, uint _newDeposit); - event FrontEndStakeChanged(address indexed _frontEnd, uint _newFrontEndStake, address _depositor); event ETHGainWithdrawn(address indexed _depositor, uint _ETH, uint _boldLoss); - event LQTYPaidToDepositor(address indexed _depositor, uint _LQTY); - event LQTYPaidToFrontEnd(address indexed _frontEnd, uint _LQTY); event EtherSent(address _to, uint _amount); // --- Contract setters --- @@ -270,8 +223,7 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { address _activePoolAddress, address _boldTokenAddress, address _sortedTrovesAddress, - address _priceFeedAddress, - address _communityIssuanceAddress + address _priceFeedAddress ) external override @@ -290,7 +242,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { boldToken = IBoldToken(_boldTokenAddress); sortedTroves = ISortedTroves(_sortedTrovesAddress); priceFeed = IPriceFeed(_priceFeedAddress); - communityIssuance = ICommunityIssuance(_communityIssuanceAddress); emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress); emit TroveManagerAddressChanged(_troveManagerAddress); @@ -298,7 +249,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { emit BoldTokenAddressChanged(_boldTokenAddress); emit SortedTrovesAddressChanged(_sortedTrovesAddress); emit PriceFeedAddressChanged(_priceFeedAddress); - emit CommunityIssuanceAddressChanged(_communityIssuanceAddress); _renounceOwnership(); } @@ -316,39 +266,20 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { // --- External Depositor Functions --- /* provideToSP(): - * - * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends - * - Tags the deposit with the provided front end tag param, if it's a new deposit - * - Sends depositor's accumulated gains (LQTY, ETH) to depositor - * - Sends the tagged front end's accumulated LQTY gains to the tagged front end - * - Increases deposit and tagged front end's stake, and takes new snapshots for each. + * - Calculates depositor's ETH gain + * - Calculates the compounded deposit + * - Increases deposit, and takes new snapshots of accumulators P and S + * - Sends depositor's accumulated ETH gains to depositor */ - function provideToSP(uint _amount, address _frontEndTag) external override { - _requireFrontEndIsRegisteredOrZero(_frontEndTag); - _requireFrontEndNotRegistered(msg.sender); + function provideToSP(uint _amount) external override { _requireNonZeroAmount(_amount); uint initialDeposit = deposits[msg.sender].initialValue; - ICommunityIssuance communityIssuanceCached = communityIssuance; - - _triggerLQTYIssuance(communityIssuanceCached); - - if (initialDeposit == 0) {_setFrontEndTag(msg.sender, _frontEndTag);} uint depositorETHGain = getDepositorETHGain(msg.sender); uint compoundedBoldDeposit = getCompoundedBoldDeposit(msg.sender); uint boldLoss = initialDeposit - compoundedBoldDeposit; // Needed only for event log - // First pay out any LQTY gains - address frontEnd = deposits[msg.sender].frontEndTag; - _payOutLQTYGains(communityIssuanceCached, msg.sender, frontEnd); - - // Update front end stake - uint compoundedFrontEndStake = getCompoundedFrontEndStake(frontEnd); - uint newFrontEndStake = compoundedFrontEndStake + _amount; - _updateFrontEndStakeAndSnapshots(frontEnd, newFrontEndStake); - emit FrontEndStakeChanged(frontEnd, newFrontEndStake, msg.sender); - _sendBoldtoStabilityPool(msg.sender, _amount); uint newDeposit = compoundedBoldDeposit + _amount; @@ -361,40 +292,23 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { } /* withdrawFromSP(): - * - * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends - * - Removes the deposit's front end tag if it is a full withdrawal - * - Sends all depositor's accumulated gains (LQTY, ETH) to depositor - * - Sends the tagged front end's accumulated LQTY gains to the tagged front end - * - Decreases deposit and tagged front end's stake, and takes new snapshots for each. - * - * If _amount > userDeposit, the user withdraws all of their compounded deposit. + * - Calculates depositor's ETH gain + * - Calculates the compounded deposit + * - Sends the requested BOLD withdrawal to depositor + * - (If _amount > userDeposit, the user withdraws all of their compounded deposit) + * - Decreases deposit by withdrawn amount and takes new snapshots of accumulators P and S */ function withdrawFromSP(uint _amount) external override { if (_amount !=0) {_requireNoUnderCollateralizedTroves();} uint initialDeposit = deposits[msg.sender].initialValue; _requireUserHasDeposit(initialDeposit); - ICommunityIssuance communityIssuanceCached = communityIssuance; - - _triggerLQTYIssuance(communityIssuanceCached); - uint depositorETHGain = getDepositorETHGain(msg.sender); uint compoundedBoldDeposit = getCompoundedBoldDeposit(msg.sender); uint BoldtoWithdraw = LiquityMath._min(_amount, compoundedBoldDeposit); uint boldLoss = initialDeposit - compoundedBoldDeposit; // Needed only for event log - // First pay out any LQTY gains - address frontEnd = deposits[msg.sender].frontEndTag; - _payOutLQTYGains(communityIssuanceCached, msg.sender, frontEnd); - - // Update front end stake - uint compoundedFrontEndStake = getCompoundedFrontEndStake(frontEnd); - uint newFrontEndStake = compoundedFrontEndStake - BoldtoWithdraw; - _updateFrontEndStakeAndSnapshots(frontEnd, newFrontEndStake); - emit FrontEndStakeChanged(frontEnd, newFrontEndStake, msg.sender); - _sendBoldToDepositor(msg.sender, BoldtoWithdraw); // Update deposit @@ -407,38 +321,22 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { _sendETHGainToDepositor(depositorETHGain); } - /* withdrawETHGainToTrove: - * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends - * - Sends all depositor's LQTY gain to depositor - * - Sends all tagged front end's LQTY gain to the tagged front end + /* withdrawETHGainToTrove(): * - Transfers the depositor's entire ETH gain from the Stability Pool to the caller's trove * - Leaves their compounded deposit in the Stability Pool - * - Updates snapshots for deposit and tagged front end stake */ + * - Takes new snapshots of accumulators P and S + */ function withdrawETHGainToTrove(address _upperHint, address _lowerHint) external override { uint initialDeposit = deposits[msg.sender].initialValue; _requireUserHasDeposit(initialDeposit); _requireUserHasTrove(msg.sender); _requireUserHasETHGain(msg.sender); - ICommunityIssuance communityIssuanceCached = communityIssuance; - - _triggerLQTYIssuance(communityIssuanceCached); - uint depositorETHGain = getDepositorETHGain(msg.sender); uint compoundedBoldDeposit = getCompoundedBoldDeposit(msg.sender); uint boldLoss = initialDeposit - compoundedBoldDeposit; // Needed only for event log - // First pay out any LQTY gains - address frontEnd = deposits[msg.sender].frontEndTag; - _payOutLQTYGains(communityIssuanceCached, msg.sender, frontEnd); - - // Update front end stake - uint compoundedFrontEndStake = getCompoundedFrontEndStake(frontEnd); - uint newFrontEndStake = compoundedFrontEndStake; - _updateFrontEndStakeAndSnapshots(frontEnd, newFrontEndStake); - emit FrontEndStakeChanged(frontEnd, newFrontEndStake, msg.sender); - _updateDepositAndSnapshots(msg.sender, compoundedBoldDeposit); /* Emit events before transferring ETH gain to Trove. @@ -454,51 +352,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { borrowerOperations.moveETHGainToTrove{ value: depositorETHGain }(msg.sender, _upperHint, _lowerHint); } - // --- LQTY issuance functions --- - - function _triggerLQTYIssuance(ICommunityIssuance _communityIssuance) internal { - uint LQTYIssuance = _communityIssuance.issueLQTY(); - _updateG(LQTYIssuance); - } - - function _updateG(uint _LQTYIssuance) internal { - uint totalBold = totalBoldDeposits; // cached to save an SLOAD - /* - * When total deposits is 0, G is not updated. In this case, the LQTY issued can not be obtained by later - * depositors - it is missed out on, and remains in the balanceof the CommunityIssuance contract. - * - */ - if (totalBold == 0 || _LQTYIssuance == 0) {return;} - - uint LQTYPerUnitStaked; - LQTYPerUnitStaked =_computeLQTYPerUnitStaked(_LQTYIssuance, totalBold); - - uint marginalLQTYGain = LQTYPerUnitStaked * P; - epochToScaleToG[currentEpoch][currentScale] = epochToScaleToG[currentEpoch][currentScale] + marginalLQTYGain; - - emit G_Updated(epochToScaleToG[currentEpoch][currentScale], currentEpoch, currentScale); - } - - function _computeLQTYPerUnitStaked(uint _LQTYIssuance, uint _totalBoldDeposits) internal returns (uint) { - /* - * Calculate the LQTY-per-unit staked. Division uses a "feedback" error correction, to keep the - * cumulative error low in the running total G: - * - * 1) Form a numerator which compensates for the floor division error that occurred the last time this - * function was called. - * 2) Calculate "per-unit-staked" ratio. - * 3) Multiply the ratio back by its denominator, to reveal the current floor division error. - * 4) Store this error for use in the next correction when this function is called. - * 5) Note: static analysis tools complain about this "division before multiplication", however, it is intended. - */ - uint LQTYNumerator = _LQTYIssuance * DECIMAL_PRECISION + lastLQTYError; - - uint LQTYPerUnitStaked = LQTYNumerator / _totalBoldDeposits; - lastLQTYError = LQTYNumerator - LQTYPerUnitStaked * _totalBoldDeposits; - - return LQTYPerUnitStaked; - } - // --- Liquidation functions --- /* @@ -511,8 +364,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { uint totalBold = totalBoldDeposits; // cached to save an SLOAD if (totalBold == 0 || _debtToOffset == 0) { return; } - _triggerLQTYIssuance(communityIssuance); - (uint ETHGainPerUnitStaked, uint boldLossPerUnitStaked) = _computeRewardsPerUnitStaked(_collToAdd, _debtToOffset, totalBold); @@ -634,7 +485,7 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { emit StabilityPoolBoldBalanceUpdated(newTotalBoldDeposits); } - // --- Reward calculator functions for depositor and front end --- + // --- Reward calculator functions for depositor --- /* Calculates the ETH gain earned by the deposit since its last snapshots were taken. * Given by the formula: E = d0 * (S - S(0))/P(0) @@ -671,71 +522,7 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { return ETHGain; } - /* - * Calculate the LQTY gain earned by a deposit since its last snapshots were taken. - * Given by the formula: LQTY = d0 * (G - G(0))/P(0) - * where G(0) and P(0) are the depositor's snapshots of the sum G and product P, respectively. - * d0 is the last recorded deposit value. - */ - function getDepositorLQTYGain(address _depositor) public view override returns (uint) { - uint initialDeposit = deposits[_depositor].initialValue; - if (initialDeposit == 0) {return 0;} - - address frontEndTag = deposits[_depositor].frontEndTag; - - /* - * If not tagged with a front end, the depositor gets a 100% cut of what their deposit earned. - * Otherwise, their cut of the deposit's earnings is equal to the kickbackRate, set by the front end through - * which they made their deposit. - */ - uint kickbackRate = frontEndTag == address(0) ? DECIMAL_PRECISION : frontEnds[frontEndTag].kickbackRate; - - Snapshots memory snapshots = depositSnapshots[_depositor]; - - uint LQTYGain = kickbackRate * _getLQTYGainFromSnapshots(initialDeposit, snapshots) / DECIMAL_PRECISION; - - return LQTYGain; - } - - /* - * Return the LQTY gain earned by the front end. Given by the formula: E = D0 * (G - G(0))/P(0) - * where G(0) and P(0) are the depositor's snapshots of the sum G and product P, respectively. - * - * D0 is the last recorded value of the front end's total tagged deposits. - */ - function getFrontEndLQTYGain(address _frontEnd) public view override returns (uint) { - uint frontEndStake = frontEndStakes[_frontEnd]; - if (frontEndStake == 0) { return 0; } - - uint kickbackRate = frontEnds[_frontEnd].kickbackRate; - uint frontEndShare = uint(DECIMAL_PRECISION) - kickbackRate; - - Snapshots memory snapshots = frontEndSnapshots[_frontEnd]; - - uint LQTYGain = frontEndShare * _getLQTYGainFromSnapshots(frontEndStake, snapshots) / DECIMAL_PRECISION; - return LQTYGain; - } - - function _getLQTYGainFromSnapshots(uint initialStake, Snapshots memory snapshots) internal view returns (uint) { - /* - * Grab the sum 'G' from the epoch at which the stake was made. The LQTY gain may span up to one scale change. - * If it does, the second portion of the LQTY gain is scaled by 1e9. - * If the gain spans no scale change, the second portion will be 0. - */ - uint128 epochSnapshot = snapshots.epoch; - uint128 scaleSnapshot = snapshots.scale; - uint G_Snapshot = snapshots.G; - uint P_Snapshot = snapshots.P; - - uint firstPortion = epochToScaleToG[epochSnapshot][scaleSnapshot] - G_Snapshot; - uint secondPortion = epochToScaleToG[epochSnapshot][scaleSnapshot + 1] / SCALE_FACTOR; - - uint LQTYGain = initialStake * (firstPortion + secondPortion) / P_Snapshot / DECIMAL_PRECISION; - - return LQTYGain; - } - - // --- Compounded deposit and compounded front end stake --- + // --- Compounded deposit --- /* * Return the user's compounded deposit. Given by the formula: d = d0 * P/P(0) @@ -751,23 +538,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { return compoundedDeposit; } - /* - * Return the front end's compounded stake. Given by the formula: D = D0 * P/P(0) - * where P(0) is the depositor's snapshot of the product P, taken at the last time - * when one of the front end's tagged deposits updated their deposit. - * - * The front end's compounded stake is equal to the sum of its depositors' compounded deposits. - */ - function getCompoundedFrontEndStake(address _frontEnd) public view override returns (uint) { - uint frontEndStake = frontEndStakes[_frontEnd]; - if (frontEndStake == 0) { return 0; } - - Snapshots memory snapshots = frontEndSnapshots[_frontEnd]; - - uint compoundedFrontEndStake = _getCompoundedStakeFromSnapshots(frontEndStake, snapshots); - return compoundedFrontEndStake; - } - // Internal function, used to calculcate compounded deposits and compounded front end stakes. function _getCompoundedStakeFromSnapshots( uint initialStake, @@ -813,7 +583,7 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { return compoundedStake; } - // --- Sender functions for Bold deposit, ETH gains and LQTY gains --- + // --- Sender functions for Bold deposit and ETH gains --- // Transfer the Bold tokens from the user to the Stability Pool's address, and update its recorded Bold function _sendBoldtoStabilityPool(address _address, uint _amount) internal { @@ -842,92 +612,30 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { _decreaseBold(BoldWithdrawal); } - // --- External Front End functions --- - - // Front end makes a one-time selection of kickback rate upon registering - function registerFrontEnd(uint _kickbackRate) external override { - _requireFrontEndNotRegistered(msg.sender); - _requireUserHasNoDeposit(msg.sender); - _requireValidKickbackRate(_kickbackRate); - - frontEnds[msg.sender].kickbackRate = _kickbackRate; - frontEnds[msg.sender].registered = true; - - emit FrontEndRegistered(msg.sender, _kickbackRate); - } - // --- Stability Pool Deposit Functionality --- - function _setFrontEndTag(address _depositor, address _frontEndTag) internal { - deposits[_depositor].frontEndTag = _frontEndTag; - emit FrontEndTagSet(_depositor, _frontEndTag); - } - - function _updateDepositAndSnapshots(address _depositor, uint _newValue) internal { deposits[_depositor].initialValue = _newValue; if (_newValue == 0) { - delete deposits[_depositor].frontEndTag; delete depositSnapshots[_depositor]; - emit DepositSnapshotUpdated(_depositor, 0, 0, 0); + emit DepositSnapshotUpdated(_depositor, 0, 0); return; } uint128 currentScaleCached = currentScale; uint128 currentEpochCached = currentEpoch; uint currentP = P; - // Get S and G for the current epoch and current scale + // Get S for the current epoch and current scale uint currentS = epochToScaleToSum[currentEpochCached][currentScaleCached]; - uint currentG = epochToScaleToG[currentEpochCached][currentScaleCached]; - // Record new snapshots of the latest running product P, sum S, and sum G, for the depositor + // Record new snapshots of the latest running product P and sum S for the depositor depositSnapshots[_depositor].P = currentP; depositSnapshots[_depositor].S = currentS; - depositSnapshots[_depositor].G = currentG; depositSnapshots[_depositor].scale = currentScaleCached; depositSnapshots[_depositor].epoch = currentEpochCached; - emit DepositSnapshotUpdated(_depositor, currentP, currentS, currentG); - } - - function _updateFrontEndStakeAndSnapshots(address _frontEnd, uint _newValue) internal { - frontEndStakes[_frontEnd] = _newValue; - - if (_newValue == 0) { - delete frontEndSnapshots[_frontEnd]; - emit FrontEndSnapshotUpdated(_frontEnd, 0, 0); - return; - } - - uint128 currentScaleCached = currentScale; - uint128 currentEpochCached = currentEpoch; - uint currentP = P; - - // Get G for the current epoch and current scale - uint currentG = epochToScaleToG[currentEpochCached][currentScaleCached]; - - // Record new snapshots of the latest running product P and sum G for the front end - frontEndSnapshots[_frontEnd].P = currentP; - frontEndSnapshots[_frontEnd].G = currentG; - frontEndSnapshots[_frontEnd].scale = currentScaleCached; - frontEndSnapshots[_frontEnd].epoch = currentEpochCached; - - emit FrontEndSnapshotUpdated(_frontEnd, currentP, currentG); - } - - function _payOutLQTYGains(ICommunityIssuance _communityIssuance, address _depositor, address _frontEnd) internal { - // Pay out front end's LQTY gain - if (_frontEnd != address(0)) { - uint frontEndLQTYGain = getFrontEndLQTYGain(_frontEnd); - _communityIssuance.sendLQTY(_frontEnd, frontEndLQTYGain); - emit LQTYPaidToFrontEnd(_frontEnd, frontEndLQTYGain); - } - - // Pay out depositor's LQTY gain - uint depositorLQTYGain = getDepositorLQTYGain(_depositor); - _communityIssuance.sendLQTY(_depositor, depositorLQTYGain); - emit LQTYPaidToDepositor(_depositor, depositorLQTYGain); + emit DepositSnapshotUpdated(_depositor, currentP, currentS); } // --- 'require' functions --- @@ -969,15 +677,6 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { require(ETHGain > 0, "StabilityPool: caller must have non-zero ETH Gain"); } - function _requireFrontEndNotRegistered(address _address) internal view { - require(!frontEnds[_address].registered, "StabilityPool: must not already be a registered front end"); - } - - function _requireFrontEndIsRegisteredOrZero(address _address) internal view { - require(frontEnds[_address].registered || _address == address(0), - "StabilityPool: Tag must be a registered front end, or the zero address"); - } - function _requireValidKickbackRate(uint _kickbackRate) internal pure { require (_kickbackRate <= DECIMAL_PRECISION, "StabilityPool: Kickback rate must be in range [0,1]"); } diff --git a/contracts/src/TestContracts/PriceFeedTestnet.sol b/contracts/src/TestContracts/PriceFeedTestnet.sol index 2f220196..3c57c514 100644 --- a/contracts/src/TestContracts/PriceFeedTestnet.sol +++ b/contracts/src/TestContracts/PriceFeedTestnet.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.18; -import "../Interfaces/IPriceFeed.sol"; +import "../test/TestContracts/Interfaces/IPriceFeedTestnet.sol"; /* * PriceFeed placeholder for testnet and development. The price is simply set manually and saved in a state * variable. The contract does not connect to a live Chainlink price feed. */ -contract PriceFeedTestnet is IPriceFeed { +contract PriceFeedTestnet is IPriceFeedTestnet { event LastGoodPriceUpdated(uint _lastGoodPrice); uint256 private _price = 200 * 1e18; diff --git a/contracts/src/TroveManager.sol b/contracts/src/TroveManager.sol index c023d45b..03d5da9f 100644 --- a/contracts/src/TroveManager.sol +++ b/contracts/src/TroveManager.sol @@ -13,6 +13,7 @@ import "./Dependencies/LiquityBase.sol"; import "./Dependencies/Ownable.sol"; import "./Dependencies/CheckContract.sol"; +// import "forge-std/console2.sol"; contract TroveManager is LiquityBase, Ownable, CheckContract, ITroveManager { string constant public NAME = "TroveManager"; @@ -1005,10 +1006,7 @@ contract TroveManager is LiquityBase, Ownable, CheckContract, ITroveManager { _requireUserAcceptsFee(totals.ETHFee, totals.totalETHDrawn, _maxFeePercentage); - // Send the ETH fee to the LQTY staking contract - contractsCache.activePool.sendETH(address(contractsCache.lqtyStaking), totals.ETHFee); - contractsCache.lqtyStaking.increaseF_ETH(totals.ETHFee); - + // Do nothing with the fee - the funds remain in ActivePool. TODO: replace with new redemption fee scheme totals.ETHToSendToRedeemer = totals.totalETHDrawn - totals.ETHFee; emit Redemption(_boldamount, totals.totalBoldToRedeem, totals.totalETHDrawn, totals.ETHFee); @@ -1496,7 +1494,7 @@ contract TroveManager is LiquityBase, Ownable, CheckContract, ITroveManager { } function _requireAfterBootstrapPeriod() internal view { - uint systemDeploymentTime = lqtyToken.getDeploymentStartTime(); + uint systemDeploymentTime = boldToken.deploymentStartTime(); require(block.timestamp >= systemDeploymentTime + BOOTSTRAP_PERIOD, "TroveManager: Redemptions are not allowed during bootstrap phase"); } diff --git a/contracts/src/test/TestContracts/BaseTest.sol b/contracts/src/test/TestContracts/BaseTest.sol index 8973d834..bc6e8257 100644 --- a/contracts/src/test/TestContracts/BaseTest.sol +++ b/contracts/src/test/TestContracts/BaseTest.sol @@ -30,14 +30,16 @@ contract BaseTest is Test { uint256 public constant MAX_UINT256 = type(uint256).max; uint256 public constant SECONDS_IN_1_YEAR = 31536000; // 60*60*24*365 + uint256 MCR = 110e16; + uint256 CCR = 150e16; address public constant ZERO_ADDRESS = address(0); + // Core contracts IActivePool activePool; IBorrowerOperations borrowerOperations; ICollSurplusPool collSurplusPool; IDefaultPool defaultPool; - IPriceFeed priceFeed; ISortedTroves sortedTroves; IStabilityPool stabilityPool; ITroveManager troveManager; @@ -60,7 +62,6 @@ contract BaseTest is Test { console.log("CollSurplusPool addr: ", address(collSurplusPool)); console.log("DefaultPool addr: ", address(defaultPool)); console.log("GasPool addr: ", address(gasPool)); - console.log("PriceFeed addr: ", address(priceFeed)); console.log("SortedTroves addr: ", address(sortedTroves)); console.log("StabilityPool addr: ", address(stabilityPool)); console.log("TroveManager addr: ", address(troveManager)); diff --git a/contracts/src/test/TestContracts/DevTestSetup.sol b/contracts/src/test/TestContracts/DevTestSetup.sol index f44f6ed9..ec44f378 100644 --- a/contracts/src/test/TestContracts/DevTestSetup.sol +++ b/contracts/src/test/TestContracts/DevTestSetup.sol @@ -2,6 +2,8 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.18; +import "./Interfaces/IPriceFeedTestnet.sol"; + import "../../ActivePool.sol"; import "../../BoldToken.sol"; import "../../BorrowerOperations.sol"; @@ -10,7 +12,7 @@ import "../../DefaultPool.sol"; import "../../GasPool.sol"; import "../../HintHelpers.sol"; import "../../MultiTroveGetter.sol"; -import "../../OldTestContracts/PriceFeedTestnet.sol"; +import "../../TestContracts/PriceFeedTestnet.sol"; import "../../SortedTroves.sol"; import "../../StabilityPool.sol"; import "../../TroveManager.sol"; @@ -18,6 +20,9 @@ import "../../TroveManager.sol"; import "./BaseTest.sol"; contract DevTestSetup is BaseTest { + + IPriceFeedTestnet priceFeed; + function setUp() public virtual { // Start tests at a non-zero timestamp vm.warp(block.timestamp + 600); @@ -29,7 +34,7 @@ contract DevTestSetup is BaseTest { (accountsList[0], accountsList[1], accountsList[2], accountsList[3], accountsList[4], accountsList[5]); // Give some StETH to test accounts - uint256 initialETHAmount = 2000e18; + uint256 initialETHAmount = 10_000e18; deal(A, initialETHAmount); deal(B, initialETHAmount); deal(C, initialETHAmount); @@ -102,8 +107,7 @@ contract DevTestSetup is BaseTest { address(activePool), address(boldToken), address(sortedTroves), - address(priceFeed), - ZERO_ADDRESS // No Community Issuance + address(priceFeed) ); activePool.setAddresses( diff --git a/contracts/src/test/TestContracts/Interfaces/IPriceFeedTestnet.sol b/contracts/src/test/TestContracts/Interfaces/IPriceFeedTestnet.sol new file mode 100644 index 00000000..06325aa0 --- /dev/null +++ b/contracts/src/test/TestContracts/Interfaces/IPriceFeedTestnet.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.18; + +import "../../../Interfaces/IPriceFeed.sol"; + +interface IPriceFeedTestnet is IPriceFeed { + function setPrice(uint256 _price) external returns (bool); +} \ No newline at end of file diff --git a/contracts/src/test/basicOps.t.sol b/contracts/src/test/basicOps.t.sol new file mode 100644 index 00000000..f5644b2a --- /dev/null +++ b/contracts/src/test/basicOps.t.sol @@ -0,0 +1,170 @@ +pragma solidity 0.8.18; + +import "./TestContracts/DevTestSetup.sol"; + +contract BasicOps is DevTestSetup { + + function testOpenTrove() public { + priceFeed.setPrice(2000e18); + uint256 trovesCount = troveManager.getTroveOwnersCount(); + assertEq(trovesCount, 0); + + vm.startPrank(A); + borrowerOperations.openTrove{value: 2 ether}(1e18, 2000e18, ZERO_ADDRESS, ZERO_ADDRESS); + + trovesCount = troveManager.getTroveOwnersCount(); + assertEq(trovesCount, 1); + } + + function testCloseTrove() public { + priceFeed.setPrice(2000e18); + vm.startPrank(A); + borrowerOperations.openTrove{value: 2 ether}(1e18, 2000e18, ZERO_ADDRESS, ZERO_ADDRESS); + vm.stopPrank(); + + vm.startPrank(B); + borrowerOperations.openTrove{value: 2 ether}(1e18, 2000e18, ZERO_ADDRESS, ZERO_ADDRESS); + + uint256 trovesCount = troveManager.getTroveOwnersCount(); + assertEq(trovesCount, 2); + + vm.startPrank(B); + borrowerOperations.closeTrove(); + vm.stopPrank(); + + // Check Troves count reduced by 1 + trovesCount = troveManager.getTroveOwnersCount(); + assertEq(trovesCount, 1); + } + + function testAdjustTrove() public { + priceFeed.setPrice(2000e18); + vm.startPrank(A); + borrowerOperations.openTrove{value: 2 ether}(1e18, 2000e18, ZERO_ADDRESS, ZERO_ADDRESS); + + // Check Trove coll and debt + uint256 debt_1 = troveManager.getTroveDebt(A); + assertGt(debt_1, 0); + uint256 coll_1 = troveManager.getTroveColl(A); + assertGt(coll_1, 0); + + // Adjust trove + borrowerOperations.adjustTrove{value: 1 ether}(1e18, 0, 500e18, true, ZERO_ADDRESS, ZERO_ADDRESS); + + // Check coll and debt altered + uint256 debt_2 = troveManager.getTroveDebt(A); + assertGt(debt_2, debt_1); + uint256 coll_2 = troveManager.getTroveColl(A); + assertGt(coll_2, coll_1); + } + + function testRedeem() public { + priceFeed.setPrice(2000e18); + vm.startPrank(A); + borrowerOperations.openTrove{value: 5 ether}(1e18, 5_000e18, ZERO_ADDRESS, ZERO_ADDRESS); + vm.stopPrank(); + + uint256 debt_1 = troveManager.getTroveDebt(A); + assertGt(debt_1, 0); + uint256 coll_1 = troveManager.getTroveColl(A); + assertGt(coll_1, 0); + + vm.startPrank(B); + borrowerOperations.openTrove{value: 5 ether}(1e18, 4_000e18, ZERO_ADDRESS, ZERO_ADDRESS); + + vm.warp(block.timestamp + troveManager.BOOTSTRAP_PERIOD() + 1); + + uint256 redemptionAmount = 1000e18; // 1k BOLD + uint256 expectedCollReduction = redemptionAmount * 1e18 / priceFeed.fetchPrice(); + + uint256 expectedColl_A = troveManager.getTroveColl(A) - expectedCollReduction; + uint256 expectedDebt_A = troveManager.getTroveDebt(A) - redemptionAmount; + uint256 expectedNICR = LiquityMath._computeNominalCR(expectedColl_A,expectedDebt_A); + + // B redeems 1k BOLD + troveManager.redeemCollateral( + redemptionAmount, + ZERO_ADDRESS, + ZERO_ADDRESS, + ZERO_ADDRESS, + expectedNICR, + 10, + 1e18 + ); + + // Check A's coll and debt reduced + uint256 debt_2 = troveManager.getTroveDebt(A); + assertLt(debt_2, debt_1); + uint256 coll_2 = troveManager.getTroveColl(A); + assertLt(coll_2, coll_1); + } + + function testLiquidation() public { + priceFeed.setPrice(2000e18); + vm.startPrank(A); + borrowerOperations.openTrove{value: 2 ether}(1e18, 2000e18, ZERO_ADDRESS, ZERO_ADDRESS); + vm.stopPrank(); + + vm.startPrank(B); + borrowerOperations.openTrove{value: 10 ether}(1e18, 2000e18, ZERO_ADDRESS, ZERO_ADDRESS); + + // Price drops + priceFeed.setPrice(1200e18); + uint256 price = priceFeed.fetchPrice(); + + // Check CR_A < MCR and TCR > CCR + assertLt(troveManager.getCurrentICR(A, price), MCR); + assertGt(troveManager.getTCR(price), CCR); + + uint256 trovesCount = troveManager.getTroveOwnersCount(); + assertEq(trovesCount, 2); + + troveManager.liquidate(A); + + // Check Troves count reduced by 1 + trovesCount = troveManager.getTroveOwnersCount(); + assertEq(trovesCount, 1); + } + + function testSPDeposit() public { + priceFeed.setPrice(2000e18); + vm.startPrank(A); + borrowerOperations.openTrove{value: 2 ether}(1e18, 2000e18, ZERO_ADDRESS, ZERO_ADDRESS); + + // A makes an SP deposit + stabilityPool.provideToSP(100e18); + + // time passes + vm.warp(block.timestamp + 7 days); + + // A tops up their SP deposit + stabilityPool.provideToSP(100e18); + + // Check A's balance decreased and SP deposit increased + assertEq(boldToken.balanceOf(A), 1800e18); + assertEq(stabilityPool.getCompoundedBoldDeposit(A), 200e18); + } + + function testSPWithdrawal() public { + priceFeed.setPrice(2000e18); + vm.startPrank(A); + borrowerOperations.openTrove{value: 2 ether}(1e18, 2000e18, ZERO_ADDRESS, ZERO_ADDRESS); + + // A makes an SP deposit + stabilityPool.provideToSP(100e18); + + // time passes + vm.warp(block.timestamp + 7 days); + + // Check A's balance decreased and SP deposit increased + assertEq(boldToken.balanceOf(A), 1900e18); + assertEq(stabilityPool.getCompoundedBoldDeposit(A), 100e18); + + // A withdraws their full SP deposit + stabilityPool.withdrawFromSP(100e18); + + // Check A's balance increased and SP deposit decreased to 0 + assertEq(boldToken.balanceOf(A), 2000e18); + assertEq(stabilityPool.getCompoundedBoldDeposit(A), 0); + } +} \ No newline at end of file