mirror of
https://github.com/Instadapp/fluid-contracts-public.git
synced 2024-07-29 21:57:37 +00:00
342 lines
13 KiB
Solidity
342 lines
13 KiB
Solidity
// SPDX-License-Identifier: BUSL-1.1
|
|
pragma solidity 0.8.21;
|
|
|
|
import { TickMath } from "../../../libraries/tickMath.sol";
|
|
import { BigMathMinified } from "../../../libraries/bigMathMinified.sol";
|
|
import { IFluidVaultResolver } from "../vault/iVaultResolver.sol";
|
|
import { IFluidVaultT1 } from "../../../protocols/vault/interfaces/iVaultT1.sol";
|
|
|
|
import { Structs } from "./structs.sol";
|
|
import { Variables } from "./variables.sol";
|
|
|
|
/// @notice Fluid Vault protocol ticks & branches resolver
|
|
contract FluidVaultTicksBranchesResolver is Variables, Structs {
|
|
/// @notice thrown if an input param address is zero
|
|
error FluidVaultTicksBranchesResolver__AddressZero();
|
|
|
|
/// @notice constructor sets the immutable vault resolver address
|
|
constructor(IFluidVaultResolver vaultResolver_) Variables(vaultResolver_) {
|
|
if (address(vaultResolver_) == address(0)) {
|
|
revert FluidVaultTicksBranchesResolver__AddressZero();
|
|
}
|
|
}
|
|
|
|
function getTicksDebt(
|
|
address vault_,
|
|
int fromTick_,
|
|
uint totalTicks_
|
|
) public view returns (TickDebt[] memory ticksDebt_, int toTick_) {
|
|
int topTick_ = _tickHelper(((VAULT_RESOLVER.getVaultVariablesRaw(vault_) >> 2) & X20));
|
|
|
|
fromTick_ = topTick_ < fromTick_ ? topTick_ : fromTick_;
|
|
if (fromTick_ > type(int).min) {
|
|
// if fromTick_ == tpye(int).min means top tick is not set, meaning no positions exist
|
|
int startMapId_ = fromTick_ < 0 ? ((fromTick_ + 1) / 256) - 1 : fromTick_ / 256;
|
|
// Removing all other after fromTick
|
|
uint tickHasDebt_;
|
|
{
|
|
uint tickHasDebtRaw_ = VAULT_RESOLVER.getTickHasDebtRaw(vault_, startMapId_);
|
|
|
|
uint bitsToRemove_ = uint(-fromTick_ + (startMapId_ * 256 + 255));
|
|
tickHasDebt_ = (tickHasDebtRaw_ << bitsToRemove_) >> bitsToRemove_;
|
|
}
|
|
|
|
// Adding 1 here as toTick_ is inclusive in the data so if totalTicks_ = 400 then it'll only check 400
|
|
toTick_ = fromTick_ - int(totalTicks_) + 1;
|
|
|
|
uint count_ = _countTicksWithDebt(vault_, toTick_, startMapId_, tickHasDebt_);
|
|
|
|
(, , uint vaultSupplyExchangePrice_, uint vaultBorrowExchangePrice_) = IFluidVaultT1(vault_)
|
|
.updateExchangePrices(VAULT_RESOLVER.getVaultVariables2Raw(vault_));
|
|
|
|
ticksDebt_ = _populateTicksDebt(
|
|
vault_,
|
|
toTick_,
|
|
startMapId_,
|
|
tickHasDebt_,
|
|
count_,
|
|
vaultSupplyExchangePrice_,
|
|
vaultBorrowExchangePrice_
|
|
);
|
|
}
|
|
}
|
|
|
|
function getMultipleVaultsTicksDebt(
|
|
address[] memory vaults_,
|
|
int[] memory fromTicks_,
|
|
uint[] memory totalTicks_
|
|
) public view returns (VaultsTickDebt[] memory vaultsTickDebt_) {
|
|
uint length_ = vaults_.length;
|
|
|
|
vaultsTickDebt_ = new VaultsTickDebt[](length_);
|
|
for (uint i = 0; i < length_; i++) {
|
|
(vaultsTickDebt_[i].tickDebt, vaultsTickDebt_[i].toTick) = getTicksDebt(
|
|
vaults_[i],
|
|
fromTicks_[i],
|
|
totalTicks_[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
function getAllVaultsTicksDebt(uint totalTicks_) public view returns (VaultsTickDebt[] memory vaultsTickDebt_) {
|
|
address[] memory vaults_ = VAULT_RESOLVER.getAllVaultsAddresses();
|
|
uint length_ = vaults_.length;
|
|
|
|
vaultsTickDebt_ = new VaultsTickDebt[](length_);
|
|
for (uint i = 0; i < length_; i++) {
|
|
(vaultsTickDebt_[i].tickDebt, vaultsTickDebt_[i].toTick) = getTicksDebt(
|
|
vaults_[i],
|
|
type(int).max,
|
|
totalTicks_
|
|
);
|
|
}
|
|
}
|
|
|
|
function getBranchesDebt(
|
|
address vault_,
|
|
uint fromBranchId_,
|
|
uint toBranchId_
|
|
) public view returns (BranchDebt[] memory branchesDebt_) {
|
|
uint vaultVariables_ = VAULT_RESOLVER.getVaultVariablesRaw(vault_);
|
|
uint totalBranch_ = (vaultVariables_ >> 52) & X30;
|
|
toBranchId_ = (toBranchId_ == 0 ? 1 : toBranchId_);
|
|
fromBranchId_ = (totalBranch_ < fromBranchId_ ? totalBranch_ : fromBranchId_);
|
|
|
|
require(fromBranchId_ >= toBranchId_, "fromBranchId_ must be greater than or equal to toBranchId_");
|
|
|
|
branchesDebt_ = new BranchDebt[](fromBranchId_ - toBranchId_ + 1);
|
|
|
|
uint index_;
|
|
|
|
for (uint i = fromBranchId_; i >= toBranchId_; i--) {
|
|
branchesDebt_[index_++] = _getBranchDebt(vault_, vaultVariables_, i);
|
|
}
|
|
}
|
|
|
|
function getMultipleVaultsBranchesDebt(
|
|
address[] memory vaults_,
|
|
uint[] memory fromBranchIds_,
|
|
uint[] memory toBranchIds_
|
|
) external view returns (BranchesDebt[] memory branchesDebt_) {
|
|
uint length_ = vaults_.length;
|
|
|
|
branchesDebt_ = new BranchesDebt[](length_);
|
|
for (uint i = 0; i < length_; i++) {
|
|
branchesDebt_[i].branchDebt = getBranchesDebt(vaults_[i], fromBranchIds_[i], toBranchIds_[i]);
|
|
}
|
|
}
|
|
|
|
function getAllVaultsBranchesDebt() external view returns (BranchesDebt[] memory branchesDebt_) {
|
|
address[] memory vaults_ = VAULT_RESOLVER.getAllVaultsAddresses();
|
|
uint length_ = vaults_.length;
|
|
|
|
branchesDebt_ = new BranchesDebt[](length_);
|
|
for (uint i = 0; i < length_; i++) {
|
|
branchesDebt_[i].branchDebt = getBranchesDebt(vaults_[i], type(uint).max, 0);
|
|
}
|
|
}
|
|
|
|
function _populateTicksDebt(
|
|
address vault_,
|
|
int toTick_,
|
|
int mapId_,
|
|
uint tickHasDebt_,
|
|
uint count_,
|
|
uint vaultSupplyExchangePrice_,
|
|
uint vaultBorrowExchangePrice_
|
|
) internal view returns (TickDebt[] memory ticksDebt_) {
|
|
ticksDebt_ = new TickDebt[](count_);
|
|
|
|
count_ = 0; // reuse var for loop index counter
|
|
int nextTick_;
|
|
uint tickExistingRawDebt_;
|
|
uint ratio_;
|
|
uint collateralRaw_;
|
|
|
|
while (true) {
|
|
while (tickHasDebt_ > 0) {
|
|
{
|
|
uint msb_ = BigMathMinified.mostSignificantBit(tickHasDebt_);
|
|
// removing next tick from tickHasDebt
|
|
tickHasDebt_ = (tickHasDebt_ << (257 - msb_)) >> (257 - msb_);
|
|
nextTick_ = mapId_ * 256 + int(msb_ - 1);
|
|
}
|
|
if (nextTick_ < toTick_) {
|
|
return ticksDebt_;
|
|
}
|
|
tickExistingRawDebt_ = (VAULT_RESOLVER.getTickDataRaw(vault_, nextTick_) >> 25) & X64;
|
|
tickExistingRawDebt_ = (tickExistingRawDebt_ >> 8) << (tickExistingRawDebt_ & X8);
|
|
ratio_ = TickMath.getRatioAtTick(nextTick_);
|
|
collateralRaw_ = (tickExistingRawDebt_ * (1 << 96)) / ratio_;
|
|
ticksDebt_[count_++] = TickDebt({
|
|
debtRaw: tickExistingRawDebt_,
|
|
collateralRaw: collateralRaw_,
|
|
debtNormal: (tickExistingRawDebt_ * vaultBorrowExchangePrice_) / 1e12,
|
|
collateralNormal: (collateralRaw_ * vaultSupplyExchangePrice_) / 1e12,
|
|
ratio: ratio_,
|
|
tick: nextTick_
|
|
});
|
|
}
|
|
|
|
if (--mapId_ == -129) {
|
|
break;
|
|
}
|
|
|
|
tickHasDebt_ = VAULT_RESOLVER.getTickHasDebtRaw(vault_, mapId_);
|
|
}
|
|
}
|
|
|
|
function _tickHelper(uint tickRaw_) internal pure returns (int tick) {
|
|
require(tickRaw_ < X20, "invalid-number");
|
|
if (tickRaw_ > 0) {
|
|
tick = tickRaw_ & 1 == 1 ? int((tickRaw_ >> 1) & X19) : -int((tickRaw_ >> 1) & X19);
|
|
} else {
|
|
tick = type(int).min;
|
|
}
|
|
}
|
|
|
|
function _countTicksWithDebt(
|
|
address vault_,
|
|
int toTick_,
|
|
int mapId_,
|
|
uint tickHasDebt_
|
|
) internal view returns (uint count_) {
|
|
uint msb_;
|
|
int nextTick_;
|
|
while (true) {
|
|
while (tickHasDebt_ > 0) {
|
|
msb_ = BigMathMinified.mostSignificantBit(tickHasDebt_);
|
|
// removing next tick from tickHasDebt
|
|
tickHasDebt_ = (tickHasDebt_ << (257 - msb_)) >> (257 - msb_);
|
|
nextTick_ = mapId_ * 256 + int(msb_ - 1);
|
|
if (nextTick_ < toTick_) {
|
|
return count_;
|
|
}
|
|
count_++;
|
|
}
|
|
|
|
if (--mapId_ == -129) {
|
|
break;
|
|
}
|
|
tickHasDebt_ = VAULT_RESOLVER.getTickHasDebtRaw(vault_, mapId_);
|
|
}
|
|
return count_;
|
|
}
|
|
|
|
function _getBranchDebt(
|
|
address vault_,
|
|
uint vaultVariables_,
|
|
uint branchId_
|
|
) internal view returns (BranchDebt memory) {
|
|
uint currentBranchData_ = VAULT_RESOLVER.getBranchDataRaw(vault_, branchId_);
|
|
|
|
int minimaTick_ = _tickHelper((currentBranchData_ >> 2) & X20);
|
|
uint status_ = currentBranchData_ & 3;
|
|
|
|
if (status_ == 0) {
|
|
// not liquidated status == 0
|
|
// only current branch can be non-liquidated branch
|
|
return _getActiveBranchDebt(vaultVariables_, currentBranchData_, branchId_, status_);
|
|
} else if (status_ == 1) {
|
|
// liquidated status == 1
|
|
return _getLiquidatedBranchDebt(vault_, currentBranchData_, branchId_, status_, minimaTick_);
|
|
} else {
|
|
// merged status == 2
|
|
// absorbed status == 3
|
|
return _getClosedOrMergedBranchDebt(currentBranchData_, branchId_, status_);
|
|
}
|
|
}
|
|
|
|
function _getActiveBranchDebt(
|
|
uint vaultVariables_,
|
|
uint currentBranchData_,
|
|
uint branchId_,
|
|
uint status_
|
|
) internal pure returns (BranchDebt memory branchDebt_) {
|
|
int topTick_ = _tickHelper((vaultVariables_ >> 2) & X20);
|
|
|
|
uint ratio_ = topTick_ > type(int).min ? TickMath.getRatioAtTick(topTick_) : 0;
|
|
|
|
branchDebt_ = BranchDebt({
|
|
debtRaw: 0,
|
|
collateralRaw: 0,
|
|
debtNormal: 0,
|
|
collateralNormal: 0,
|
|
branchId: branchId_,
|
|
status: status_, // active status
|
|
tick: topTick_, // as branch is not liquidated, just returning topTick for now, as whenever liquidation starts it'll start from topTick
|
|
partials: 0,
|
|
ratio: ratio_,
|
|
debtFactor: (currentBranchData_ >> 116) & X50,
|
|
baseBranchId: ((currentBranchData_ >> 166) & X30),
|
|
baseBranchTick: _tickHelper((currentBranchData_ >> 196) & X20) // if == type(int).min, then current branch is master branch
|
|
});
|
|
}
|
|
|
|
function _getClosedOrMergedBranchDebt(
|
|
uint currentBranchData_,
|
|
uint branchId_,
|
|
uint status_
|
|
) internal pure returns (BranchDebt memory branchDebt_) {
|
|
int baseBranchTick_ = _tickHelper((currentBranchData_ >> 196) & X20);
|
|
uint ratio_ = baseBranchTick_ > type(int).min ? TickMath.getRatioAtTick(baseBranchTick_) : 0;
|
|
|
|
branchDebt_ = BranchDebt({
|
|
debtRaw: 0,
|
|
collateralRaw: 0,
|
|
debtNormal: 0,
|
|
collateralNormal: 0,
|
|
branchId: branchId_,
|
|
status: status_,
|
|
tick: baseBranchTick_, // as branch is merged/closed, so adding baseBranchTick_ as this is where it went out of existance
|
|
partials: 0,
|
|
ratio: ratio_,
|
|
debtFactor: (currentBranchData_ >> 116) & X50,
|
|
baseBranchId: ((currentBranchData_ >> 166) & X30),
|
|
baseBranchTick: baseBranchTick_ // if == type(int).min, then current branch is master branch
|
|
});
|
|
}
|
|
|
|
function _getLiquidatedBranchDebt(
|
|
address vault_,
|
|
uint currentBranchData_,
|
|
uint branchId_,
|
|
uint status_,
|
|
int minimaTick_
|
|
) internal view returns (BranchDebt memory branchDebt_) {
|
|
uint debtLiquidity_ = BigMathMinified.fromBigNumber((currentBranchData_ >> 52) & X64, 8, X8);
|
|
(uint collateralRaw_, uint ratio_) = _getCollateralRaw(currentBranchData_, debtLiquidity_, minimaTick_);
|
|
|
|
(, , uint256 vaultSupplyExchangePrice_, uint256 vaultBorrowExchangePrice_) = IFluidVaultT1(vault_)
|
|
.updateExchangePrices(VAULT_RESOLVER.getVaultVariables2Raw(vault_));
|
|
|
|
branchDebt_ = BranchDebt({
|
|
debtRaw: debtLiquidity_,
|
|
collateralRaw: collateralRaw_,
|
|
debtNormal: (debtLiquidity_ * vaultBorrowExchangePrice_) / 1e12,
|
|
collateralNormal: (collateralRaw_ * vaultSupplyExchangePrice_) / 1e12,
|
|
branchId: branchId_,
|
|
status: status_,
|
|
tick: minimaTick_, // as branch is merged/closed, so adding baseBranchTick_ as this is where it went out of existance
|
|
partials: 0,
|
|
ratio: ratio_,
|
|
debtFactor: (currentBranchData_ >> 116) & X50,
|
|
baseBranchId: ((currentBranchData_ >> 166) & X30),
|
|
baseBranchTick: _tickHelper((currentBranchData_ >> 196) & X20) // if == type(int).min, then current branch is master branch
|
|
});
|
|
}
|
|
|
|
function _getCollateralRaw(
|
|
uint currentBranchData_,
|
|
uint debtLiquidity_,
|
|
int minimaTick_
|
|
) internal pure returns (uint collateralRaw_, uint ratio_) {
|
|
ratio_ = TickMath.getRatioAtTick(int24(minimaTick_));
|
|
uint ratioOneLess_ = (ratio_ * 10000) / 10015;
|
|
uint length_ = ratio_ - ratioOneLess_;
|
|
uint partials_ = (currentBranchData_ >> 22) & X30;
|
|
uint currentRatio_ = ratioOneLess_ + ((length_ * partials_) / X30);
|
|
collateralRaw_ = (debtLiquidity_ * TickMath.ZERO_TICK_SCALED_RATIO) / currentRatio_;
|
|
}
|
|
}
|