-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
144 changed files
with
231,297 additions
and
7,258 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
pragma solidity 0.8.2; | ||
pragma abicoder v2; | ||
|
||
// External | ||
import { IPlatformIntegration } from "../interfaces/IPlatformIntegration.sol"; | ||
|
||
// Internal | ||
import "../masset/MassetStructs.sol"; | ||
|
||
// Libs | ||
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import { StableMath } from "../shared/StableMath.sol"; | ||
|
||
/** | ||
* @title FeederManager | ||
* @author mStable | ||
* @notice Manager contract for fPools. Forked from `masset/Manager.sol`, and performs a subset of functionality | ||
* related to basket management. | ||
* @dev VERSION: 1.0 | ||
* DATE: 2021-03-01 | ||
*/ | ||
library FeederManager { | ||
using SafeERC20 for IERC20; | ||
using StableMath for uint256; | ||
|
||
event BassetsMigrated(address[] bAssets, address newIntegrator); | ||
event StartRampA(uint256 currentA, uint256 targetA, uint256 startTime, uint256 rampEndTime); | ||
event StopRampA(uint256 currentA, uint256 time); | ||
|
||
uint256 private constant MIN_RAMP_TIME = 1 days; | ||
uint256 private constant MAX_A = 1e6; | ||
|
||
/** | ||
* @dev Calculates the gains accrued across all lending markets. | ||
* @param _bAssetPersonal Basset personal storage array | ||
* @param _bAssetData Basset data storage array | ||
* @return idxs Array [0,1] | ||
* @return rawGains Raw increases in vault Balance | ||
*/ | ||
function calculatePlatformInterest( | ||
BassetPersonal[] memory _bAssetPersonal, | ||
BassetData[] storage _bAssetData | ||
) external returns (uint8[] memory idxs, uint256[] memory rawGains) { | ||
// Get basket details | ||
BassetData[] memory bAssetData_ = _bAssetData; | ||
uint256 count = bAssetData_.length; | ||
idxs = new uint8[](count); | ||
rawGains = new uint256[](count); | ||
// 1. Calculate rawGains in each bAsset, in comparison to current vault balance | ||
for (uint256 i = 0; i < count; i++) { | ||
idxs[i] = uint8(i); | ||
BassetPersonal memory bPersonal = _bAssetPersonal[i]; | ||
BassetData memory bData = bAssetData_[i]; | ||
// If there is no integration, then nothing can have accrued | ||
if (bPersonal.integrator == address(0)) continue; | ||
uint256 lending = | ||
IPlatformIntegration(bPersonal.integrator).checkBalance(bPersonal.addr); | ||
uint256 cache = 0; | ||
if (!bPersonal.hasTxFee) { | ||
cache = IERC20(bPersonal.addr).balanceOf(bPersonal.integrator); | ||
} | ||
uint256 balance = lending + cache; | ||
uint256 oldVaultBalance = bData.vaultBalance; | ||
if (balance > oldVaultBalance && bPersonal.status == BassetStatus.Normal) { | ||
_bAssetData[i].vaultBalance = SafeCast.toUint128(balance); | ||
uint256 interestDelta = balance - oldVaultBalance; | ||
rawGains[i] = interestDelta; | ||
} else { | ||
rawGains[i] = 0; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @dev Transfers all collateral from one lending market to another - used initially | ||
* to handle the migration between Aave V1 and Aave V2. Note - only supports non | ||
* tx fee enabled assets. Supports going from no integration to integration, but | ||
* not the other way around. | ||
* @param _bAssetPersonal Basset data storage array | ||
* @param _bAssets Array of basket assets to migrate | ||
* @param _newIntegration Address of the new platform integration | ||
*/ | ||
function migrateBassets( | ||
BassetPersonal[] storage _bAssetPersonal, | ||
address[] calldata _bAssets, | ||
address _newIntegration | ||
) external { | ||
uint256 len = _bAssets.length; | ||
require(len > 0, "Must migrate some bAssets"); | ||
|
||
for (uint256 i = 0; i < len; i++) { | ||
// 1. Check that the bAsset is in the basket | ||
address bAsset = _bAssets[i]; | ||
uint256 index = _getAssetIndex(_bAssetPersonal, bAsset); | ||
require(!_bAssetPersonal[index].hasTxFee, "A bAsset has a transfer fee"); | ||
|
||
// 2. Withdraw everything from the old platform integration | ||
address oldAddress = _bAssetPersonal[index].integrator; | ||
require(oldAddress != _newIntegration, "Must transfer to new integrator"); | ||
(uint256 cache, uint256 lendingBal) = (0, 0); | ||
if (oldAddress == address(0)) { | ||
cache = IERC20(bAsset).balanceOf(address(this)); | ||
} else { | ||
IPlatformIntegration oldIntegration = IPlatformIntegration(oldAddress); | ||
cache = IERC20(bAsset).balanceOf(address(oldIntegration)); | ||
// 2.1. Withdraw from the lending market | ||
lendingBal = oldIntegration.checkBalance(bAsset); | ||
if (lendingBal > 0) { | ||
oldIntegration.withdraw(address(this), bAsset, lendingBal, false); | ||
} | ||
// 2.2. Withdraw from the cache, if any | ||
if (cache > 0) { | ||
oldIntegration.withdrawRaw(address(this), bAsset, cache); | ||
} | ||
} | ||
uint256 sum = lendingBal + cache; | ||
|
||
// 3. Update the integration address for this bAsset | ||
_bAssetPersonal[index].integrator = _newIntegration; | ||
|
||
// 4. Deposit everything into the new | ||
// This should fail if we did not receive the full amount from the platform withdrawal | ||
// 4.1. Deposit all bAsset | ||
IERC20(bAsset).safeTransfer(_newIntegration, sum); | ||
IPlatformIntegration newIntegration = IPlatformIntegration(_newIntegration); | ||
if (lendingBal > 0) { | ||
newIntegration.deposit(bAsset, lendingBal, false); | ||
} | ||
// 4.2. Check balances | ||
uint256 newLendingBal = newIntegration.checkBalance(bAsset); | ||
uint256 newCache = IERC20(bAsset).balanceOf(address(newIntegration)); | ||
uint256 upperMargin = 10001e14; | ||
uint256 lowerMargin = 9999e14; | ||
|
||
require( | ||
newLendingBal >= lendingBal.mulTruncate(lowerMargin) && | ||
newLendingBal <= lendingBal.mulTruncate(upperMargin), | ||
"Must transfer full amount" | ||
); | ||
require( | ||
newCache >= cache.mulTruncate(lowerMargin) && | ||
newCache <= cache.mulTruncate(upperMargin), | ||
"Must transfer full amount" | ||
); | ||
} | ||
|
||
emit BassetsMigrated(_bAssets, _newIntegration); | ||
} | ||
|
||
/** | ||
* @dev Simply gets the asset index by looping through bAssets. Given there are only | ||
* ever 2 assets, should not be gas intensive. | ||
*/ | ||
function _getAssetIndex(BassetPersonal[] storage _bAssetPersonal, address _asset) | ||
internal | ||
view | ||
returns (uint8 idx) | ||
{ | ||
uint256 len = _bAssetPersonal.length; | ||
for (uint8 i = 0; i < len; i++) { | ||
if (_bAssetPersonal[i].addr == _asset) return i; | ||
} | ||
revert("Invalid asset"); | ||
} | ||
|
||
/** | ||
* @dev Starts changing of the amplification var A | ||
* @param _targetA Target A value | ||
* @param _rampEndTime Time at which A will arrive at _targetA | ||
*/ | ||
function startRampA( | ||
AmpData storage _ampData, | ||
uint256 _targetA, | ||
uint256 _rampEndTime, | ||
uint256 _currentA, | ||
uint256 _precision | ||
) external { | ||
require( | ||
block.timestamp >= (_ampData.rampStartTime + MIN_RAMP_TIME), | ||
"Sufficient period of previous ramp has not elapsed" | ||
); | ||
require(_rampEndTime >= (block.timestamp + MIN_RAMP_TIME), "Ramp time too short"); | ||
require(_targetA > 0 && _targetA < MAX_A, "A target out of bounds"); | ||
|
||
uint256 preciseTargetA = _targetA * _precision; | ||
|
||
if (preciseTargetA > _currentA) { | ||
require(preciseTargetA <= _currentA * 10, "A target increase too big"); | ||
} else { | ||
require(preciseTargetA >= _currentA / 10, "A target decrease too big"); | ||
} | ||
|
||
_ampData.initialA = SafeCast.toUint64(_currentA); | ||
_ampData.targetA = SafeCast.toUint64(preciseTargetA); | ||
_ampData.rampStartTime = SafeCast.toUint64(block.timestamp); | ||
_ampData.rampEndTime = SafeCast.toUint64(_rampEndTime); | ||
|
||
emit StartRampA(_currentA, preciseTargetA, block.timestamp, _rampEndTime); | ||
} | ||
|
||
/** | ||
* @dev Stops the changing of the amplification var A, setting | ||
* it to whatever the current value is. | ||
*/ | ||
function stopRampA(AmpData storage _ampData, uint256 _currentA) external { | ||
require(block.timestamp < _ampData.rampEndTime, "Amplification not changing"); | ||
|
||
_ampData.initialA = SafeCast.toUint64(_currentA); | ||
_ampData.targetA = SafeCast.toUint64(_currentA); | ||
_ampData.rampStartTime = SafeCast.toUint64(block.timestamp); | ||
_ampData.rampEndTime = SafeCast.toUint64(block.timestamp); | ||
|
||
emit StopRampA(_currentA, block.timestamp); | ||
} | ||
} |
Oops, something went wrong.