Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 1,017 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Operate | 23498856 | 160 days ago | IN | 0 ETH | 0.000194 | ||||
| Operate | 23498832 | 160 days ago | IN | 279.9 ETH | 0.00024712 | ||||
| Operate | 23225790 | 198 days ago | IN | 0 ETH | 0.00047205 | ||||
| Operate | 22669776 | 276 days ago | IN | 0 ETH | 0.0003398 | ||||
| Operate | 22669772 | 276 days ago | IN | 0 ETH | 0.00048655 | ||||
| Operate | 22666880 | 276 days ago | IN | 0 ETH | 0.00104503 | ||||
| Operate | 22192835 | 342 days ago | IN | 0 ETH | 0.00008617 | ||||
| Operate | 22192831 | 342 days ago | IN | 0 ETH | 0.00012637 | ||||
| Operate | 22076303 | 359 days ago | IN | 0.00415041 ETH | 0.00008696 | ||||
| Operate | 22018144 | 367 days ago | IN | 40 ETH | 0.00210226 | ||||
| Operate | 22018101 | 367 days ago | IN | 0 ETH | 0.00302254 | ||||
| Operate | 21829197 | 393 days ago | IN | 0.001 ETH | 0.00087426 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026169 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026169 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026169 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026169 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026169 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026173 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026179 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026183 | ||||
| Absorb | 21776922 | 401 days ago | IN | 0 ETH | 0.00026152 | ||||
| Absorb | 21776921 | 401 days ago | IN | 0 ETH | 0.0002685 | ||||
| Absorb | 21776918 | 401 days ago | IN | 0 ETH | 0.0002308 | ||||
| Absorb | 21776918 | 401 days ago | IN | 0 ETH | 0.0002308 | ||||
| Absorb | 21776914 | 401 days ago | IN | 0 ETH | 0.00023344 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Operate | 23498832 | 160 days ago | 279.9 ETH | ||||
| Operate | 23225779 | 198 days ago | 0.05314211 ETH | ||||
| Operate | 23225779 | 198 days ago | 0.05314211 ETH | ||||
| Operate | 22076303 | 359 days ago | 0.00415041 ETH | ||||
| Operate | 22018144 | 367 days ago | 40 ETH | ||||
| Operate | 22007594 | 368 days ago | 5.7 ETH | ||||
| Operate | 22007594 | 368 days ago | 5.7 ETH | ||||
| Operate | 21760481 | 403 days ago | 50 ETH | ||||
| Operate | 21662923 | 416 days ago | 16.31236563 ETH | ||||
| Operate | 21662923 | 416 days ago | 16.31236563 ETH | ||||
| Operate | 21526878 | 435 days ago | 0.05 ETH | ||||
| Operate | 21526878 | 435 days ago | 0.05 ETH | ||||
| Operate | 21488933 | 441 days ago | 63 ETH | ||||
| Operate | 21462072 | 444 days ago | 0.6 ETH | ||||
| Operate | 21462014 | 444 days ago | 2.1 ETH | ||||
| Operate | 21268500 | 472 days ago | 2 ETH | ||||
| Operate | 21210097 | 480 days ago | 1.13 ETH | ||||
| Operate | 21207111 | 480 days ago | 0.5 ETH | ||||
| Operate | 21204048 | 481 days ago | 1 ETH | ||||
| Operate | 21183956 | 483 days ago | 0.5 ETH | ||||
| Operate | 21183924 | 483 days ago | 1.2 ETH | ||||
| Operate | 21101035 | 495 days ago | 800 ETH | ||||
| Operate | 21003806 | 508 days ago | 0.0222 ETH | ||||
| Operate | 21003806 | 508 days ago | 0.0222 ETH | ||||
| Operate | 20990966 | 510 days ago | 78 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
FluidVaultT1
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 10000000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IFluidOracle } from "../../../../oracle/fluidOracle.sol";
import { TickMath } from "../../../../libraries/tickMath.sol";
import { BigMathMinified } from "../../../../libraries/bigMathMinified.sol";
import { BigMathVault } from "../../../../libraries/bigMathVault.sol";
import { LiquidityCalcs } from "../../../../libraries/liquidityCalcs.sol";
import { SafeTransfer } from "../../../../libraries/safeTransfer.sol";
import { Helpers } from "./helpers.sol";
import { LiquiditySlotsLink } from "../../../../libraries/liquiditySlotsLink.sol";
import { ErrorTypes } from "../../errorTypes.sol";
/// @notice Fluid "VaultT1" (Vault Type 1). Fluid vault protocol main contract.
/// Fluid Vault protocol is a borrow / lending protocol, allowing users to create collateral / borrow positions.
/// All funds are deposited into / borrowed from Fluid Liquidity layer.
/// Positions are represented through NFTs minted by the VaultFactory.
/// Deployed by "VaultFactory" and linked together with VaultT1 AdminModule `ADMIN_IMPLEMENTATION` and
/// FluidVaultT1Secondary (main2.sol) `SECONDARY_IMPLEMENTATION`.
/// AdminModule & FluidVaultT1Secondary methods are delegateCalled, if the msg.sender has the required authorization.
/// This contract links to an Oracle, which is used to assess collateral / debt value. Oracles implement the
/// "FluidOracle" base contract and return the price in 1e27 precision.
/// @dev For view methods / accessing data, use the "VaultResolver" periphery contract.
contract FluidVaultT1 is Helpers {
using BigMathMinified for uint256;
using BigMathVault for uint256;
/// @dev Single function which handles supply, withdraw, borrow & payback
/// @param nftId_ NFT ID for interaction. If 0 then create new NFT/position.
/// @param newCol_ new collateral. If positive then deposit, if negative then withdraw, if 0 then do nohing
/// @param newDebt_ new debt. If positive then borrow, if negative then payback, if 0 then do nohing
/// @param to_ address where withdraw or borrow should go. If address(0) then msg.sender
/// @return nftId_ if 0 then this returns the newly created NFT Id else returns the same NFT ID
/// @return newCol_ final supply amount. Mainly if max withdraw using type(int).min then this is useful to get perfect amount else remain same as newCol_
/// @return newDebt_ final borrow amount. Mainly if max payback using type(int).min then this is useful to get perfect amount else remain same as newDebt_
function operate(
uint256 nftId_, // if 0 then new position
int256 newCol_, // if negative then withdraw
int256 newDebt_, // if negative then payback
address to_ // address at which the borrow & withdraw amount should go to. If address(0) then it'll go to msg.sender
)
public
payable
returns (
uint256, // nftId_
int256, // final supply amount. if - then withdraw
int256 // final borrow amount. if - then payback
)
{
uint256 vaultVariables_ = vaultVariables;
// re-entrancy check
if (vaultVariables_ & 1 == 0) {
// Updating on storage
vaultVariables = vaultVariables_ | 1;
} else {
revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);
}
if (
(newCol_ == 0 && newDebt_ == 0) ||
// withdrawal or deposit cannot be too small
((newCol_ != 0) && (newCol_ > -10000 && newCol_ < 10000)) ||
// borrow or payback cannot be too small
((newDebt_ != 0) && (newDebt_ > -10000 && newDebt_ < 10000))
) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidOperateAmount);
}
// Check msg.value aligns with input amounts if supply or borrow token is native token.
// Note that it's not possible for a vault to have both supply token and borrow token as native token.
if (SUPPLY_TOKEN == NATIVE_TOKEN && newCol_ > 0) {
if (uint(newCol_) != msg.value) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);
}
} else if (msg.value > 0) {
if (!(BORROW_TOKEN == NATIVE_TOKEN && newDebt_ < 0)) {
// msg.value sent along for withdraw, borrow, or non-native token operations
revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);
}
}
OperateMemoryVars memory o_;
// Temporary variables used as helpers at many places
uint256 temp_;
uint256 temp2_;
int256 temp3_;
o_.vaultVariables2 = vaultVariables2;
temp_ = (vaultVariables_ >> 2) & X20;
unchecked {
o_.topTick = (temp_ == 0) ? type(int).min : ((temp_ & 1) == 1)
? int((temp_ >> 1) & X19)
: -int((temp_ >> 1) & X19);
}
{
// Fetching user's position
if (nftId_ == 0) {
// creating new position.
o_.tick = type(int).min;
// minting new NFT vault for user.
nftId_ = VAULT_FACTORY.mint(VAULT_ID, msg.sender);
// Adding 1 in total positions. Total positions cannot exceed 32bits as NFT minting checks for that
unchecked {
vaultVariables_ = vaultVariables_ + (1 << 210);
}
} else {
// Updating existing position
// checking owner only in case of withdraw or borrow
if ((newCol_ < 0 || newDebt_ > 0) && (VAULT_FACTORY.ownerOf(nftId_) != msg.sender)) {
revert FluidVaultError(ErrorTypes.VaultT1__NotAnOwner);
}
// temp_ => user's position data
temp_ = positionData[nftId_];
if (temp_ == 0) {
revert FluidVaultError(ErrorTypes.VaultT1__NftNotOfThisVault);
}
// temp2_ => user's supply amount
temp2_ = (temp_ >> 45) & X64;
// Converting big number into normal number
o_.colRaw = (temp2_ >> 8) << (temp2_ & X8);
// temp2_ => user's dust debt amount
temp2_ = (temp_ >> 109) & X64;
// Converting big number into normal number
o_.dustDebtRaw = (temp2_ >> 8) << (temp2_ & X8);
// 1 is supply & 0 is borrow
if (temp_ & 1 == 1) {
// only supply position (has no debt)
o_.tick = type(int).min;
} else {
// borrow position (has collateral & debt)
unchecked {
o_.tick = temp_ & 2 == 2 ? int((temp_ >> 2) & X19) : -int((temp_ >> 2) & X19);
}
o_.tickId = (temp_ >> 21) & X24;
}
}
}
// Get latest updated Position's debt & supply (if position is with debt -> not new / supply position)
if (o_.tick > type(int).min) {
// if entering this if statement then temp_ here will always be user's position data
// extracting collateral exponent
temp_ = (temp_ >> 45) & X8;
// if exponent is > 0 then rounding up the collateral just for calculating debt
unchecked {
temp_ = temp_ == 0 ? (o_.colRaw + 1) : o_.colRaw + (1 << temp_);
}
// fetch current debt
o_.debtRaw = ((TickMath.getRatioAtTick(int24(o_.tick)) * temp_) >> 96) + 1;
// Tick data from user's tick
temp_ = tickData[o_.tick];
// Checking if tick is liquidated (first bit 1) OR if the total IDs of tick is greater than user's tick ID
if (((temp_ & 1) == 1) || (((temp_ >> 1) & X24) > o_.tickId)) {
// User got liquidated
(
// returns the position of the user if the user got liquidated.
o_.tick,
o_.debtRaw,
o_.colRaw,
temp2_, // final branchId from liquidation where position exist right now
o_.branchData
) = fetchLatestPosition(o_.tick, o_.tickId, o_.debtRaw, temp_);
if (o_.debtRaw > o_.dustDebtRaw) {
// temp_ => branch's Debt
temp_ = (o_.branchData >> 52) & X64;
temp_ = (temp_ >> 8) << (temp_ & X8);
// o_.debtRaw should always be < branch's Debt (temp_).
// Taking margin (0.01%) in fetchLatestPosition to make sure it's always less
temp_ -= o_.debtRaw;
if (temp_ < 100) {
// explicitly making sure that branch debt/liquidity doesn't get super low.
temp_ = 100;
}
// Inserting updated branch's debt
branchData[temp2_] =
(o_.branchData & 0xfffffffffffffffffffffffffffffffffff0000000000000000fffffffffffff) |
(temp_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52);
unchecked {
// Converted positionRawDebt_ in net position debt
o_.debtRaw -= o_.dustDebtRaw;
}
} else {
// Liquidated 100% or almost 100%
// absorbing dust debt
absorbedDustDebt = absorbedDustDebt + o_.dustDebtRaw - o_.debtRaw;
o_.debtRaw = 0;
o_.colRaw = 0;
}
} else {
// User didn't got liquidated
// Removing user's debt from tick data
// temp2_ => debt in tick
temp2_ = (temp_ >> 25) & X64;
// below require can fail when a user liquidity is extremely low (talking about way less than even $1)
// adding require meaning this vault user won't be able to interact unless someone makes the liquidity in tick as non 0.
// reason of adding is the tick has already removed from everywhere. Can removing it again break something? Better to simply remove that case entirely
if (temp2_ == 0) {
revert FluidVaultError(ErrorTypes.VaultT1__TickIsEmpty);
}
// Converting big number into normal number
temp2_ = (temp2_ >> 8) << (temp2_ & X8);
// debtInTick (temp2_) < debtToRemove (o_.debtRaw) that means minor precision error. Hence make the debtInTick as 0.
// The precision error can be caused with Bigmath library limiting the precision to 2**56.
unchecked {
temp2_ = o_.debtRaw < temp2_ ? temp2_ - o_.debtRaw : 0;
}
if (temp2_ < 10000) {
temp2_ = 0;
// if debt becomes 0 then remove from tick has debt
if (o_.tick == o_.topTick) {
// if tick is top tick then current top tick is perfect tick -> fetch & set new top tick
// Updating new top tick in vaultVariables_ and topTick_
(vaultVariables_, o_.topTick) = _setNewTopTick(o_.topTick, vaultVariables_);
}
// Removing from tickHasDebt
_updateTickHasDebt(o_.tick, false);
}
tickData[o_.tick] = (temp_ & X25) | (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 25);
// Converted positionRawDebt_ in net position debt
o_.debtRaw -= o_.dustDebtRaw;
}
o_.dustDebtRaw = 0;
}
// Setting the current tick into old tick as the position tick is going to change now.
o_.oldTick = o_.tick;
o_.oldColRaw = o_.colRaw;
o_.oldNetDebtRaw = o_.debtRaw;
{
(o_.liquidityExPrice, , o_.supplyExPrice, o_.borrowExPrice) = updateExchangePrices(o_.vaultVariables2);
{
// supply or withdraw
if (newCol_ > 0) {
// supply new col, rounding down
o_.colRaw += (uint256(newCol_) * EXCHANGE_PRICES_PRECISION) / o_.supplyExPrice;
// final user's collateral should not be above 2**128 bits
if (o_.colRaw > X128) {
revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);
}
} else if (newCol_ < 0) {
// if withdraw equals type(int).min then max withdraw
if (newCol_ > type(int128).min) {
// partial withdraw, rounding up removing extra wei from collateral
temp3_ = ((newCol_ * int(EXCHANGE_PRICES_PRECISION)) / int256(o_.supplyExPrice)) - 1;
unchecked {
if (uint256(-temp3_) > o_.colRaw) {
revert FluidVaultError(ErrorTypes.VaultT1__ExcessCollateralWithdrawal);
}
o_.colRaw -= uint256(-temp3_);
}
} else if (newCol_ == type(int).min) {
// max withdraw, rounding up:
// adding +1 to negative withdrawAmount newCol_ for safe rounding (reducing withdraw)
newCol_ = -(int256((o_.colRaw * o_.supplyExPrice) / EXCHANGE_PRICES_PRECISION)) + 1;
o_.colRaw = 0;
} else {
revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);
}
}
}
{
// borrow or payback
if (newDebt_ > 0) {
// borrow new debt, rounding up adding extra wei in debt
temp_ = ((uint(newDebt_) * EXCHANGE_PRICES_PRECISION) / o_.borrowExPrice) + 1;
// if borrow fee is 0 then it'll become temp_ + 0.
// Only adding fee in o_.debtRaw and not in newDebt_ as newDebt_ is debt that needs to be borrowed from Liquidity
// as we have added fee in debtRaw hence it will get added in user's position & vault's total borrow.
// It can be collected with rebalance function.
o_.debtRaw += temp_ + (temp_ * ((o_.vaultVariables2 >> 82) & X10)) / 10000;
// final user's debt should not be above 2**128 bits
if (o_.debtRaw > X128) {
revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);
}
} else if (newDebt_ < 0) {
// if payback equals type(int).min then max payback
if (newDebt_ > type(int128).min) {
// partial payback.
// temp3_ => newDebt_ in raw terms, safe rounding up negative amount to rounding reduce payback
temp3_ = (newDebt_ * int256(EXCHANGE_PRICES_PRECISION)) / int256(o_.borrowExPrice) + 1;
unchecked {
temp3_ = -temp3_;
if (uint256(temp3_) > o_.debtRaw) {
revert FluidVaultError(ErrorTypes.VaultT1__ExcessDebtPayback);
}
o_.debtRaw -= uint256(temp3_);
}
} else if (newDebt_ == type(int).min) {
// max payback, rounding up amount that will be transferred in to pay back full debt:
// subtracting -1 of negative debtAmount newDebt_ for safe rounding (increasing payback)
newDebt_ = -(int256((o_.debtRaw * o_.borrowExPrice) / EXCHANGE_PRICES_PRECISION)) - 1;
o_.debtRaw = 0;
} else {
revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);
}
}
}
}
// if position has no collateral or debt and user sends type(int).min for withdraw and payback then this results in 0
// there's is no issue if it stays 0 but better to throw here to avoid checking for potential issues if there could be
if (newCol_ == 0 && newDebt_ == 0) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidOperateAmount);
}
// Assign new tick
if (o_.debtRaw > 0) {
// updating tickHasDebt in the below function if required
// o_.debtRaw here is updated to new debt raw incl. dust debt (not net debt)
unchecked {
(o_.tick, o_.tickId, o_.debtRaw, o_.dustDebtRaw) = _addDebtToTickWrite(
o_.colRaw,
((o_.debtRaw * 1000000001) / 1000000000) + 1
);
}
if (newDebt_ < 0) {
// anyone can payback debt of any position
// hence, explicitly checking the debt should decrease
if ((o_.debtRaw - o_.dustDebtRaw) > o_.oldNetDebtRaw) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidPaybackOrDeposit);
}
}
if ((newCol_ > 0) && (newDebt_ == 0)) {
// anyone can deposit collateral in any position
// Hence, explicitly checking that new ratio should be less than old ratio
if (
(((o_.debtRaw - o_.dustDebtRaw) * TickMath.ZERO_TICK_SCALED_RATIO) / o_.colRaw) >
((o_.oldNetDebtRaw * TickMath.ZERO_TICK_SCALED_RATIO) / o_.oldColRaw)
) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidPaybackOrDeposit);
}
}
if (o_.tick >= o_.topTick) {
// Updating topTick in storage
// temp_ => tick to insert in vault variables
unchecked {
temp_ = o_.tick < 0 ? uint(-o_.tick) << 1 : (uint(o_.tick) << 1) | 1;
}
if (vaultVariables_ & 2 == 0) {
// Current branch not liquidated. Hence, just update top tick
vaultVariables_ =
(vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000) |
(temp_ << 2);
} else {
// Current branch liquidated
// Initialize a new branch
// temp2_ => totalBranchId_
unchecked {
temp2_ = ((vaultVariables_ >> 52) & X30) + 1; // would take 34 years to overflow if a new branch is created every second
}
// Connecting new active branch with current active branch which is now base branch
// Current top tick is now base branch's minima tick
branchData[temp2_] =
(((vaultVariables_ >> 22) & X30) << 166) | // current branch id set as base branch id
(((vaultVariables_ >> 2) & X20) << 196); // current top tick set as base branch minima tick
// Updating new vault variables in memory with new branch
vaultVariables_ =
(vaultVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00000000000000000000) |
(temp_ << 2) | // new top tick
(temp2_ << 22) | // new branch id
(temp2_ << 52); // total branch ids
}
}
} else {
// debtRaw_ remains 0 in this situation
// This kind of position will not have any tick. Meaning it'll be a supply position.
o_.tick = type(int).min;
}
{
if (newCol_ < 0 || newDebt_ > 0) {
// withdraw or borrow
if (to_ == address(0)) {
to_ = msg.sender;
}
// if debt is greater than 0 & transaction includes borrow or withdraw (incl. combinations such as deposit + borrow etc.)
// -> check collateral factor
if (o_.debtRaw > 0) {
// Oracle returns price at 100% ratio.
// converting oracle 160 bits into oracle address
// temp_ => debt price w.r.t to col in 1e27
temp_ = IFluidOracle(address(uint160(o_.vaultVariables2 >> 96))).getExchangeRate();
// Note if price would come back as 0 `getTickAtRatio` will fail
// Converting price in terms of raw amounts
temp_ = (temp_ * o_.supplyExPrice) / o_.borrowExPrice;
unchecked {
// temp2_ => ratio at CF. CF is in 3 decimals. 900 = 90%
temp2_ = ((temp_ * ((o_.vaultVariables2 >> 32) & X10)) / 1000);
// Price from oracle is in 1e27 decimals. Converting it into (1 << 96) decimals
temp2_ = (temp2_ < 1e45)
? ((temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27)
: (temp2_ / 1e27) * TickMath.ZERO_TICK_SCALED_RATIO;
}
// temp3_ => tickAtCF_
(temp3_, ) = TickMath.getTickAtRatio(temp2_);
if (o_.tick > temp3_) {
unchecked {
// calc for net debt can be unchecked as o_.dustDebtRaw can not be > o_.debtRaw:
// o_.dustDebtRaw is the result of o_.debtRaw - x where x > 0 see _addDebtToTickWrite()
if (
o_.oldTick <= o_.tick ||
(o_.debtRaw - o_.dustDebtRaw) > (((o_.oldNetDebtRaw * 1000000001) / 1000000000) + 1)
) {
// Above CF, user should only be allowed to reduce ratio either by paying debt or by depositing more collateral
// Not comparing collateral as user can potentially use safe/deleverage to reduce tick & debt.
// On use of safe/deleverage, collateral will decrease but debt will decrease as well making the overall position safer.
revert FluidVaultError(ErrorTypes.VaultT1__PositionAboveCF);
}
}
}
}
}
}
{
// Updating user's new position on storage
// temp_ => tick to insert as user position tick
if (o_.tick > type(int).min) {
unchecked {
temp_ = o_.tick < 0 ? (uint(-o_.tick) << 1) : ((uint(o_.tick) << 1) | 1);
}
} else {
// if positionTick_ = type(int).min OR positionRawDebt_ == 0 then that means it's only supply position
// (for case of positionRawDebt_ == 0, tick is set to type(int).min further up)
temp_ = 0;
}
positionData[nftId_] =
((temp_ == 0) ? 1 : 0) | // setting if supply only position (1) or not (first bit)
(temp_ << 1) |
(o_.tickId << 21) |
(o_.colRaw.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 45) |
// dust debt is rounded down because user debt = debt - dustDebt. rounding up would mean we reduce user debt
(o_.dustDebtRaw.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 109);
}
// Withdrawal gap to make sure there's always liquidity for liquidation
// For example if withdrawal allowance is 15% on liquidity then we can limit operate's withdrawal allowance to 10%
// this will allow liquidate function to get extra 5% buffer for potential liquidations.
if (newCol_ < 0) {
// extracting withdrawal gap which is in 0.1% precision.
temp_ = (o_.vaultVariables2 >> 62) & X10;
if (temp_ > 0) {
// fetching user's supply slot data
o_.userSupplyLiquidityData = LIQUIDITY.readFromStorage(LIQUIDITY_USER_SUPPLY_SLOT);
// converting current user's supply from big number to normal
temp2_ = (o_.userSupplyLiquidityData >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;
temp2_ = (temp2_ >> 8) << (temp2_ & X8);
// fetching liquidity's withdrawal limit
temp3_ = int(LiquidityCalcs.calcWithdrawalLimitBeforeOperate(o_.userSupplyLiquidityData, temp2_));
// max the number could go is vault's supply * 1000. Overflowing is almost impossible.
unchecked {
// (liquidityUserSupply - withdrawalGap - liquidityWithdrawaLimit) should be less than user's withdrawal
if (
(temp3_ > 0) &&
(((int(temp2_ * (1000 - temp_)) / 1000)) - temp3_) <
(((-newCol_) * int(EXCHANGE_PRICES_PRECISION)) / int(o_.liquidityExPrice))
) {
revert FluidVaultError(ErrorTypes.VaultT1__WithdrawMoreThanOperateLimit);
}
}
}
}
{
// execute actions at Liquidity: deposit & payback is first and then withdraw & borrow
if (newCol_ > 0) {
// deposit
LIQUIDITY.operate{ value: SUPPLY_TOKEN == NATIVE_TOKEN ? msg.value : 0 }(
SUPPLY_TOKEN,
newCol_,
0,
address(0),
address(0),
abi.encode(msg.sender)
);
}
if (newDebt_ < 0) {
if (BORROW_TOKEN == NATIVE_TOKEN) {
unchecked {
temp_ = uint(-newDebt_);
if (msg.value > temp_) {
SafeTransfer.safeTransferNative(msg.sender, msg.value - temp_);
} else if (msg.value < temp_) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);
}
}
} else {
temp_ = 0;
}
// payback
LIQUIDITY.operate{ value: temp_ }(
BORROW_TOKEN,
0,
newDebt_,
address(0),
address(0),
abi.encode(msg.sender)
);
}
if (newCol_ < 0) {
// withdraw
LIQUIDITY.operate(SUPPLY_TOKEN, newCol_, 0, to_, address(0), new bytes(0));
}
if (newDebt_ > 0) {
// borrow
LIQUIDITY.operate(BORROW_TOKEN, 0, newDebt_, address(0), to_, new bytes(0));
}
}
{
// Updating vault variables on storage
// Calculating new total collateral & total debt.
temp_ = (vaultVariables_ >> 82) & X64;
temp_ = ((temp_ >> 8) << (temp_ & X8)) + o_.colRaw - o_.oldColRaw;
temp2_ = (vaultVariables_ >> 146) & X64;
temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) + (o_.debtRaw - o_.dustDebtRaw) - o_.oldNetDebtRaw;
// Updating vault variables on storage. This will also reentrancy 0 back again
// Converting total supply & total borrow in 64 bits (56 | 8) bignumber
vaultVariables =
(vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) |
(temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply
(temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow
}
emit LogOperate(msg.sender, nftId_, newCol_, newDebt_, to_);
return (nftId_, newCol_, newDebt_);
}
/// @dev allows to liquidate all bad debt of all users at once. Liquidator can also liquidate partially any amount they want.
/// @param debtAmt_ total debt to liquidate (aka debt token to swap into collateral token)
/// @param colPerUnitDebt_ minimum collateral token per unit of debt in 1e18 decimals
/// @param to_ address at which collateral token should go to.
/// If dead address (0x000000000000000000000000000000000000dEaD) then reverts with custom error "FluidLiquidateResult"
/// returning the actual collateral and actual debt liquidated. Useful to find max liquidatable amounts via try / catch.
/// @param absorb_ if true then liquidate from absorbed first
/// @return actualDebtAmt_ if liquidator sends debtAmt_ more than debt remaining to liquidate then actualDebtAmt_ changes from debtAmt_ else remains same
/// @return actualColAmt_ total liquidated collateral which liquidator will get
function liquidate(
uint256 debtAmt_,
uint256 colPerUnitDebt_, // min collateral needed per unit of debt in 1e18
address to_,
bool absorb_
) public payable returns (uint actualDebtAmt_, uint actualColAmt_) {
LiquidateMemoryVars memory memoryVars_;
uint vaultVariables_ = vaultVariables;
// ############# turning re-entrancy bit on #############
if (vaultVariables_ & 1 == 0) {
// Updating on storage
vaultVariables = vaultVariables_ | 1;
} else {
revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);
}
if (debtAmt_ < 10000 || debtAmt_ > X128) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidationAmt);
}
if (BORROW_TOKEN == NATIVE_TOKEN) {
if ((msg.value != debtAmt_) && (to_ != 0x000000000000000000000000000000000000dEaD)) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueLiquidate);
}
} else if (msg.value > 0) {
revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueLiquidate);
}
memoryVars_.vaultVariables2 = vaultVariables2;
if (((vaultVariables_ >> 2) & X20) == 0) {
revert FluidVaultError(ErrorTypes.VaultT1__TopTickDoesNotExist);
}
// Below are exchange prices of vaults
(, , memoryVars_.supplyExPrice, memoryVars_.borrowExPrice) = updateExchangePrices(memoryVars_.vaultVariables2);
CurrentLiquidity memory currentData_;
BranchData memory branch_;
// Temporary holder variables, used many times for different small things
uint temp_;
uint temp2_;
{
// ############# Setting current branch in memory #############
// Updating branch related data
branch_.id = (vaultVariables_ >> 22) & X30;
branch_.data = branchData[branch_.id];
branch_.debtFactor = (branch_.data >> 116) & X50;
if (branch_.debtFactor == 0) {
// Initializing branch debt factor. 35 | 15 bit number. Where full 35 bits and 15th bit is occupied.
// Making the total number as (2**35 - 1) << 2**14.
// note: initial debt factor can be any number.
branch_.debtFactor = ((X35 << 15) | (1 << 14));
}
// fetching base branch's minima tick. if 0 that means it's a master branch
temp_ = (branch_.data >> 196) & X20;
if (temp_ > 0) {
unchecked {
branch_.minimaTick = (temp_ & 1) == 1 ? int256((temp_ >> 1) & X19) : -int256((temp_ >> 1) & X19);
}
} else {
branch_.minimaTick = type(int).min;
}
}
// extracting top tick as top tick will be the current tick
unchecked {
currentData_.tick = (vaultVariables_ & 4) == 4
? int256((vaultVariables_ >> 3) & X19)
: -int256((vaultVariables_ >> 3) & X19);
}
// setting up status if top tick is liquidated or not
currentData_.tickStatus = vaultVariables_ & 2 == 0 ? 1 : 2;
// Tick info is mainly used as a place holder to store temporary tick related data
// (it can be current or ref using same memory variable)
TickData memory tickInfo_;
tickInfo_.tick = currentData_.tick;
{
// ############# Oracle related stuff #############
// Col price w.r.t debt. For example: 1 ETH = 1000 DAI
// temp_ -> debtPerCol
temp_ = IFluidOracle(address(uint160(memoryVars_.vaultVariables2 >> 96))).getExchangeRate(); // Price in 27 decimals
// temp_ -> debtPerCol Converting in terms of raw amount
temp_ = (temp_ * memoryVars_.supplyExPrice) / memoryVars_.borrowExPrice;
// temp2_ -> Raw colPerDebt_ in 27 decimals
temp2_ = 1e54 / temp_;
// temp2_ can never be > 1e54
// Oracle price should never be > 1e54
unchecked {
// Liquidation penalty in 4 decimals (1e2 = 1%) (max: 10.23%) -> (vaultVariables2_ >> 72) & X10
currentData_.colPerDebt = (temp2_ * (10000 + ((memoryVars_.vaultVariables2 >> 72) & X10))) / 10000;
// get liquidiation tick (tick at liquidation threshold ratio)
// Liquidation threshold in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 42) & X10
// Dividing by 1e27 to convert temp_ into normal number
temp_ = (temp_ < 1e45)
? ((temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27)
: ((temp_ / 1e27) * TickMath.ZERO_TICK_SCALED_RATIO);
// temp2_ -> liquidationRatio_
temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 42) & X10)) / 1000;
}
(memoryVars_.liquidationTick, ) = TickMath.getTickAtRatio(temp2_);
// get liquidiation max limit tick (tick at liquidation max limit ratio)
// Max limit in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 52) & X10
// temp2_ -> maxRatio_
unchecked {
temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 52) & X10)) / 1000;
}
(memoryVars_.maxTick, ) = TickMath.getTickAtRatio(temp2_);
}
// debtAmt_ should be less than 2**128 & EXCHANGE_PRICES_PRECISION is 1e12
unchecked {
currentData_.debtRemaining = (debtAmt_ * EXCHANGE_PRICES_PRECISION) / memoryVars_.borrowExPrice;
}
if (absorb_) {
temp_ = absorbedLiquidity;
// temp2_ -> absorbed col
temp2_ = (temp_ >> 128) & X128;
// temp_ -> absorbed debt
temp_ = temp_ & X128;
if (temp_ > currentData_.debtRemaining) {
// Removing collateral in equal proportion as debt
currentData_.totalColLiq = ((temp2_ * currentData_.debtRemaining) / temp_);
temp2_ -= currentData_.totalColLiq;
// Removing debt
currentData_.totalDebtLiq = currentData_.debtRemaining;
unchecked {
temp_ -= currentData_.debtRemaining;
}
currentData_.debtRemaining = 0;
// updating on storage
absorbedLiquidity = temp_ | (temp2_ << 128);
} else {
// updating on storage
absorbedLiquidity = 0;
unchecked {
currentData_.debtRemaining -= temp_;
}
currentData_.totalDebtLiq = temp_;
currentData_.totalColLiq = temp2_;
}
}
if (
currentData_.tick > memoryVars_.liquidationTick && // current tick > liquidation tick
currentData_.tick <= memoryVars_.maxTick // current tick <= max tick
) {
if (currentData_.debtRemaining > 0) {
// Stores liquidated debt & collateral in each loop
uint debtLiquidated_;
uint colLiquidated_;
uint debtFactor_ = BigMathVault.TWO_POWER_64;
TickHasDebt memory tickHasDebt_;
unchecked {
tickHasDebt_.mapId = (currentData_.tick < 0)
? (((currentData_.tick + 1) / 256) - 1)
: (currentData_.tick / 256);
}
tickInfo_.ratio = TickMath.getRatioAtTick(tickInfo_.tick);
if (currentData_.tickStatus == 1) {
// top tick is not liquidated. Hence it's a perfect tick.
currentData_.ratio = tickInfo_.ratio;
// if current tick in liquidation is a perfect tick then it is also the next tick that has debt.
tickHasDebt_.nextTick = currentData_.tick;
} else {
// top tick is liquidated. Hence it has partials.
// next tick that has debt liquidity will have to be fetched from tickHasDebt
unchecked {
tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;
tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;
tickInfo_.partials = (branch_.data >> 22) & X30;
currentData_.ratio = tickInfo_.ratioOneLess + ((tickInfo_.length * tickInfo_.partials) / X30);
if ((memoryVars_.liquidationTick + 1) == tickInfo_.tick && (tickInfo_.partials == 1)) {
if (to_ == 0x000000000000000000000000000000000000dEaD) {
// revert with liquidated amounts if to_ address is the dead address.
// this can be used in a resolver to find the max liquidatable amounts.
revert FluidLiquidateResult(0, 0);
}
revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidation);
}
}
}
while (true) {
if (currentData_.tickStatus == 1) {
// not liquidated -> Getting the debt from tick data itself
temp2_ = tickData[currentData_.tick];
// temp_ => tick debt
temp_ = (temp2_ >> 25) & X64;
// Converting big number into normal number
temp_ = (temp_ >> 8) << (temp_ & X8);
// Updating tickData on storage with removing debt & adding connection to branch
tickData[currentData_.tick] =
1 | // set tick as liquidated
(temp2_ & 0x1fffffe) | // set same total tick ids
(branch_.id << 26) | // branch id where this tick got liquidated
(branch_.debtFactor << 56);
} else {
// already liquidated -> Get the debt from branch data in big number
// temp_ => tick debt
temp_ = (branch_.data >> 52) & X64;
// Converting big number into normal number
temp_ = (temp_ >> 8) << (temp_ & X8);
// Branch is getting updated over the end
}
// Adding new debt into active debt for liquidation
currentData_.debt += temp_;
// Adding new col into active col for liquidation
// Ratio is in 2**96 decimals hence multiplying debt with 2**96 to get proper collateral
currentData_.col += (temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / currentData_.ratio;
if (
(tickHasDebt_.nextTick == currentData_.tick && currentData_.tickStatus == 1) ||
tickHasDebt_.tickHasDebt == 0
) {
// Fetching next perfect tick with liquidity
// tickHasDebt_.tickHasDebt == 0 will only happen in the first while loop
// in the very first perfect tick liquidation it'll be 0
if (tickHasDebt_.tickHasDebt == 0) {
tickHasDebt_.tickHasDebt = tickHasDebt[tickHasDebt_.mapId];
}
// in 1st loop tickStatus can be 2. Meaning not a perfect current tick
if (currentData_.tickStatus == 1) {
unchecked {
tickHasDebt_.bitsToRemove = uint(-currentData_.tick + (tickHasDebt_.mapId * 256 + 256));
}
// Removing current top tick from tickHasDebt
tickHasDebt_.tickHasDebt =
(tickHasDebt_.tickHasDebt << tickHasDebt_.bitsToRemove) >>
tickHasDebt_.bitsToRemove;
// Updating in storage if tickHasDebt becomes 0.
if (tickHasDebt_.tickHasDebt == 0) {
tickHasDebt[tickHasDebt_.mapId] = 0;
}
}
// For last user remaining in vault there could be a lot of while loop.
// Chances of this to happen is extremely low (like ~0%)
while (true) {
if (tickHasDebt_.tickHasDebt > 0) {
unchecked {
tickHasDebt_.nextTick =
tickHasDebt_.mapId *
256 +
int(tickHasDebt_.tickHasDebt.mostSignificantBit()) -
1;
}
break;
}
// tickHasDebt_.tickHasDebt == 0. Checking if minimum tick of this mapID is less than liquidationTick_
// if true that means now the next tick is not needed as liquidation gets over minimum at liquidationTick_
unchecked {
if ((tickHasDebt_.mapId * 256) < memoryVars_.liquidationTick) {
tickHasDebt_.nextTick = type(int).min;
break;
}
// Fetching next tick has debt by decreasing tickHasDebt_.mapId first
tickHasDebt_.tickHasDebt = tickHasDebt[--tickHasDebt_.mapId];
}
}
}
// Fetching refTick. refTick is the biggest tick of these 3:
// 1. Next tick with liquidity (from tickHasDebt)
// 2. Minima tick of current branch
// 3. Liquidation threshold tick
{
// Setting currentData_.refTick & currentData_.refTickStatus
if (
branch_.minimaTick > tickHasDebt_.nextTick &&
branch_.minimaTick > memoryVars_.liquidationTick
) {
// next tick will be of base branch (merge)
currentData_.refTick = branch_.minimaTick;
currentData_.refTickStatus = 2;
} else if (tickHasDebt_.nextTick > memoryVars_.liquidationTick) {
// next tick will be next tick from perfect tick
currentData_.refTick = tickHasDebt_.nextTick;
currentData_.refTickStatus = 1;
} else {
// next tick is threshold tick
currentData_.refTick = memoryVars_.liquidationTick;
currentData_.refTickStatus = 3; // leads to end of liquidation loop
}
}
// using tickInfo variable again for ref tick as we don't have the need for it any more
tickInfo_.ratio = TickMath.getRatioAtTick(int24(currentData_.refTick));
if (currentData_.refTickStatus == 2) {
// merge current branch with base branch
unchecked {
tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;
tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;
// Fetching base branch data to get the base branch's partial
branch_.baseBranchData = branchData[((branch_.data >> 166) & X30)];
tickInfo_.partials = (branch_.baseBranchData >> 22) & X30;
tickInfo_.currentRatio =
tickInfo_.ratioOneLess +
((tickInfo_.length * tickInfo_.partials) / X30);
currentData_.refRatio = tickInfo_.currentRatio;
}
} else {
// refTickStatus can only be 1 (next tick from perfect tick) or 3 (liquidation threshold tick)
tickInfo_.currentRatio = tickInfo_.ratio;
currentData_.refRatio = tickInfo_.ratio;
tickInfo_.partials = X30;
}
// Formula: (debt_ - x) / (col_ - (x * colPerDebt_)) = ratioEnd_
// x = ((ratioEnd_ * col) - debt_) / ((colPerDebt_ * ratioEnd_) - 1)
// x is debtToLiquidate_
// col_ = debt_ / ratioStart_ -> (currentData_.debt / currentData_.ratio)
// ratioEnd_ is currentData_.refRatio
//
// Calculation results of numerator & denominator is always negative
// which will cancel out to give positive output in the end so we can safely cast to uint.
// for nominator:
// ratioStart can only be >= ratioEnd so first part can only be reducing currentData_.debt leading to
// currentData_.debt reduced - currentData_.debt original * 1e27 -> can only be a negative number
// for denominator:
// currentData_.colPerDebt and currentData_.refRatio are inversely proportional to each other.
// the maximum value they can ever be is ~9.97e26 which is the 0.3% away from 100% because liquidation
// threshold + liquidation penalty can never be > 99.7%. This can also be verified by going back from
// min / max ratio values further up where we fetch oracle price etc.
// as optimization we can inverse nominator and denominator subtraction to directly get a positive number.
debtLiquidated_ =
// nominator
((currentData_.debt - (currentData_.refRatio * currentData_.debt) / currentData_.ratio) *
1e27) /
// denominator
(1e27 - ((currentData_.colPerDebt * currentData_.refRatio) / TickMath.ZERO_TICK_SCALED_RATIO));
colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27;
if (currentData_.debt == debtLiquidated_) {
debtLiquidated_ -= 1;
}
if (debtLiquidated_ >= currentData_.debtRemaining || currentData_.refTickStatus == 3) {
// End of liquidation as full amount to liquidate or liquidation threshold tick has been reached;
// Updating tickHasDebt on storage.
tickHasDebt[tickHasDebt_.mapId] = tickHasDebt_.tickHasDebt;
if (debtLiquidated_ >= currentData_.debtRemaining) {
// Liquidation ended between currentTick & refTick.
// Not all of liquidatable debt is actually liquidated -> recalculate
debtLiquidated_ = currentData_.debtRemaining;
colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27;
// Liquidating to debt. temp_ => final ratio after liquidation
// liquidatable debt - debtLiquidated / liquidatable col - colLiquidated
temp_ =
((currentData_.debt - debtLiquidated_) * TickMath.ZERO_TICK_SCALED_RATIO) /
(currentData_.col - colLiquidated_);
// Fetching tick of where liquidation ended
(tickInfo_.tick, tickInfo_.ratioOneLess) = TickMath.getTickAtRatio(temp_);
if (tickInfo_.tick < memoryVars_.liquidationTick) {
// this situation might never happen
// if this happens then there might be some very edge case precision of few weis which is returning 1 tick less
// if the above were to ever happen then tickInfo_.tick only be memoryVars_.liquidationTick - 1
// in this case the partial will be very very near to full (X30)
// increasing tick by 2 and making partial as 1 which is basically very very near to memoryVars_.liquidationTick
unchecked {
tickInfo_.tick += 2;
}
tickInfo_.partials = 1;
} else {
// Increasing tick by 1 as final ratio will probably be a partial
unchecked {
++tickInfo_.tick;
tickInfo_.ratio = (tickInfo_.ratioOneLess * 10015) / 10000;
tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;
tickInfo_.partials = ((temp_ - tickInfo_.ratioOneLess) * X30) / tickInfo_.length;
// Taking edge cases where partial comes as 0 or X30 meaning perfect tick.
// Hence, increasing or reducing it by 1 as liquidation tick cannot be perfect tick.
tickInfo_.partials = tickInfo_.partials == 0 ? 1 : tickInfo_.partials >= X30
? X30 - 1
: tickInfo_.partials;
}
}
} else {
// End in liquidation threshold.
// finalRatio_ = currentData_.refRatio;
// Increasing liquidation threshold tick by 1 partial. With 1 partial it'll reach to the next tick.
// Ratio change will be negligible. Doing this as liquidation threshold tick can also be a perfect non-liquidated tick.
unchecked {
tickInfo_.tick = currentData_.refTick + 1;
}
// Making partial as 1 so it doesn't stay perfect tick
tickInfo_.partials = 1;
// length is not needed as only partials are written to storage
}
// debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt
// -> debtFactor * leftOverDebt / liquidatableDebt
debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt;
currentData_.totalDebtLiq += debtLiquidated_;
currentData_.debt -= debtLiquidated_; // currentData_.debt => leftOverDebt after debtLiquidated_
currentData_.totalColLiq += colLiquidated_;
currentData_.col -= colLiquidated_; // currentData_.col => leftOverCol after colLiquidated_
// Updating branch's debt factor & write to storage as liquidation is over
branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_);
if (currentData_.debt < 100) {
// this can happen when someone tries to create a dust tick
revert FluidVaultError(ErrorTypes.VaultT1__BranchDebtTooLow);
}
unchecked {
// Tick to insert
temp2_ = tickInfo_.tick < 0
? (uint(-tickInfo_.tick) << 1)
: ((uint(tickInfo_.tick) << 1) | 1);
}
// Updating Branch data with debt factor, debt, partials, minima tick & assigning is liquidated
branchData[branch_.id] =
((branch_.data >> 166) << 166) |
1 | // set as liquidated
(temp2_ << 2) | // minima tick of branch
(tickInfo_.partials << 22) |
(currentData_.debt.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52) | // branch debt
(branch_.debtFactor << 116);
// Updating vault variables with current branch & tick
vaultVariables_ =
((vaultVariables_ >> 52) << 52) |
2 | // set as liquidated
(temp2_ << 2) | // top tick
(branch_.id << 22);
break;
}
unchecked {
// debtLiquidated_ >= currentData_.debtRemaining leads to loop break in if statement above
// so this can be unchecked
currentData_.debtRemaining -= debtLiquidated_;
}
// debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt
// -> debtFactor * leftOverDebt / liquidatableDebt
debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt;
currentData_.totalDebtLiq += debtLiquidated_;
currentData_.debt -= debtLiquidated_;
currentData_.totalColLiq += colLiquidated_;
currentData_.col -= colLiquidated_;
// updating branch's debt factor
branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_);
// Setting debt factor as 1 << 64 again
debtFactor_ = BigMathVault.TWO_POWER_64;
if (currentData_.refTickStatus == 2) {
// ref tick is base branch's minima hence merging current branch to base branch
// and making base branch as current branch.
// read base branch related data
temp_ = (branch_.data >> 166) & X30; // temp_ -> base branch id
temp2_ = branch_.baseBranchData;
{
uint newBranchDebtFactor_ = (temp2_ >> 116) & X50;
// connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor
uint connectionFactor_ = newBranchDebtFactor_.divBigNumber(branch_.debtFactor);
// Updating current branch in storage
branchData[branch_.id] =
((branch_.data >> 166) << 166) | // deleting debt / partials / minima tick
2 | // setting as merged
(connectionFactor_ << 116); // set new connectionFactor
// Storing base branch in memory
// Updating branch ID to base branch ID
branch_.id = temp_;
// Updating branch data with base branch data
branch_.data = temp2_;
// Remove next branch connection from base branch
branch_.debtFactor = newBranchDebtFactor_;
// temp_ => minima tick of base branch
temp_ = (temp2_ >> 196) & X20;
if (temp_ > 0) {
unchecked {
branch_.minimaTick = (temp_ & 1) == 1
? int256((temp_ >> 1) & X19)
: -int256((temp_ >> 1) & X19);
}
} else {
branch_.minimaTick = type(int).min;
}
}
}
// Making refTick as currentTick
currentData_.tick = currentData_.refTick;
currentData_.tickStatus = currentData_.refTickStatus;
currentData_.ratio = currentData_.refRatio;
}
}
}
// calculating net token amounts using exchange price
actualDebtAmt_ = (currentData_.totalDebtLiq * memoryVars_.borrowExPrice) / EXCHANGE_PRICES_PRECISION;
actualColAmt_ = (currentData_.totalColLiq * memoryVars_.supplyExPrice) / EXCHANGE_PRICES_PRECISION;
// Chances of this to happen are in few wei
if (actualDebtAmt_ > debtAmt_) {
// calc new actualColAmt_ via ratio.
actualColAmt_ = actualColAmt_ * (debtAmt_ / actualDebtAmt_);
actualDebtAmt_ = debtAmt_;
}
if (((actualColAmt_ * 1e18) / actualDebtAmt_) < colPerUnitDebt_) {
revert FluidVaultError(ErrorTypes.VaultT1__ExcessSlippageLiquidation);
}
if (to_ == 0x000000000000000000000000000000000000dEaD) {
// revert with liquidated amounts if to_ address is the dead address.
// this can be used in a resolver to find the max liquidatable amounts.
revert FluidLiquidateResult(actualColAmt_, actualDebtAmt_);
}
// payback at Liquidity
if (BORROW_TOKEN == NATIVE_TOKEN) {
temp_ = actualDebtAmt_;
if (actualDebtAmt_ < msg.value) {
unchecked {
// subtraction can be unchecked because of if check above
SafeTransfer.safeTransferNative(msg.sender, msg.value - actualDebtAmt_);
}
}
// else if actualDebtAmt_ > msg.value not possible as actualDebtAmt_ can maximally be debtAmt_ and
// msg.value == debtAmt_ is checked in the beginning of function.
} else {
temp_ = 0;
}
unchecked {
// payback at liquidity
LIQUIDITY.operate{ value: temp_ }(
BORROW_TOKEN,
0,
-int(actualDebtAmt_),
address(0),
address(0),
abi.encode(msg.sender)
);
// withdraw at liquidity
LIQUIDITY.operate(SUPPLY_TOKEN, -int(actualColAmt_), 0, to_, address(0), new bytes(0));
}
// Calculating new total collateral & total debt.
// temp_ -> total supply
temp_ = (vaultVariables_ >> 82) & X64;
temp_ = ((temp_ >> 8) << (temp_ & X8)) - currentData_.totalColLiq;
// temp2_ -> total borrow
temp2_ = (vaultVariables_ >> 146) & X64;
temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) - currentData_.totalDebtLiq;
// Updating vault variables on storage
// Converting total supply & total borrow in 64 bits (56 | 8) bignumber
vaultVariables =
(vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) |
(temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply
(temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow
emit LogLiquidate(msg.sender, actualColAmt_, actualDebtAmt_, to_);
}
/// @dev absorb function absorbs the bad debt if the bad debt is above max limit. The main use of it is
/// if the bad debt didn't got liquidated in time maybe due to sudden price drop or bad debt was extremely small to liquidate
/// and the bad debt goes above 100% ratio then there's no incentive for anyone to liquidate now
/// hence absorb functions absorbs that bad debt to allow newer bad debt to liquidate seamlessly
/// if absorbing were to happen after this it's on governance on how to deal with it
/// although it can still be removed through liquidate via liquidator if the price goes back up and liquidation becomes beneficial
/// upon absorbed user position gets 100% liquidated.
function absorb() public {
_spell(SECONDARY_IMPLEMENTATION, msg.data);
}
/// @dev Checks total supply of vault's in Liquidity Layer & Vault contract and rebalance it accordingly
/// if vault supply is more than Liquidity Layer then deposit difference through reserve/rebalance contract
/// if vault supply is less than Liquidity Layer then withdraw difference to reserve/rebalance contract
/// if vault borrow is more than Liquidity Layer then borrow difference to reserve/rebalance contract
/// if vault borrow is less than Liquidity Layer then payback difference through reserve/rebalance contract
function rebalance() external payable returns (int supplyAmt_, int borrowAmt_) {
(supplyAmt_, borrowAmt_) = abi.decode(_spell(SECONDARY_IMPLEMENTATION, msg.data), (int, int));
}
/// @dev liquidity callback for cheaper token transfers in case of deposit or payback.
/// only callable by Liquidity during an operation.
function liquidityCallback(address token_, uint amount_, bytes calldata data_) external {
if (msg.sender != address(LIQUIDITY))
revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidityCallbackAddress);
if (vaultVariables & 1 == 0) revert FluidVaultError(ErrorTypes.VaultT1__NotEntered);
SafeTransfer.safeTransferFrom(token_, abi.decode(data_, (address)), address(LIQUIDITY), amount_);
}
constructor(ConstantViews memory constants_) Helpers(constants_) {
// Note that vaults are deployed by VaultFactory so we somewhat trust the values being passed in
// Setting branch in vault.
vaultVariables = (vaultVariables) | (1 << 22) | (1 << 52);
uint liqSupplyExchangePrice_ = (LIQUIDITY.readFromStorage(LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT) >>
LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64;
uint liqBorrowExchangePrice_ = (LIQUIDITY.readFromStorage(LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT) >>
LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64;
if (
liqSupplyExchangePrice_ < EXCHANGE_PRICES_PRECISION || liqBorrowExchangePrice_ < EXCHANGE_PRICES_PRECISION
) {
revert FluidVaultError(ErrorTypes.VaultT1__TokenNotInitialized);
}
// Updating initial rates in storage
rates =
liqSupplyExchangePrice_ |
(liqBorrowExchangePrice_ << 64) |
(EXCHANGE_PRICES_PRECISION << 128) |
(EXCHANGE_PRICES_PRECISION << 192);
}
fallback() external {
if (!(VAULT_FACTORY.isGlobalAuth(msg.sender) || VAULT_FACTORY.isVaultAuth(address(this), msg.sender))) {
revert FluidVaultError(ErrorTypes.VaultT1__NotAnAuth);
}
// Delegate the current call to `implementation`.
// This does not return to its internall call site, it will return directly to the external caller.
// solhint-disable-next-line no-inline-assembly
_spell(ADMIN_IMPLEMENTATION, msg.data);
}
function _spell(address target_, bytes memory data_) private returns (bytes memory response_) {
assembly {
let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
let size := returndatasize()
response_ := mload(0x40)
mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(response_, size)
returndatacopy(add(response_, 0x20), 0, size)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
returndatacopy(0x00, 0x00, size)
revert(0x00, size)
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IProxy {
function setAdmin(address newAdmin_) external;
function setDummyImplementation(address newDummyImplementation_) external;
function addImplementation(address implementation_, bytes4[] calldata sigs_) external;
function removeImplementation(address implementation_) external;
function getAdmin() external view returns (address);
function getDummyImplementation() external view returns (address);
function getImplementationSigs(address impl_) external view returns (bytes4[] memory);
function getSigsImplementation(bytes4 sig_) external view returns (address);
function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.
/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision
/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can
/// result in significant gas cost reduction due to storage space reduction.
/// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision.
/// @dev roundUp is more like a increase 1, which happens everytime for the same number.
/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number.
library BigMathMinified {
/// @dev constants to use for `roundUp` input param to increase readability
bool internal constant ROUND_DOWN = false;
bool internal constant ROUND_UP = true;
/// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).
/// e.g.:
/// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]
/// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)
/// => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000
/// ^-------------------- 51(exponent) -------------- ^
/// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563)
/// exponent = 0011,0011 (51)
/// bigNumber = 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 (572493200179)
///
/// @param normal number which needs to be converted into Big Number
/// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))
/// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))
/// @param roundUp signals if result should be rounded down or up
/// @return bigNumber converted bigNumber (coefficient << exponent)
function toBigNumber(
uint256 normal,
uint256 coefficientSize,
uint256 exponentSize,
bool roundUp
) internal pure returns (uint256 bigNumber) {
assembly {
let lastBit_
let number_ := normal
if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
number_ := shr(0x80, number_)
lastBit_ := 0x80
}
if gt(number_, 0xFFFFFFFFFFFFFFFF) {
number_ := shr(0x40, number_)
lastBit_ := add(lastBit_, 0x40)
}
if gt(number_, 0xFFFFFFFF) {
number_ := shr(0x20, number_)
lastBit_ := add(lastBit_, 0x20)
}
if gt(number_, 0xFFFF) {
number_ := shr(0x10, number_)
lastBit_ := add(lastBit_, 0x10)
}
if gt(number_, 0xFF) {
number_ := shr(0x8, number_)
lastBit_ := add(lastBit_, 0x8)
}
if gt(number_, 0xF) {
number_ := shr(0x4, number_)
lastBit_ := add(lastBit_, 0x4)
}
if gt(number_, 0x3) {
number_ := shr(0x2, number_)
lastBit_ := add(lastBit_, 0x2)
}
if gt(number_, 0x1) {
lastBit_ := add(lastBit_, 1)
}
if gt(number_, 0) {
lastBit_ := add(lastBit_, 1)
}
if lt(lastBit_, coefficientSize) {
// for throw exception
lastBit_ := coefficientSize
}
let exponent := sub(lastBit_, coefficientSize)
let coefficient := shr(exponent, normal)
if and(roundUp, gt(exponent, 0)) {
// rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number
coefficient := add(coefficient, 1)
if eq(shl(coefficientSize, 1), coefficient) {
// case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits
// final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.
coefficient := shl(sub(coefficientSize, 1), 1)
exponent := add(exponent, 1)
}
}
if iszero(lt(exponent, shl(exponentSize, 1))) {
// if exponent is >= exponentSize, the normal number is too big to fit within
// BigNumber with too small sizes for coefficient and exponent
revert(0, 0)
}
bigNumber := shl(exponentSize, coefficient)
bigNumber := add(bigNumber, exponent)
}
}
/// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`
function fromBigNumber(
uint256 bigNumber,
uint256 exponentSize,
uint256 exponentMask
) internal pure returns (uint256 normal) {
assembly {
let coefficient := shr(exponentSize, bigNumber)
let exponent := and(bigNumber, exponentMask)
normal := shl(exponent, coefficient)
}
}
/// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).
/// e.g.
/// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111
/// lastBit = ^--------------------------------- 83 ----------------------------------------^
function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {
assembly {
let number_ := normal
if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
number_ := shr(0x80, number_)
lastBit := 0x80
}
if gt(number_, 0xFFFFFFFFFFFFFFFF) {
number_ := shr(0x40, number_)
lastBit := add(lastBit, 0x40)
}
if gt(number_, 0xFFFFFFFF) {
number_ := shr(0x20, number_)
lastBit := add(lastBit, 0x20)
}
if gt(number_, 0xFFFF) {
number_ := shr(0x10, number_)
lastBit := add(lastBit, 0x10)
}
if gt(number_, 0xFF) {
number_ := shr(0x8, number_)
lastBit := add(lastBit, 0x8)
}
if gt(number_, 0xF) {
number_ := shr(0x4, number_)
lastBit := add(lastBit, 0x4)
}
if gt(number_, 0x3) {
number_ := shr(0x2, number_)
lastBit := add(lastBit, 0x2)
}
if gt(number_, 0x1) {
lastBit := add(lastBit, 1)
}
if gt(number_, 0) {
lastBit := add(lastBit, 1)
}
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { BigMathMinified } from "./bigMathMinified.sol";
/// @title Extended version of BigMathMinified. Implements functions for normal operators (*, /, etc) modified to interact with big numbers.
/// @notice this is an optimized version mainly created by taking Fluid vault's codebase into consideration so it's use is limited for other cases.
//
// @dev IMPORTANT: for any change here, make sure to uncomment and run the fuzz tests in bigMathVault.t.sol
library BigMathVault {
uint private constant COEFFICIENT_SIZE_DEBT_FACTOR = 35;
uint private constant EXPONENT_SIZE_DEBT_FACTOR = 15;
uint private constant COEFFICIENT_MAX_DEBT_FACTOR = (1 << COEFFICIENT_SIZE_DEBT_FACTOR) - 1;
uint private constant EXPONENT_MAX_DEBT_FACTOR = (1 << EXPONENT_SIZE_DEBT_FACTOR) - 1;
uint private constant DECIMALS_DEBT_FACTOR = 16384;
uint internal constant MAX_MASK_DEBT_FACTOR = (1 << (COEFFICIENT_SIZE_DEBT_FACTOR + EXPONENT_SIZE_DEBT_FACTOR)) - 1;
// Having precision as 2**64 on vault
uint internal constant PRECISION = 64;
uint internal constant TWO_POWER_64 = 1 << PRECISION;
// Max bit for 35 bits * 35 bits number will be 70
// why do we use 69 then here instead of 70
uint internal constant TWO_POWER_69_MINUS_1 = (1 << 69) - 1;
uint private constant COEFFICIENT_PLUS_PRECISION = COEFFICIENT_SIZE_DEBT_FACTOR + PRECISION; // 99
uint private constant COEFFICIENT_PLUS_PRECISION_MINUS_1 = COEFFICIENT_PLUS_PRECISION - 1; // 98
uint private constant TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1 = (1 << COEFFICIENT_PLUS_PRECISION_MINUS_1) - 1; // (1 << 98) - 1;
uint private constant TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1_MINUS_1 =
(1 << (COEFFICIENT_PLUS_PRECISION_MINUS_1 - 1)) - 1; // (1 << 97) - 1;
/// @dev multiplies a `normal` number with a `bigNumber1` and then divides by `bigNumber2`.
/// @dev For vault's use case MUST always:
/// - bigNumbers have exponent size 15 bits
/// - bigNumbers have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)
/// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.
/// - bigNumber1 (debt factor) always have exponent >= 1 & <= 16384
/// - bigNumber2 (connection factor) always have exponent >= 1 & <= 32767 (15 bits)
/// - bigNumber2 always >= bigNumber1 (connection factor can never be < base branch debt factor)
/// - as a result of previous points, numbers must never be 0
/// - normal is positionRawDebt and is always within 10000 and type(int128).max
/// @return normal * bigNumber1 / bigNumber2
function mulDivNormal(uint256 normal, uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {
unchecked {
// exponent2_ - exponent1_
uint netExponent_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) - (bigNumber1 & EXPONENT_MAX_DEBT_FACTOR);
if (netExponent_ < 129) {
// (normal * coefficient1_) / (coefficient2_ << netExponent_);
return ((normal * (bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR)) /
((bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR) << netExponent_));
}
// else:
// biggest possible nominator: type(int128).max * 35bits max = 5846006549323611672814739330865132078589370433536
// smallest possible denominator: 17179869184 << 129 (= 1 << 163) = 11692013098647223345629478661730264157247460343808
// -> can only ever be 0
return 0;
}
}
/// @dev multiplies a `bigNumber` with normal `number1` and then divides by `TWO_POWER_64`.
/// @dev For vault's use case (calculating new branch debt factor after liquidation):
/// - number1 is debtFactor, intialized as TWO_POWER_64 and reduced from there, hence it's always <= TWO_POWER_64 and always > 0.
/// - bigNumber is branch debt factor, which starts as ((X35 << 15) | (1 << 14)) and reduces from there.
/// - bigNumber must have have exponent size 15 bits and be >= 1 & <= 16384
/// - bigNumber must have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)
/// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.
/// @param bigNumber Coefficient | Exponent.
/// @param number1 normal number.
/// @return result bigNumber * number1 / TWO_POWER_64.
function mulDivBigNumber(uint256 bigNumber, uint256 number1) internal pure returns (uint256 result) {
// using unchecked as we are only at 1 place in Vault and it won't overflow there.
unchecked {
uint256 _resultNumerator = (bigNumber >> EXPONENT_SIZE_DEBT_FACTOR) * number1; // bigNumber coefficient * normal number
// 99% chances are that most sig bit should be 64 + 35 - 1 or 64 + 35 - 2
// diff = mostSigBit. Can only ever be >= 35 and <= 98
uint256 diff = (_resultNumerator > TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1)
? COEFFICIENT_PLUS_PRECISION
: (_resultNumerator > TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1_MINUS_1)
? COEFFICIENT_PLUS_PRECISION_MINUS_1
: BigMathMinified.mostSignificantBit(_resultNumerator);
// diff = difference in bits to make the _resultNumerator 35 bits again
diff = diff - COEFFICIENT_SIZE_DEBT_FACTOR;
_resultNumerator = _resultNumerator >> diff;
// starting exponent is 16384, so exponent should never get 0 here
result = (bigNumber & EXPONENT_MAX_DEBT_FACTOR) + diff;
if (result > PRECISION) {
result = (_resultNumerator << EXPONENT_SIZE_DEBT_FACTOR) + result - PRECISION; // divides by TWO_POWER_64 by reducing exponent by 64
} else {
// if number1 is small, e.g. 1e4 and bigNumber is also small e.g. coefficient = 17179869184 & exponent is at 50
// then: resultNumerator = 171798691840000, diff most significant bit = 48, ending up with diff = 13
// for exponent in result we end up doing: 50 + 13 - 64 -> underflowing exponent.
// this should never happen anyway, but if it does better to revert than to continue with unknown effects.
revert(); // debt factor should never become a BigNumber with exponent <= 0
}
}
}
/// @dev multiplies a `bigNumber1` with another `bigNumber2`.
/// @dev For vault's use case (calculating connection factor of merged branches userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor):
/// - bigNumbers must have have exponent size 15 bits and be >= 1 & <= 32767
/// - bigNumber must have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)
/// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.
/// @dev sum of exponents from `bigNumber1` `bigNumber2` should be > 16384.
/// e.g. res = bigNumber1 * bigNumber2 = [(coe1, exp1) * (coe2, exp2)] >> decimal
/// = (coe1*coe2>>overflow, exp1+exp2+overflow-decimal)
/// @param bigNumber1 BigNumber format with coefficient and exponent.
/// @param bigNumber2 BigNumber format with coefficient and exponent.
/// @return BigNumber format with coefficient and exponent
function mulBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {
unchecked {
// coefficient1_ * coefficient2_
uint resCoefficient_ = (bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) *
(bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR);
// res coefficient at min can be 17179869184 * 17179869184 = 295147905179352825856 (= 1 << 68; 69th bit as 1)
// res coefficient at max can be 34359738367 * 34359738367 = 1180591620648691826689 (X35 * X35 fits in 70 bits)
uint overflowLen_ = resCoefficient_ > TWO_POWER_69_MINUS_1
? COEFFICIENT_SIZE_DEBT_FACTOR
: COEFFICIENT_SIZE_DEBT_FACTOR - 1;
// overflowLen_ is either 34 or 35
resCoefficient_ = resCoefficient_ >> overflowLen_;
// bigNumber2 is connection factor
// exponent1_ + exponent2_ + overflowLen_ - decimals
uint resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) +
(bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) +
overflowLen_);
if (resExponent_ < DECIMALS_DEBT_FACTOR) {
// for this ever to happen, the debt factors used to calculate connection factors would have to be at extremely
// unrealistic values. Like e.g.
// branch3 (debt factor X35 << 15 | 16383) got merged into branch2 (debt factor X35 << 15 | 8190)
// -> connection factor (divBigNumber): ((coe1<<precision_)/coe2>>overflowLen, exp1+decimal+overflowLen-exp2-precision_) so:
// coefficient: (X35<<64)/X35 >> 30 = 17179869184
// exponent: 8190+16384+30-16383-64 = 8157.
// result: 17179869184 << 15 | 8157
// and then branch2 into branch1 (debt factor X35 << 15 | 22). -> connection factor:
// coefficient: (X35<<64)/X35 >> 30 = 17179869184
// exponent: 22+16384+30-8190-64 = 8182.
// result: 17179869184 << 15 | 8182
// connection factors sum up (mulBigNumber): (coe1*coe2>>overflow, exp1+exp2+overflow-decimal)
// exponent: 8182+8157+35-16384=16374-16384=-10. underflow.
// this should never happen anyway, but if it does better to revert than to continue with unknown effects.
revert();
}
resExponent_ = resExponent_ - DECIMALS_DEBT_FACTOR;
if (resExponent_ > EXPONENT_MAX_DEBT_FACTOR) {
// if resExponent_ is not within limits that means user's got ~100% (something like 99.999999999999...)
// this situation will probably never happen and this basically means user's position is ~100% liquidated
return MAX_MASK_DEBT_FACTOR;
}
return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_);
}
}
/// @dev divides a `bigNumber1` by `bigNumber2`.
/// @dev For vault's use case (calculating connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor) bigNumbers MUST always:
/// - have exponent size 15 bits and be >= 1 & <= 16384
/// - have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)
/// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.
/// - as a result of previous points, numbers must never be 0
/// e.g. res = bigNumber1 / bigNumber2 = [(coe1, exp1) / (coe2, exp2)] << decimal
/// = ((coe1<<precision_)/coe2, exp1+decimal-exp2-precision_)
/// @param bigNumber1 BigNumber format with coefficient and exponent
/// @param bigNumber2 BigNumber format with coefficient and exponent
/// @return BigNumber format with coefficient and exponent
/// Returned connection factor can only ever be >= baseBranchDebtFactor (c = x*100/y with both x,y > 0 & x,y <= 100: c can only ever be >= x)
function divBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {
unchecked {
// (coefficient1_ << PRECISION) / coefficient2_
uint256 resCoefficient_ = ((bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) << PRECISION) /
(bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR);
// nominator at min 17179869184 << 64 = 316912650057057350374175801344. at max 34359738367 << 64 = 633825300095667956674642051072.
// so min value resCoefficient_ 9223372037123211264 (64 bits) vs max 36893488146345361408 (fits in 65 bits)
// mostSigBit will be PRECISION + 1 or PRECISION
uint256 overflowLen_ = ((resCoefficient_ >> PRECISION) == 1) ? (PRECISION + 1) : PRECISION;
// Overflow will be PRECISION - COEFFICIENT_SIZE_DEBT_FACTOR or (PRECISION + 1) - COEFFICIENT_SIZE_DEBT_FACTOR
// Meaning 64 - 35 = 29 or 65 - 35 = 30
overflowLen_ = overflowLen_ - COEFFICIENT_SIZE_DEBT_FACTOR;
resCoefficient_ = resCoefficient_ >> overflowLen_;
// exponent1_ will always be less than or equal to 16384
// exponent2_ will always be less than or equal to 16384
// Even if exponent2_ is 0 (not possible) & resExponent_ = DECIMALS_DEBT_FACTOR then also resExponent_ will be less than max limit, so no overflow
// result exponent = (exponent1_ + DECIMALS_DEBT_FACTOR + overflowLen_) - (exponent2_ + PRECISION);
uint256 resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) + // exponent1_
DECIMALS_DEBT_FACTOR + // DECIMALS_DEBT_FACTOR is 100% as it is percentage value
overflowLen_); // addition part resExponent_ here min 16414, max 32798
// reuse overFlowLen_ variable for subtraction sum of exponent
overflowLen_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) + PRECISION; // subtraction part overflowLen_ here: min 65, max 16448
if (resExponent_ > overflowLen_) {
resExponent_ = resExponent_ - overflowLen_;
return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_);
}
// Can happen if bigNumber1 exponent is < 35 (35+16384+29 = 16448) and bigNumber2 exponent is e.g. max 16384.
// this would mean a branch with a normal big debt factor (bigNumber2) is merged into a base branch with an extremely small
// debt factor (bigNumber1).
// this should never happen anyway, but if it does better to revert than to continue with unknown effects.
revert(); // connection factor should never become a BigNumber with exponent <= 0
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
library LibsErrorTypes {
/***********************************|
| LiquidityCalcs |
|__________________________________*/
/// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)
uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;
/// @notice thrown when rate data is set to a version that is not implemented
uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;
/***********************************|
| SafeTransfer |
|__________________________________*/
/// @notice thrown when safe transfer from for an ERC20 fails
uint256 internal constant SafeTransfer__TransferFromFailed = 71001;
/// @notice thrown when safe transfer for an ERC20 fails
uint256 internal constant SafeTransfer__TransferFailed = 71002;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol";
import { LiquiditySlotsLink } from "./liquiditySlotsLink.sol";
import { BigMathMinified } from "./bigMathMinified.sol";
/// @notice implements calculation methods used for Fluid liquidity such as updated exchange prices,
/// borrow rate, withdrawal / borrow limits, revenue amount.
library LiquidityCalcs {
error FluidLiquidityCalcsError(uint256 errorId_);
/// @notice emitted if the calculated borrow rate surpassed max borrow rate (16 bits) and was capped at maximum value 65535
event BorrowRateMaxCap();
/// @dev constants as from Liquidity variables.sol
uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;
/// @dev Ignoring leap years
uint256 internal constant SECONDS_PER_YEAR = 365 days;
// constants used for BigMath conversion from and to storage
uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;
uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;
uint256 internal constant FOUR_DECIMALS = 1e4;
uint256 internal constant TWELVE_DECIMALS = 1e12;
uint256 internal constant X14 = 0x3fff;
uint256 internal constant X15 = 0x7fff;
uint256 internal constant X16 = 0xffff;
uint256 internal constant X18 = 0x3ffff;
uint256 internal constant X24 = 0xffffff;
uint256 internal constant X33 = 0x1ffffffff;
uint256 internal constant X64 = 0xffffffffffffffff;
///////////////////////////////////////////////////////////////////////////
////////// CALC EXCHANGE PRICES /////////
///////////////////////////////////////////////////////////////////////////
/// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage.
/// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage
/// @return supplyExchangePrice_ updated supplyExchangePrice
/// @return borrowExchangePrice_ updated borrowExchangePrice
function calcExchangePrices(
uint256 exchangePricesAndConfig_
) internal view returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {
// Extracting exchange prices
supplyExchangePrice_ =
(exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &
X64;
borrowExchangePrice_ =
(exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &
X64;
if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) {
revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__ExchangePriceZero);
}
uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate
unchecked {
// last timestamp can not be > current timestamp
uint256 secondsSinceLastUpdate_ = block.timestamp -
((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33);
uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) &
X15;
if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) {
// if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed
// (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0)
return (supplyExchangePrice_, borrowExchangePrice_);
}
// calculate new borrow exchange price.
// formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_.
// nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0.
borrowExchangePrice_ +=
(borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) /
(SECONDS_PER_YEAR * FOUR_DECIMALS);
// FOR SUPPLY EXCHANGE PRICE:
// all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest.
// formula: previous price * supply rate * secondsSinceLastUpdate_.
// where supply rate = (borrow rate - revenueFee%) * ratioSupplyYield. And
// ratioSupplyYield = utilization * supplyRatio * borrowRatio
//
// Example:
// supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50.
// BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%.
// yield is 10 (so half a year must have passed).
// supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60.
// borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70.
// supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2).
// utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield).
// borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield):
// x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%.
// ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5%
// supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5%
// increase in supplyExchangePrice, assuming 100 as previous price.
// 100 * 22,5% * 1/2 (half a year) = 0,1125.
// cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20.
// -------------- 1. calculate ratioSupplyYield --------------------------------
// step1: utilization * supplyRatio (or actually part of lenders receiving yield)
// temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)
// if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)
// else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)
temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15;
if (temp_ == 1) {
// if no raw supply: no exchange price update needed
// (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0)
return (supplyExchangePrice_, borrowExchangePrice_);
}
// ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest
if (temp_ & 1 == 1) {
// ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)
temp_ = temp_ >> 1;
// Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return
// in the if statement a little above.
// based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee.
// supplyRawInterest must become worth 30. totalSupply must become 110.
// supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2.
// so ratioSupplyYield must come out as 2.5 (250%).
// supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted.
temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision)
// e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%).
temp_ =
// utilization * (100% + 100% / supplyRatio)
(((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) *
(1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow).
(FOUR_DECIMALS);
// max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31
} else {
// ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)
temp_ = temp_ >> 1;
// if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0
// e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%).
temp_ =
// 1e27 * utilization * (100% + supplyRatio) / 100%
(1e27 *
((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow).
(FOUR_DECIMALS + temp_)) /
(FOUR_DECIMALS * FOUR_DECIMALS);
// max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27
}
// from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31
// step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield)
if (borrowRatio_ & 1 == 1) {
// ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger)
borrowRatio_ = borrowRatio_ >> 1;
// borrowRatio_ => x of total bororwers paying yield. scale to 1e27.
// Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered
// at the beginning of the method by early return if `borrowRatio_ == 1`.
// based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%.
// so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed.
// x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666%
// borrowRatio_ => x of total bororwers paying yield. scale to 1e27.
borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_);
// max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield).
} else {
// ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger)
borrowRatio_ = borrowRatio_ >> 1;
// borrowRatio_ => x of total bororwers paying yield. scale to 1e27.
// x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%.
borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_)));
// borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%.
// or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101.
// max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield).
}
// temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%.
// max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8
temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54;
// 2. calculate supply rate
// temp_ => supply rate (borrow rate - revenueFee%) * ratioSupplyYield.
// division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate)
// Note that all calculation divisions for supplyExchangePrice are rounded down.
// Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest
// but more suppliers not earning interest.
temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate
temp_ * // ratioSupplyYield
(FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee
// fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17.
// 3. calculate increase in supply exchange price
supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) /
(SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS));
// max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0.
}
}
///////////////////////////////////////////////////////////////////////////
////////// CALC REVENUE /////////
///////////////////////////////////////////////////////////////////////////
/// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage
/// and the current balance of the Fluid liquidity contract for the token.
/// @param totalAmounts_ total amounts packed uint256 read from storage
/// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage
/// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))
/// @return revenueAmount_ collectable revenue amount
function calcRevenue(
uint256 totalAmounts_,
uint256 exchangePricesAndConfig_,
uint256 liquidityTokenBalance_
) internal view returns (uint256 revenueAmount_) {
// @dev no need to super-optimize this method as it is only used by admin
// calculate the new exchange prices based on earned interest
(uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(exchangePricesAndConfig_);
// total supply = interest free + with interest converted from raw
uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_);
if (totalSupply_ > 0) {
// available revenue: balanceOf(token) + totalBorrowings - totalLendings.
revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_);
// ensure there is no possible case because of rounding etc. where this would revert,
// explicitly check if >
revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0;
// Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization
// can only be revenue.
} else {
// if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck
revenueAmount_ = liquidityTokenBalance_;
}
}
///////////////////////////////////////////////////////////////////////////
////////// CALC LIMITS /////////
///////////////////////////////////////////////////////////////////////////
/// @dev calculates withdrawal limit before an operate execution:
/// amount of user supply that must stay supplied (not amount that can be withdrawn).
/// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
/// @param userSupplyData_ user supply data packed uint256 from storage
/// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath
/// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction.
/// returned value is in raw for with interest mode, normal amount for interest free mode!
function calcWithdrawalLimitBeforeOperate(
uint256 userSupplyData_,
uint256 userSupply_
) internal view returns (uint256 currentWithdrawalLimit_) {
// @dev must support handling the case where timestamp is 0 (config is set but no interactions yet).
// first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet.
// returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be
// a deposit anyway. Important is that it would not revert.
// Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit
// is the fully expanded limit immediately.
// extract last set withdrawal limit
uint256 lastWithdrawalLimit_ = (userSupplyData_ >>
LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) & X64;
lastWithdrawalLimit_ =
(lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) <<
(lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK);
if (lastWithdrawalLimit_ == 0) {
// withdrawal limit is not activated. Max withdrawal allowed
return 0;
}
uint256 maxWithdrawableLimit_;
uint256 temp_;
unchecked {
// extract max withdrawable percent of user supply and
// calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed
// e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed.
// userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
maxWithdrawableLimit_ =
(((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) /
FOUR_DECIMALS;
// time elapsed since last withdrawal limit was set (in seconds)
// @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before.
// last timestamp can not be > current timestamp
temp_ =
block.timestamp -
((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33);
}
// calculate withdrawable amount of expandPercent that is elapsed of expandDuration.
// e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%.
// Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed.
temp_ =
(maxWithdrawableLimit_ * temp_) /
// extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit)
((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0
// calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount.
// Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration,
// which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0
// which will cause minimum (fully expanded) withdrawal limit to be set in lines below.
unchecked {
// underflow explicitly checked & handled
currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0;
// calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion.
// subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
temp_ = userSupply_ - maxWithdrawableLimit_;
}
// if withdrawal limit is decreased below minimum then set minimum
// (e.g. when more than expandDuration time has elapsed)
if (temp_ > currentWithdrawalLimit_) {
currentWithdrawalLimit_ = temp_;
}
}
/// @dev calculates withdrawal limit after an operate execution:
/// amount of user supply that must stay supplied (not amount that can be withdrawn).
/// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
/// @param userSupplyData_ user supply data packed uint256 from storage
/// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount
/// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate`
/// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in
/// raw for with interest mode, normal amount for interest free mode!
function calcWithdrawalLimitAfterOperate(
uint256 userSupplyData_,
uint256 userSupply_,
uint256 newWithdrawalLimit_
) internal pure returns (uint256) {
// temp_ => base withdrawal limit. below this, maximum withdrawals are allowed
uint256 temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18;
temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);
// if user supply is below base limit then max withdrawals are allowed
if (userSupply_ < temp_) {
return 0;
}
// temp_ => withdrawal limit expandPercent (is in 1e2 decimals)
temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;
unchecked {
// temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent))
// userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
// subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS);
}
// if new (before operation) withdrawal limit is less than minimum limit then set minimum limit.
// e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where
// increased deposit amount outpaces withrawals.
if (temp_ > newWithdrawalLimit_) {
return temp_;
}
return newWithdrawalLimit_;
}
/// @dev calculates borrow limit before an operate execution:
/// total amount user borrow can reach (not borrowable amount in current operation).
/// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
/// @param userBorrowData_ user borrow data packed uint256 from storage
/// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_`
/// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in
/// raw for with interest mode, normal amount for interest free mode!
function calcBorrowLimitBeforeOperate(
uint256 userBorrowData_,
uint256 userBorrow_
) internal view returns (uint256 currentBorrowLimit_) {
// @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit.
// first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus
// `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0.
// temp_ = extract borrow expand percent (is in 1e2 decimals)
uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;
uint256 maxExpansionLimit_;
uint256 maxExpandedBorrowLimit_;
unchecked {
// calculate max expansion limit: Max amount limit can expand to since last interaction
// userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS);
// calculate max borrow limit: Max point limit can increase to since last interaction
maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_;
}
// currentBorrowLimit_ = extract base borrow limit
currentBorrowLimit_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
currentBorrowLimit_ =
(currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) <<
(currentBorrowLimit_ & DEFAULT_EXPONENT_MASK);
if (maxExpandedBorrowLimit_ < currentBorrowLimit_) {
return currentBorrowLimit_;
}
// time elapsed since last borrow limit was set (in seconds)
unchecked {
// temp_ = timeElapsed_ (last timestamp can not be > current timestamp)
temp_ =
block.timestamp -
((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last udpate timestamp
}
// currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit
currentBorrowLimit_ =
// calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`.
// divisor is extract expand duration (after this, full expansion to expandPercentage happened).
((maxExpansionLimit_ * temp_) /
((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0
// extract last set borrow limit
BigMathMinified.fromBigNumber(
(userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64,
DEFAULT_EXPONENT_SIZE,
DEFAULT_EXPONENT_MASK
);
// if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion,
// so set to `maxExpandedBorrowLimit_` in that case.
// also covers the case where last process timestamp = 0 (timeElapsed would simply be very big)
if (currentBorrowLimit_ > maxExpandedBorrowLimit_) {
currentBorrowLimit_ = maxExpandedBorrowLimit_;
}
// temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);
if (currentBorrowLimit_ > temp_) {
currentBorrowLimit_ = temp_;
}
}
/// @dev calculates borrow limit after an operate execution:
/// total amount user borrow can reach (not borrowable amount in current operation).
/// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
/// @param userBorrowData_ user borrow data packed uint256 from storage
/// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount
/// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate`
/// @return borrowLimit_ updated borrow limit that should be written to storage.
/// returned value is in raw for with interest mode, normal amount for interest free mode!
function calcBorrowLimitAfterOperate(
uint256 userBorrowData_,
uint256 userBorrow_,
uint256 newBorrowLimit_
) internal pure returns (uint256 borrowLimit_) {
// temp_ = extract borrow expand percent
uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals)
unchecked {
// borrowLimit_ = calculate maximum borrow limit at full expansion.
// userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS);
}
// temp_ = extract base borrow limit
temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);
if (borrowLimit_ < temp_) {
// below base limit, borrow limit is always base limit
return temp_;
}
// temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);
// make sure fully expanded borrow limit is not above hard max borrow limit
if (borrowLimit_ > temp_) {
borrowLimit_ = temp_;
}
// if new borrow limit (from before operate) is > max borrow limit, set max borrow limit.
// (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)
if (newBorrowLimit_ > borrowLimit_) {
return borrowLimit_;
}
return newBorrowLimit_;
}
///////////////////////////////////////////////////////////////////////////
////////// CALC RATES /////////
///////////////////////////////////////////////////////////////////////////
/// @dev Calculates new borrow rate from utilization for a token
/// @param rateData_ rate data packed uint256 from storage for the token
/// @param utilization_ totalBorrow / totalSupply. 1e4 = 100% utilization
/// @return rate_ rate for that particular token in 1e2 precision (e.g. 5% rate = 500)
function calcBorrowRateFromUtilization(uint256 rateData_, uint256 utilization_) internal returns (uint256 rate_) {
// extract rate version: 4 bits (0xF) starting from bit 0
uint256 rateVersion_ = (rateData_ & 0xF);
if (rateVersion_ == 1) {
rate_ = calcRateV1(rateData_, utilization_);
} else if (rateVersion_ == 2) {
rate_ = calcRateV2(rateData_, utilization_);
} else {
revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__UnsupportedRateVersion);
}
if (rate_ > X16) {
// hard cap for borrow rate at maximum value 16 bits (65535) to make sure it does not overflow storage space.
// this is unlikely to ever happen if configs stay within expected levels.
rate_ = X16;
// emit event to more easily become aware
emit BorrowRateMaxCap();
}
}
/// @dev calculates the borrow rate based on utilization for rate data version 1 (with one kink) in 1e2 precision
/// @param rateData_ rate data packed uint256 from storage for the token
/// @param utilization_ in 1e2 (100% = 1e4)
/// @return rate_ rate in 1e2 precision
function calcRateV1(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {
/// For rate v1 (one kink) ------------------------------------------------------
/// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Next 16 bits => 52- 67 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Last 188 bits => 68-255 => blank, might come in use in future
// y = mx + c.
// y is borrow rate
// x is utilization
// m = slope (m can be 0 but never negative)
// c is constant (c can be negative)
uint256 y1_;
uint256 y2_;
uint256 x1_;
uint256 x2_;
// extract kink1: 16 bits (0xFFFF) starting from bit 20
// kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two
uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16;
if (utilization_ < kink1_) {
// if utilization is less than kink
y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) & X16;
y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;
x1_ = 0; // 0%
x2_ = kink1_;
} else {
// else utilization is greater than kink
y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;
y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) & X16;
x1_ = kink1_;
x2_ = FOUR_DECIMALS; // 100%
}
int256 constant_;
uint256 slope_;
unchecked {
// calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).
// utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor) and rate at y2 can not be < rate at y1
// y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS
slope_ = ((y2_ - y1_) * TWELVE_DECIMALS) / (x2_ - x1_);
// calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.
// maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256
// maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;
// maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256
// subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256
constant_ = int256(y1_ * TWELVE_DECIMALS) - int256(slope_ * x1_);
// calculating new borrow rate
// - slope_ max value is 65535 * 1e12,
// - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)
// - constant max value is 65535 * 1e12
// so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256
// divisor TWELVE_DECIMALS can not be 0
rate_ = (uint256(int256(slope_ * utilization_) + constant_)) / TWELVE_DECIMALS;
}
}
/// @dev calculates the borrow rate based on utilization for rate data version 2 (with two kinks) in 1e4 precision
/// @param rateData_ rate data packed uint256 from storage for the token
/// @param utilization_ in 1e2 (100% = 1e4)
/// @return rate_ rate in 1e4 precision
function calcRateV2(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {
/// For rate v2 (two kinks) -----------------------------------------------------
/// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Next 16 bits => 52- 67 => Utilization at kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Next 16 bits => 68- 83 => Rate at utilization kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Next 16 bits => 84- 99 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
/// Last 156 bits => 100-255 => blank, might come in use in future
// y = mx + c.
// y is borrow rate
// x is utilization
// m = slope (m can be 0 but never negative)
// c is constant (c can be negative)
uint256 y1_;
uint256 y2_;
uint256 x1_;
uint256 x2_;
// extract kink1: 16 bits (0xFFFF) starting from bit 20
// kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two
uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) & X16;
if (utilization_ < kink1_) {
// if utilization is less than kink1
y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) & X16;
y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;
x1_ = 0; // 0%
x2_ = kink1_;
} else {
// extract kink2: 16 bits (0xFFFF) starting from bit 52
uint256 kink2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) & X16;
if (utilization_ < kink2_) {
// if utilization is less than kink2
y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;
y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;
x1_ = kink1_;
x2_ = kink2_;
} else {
// else utilization is greater than kink2
y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;
y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) & X16;
x1_ = kink2_;
x2_ = FOUR_DECIMALS;
}
}
int256 constant_;
uint256 slope_;
unchecked {
// calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).
// utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor) and rate at y2 can not be < rate at y1
// y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS
slope_ = ((y2_ - y1_) * TWELVE_DECIMALS) / (x2_ - x1_);
// calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.
// maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256
// maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;
// maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256
// subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256
constant_ = int256(y1_ * TWELVE_DECIMALS) - int256(slope_ * x1_);
// calculating new borrow rate
// - slope_ max value is 65535 * 1e12,
// - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)
// - constant max value is 65535 * 1e12
// so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256
// divisor TWELVE_DECIMALS can not be 0
rate_ = (uint256(int256(slope_ * utilization_) + constant_)) / TWELVE_DECIMALS;
}
}
/// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_`
function getTotalSupply(
uint256 totalAmounts_,
uint256 supplyExchangePrice_
) internal pure returns (uint256 totalSupply_) {
// totalSupply_ => supplyInterestFree
totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;
totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK);
uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits
totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK);
// totalSupply = supplyInterestFree + supplyRawInterest normalized from raw
totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION);
}
/// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_`
function getTotalBorrow(
uint256 totalAmounts_,
uint256 borrowExchangePrice_
) internal pure returns (uint256 totalBorrow_) {
// totalBorrow_ => borrowInterestFree
// no & mask needed for borrow interest free as it occupies the last bits in the storage slot
totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);
totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK);
uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;
totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK);
// totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw
totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
/// @notice library that helps in reading / working with storage slot data of Fluid Liquidity.
/// @dev as all data for Fluid Liquidity is internal, any data must be fetched directly through manual
/// slot reading through this library or, if gas usage is less important, through the FluidLiquidityResolver.
library LiquiditySlotsLink {
/// @dev storage slot for status at Liquidity
uint256 internal constant LIQUIDITY_STATUS_SLOT = 1;
/// @dev storage slot for auths mapping at Liquidity
uint256 internal constant LIQUIDITY_AUTHS_MAPPING_SLOT = 2;
/// @dev storage slot for guardians mapping at Liquidity
uint256 internal constant LIQUIDITY_GUARDIANS_MAPPING_SLOT = 3;
/// @dev storage slot for user class mapping at Liquidity
uint256 internal constant LIQUIDITY_USER_CLASS_MAPPING_SLOT = 4;
/// @dev storage slot for exchangePricesAndConfig mapping at Liquidity
uint256 internal constant LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT = 5;
/// @dev storage slot for rateData mapping at Liquidity
uint256 internal constant LIQUIDITY_RATE_DATA_MAPPING_SLOT = 6;
/// @dev storage slot for totalAmounts mapping at Liquidity
uint256 internal constant LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT = 7;
/// @dev storage slot for user supply double mapping at Liquidity
uint256 internal constant LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT = 8;
/// @dev storage slot for user borrow double mapping at Liquidity
uint256 internal constant LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT = 9;
/// @dev storage slot for listed tokens array at Liquidity
uint256 internal constant LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT = 10;
// --------------------------------
// @dev stacked uint256 storage slots bits position data for each:
// ExchangePricesAndConfig
uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATE = 0;
uint256 internal constant BITS_EXCHANGE_PRICES_FEE = 16;
uint256 internal constant BITS_EXCHANGE_PRICES_UTILIZATION = 30;
uint256 internal constant BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD = 44;
uint256 internal constant BITS_EXCHANGE_PRICES_LAST_TIMESTAMP = 58;
uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE = 91;
uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE = 155;
uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_RATIO = 219;
uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATIO = 234;
// RateData:
uint256 internal constant BITS_RATE_DATA_VERSION = 0;
// RateData: V1
uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO = 4;
uint256 internal constant BITS_RATE_DATA_V1_UTILIZATION_AT_KINK = 20;
uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK = 36;
uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX = 52;
// RateData: V2
uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO = 4;
uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1 = 20;
uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1 = 36;
uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2 = 52;
uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2 = 68;
uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX = 84;
// TotalAmounts
uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_WITH_INTEREST = 0;
uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE = 64;
uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST = 128;
uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE = 192;
// UserSupplyData
uint256 internal constant BITS_USER_SUPPLY_MODE = 0;
uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;
uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;
uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;
uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;
uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;
uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;
uint256 internal constant BITS_USER_SUPPLY_IS_PAUSED = 255;
// UserBorrowData
uint256 internal constant BITS_USER_BORROW_MODE = 0;
uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;
uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;
uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;
uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;
uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;
uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;
uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;
uint256 internal constant BITS_USER_BORROW_IS_PAUSED = 255;
// --------------------------------
/// @notice Calculating the slot ID for Liquidity contract for single mapping at `slot_` for `key_`
function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {
return keccak256(abi.encode(key_, slot_));
}
/// @notice Calculating the slot ID for Liquidity contract for double mapping at `slot_` for `key1_` and `key2_`
function calculateDoubleMappingStorageSlot(
uint256 slot_,
address key1_,
address key2_
) internal pure returns (bytes32) {
bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));
return keccak256(abi.encode(key2_, intermediateSlot_));
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.21;
import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol";
/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom
library SafeTransfer {
error FluidSafeTransferError(uint256 errorId_);
/// @dev Transfer `amount_` of `token_` from `from_` to `to_`, spending the approval given by `from_` to the
/// calling contract. If `token_` returns no value, non-reverting calls are assumed to be successful.
/// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
/// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63
function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {
bool success_;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from_" argument.
mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
mstore(add(freeMemoryPointer, 68), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.
success_ := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)
)
}
if (!success_) {
revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);
}
}
/// @dev Transfer `amount_` of `token_` to `to_`.
/// If `token_` returns no value, non-reverting calls are assumed to be successful.
/// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
/// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95
function safeTransfer(address token_, address to_, uint256 amount_) internal {
bool success_;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
mstore(add(freeMemoryPointer, 36), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.
success_ := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)
)
}
if (!success_) {
revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
}
}
/// @dev Transfer `amount_` of ` native token to `to_`.
/// Minimally modified from Solmate SafeTransferLib (Custom Error):
/// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25
function safeTransferNative(address to_, uint256 amount_) internal {
bool success_;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success_ := call(gas(), to_, amount_, 0, 0, 0, 0)
}
if (!success_) {
revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
/// @notice implements a method to read uint256 data from storage at a bytes32 storage slot key.
contract StorageRead {
function readFromStorage(bytes32 slot_) public view returns (uint256 result_) {
assembly {
result_ := sload(slot_) // read value from the storage slot
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
/// @title library that calculates number "tick" and "ratioX96" from this: ratioX96 = (1.0015^tick) * 2^96
/// @notice this library is used in Fluid Vault protocol for optimiziation.
/// @dev "tick" supports between -32767 and 32767. "ratioX96" supports between 37075072 and 169307877264527972847801929085841449095838922544595
library TickMath {
/// The minimum tick that can be passed in getRatioAtTick. 1.0015**-32767
int24 internal constant MIN_TICK = -32767;
/// The maximum tick that can be passed in getRatioAtTick. 1.0015**32767
int24 internal constant MAX_TICK = 32767;
uint256 internal constant FACTOR00 = 0x100000000000000000000000000000000;
uint256 internal constant FACTOR01 = 0xff9dd7de423466c20352b1246ce4856f; // 2^128/1.0015**1 = 339772707859149738855091969477551883631
uint256 internal constant FACTOR02 = 0xff3bd55f4488ad277531fa1c725a66d0; // 2^128/1.0015**2 = 339263812140938331358054887146831636176
uint256 internal constant FACTOR03 = 0xfe78410fd6498b73cb96a6917f853259; // 2^128/1.0015**4 = 338248306163758188337119769319392490073
uint256 internal constant FACTOR04 = 0xfcf2d9987c9be178ad5bfeffaa123273; // 2^128/1.0015**8 = 336226404141693512316971918999264834163
uint256 internal constant FACTOR05 = 0xf9ef02c4529258b057769680fc6601b3; // 2^128/1.0015**16 = 332218786018727629051611634067491389875
uint256 internal constant FACTOR06 = 0xf402d288133a85a17784a411f7aba082; // 2^128/1.0015**32 = 324346285652234375371948336458280706178
uint256 internal constant FACTOR07 = 0xe895615b5beb6386553757b0352bda90; // 2^128/1.0015**64 = 309156521885964218294057947947195947664
uint256 internal constant FACTOR08 = 0xd34f17a00ffa00a8309940a15930391a; // 2^128/1.0015**128 = 280877777739312896540849703637713172762
uint256 internal constant FACTOR09 = 0xae6b7961714e20548d88ea5123f9a0ff; // 2^128/1.0015**256 = 231843708922198649176471782639349113087
uint256 internal constant FACTOR10 = 0x76d6461f27082d74e0feed3b388c0ca1; // 2^128/1.0015**512 = 157961477267171621126394973980180876449
uint256 internal constant FACTOR11 = 0x372a3bfe0745d8b6b19d985d9a8b85bb; // 2^128/1.0015**1024 = 73326833024599564193373530205717235131
uint256 internal constant FACTOR12 = 0x0be32cbee48979763cf7247dd7bb539d; // 2^128/1.0015**2048 = 15801066890623697521348224657638773661
uint256 internal constant FACTOR13 = 0x8d4f70c9ff4924dac37612d1e2921e; // 2^128/1.0015**4096 = 733725103481409245883800626999235102
uint256 internal constant FACTOR14 = 0x4e009ae5519380809a02ca7aec77; // 2^128/1.0015**8192 = 1582075887005588088019997442108535
uint256 internal constant FACTOR15 = 0x17c45e641b6e95dee056ff10; // 2^128/1.0015**16384 = 7355550435635883087458926352
/// The minimum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MIN_TICK). ~ Equivalent to `(1 << 96) * (1.0015**-32767)`
uint256 internal constant MIN_RATIOX96 = 37075072;
/// The maximum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MAX_TICK).
/// ~ Equivalent to `(1 << 96) * (1.0015**32767)`, rounding etc. leading to minor difference
uint256 internal constant MAX_RATIOX96 = 169307877264527972847801929085841449095838922544595;
uint256 internal constant ZERO_TICK_SCALED_RATIO = 0x1000000000000000000000000; // 1 << 96 // 79228162514264337593543950336
uint256 internal constant _1E26 = 1e26;
/// @notice ratioX96 = (1.0015^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return ratioX96 ratio = (debt amount/collateral amount)
function getRatioAtTick(int tick) internal pure returns (uint256 ratioX96) {
assembly {
let absTick_ := sub(xor(tick, sar(255, tick)), sar(255, tick))
if gt(absTick_, MAX_TICK) {
revert(0, 0)
}
let factor_ := FACTOR00
if and(absTick_, 0x1) {
factor_ := FACTOR01
}
if and(absTick_, 0x2) {
factor_ := shr(128, mul(factor_, FACTOR02))
}
if and(absTick_, 0x4) {
factor_ := shr(128, mul(factor_, FACTOR03))
}
if and(absTick_, 0x8) {
factor_ := shr(128, mul(factor_, FACTOR04))
}
if and(absTick_, 0x10) {
factor_ := shr(128, mul(factor_, FACTOR05))
}
if and(absTick_, 0x20) {
factor_ := shr(128, mul(factor_, FACTOR06))
}
if and(absTick_, 0x40) {
factor_ := shr(128, mul(factor_, FACTOR07))
}
if and(absTick_, 0x80) {
factor_ := shr(128, mul(factor_, FACTOR08))
}
if and(absTick_, 0x100) {
factor_ := shr(128, mul(factor_, FACTOR09))
}
if and(absTick_, 0x200) {
factor_ := shr(128, mul(factor_, FACTOR10))
}
if and(absTick_, 0x400) {
factor_ := shr(128, mul(factor_, FACTOR11))
}
if and(absTick_, 0x800) {
factor_ := shr(128, mul(factor_, FACTOR12))
}
if and(absTick_, 0x1000) {
factor_ := shr(128, mul(factor_, FACTOR13))
}
if and(absTick_, 0x2000) {
factor_ := shr(128, mul(factor_, FACTOR14))
}
if and(absTick_, 0x4000) {
factor_ := shr(128, mul(factor_, FACTOR15))
}
let precision_ := 0
if iszero(and(tick, 0x8000000000000000000000000000000000000000000000000000000000000000)) {
factor_ := div(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, factor_)
// we round up in the division so getTickAtRatio of the output price is always consistent
if mod(factor_, 0x100000000) {
precision_ := 1
}
}
ratioX96 := add(shr(32, factor_), precision_)
}
}
/// @notice ratioX96 = (1.0015^tick) * 2^96
/// @dev Throws if ratioX96 > max ratio || ratioX96 < min ratio
/// @param ratioX96 The input ratio; ratio = (debt amount/collateral amount)
/// @return tick The output tick for the above formula. Returns in round down form. if tick is 123.23 then 123, if tick is -123.23 then returns -124
/// @return perfectRatioX96 perfect ratio for the above tick
function getTickAtRatio(uint256 ratioX96) internal pure returns (int tick, uint perfectRatioX96) {
assembly {
if or(gt(ratioX96, MAX_RATIOX96), lt(ratioX96, MIN_RATIOX96)) {
revert(0, 0)
}
let cond := lt(ratioX96, ZERO_TICK_SCALED_RATIO)
let factor_
if iszero(cond) {
// if ratioX96 >= ZERO_TICK_SCALED_RATIO
factor_ := div(mul(ratioX96, _1E26), ZERO_TICK_SCALED_RATIO)
}
if cond {
// ratioX96 < ZERO_TICK_SCALED_RATIO
factor_ := div(mul(ZERO_TICK_SCALED_RATIO, _1E26), ratioX96)
}
// put in https://www.wolframalpha.com/ whole equation: (1.0015^tick) * 2^96 * 10^26 / 79228162514264337593543950336
// for tick = 16384
// ratioX96 = (1.0015^16384) * 2^96 = 3665252098134783297721995888537077351735
// 3665252098134783297721995888537077351735 * 10^26 / 79228162514264337593543950336 =
// 4626198540796508716348404308345255985.06131964639489434655721
if iszero(lt(factor_, 4626198540796508716348404308345255985)) {
tick := or(tick, 0x4000)
factor_ := div(mul(factor_, _1E26), 4626198540796508716348404308345255985)
}
// for tick = 8192
// ratioX96 = (1.0015^8192) * 2^96 = 17040868196391020479062776466509865
// 17040868196391020479062776466509865 * 10^26 / 79228162514264337593543950336 =
// 21508599537851153911767490449162.3037648642153898377655505172
if iszero(lt(factor_, 21508599537851153911767490449162)) {
tick := or(tick, 0x2000)
factor_ := div(mul(factor_, _1E26), 21508599537851153911767490449162)
}
// for tick = 4096
// ratioX96 = (1.0015^4096) * 2^96 = 36743933851015821532611831851150
// 36743933851015821532611831851150 * 10^26 / 79228162514264337593543950336 =
// 46377364670549310883002866648.9777607649742626173648716941385
if iszero(lt(factor_, 46377364670549310883002866649)) {
tick := or(tick, 0x1000)
factor_ := div(mul(factor_, _1E26), 46377364670549310883002866649)
}
// for tick = 2048
// ratioX96 = (1.0015^2048) * 2^96 = 1706210527034005899209104452335
// 1706210527034005899209104452335 * 10^26 / 79228162514264337593543950336 =
// 2153540449365864845468344760.06357108484096046743300420319322
if iszero(lt(factor_, 2153540449365864845468344760)) {
tick := or(tick, 0x800)
factor_ := div(mul(factor_, _1E26), 2153540449365864845468344760)
}
// for tick = 1024
// ratioX96 = (1.0015^1024) * 2^96 = 367668226692760093024536487236
// 367668226692760093024536487236 * 10^26 / 79228162514264337593543950336 =
// 464062544207767844008185024.950588990554136265212906454481127
if iszero(lt(factor_, 464062544207767844008185025)) {
tick := or(tick, 0x400)
factor_ := div(mul(factor_, _1E26), 464062544207767844008185025)
}
// for tick = 512
// ratioX96 = (1.0015^512) * 2^96 = 170674186729409605620119663668
// 170674186729409605620119663668 * 10^26 / 79228162514264337593543950336 =
// 215421109505955298802281577.031879604792139232258508172947569
if iszero(lt(factor_, 215421109505955298802281577)) {
tick := or(tick, 0x200)
factor_ := div(mul(factor_, _1E26), 215421109505955298802281577)
}
// for tick = 256
// ratioX96 = (1.0015^256) * 2^96 = 116285004205991934861656513301
// 116285004205991934861656513301 * 10^26 / 79228162514264337593543950336 =
// 146772309890508740607270614.667650899656438875541505058062410
if iszero(lt(factor_, 146772309890508740607270615)) {
tick := or(tick, 0x100)
factor_ := div(mul(factor_, _1E26), 146772309890508740607270615)
}
// for tick = 128
// ratioX96 = (1.0015^128) * 2^96 = 95984619659632141743747099590
// 95984619659632141743747099590 * 10^26 / 79228162514264337593543950336 =
// 121149622323187099817270416.157248837742741760456796835775887
if iszero(lt(factor_, 121149622323187099817270416)) {
tick := or(tick, 0x80)
factor_ := div(mul(factor_, _1E26), 121149622323187099817270416)
}
// for tick = 64
// ratioX96 = (1.0015^64) * 2^96 = 87204845308406958006717891124
// 87204845308406958006717891124 * 10^26 / 79228162514264337593543950336 =
// 110067989135437147685980801.568068573422377364214113968609839
if iszero(lt(factor_, 110067989135437147685980801)) {
tick := or(tick, 0x40)
factor_ := div(mul(factor_, _1E26), 110067989135437147685980801)
}
// for tick = 32
// ratioX96 = (1.0015^32) * 2^96 = 83120873769022354029916374475
// 83120873769022354029916374475 * 10^26 / 79228162514264337593543950336 =
// 104913292358707887270979599.831816586773651266562785765558183
if iszero(lt(factor_, 104913292358707887270979600)) {
tick := or(tick, 0x20)
factor_ := div(mul(factor_, _1E26), 104913292358707887270979600)
}
// for tick = 16
// ratioX96 = (1.0015^16) * 2^96 = 81151180492336368327184716176
// 81151180492336368327184716176 * 10^26 / 79228162514264337593543950336 =
// 102427189924701091191840927.762844039579442328381455567932128
if iszero(lt(factor_, 102427189924701091191840928)) {
tick := or(tick, 0x10)
factor_ := div(mul(factor_, _1E26), 102427189924701091191840928)
}
// for tick = 8
// ratioX96 = (1.0015^8) * 2^96 = 80183906840906820640659903620
// 80183906840906820640659903620 * 10^26 / 79228162514264337593543950336 =
// 101206318935480056907421312.890625
if iszero(lt(factor_, 101206318935480056907421313)) {
tick := or(tick, 0x8)
factor_ := div(mul(factor_, _1E26), 101206318935480056907421313)
}
// for tick = 4
// ratioX96 = (1.0015^4) * 2^96 = 79704602139525152702959747603
// 79704602139525152702959747603 * 10^26 / 79228162514264337593543950336 =
// 100601351350506250000000000
if iszero(lt(factor_, 100601351350506250000000000)) {
tick := or(tick, 0x4)
factor_ := div(mul(factor_, _1E26), 100601351350506250000000000)
}
// for tick = 2
// ratioX96 = (1.0015^2) * 2^96 = 79466025265172787701084167660
// 79466025265172787701084167660 * 10^26 / 79228162514264337593543950336 =
// 100300225000000000000000000
if iszero(lt(factor_, 100300225000000000000000000)) {
tick := or(tick, 0x2)
factor_ := div(mul(factor_, _1E26), 100300225000000000000000000)
}
// for tick = 1
// ratioX96 = (1.0015^1) * 2^96 = 79347004758035734099934266261
// 79347004758035734099934266261 * 10^26 / 79228162514264337593543950336 =
// 100150000000000000000000000
if iszero(lt(factor_, 100150000000000000000000000)) {
tick := or(tick, 0x1)
factor_ := div(mul(factor_, _1E26), 100150000000000000000000000)
}
if iszero(cond) {
// if ratioX96 >= ZERO_TICK_SCALED_RATIO
perfectRatioX96 := div(mul(ratioX96, _1E26), factor_)
}
if cond {
// ratioX96 < ZERO_TICK_SCALED_RATIO
tick := not(tick)
perfectRatioX96 := div(mul(ratioX96, factor_), 100150000000000000000000000)
}
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
abstract contract Structs {
struct AddressBool {
address addr;
bool value;
}
struct AddressUint256 {
address addr;
uint256 value;
}
/// @notice struct to set borrow rate data for version 1
struct RateDataV1Params {
///
/// @param token for rate data
address token;
///
/// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
/// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast
uint256 kink;
///
/// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
/// i.e. constant minimum borrow rate
/// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
uint256 rateAtUtilizationZero;
///
/// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700
uint256 rateAtUtilizationKink;
///
/// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
uint256 rateAtUtilizationMax;
}
/// @notice struct to set borrow rate data for version 2
struct RateDataV2Params {
///
/// @param token for rate data
address token;
///
/// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
/// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster
uint256 kink1;
///
/// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
/// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast
uint256 kink2;
///
/// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
/// i.e. constant minimum borrow rate
/// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
uint256 rateAtUtilizationZero;
///
/// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700
uint256 rateAtUtilizationKink1;
///
/// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200
uint256 rateAtUtilizationKink2;
///
/// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
uint256 rateAtUtilizationMax;
}
/// @notice struct to set token config
struct TokenConfig {
///
/// @param token address
address token;
///
/// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100
uint256 fee;
///
/// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100
uint256 threshold;
}
/// @notice struct to set user supply & withdrawal config
struct UserSupplyConfig {
///
/// @param user address
address user;
///
/// @param token address
address token;
///
/// @param mode: 0 = without interest. 1 = with interest
uint8 mode;
///
/// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100
/// Also used to calculate rate at which withdrawal limit should decrease (instant).
uint256 expandPercent;
///
/// @param expandDuration withdrawal limit expand duration in seconds.
/// used to calculate rate together with expandPercent
uint256 expandDuration;
///
/// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount.
/// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
/// with interest -> raw, without interest -> normal
uint256 baseWithdrawalLimit;
}
/// @notice struct to set user borrow & payback config
struct UserBorrowConfig {
///
/// @param user address
address user;
///
/// @param token address
address token;
///
/// @param mode: 0 = without interest. 1 = with interest
uint8 mode;
///
/// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100
/// Also used to calculate rate at which debt limit should decrease (instant).
uint256 expandPercent;
///
/// @param expandDuration debt limit expand duration in seconds.
/// used to calculate rate together with expandPercent
uint256 expandDuration;
///
/// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling
/// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place.
/// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
/// with interest -> raw, without interest -> normal
uint256 baseDebtCeiling;
///
/// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow.
/// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
/// with interest -> raw, without interest -> normal
uint256 maxDebtCeiling;
}
}//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import { IProxy } from "../../infiniteProxy/interfaces/iProxy.sol";
import { Structs as AdminModuleStructs } from "../adminModule/structs.sol";
interface IFluidLiquidityAdmin {
/// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract.
/// auths can be helpful in reducing governance overhead where it's not needed.
/// @param authsStatus_ array of structs setting allowed status for an address.
/// status true => add auth, false => remove auth
function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external;
/// @notice adds/removes guardians. Only callable by Governance.
/// @param guardiansStatus_ array of structs setting allowed status for an address.
/// status true => add guardian, false => remove guardian
function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external;
/// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance.
/// @param revenueCollector_ new revenue collector address
function updateRevenueCollector(address revenueCollector_) external;
/// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths.
/// @param newStatus_ new status
/// status = 2 -> pause, status = 1 -> resume.
function changeStatus(uint256 newStatus_) external;
/// @notice update tokens rate data version 1. Only callable by Auths.
/// @param tokensRateData_ array of RateDataV1Params with rate data to set for each token
function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external;
/// @notice update tokens rate data version 2. Only callable by Auths.
/// @param tokensRateData_ array of RateDataV2Params with rate data to set for each token
function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external;
/// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold.
/// Only callable by Auths.
/// @param tokenConfigs_ contains token address, fee & utilization threshold
function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external;
/// @notice updates user classes: 0 is for new protocols, 1 is for established protocols.
/// Only callable by Auths.
/// @param userClasses_ struct array of uint256 value to assign for each user address
function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external;
/// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits.
/// Only callable by Auths.
/// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info
function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external;
/// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits.
/// Only callable by Auths.
/// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info
function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external;
/// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
/// Only callable by Guardians.
/// @param user_ address of user to pause operations for
/// @param supplyTokens_ token addresses to pause withdrawals for
/// @param borrowTokens_ token addresses to pause borrowings for
function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;
/// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
/// Only callable by Guardians.
/// @param user_ address of user to unpause operations for
/// @param supplyTokens_ token addresses to unpause withdrawals for
/// @param borrowTokens_ token addresses to unpause borrowings for
function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;
/// @notice collects revenue for tokens to configured revenueCollector address.
/// @param tokens_ array of tokens to collect revenue for
/// @dev Note that this can revert if token balance is < revenueAmount (utilization > 100%)
function collectRevenue(address[] calldata tokens_) external;
/// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage.
/// @param tokens_ tokens to update exchange prices for
/// @return supplyExchangePrices_ new supply rates of overall system for each token
/// @return borrowExchangePrices_ new borrow rates of overall system for each token
function updateExchangePrices(
address[] calldata tokens_
) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_);
}
interface IFluidLiquidityLogic is IFluidLiquidityAdmin {
/// @notice Single function which handles supply, withdraw, borrow & payback
/// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native)
/// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing
/// @param borrowAmount_ if +ve then borrow, if -ve then payback, if 0 then nothing
/// @param withdrawTo_ if withdrawal then to which address
/// @param borrowTo_ if borrow then to which address
/// @param callbackData_ callback data passed to `liquidityCallback` method of protocol
/// @return memVar3_ updated supplyExchangePrice
/// @return memVar4_ updated borrowExchangePrice
/// @dev to trigger skipping in / out transfers when in&out amounts balance themselves out (gas optimization):
/// - supply(+) == borrow(+), withdraw(-) == payback(-).
/// - `withdrawTo_` / `borrowTo_` must be msg.sender (protocol)
/// - `callbackData_` MUST be encoded so that "from" address is at last 20 bytes (if this optimization is desired),
/// also for native token operations where liquidityCallback is not triggered!
/// from address must come at last position if there is more data. I.e. encode like:
/// abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end
/// so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end.
function operate(
address token_,
int256 supplyAmount_,
int256 borrowAmount_,
address withdrawTo_,
address borrowTo_,
bytes calldata callbackData_
) external payable returns (uint256 memVar3_, uint256 memVar4_);
}
interface IFluidLiquidity is IProxy, IFluidLiquidityLogic {}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IFluidOracle } from "./interfaces/iFluidOracle.sol";
/// @title FluidOracle
/// @notice Base contract that any Fluid Oracle must implement
abstract contract FluidOracle is IFluidOracle {
/// @inheritdoc IFluidOracle
function getExchangeRate() external view virtual returns (uint256 exchangeRate_);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IFluidOracle {
/// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27
function getExchangeRate() external view returns (uint256 exchangeRate_);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
contract Error {
error FluidVaultError(uint256 errorId_);
/// @notice used to simulate liquidation to find the maximum liquidatable amounts
error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
library ErrorTypes {
/***********************************|
| Vault Factory |
|__________________________________*/
uint256 internal constant VaultFactory__InvalidOperation = 30001;
uint256 internal constant VaultFactory__Unauthorized = 30002;
uint256 internal constant VaultFactory__SameTokenNotAllowed = 30003;
uint256 internal constant VaultFactory__InvalidParams = 30004;
uint256 internal constant VaultFactory__InvalidVault = 30005;
uint256 internal constant VaultFactory__InvalidVaultAddress = 30006;
uint256 internal constant VaultFactory__OnlyDelegateCallAllowed = 30007;
/***********************************|
| VaultT1 |
|__________________________________*/
/// @notice thrown at reentrancy
uint256 internal constant VaultT1__AlreadyEntered = 31001;
/// @notice thrown when user sends deposit & borrow amount as 0
uint256 internal constant VaultT1__InvalidOperateAmount = 31002;
/// @notice thrown when msg.value is not in sync with native token deposit or payback
uint256 internal constant VaultT1__InvalidMsgValueOperate = 31003;
/// @notice thrown when msg.sender is not the owner of the vault
uint256 internal constant VaultT1__NotAnOwner = 31004;
/// @notice thrown when user's position does not exist. Sending the wrong index from the frontend
uint256 internal constant VaultT1__TickIsEmpty = 31005;
/// @notice thrown when the user's position is above CF and the user tries to make it more risky by trying to withdraw or borrow
uint256 internal constant VaultT1__PositionAboveCF = 31006;
/// @notice thrown when the top tick is not initialized. Happens if the vault is totally new or all the user's left
uint256 internal constant VaultT1__TopTickDoesNotExist = 31007;
/// @notice thrown when msg.value in liquidate is not in sync payback
uint256 internal constant VaultT1__InvalidMsgValueLiquidate = 31008;
/// @notice thrown when slippage is more on liquidation than what the liquidator sent
uint256 internal constant VaultT1__ExcessSlippageLiquidation = 31009;
/// @notice thrown when msg.sender is not the rebalancer/reserve contract
uint256 internal constant VaultT1__NotRebalancer = 31010;
/// @notice thrown when NFT of one vault interacts with the NFT of other vault
uint256 internal constant VaultT1__NftNotOfThisVault = 31011;
/// @notice thrown when the token is not initialized on the liquidity contract
uint256 internal constant VaultT1__TokenNotInitialized = 31012;
/// @notice thrown when admin updates fallback if a non-auth calls vault
uint256 internal constant VaultT1__NotAnAuth = 31013;
/// @notice thrown in operate when user tries to witdhraw more collateral than deposited
uint256 internal constant VaultT1__ExcessCollateralWithdrawal = 31014;
/// @notice thrown in operate when user tries to payback more debt than borrowed
uint256 internal constant VaultT1__ExcessDebtPayback = 31015;
/// @notice thrown when user try to withdrawal more than operate's withdrawal limit
uint256 internal constant VaultT1__WithdrawMoreThanOperateLimit = 31016;
/// @notice thrown when caller of liquidityCallback is not Liquidity
uint256 internal constant VaultT1__InvalidLiquidityCallbackAddress = 31017;
/// @notice thrown when reentrancy is not already on
uint256 internal constant VaultT1__NotEntered = 31018;
/// @notice thrown when someone directly calls secondary implementation contract
uint256 internal constant VaultT1__OnlyDelegateCallAllowed = 31019;
/// @notice thrown when the safeTransferFrom for a token amount failed
uint256 internal constant VaultT1__TransferFromFailed = 31020;
/// @notice thrown when exchange price overflows while updating on storage
uint256 internal constant VaultT1__ExchangePriceOverFlow = 31021;
/// @notice thrown when debt to liquidate amt is sent wrong
uint256 internal constant VaultT1__InvalidLiquidationAmt = 31022;
/// @notice thrown when user debt or collateral goes above 2**128 or below -2**128
uint256 internal constant VaultT1__UserCollateralDebtExceed = 31023;
/// @notice thrown if on liquidation branch debt becomes lower than 100
uint256 internal constant VaultT1__BranchDebtTooLow = 31024;
/// @notice thrown when tick's debt is less than 10000
uint256 internal constant VaultT1__TickDebtTooLow = 31025;
/// @notice thrown when the received new liquidity exchange price is of unexpected value (< than the old one)
uint256 internal constant VaultT1__LiquidityExchangePriceUnexpected = 31026;
/// @notice thrown when user's debt is less than 10000
uint256 internal constant VaultT1__UserDebtTooLow = 31027;
/// @notice thrown when on only payback and only deposit the ratio of position increases
uint256 internal constant VaultT1__InvalidPaybackOrDeposit = 31028;
/// @notice thrown when liquidation just happens of a single partial
uint256 internal constant VaultT1__InvalidLiquidation = 31029;
/// @notice thrown when msg.value is sent wrong in rebalance
uint256 internal constant VaultT1__InvalidMsgValueInRebalance = 31030;
/// @notice thrown when nothing rebalanced
uint256 internal constant VaultT1__NothingToRebalance = 31031;
/***********************************|
| ERC721 |
|__________________________________*/
uint256 internal constant ERC721__InvalidParams = 32001;
uint256 internal constant ERC721__Unauthorized = 32002;
uint256 internal constant ERC721__InvalidOperation = 32003;
uint256 internal constant ERC721__UnsafeRecipient = 32004;
uint256 internal constant ERC721__OutOfBoundsIndex = 32005;
/***********************************|
| Vault Admin |
|__________________________________*/
/// @notice thrown when admin tries to setup invalid value which are crossing limits
uint256 internal constant VaultT1Admin__ValueAboveLimit = 33001;
/// @notice when someone directly calls admin implementation contract
uint256 internal constant VaultT1Admin__OnlyDelegateCallAllowed = 33002;
/// @notice thrown when auth sends NFT ID as 0 while collecting dust debt
uint256 internal constant VaultT1Admin__NftIdShouldBeNonZero = 33003;
/// @notice thrown when trying to collect dust debt of NFT which is not of this vault
uint256 internal constant VaultT1Admin__NftNotOfThisVault = 33004;
/// @notice thrown when dust debt of NFT is 0, meaning nothing to collect
uint256 internal constant VaultT1Admin__DustDebtIsZero = 33005;
/// @notice thrown when final debt after liquidation is not 0, meaning position 100% liquidated
uint256 internal constant VaultT1Admin__FinalDebtShouldBeZero = 33006;
/// @notice thrown when NFT is not liquidated state
uint256 internal constant VaultT1Admin__NftNotLiquidated = 33007;
/// @notice thrown when total absorbed dust debt is 0
uint256 internal constant VaultT1Admin__AbsorbedDustDebtIsZero = 33008;
/// @notice thrown when address is set as 0
uint256 internal constant VaultT1Admin__AddressZeroNotAllowed = 33009;
/***********************************|
| Vault Rewards |
|__________________________________*/
uint256 internal constant VaultRewards__Unauthorized = 34001;
uint256 internal constant VaultRewards__AddressZero = 34002;
uint256 internal constant VaultRewards__InvalidParams = 34003;
uint256 internal constant VaultRewards__NewMagnifierSameAsOldMagnifier = 34004;
uint256 internal constant VaultRewards__NotTheInitiator = 34005;
uint256 internal constant VaultRewards__AlreadyStarted = 34006;
uint256 internal constant VaultRewards__RewardsNotStartedOrEnded = 34007;
}//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import { IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
interface IFluidVaultFactory is IERC721Enumerable {
/// @notice Minting an NFT Vault for the user
function mint(uint256 vaultId_, address user_) external returns (uint256 tokenId_);
/// @notice returns owner of Vault which is also an NFT
function ownerOf(uint256 tokenId) external view returns (address owner);
/// @notice Global auth is auth for all vaults
function isGlobalAuth(address auth_) external view returns (bool);
/// @notice Vault auth is auth for a specific vault
function isVaultAuth(address vault_, address auth_) external view returns (bool);
/// @notice Total vaults deployed.
function totalVaults() external view returns (uint256);
/// @notice Compute vaultAddress
function getVaultAddress(uint256 vaultId) external view returns (address);
/// @notice read uint256 `result_` for a storage `slot_` key
function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
contract Variables {
/***********************************|
| Storage Variables |
|__________________________________*/
/// note: in all variables. For tick >= 0 are represented with bit as 1, tick < 0 are represented with bit as 0
/// note: read all the variables through storageRead.sol
/// note: vaultVariables contains vault variables which need regular updates through transactions
/// First 1 bit => 0 => re-entrancy. If 0 then allow transaction to go, else throw.
/// Next 1 bit => 1 => Is the current active branch liquidated? If true then check the branch's minima tick before creating a new position
/// If the new tick is greater than minima tick then initialize a new branch, make that as current branch & do proper linking
/// Next 1 bit => 2 => sign of topmost tick (0 -> negative; 1 -> positive)
/// Next 19 bits => 3-21 => absolute value of topmost tick
/// Next 30 bits => 22-51 => current branch ID
/// Next 30 bits => 52-81 => total branch ID
/// Next 64 bits => 82-145 => Total supply
/// Next 64 bits => 146-209 => Total borrow
/// Next 32 bits => 210-241 => Total positions
uint256 internal vaultVariables;
/// note: vaultVariables2 contains variables which do not update on every transaction. So mainly admin/auth set amount
/// First 16 bits => 0-15 => supply rate magnifier; 10000 = 1x (Here 16 bits should be more than enough)
/// Next 16 bits => 16-31 => borrow rate magnifier; 10000 = 1x (Here 16 bits should be more than enough)
/// Next 10 bits => 32-41 => collateral factor. 800 = 0.8 = 80% (max precision of 0.1%)
/// Next 10 bits => 42-51 => liquidation Threshold. 900 = 0.9 = 90% (max precision of 0.1%)
/// Next 10 bits => 52-61 => liquidation Max Limit. 950 = 0.95 = 95% (max precision of 0.1%) (above this 100% liquidation can happen)
/// Next 10 bits => 62-71 => withdraw gap. 100 = 0.1 = 10%. (max precision of 0.1%) (max 7 bits can also suffice for the requirement here of 0.1% to 10%). Needed to save some limits on withdrawals so liquidate can work seamlessly.
/// Next 10 bits => 72-81 => liquidation penalty. 100 = 0.01 = 1%. (max precision of 0.01%) (max liquidation penantly can be 10.23%). Applies when tick is in between liquidation Threshold & liquidation Max Limit.
/// Next 10 bits => 82-91 => borrow fee. 100 = 0.01 = 1%. (max precision of 0.01%) (max borrow fee can be 10.23%). Fees on borrow.
/// Next 4 bits => 92-95 => empty
/// Next 160 bits => 96-255 => Oracle address
uint256 internal vaultVariables2;
/// note: stores absorbed liquidity
/// First 128 bits raw debt amount
/// last 128 bits raw col amount
uint256 internal absorbedLiquidity;
/// position index => position data uint
/// if the entire variable is 0 (meaning not initialized) at the start that means no position at all
/// First 1 bit => 0 => position type (0 => borrow position; 1 => supply position)
/// Next 1 bit => 1 => sign of user's tick (0 => negative; 1 => positive)
/// Next 19 bits => 2-20 => absolute value of user's tick
/// Next 24 bits => 21-44 => user's tick's id
/// Below we are storing user's collateral & not debt, because the position can also be only collateral with no tick but it can never be only debt
/// Next 64 bits => 45-108 => user's supply amount. Debt will be calculated through supply & ratio.
/// Next 64 bits => 109-172 => user's dust debt amount. User's net debt = total debt - dust amount. Total debt is calculated through supply & ratio
/// User won't pay any extra interest on dust debt & hence we will not show it as a debt on UI. For user's there's no dust.
mapping(uint256 => uint256) internal positionData;
/// Tick has debt only keeps data of non liquidated positions. liquidated tick's data stays in branch itself
/// tick parent => uint (represents bool for 256 children)
/// parent of (i)th tick:-
/// if (i>=0) (i / 256);
/// else ((i + 1) / 256) - 1
/// first bit of the variable is the smallest tick & last bit is the biggest tick of that slot
mapping(int256 => uint256) internal tickHasDebt;
/// mapping tickId => tickData
/// Tick related data. Total debt & other things
/// First bit => 0 => If 1 then liquidated else not liquidated
/// Next 24 bits => 1-24 => Total IDs. ID should start from 1.
/// If not liquidated:
/// Next 64 bits => 25-88 => raw debt
/// If liquidated
/// The below 3 things are of last ID. This is to be updated when user creates a new position
/// Next 1 bit => 25 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated (100% liquidated)
/// Next 30 bits => 26-55 => branch ID where this tick got liquidated
/// Next 50 bits => 56-105 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)
mapping(int256 => uint256) internal tickData;
/// tick id => previous tick id liquidation data. ID starts from 1
/// One tick ID contains 3 IDs of 80 bits in it, holding liquidation data of previously active but liquidated ticks
/// 81 bits data below
/// #### First 85 bits ####
/// 1st bit => 0 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated
/// Next 30 bits => 1-30 => branch ID where this tick got liquidated
/// Next 50 bits => 31-80 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)
/// #### Second 85 bits ####
/// 85th bit => 85 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated
/// Next 30 bits => 86-115 => branch ID where this tick got liquidated
/// Next 50 bits => 116-165 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)
/// #### Third 85 bits ####
/// 170th bit => 170 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated
/// Next 30 bits => 171-200 => branch ID where this tick got liquidated
/// Next 50 bits => 201-250 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)
mapping(int256 => mapping(uint256 => uint256)) internal tickId;
/// mapping branchId => branchData
/// First 2 bits => 0-1 => if 0 then not liquidated, if 1 then liquidated, if 2 then merged, if 3 then closed
/// merged means the branch is merged into it's base branch
/// closed means all the users are 100% liquidated
/// Next 1 bit => 2 => minima tick sign of this branch. Will only be there if any liquidation happened.
/// Next 19 bits => 3-21 => minima tick of this branch. Will only be there if any liquidation happened.
/// Next 30 bits => 22-51 => Partials of minima tick of branch this is connected to. 0 if master branch.
/// Next 64 bits => 52-115 Debt liquidity at this branch. Similar to last's top tick data. Remaining debt will move here from tickData after first liquidation
/// If not merged
/// Next 50 bits => 116-165 => Debt factor or of this branch. (35 bits coefficient | 15 bits expansion)
/// If merged
/// Next 50 bits => 116-165 => Connection/adjustment debt factor of this branch with the next branch.
/// If closed
/// Next 50 bits => 116-165 => Debt factor as 0. As all the user's positions are now fully gone
/// following values are present always again (merged / not merged / closed)
/// Next 30 bits => 166-195 => Branch's ID with which this branch is connected. If 0 then that means this is the master branch
/// Next 1 bit => 196 => sign of minima tick of branch this is connected to. 0 if master branch.
/// Next 19 bits => 197-215 => minima tick of branch this is connected to. 0 if master branch.
mapping(uint256 => uint256) internal branchData;
/// Exchange prices are in 1e12
/// First 64 bits => 0-63 => Liquidity's collateral token supply exchange price
/// First 64 bits => 64-127 => Liquidity's debt token borrow exchange price
/// First 64 bits => 128-191 => Vault's collateral token supply exchange price
/// First 64 bits => 192-255 => Vault's debt token borrow exchange price
uint256 internal rates;
/// address of rebalancer
address internal rebalancer;
uint256 internal absorbedDustDebt;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IFluidVaultFactory } from "../../interfaces/iVaultFactory.sol";
import { IFluidLiquidity } from "../../../../liquidity/interfaces/iLiquidity.sol";
import { StorageRead } from "../../../../libraries/storageRead.sol";
import { Structs } from "./structs.sol";
interface TokenInterface {
function decimals() external view returns (uint8);
}
contract ConstantVariables is StorageRead, Structs {
/***********************************|
| Constant Variables |
|__________________________________*/
address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev collateral token address
address internal immutable SUPPLY_TOKEN;
/// @dev borrow token address
address internal immutable BORROW_TOKEN;
/// @dev Token decimals. For example wETH is 18 decimals
uint8 internal immutable SUPPLY_DECIMALS;
/// @dev Token decimals. For example USDC is 6 decimals
uint8 internal immutable BORROW_DECIMALS;
/// @dev VaultT1 AdminModule implemenation address
address internal immutable ADMIN_IMPLEMENTATION;
/// @dev VaultT1 Secondary implemenation (main2.sol) address
address internal immutable SECONDARY_IMPLEMENTATION;
/// @dev liquidity proxy contract address
IFluidLiquidity public immutable LIQUIDITY;
/// @dev vault factory contract address
IFluidVaultFactory public immutable VAULT_FACTORY;
uint public immutable VAULT_ID;
uint internal constant X8 = 0xff;
uint internal constant X10 = 0x3ff;
uint internal constant X16 = 0xffff;
uint internal constant X19 = 0x7ffff;
uint internal constant X20 = 0xfffff;
uint internal constant X24 = 0xffffff;
uint internal constant X25 = 0x1ffffff;
uint internal constant X30 = 0x3fffffff;
uint internal constant X35 = 0x7ffffffff;
uint internal constant X50 = 0x3ffffffffffff;
uint internal constant X64 = 0xffffffffffffffff;
uint internal constant X96 = 0xffffffffffffffffffffffff;
uint internal constant X128 = 0xffffffffffffffffffffffffffffffff;
uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;
/// @dev slot ids in Liquidity contract. Helps in low gas fetch from liquidity contract by skipping delegate call
bytes32 internal immutable LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT;
bytes32 internal immutable LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT;
bytes32 internal immutable LIQUIDITY_USER_SUPPLY_SLOT;
bytes32 internal immutable LIQUIDITY_USER_BORROW_SLOT;
/// @notice returns all Vault constants
function constantsView() external view returns (ConstantViews memory constantsView_) {
constantsView_.liquidity = address(LIQUIDITY);
constantsView_.factory = address(VAULT_FACTORY);
constantsView_.adminImplementation = ADMIN_IMPLEMENTATION;
constantsView_.secondaryImplementation = SECONDARY_IMPLEMENTATION;
constantsView_.supplyToken = SUPPLY_TOKEN;
constantsView_.borrowToken = BORROW_TOKEN;
constantsView_.supplyDecimals = SUPPLY_DECIMALS;
constantsView_.borrowDecimals = BORROW_DECIMALS;
constantsView_.vaultId = VAULT_ID;
constantsView_.liquiditySupplyExchangePriceSlot = LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT;
constantsView_.liquidityBorrowExchangePriceSlot = LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT;
constantsView_.liquidityUserSupplySlot = LIQUIDITY_USER_SUPPLY_SLOT;
constantsView_.liquidityUserBorrowSlot = LIQUIDITY_USER_BORROW_SLOT;
}
constructor(ConstantViews memory constants_) {
LIQUIDITY = IFluidLiquidity(constants_.liquidity);
VAULT_FACTORY = IFluidVaultFactory(constants_.factory);
VAULT_ID = constants_.vaultId;
SUPPLY_TOKEN = constants_.supplyToken;
BORROW_TOKEN = constants_.borrowToken;
SUPPLY_DECIMALS = constants_.supplyDecimals;
BORROW_DECIMALS = constants_.borrowDecimals;
// @dev those slots are calculated in the deploymentLogics / VaultFactory
LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT = constants_.liquiditySupplyExchangePriceSlot;
LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT = constants_.liquidityBorrowExchangePriceSlot;
LIQUIDITY_USER_SUPPLY_SLOT = constants_.liquidityUserSupplySlot;
LIQUIDITY_USER_BORROW_SLOT = constants_.liquidityUserBorrowSlot;
ADMIN_IMPLEMENTATION = constants_.adminImplementation;
SECONDARY_IMPLEMENTATION = constants_.secondaryImplementation;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
contract Events {
/// @notice emitted when an operate() method is executed that changes collateral (`colAmt_`) / debt (debtAmt_`)
/// amount for a `user_` position with `nftId_`. Receiver of any funds is the address `to_`.
event LogOperate(address user_, uint256 nftId_, int256 colAmt_, int256 debtAmt_, address to_);
/// @notice emitted when the exchange prices are updated in storage.
event LogUpdateExchangePrice(uint256 supplyExPrice_, uint256 borrowExPrice_);
/// @notice emitted when a liquidation has been executed.
event LogLiquidate(address liquidator_, uint256 colAmt_, uint256 debtAmt_, address to_);
/// @notice emitted when `absorb()` was executed to absorb bad debt.
event LogAbsorb(uint colAbsorbedRaw_, uint debtAbsorbedRaw_);
/// @notice emitted when a `rebalance()` has been executed, balancing out total supply / borrow between Vault
/// and Fluid Liquidity pools.
/// if `colAmt_` is positive then profit, meaning withdrawn from vault and sent to rebalancer address.
/// if `colAmt_` is negative then loss, meaning transfer from rebalancer address to vault and deposit.
/// if `debtAmt_` is positive then profit, meaning borrow from vault and sent to rebalancer address.
/// if `debtAmt_` is negative then loss, meaning transfer from rebalancer address to vault and payback.
event LogRebalance(int colAmt_, int debtAmt_);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { Variables } from "../common/variables.sol";
import { ConstantVariables } from "./constantVariables.sol";
import { Events } from "./events.sol";
import { TickMath } from "../../../../libraries/tickMath.sol";
import { BigMathMinified } from "../../../../libraries/bigMathMinified.sol";
import { BigMathVault } from "../../../../libraries/bigMathVault.sol";
import { LiquidityCalcs } from "../../../../libraries/liquidityCalcs.sol";
import { ErrorTypes } from "../../errorTypes.sol";
import { Error } from "../../error.sol";
/// @dev Fluid vault protocol helper methods. Mostly used for `operate()` and `liquidate()` methods of CoreModule.
abstract contract Helpers is Variables, ConstantVariables, Events, Error {
using BigMathMinified for uint256;
using BigMathVault for uint256;
/// @notice Calculates new vault exchange prices. Does not update values in storage.
/// @param vaultVariables2_ exactly same as vaultVariables2 from storage
/// @return liqSupplyExPrice_ latest liquidity's supply token supply exchange price
/// @return liqBorrowExPrice_ latest liquidity's borrow token borrow exchange price
/// @return vaultSupplyExPrice_ latest vault's supply token exchange price
/// @return vaultBorrowExPrice_ latest vault's borrow token exchange price
function updateExchangePrices(
uint256 vaultVariables2_
)
public
view
returns (
uint256 liqSupplyExPrice_,
uint256 liqBorrowExPrice_,
uint256 vaultSupplyExPrice_,
uint256 vaultBorrowExPrice_
)
{
// Fetching last stored rates
uint rates_ = rates;
(liqSupplyExPrice_, ) = LiquidityCalcs.calcExchangePrices(
LIQUIDITY.readFromStorage(LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT)
);
(, liqBorrowExPrice_) = LiquidityCalcs.calcExchangePrices(
LIQUIDITY.readFromStorage(LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT)
);
uint256 oldLiqSupplyExPrice_ = (rates_ & X64);
uint256 oldLiqBorrowExPrice_ = ((rates_ >> 64) & X64);
if (liqSupplyExPrice_ < oldLiqSupplyExPrice_ || liqBorrowExPrice_ < oldLiqBorrowExPrice_) {
// new liquidity exchange price is < than the old one. liquidity exchange price should only ever increase.
// If not, something went wrong and avoid proceeding with unknown outcome.
revert FluidVaultError(ErrorTypes.VaultT1__LiquidityExchangePriceUnexpected);
}
// liquidity Exchange Prices always increases in next block. Hence substraction with old will never be negative
// uint64 * 1e18 is the max the number that could be
unchecked {
// Calculating increase in supply exchange price w.r.t last stored liquidity's exchange price
// vaultSupplyExPrice_ => supplyIncreaseInPercent_
vaultSupplyExPrice_ = ((((liqSupplyExPrice_ * 1e18) / oldLiqSupplyExPrice_) - 1e18) *
(vaultVariables2_ & X16)) / 10000; // supply rate magnifier
// Calculating increase in borrow exchange price w.r.t last stored liquidity's exchange price
// vaultBorrowExPrice_ => borrowIncreaseInPercent_
vaultBorrowExPrice_ = ((((liqBorrowExPrice_ * 1e18) / oldLiqBorrowExPrice_) - 1e18) *
((vaultVariables2_ >> 16) & X16)) / 10000; // borrow rate magnifier
// It's extremely hard the exchange prices to overflow even in 100 years but if it does it's not an
// issue here as we are not updating on storage
// (rates_ >> 128) & X64) -> last stored vault's supply token exchange price
vaultSupplyExPrice_ = (((rates_ >> 128) & X64) * (1e18 + vaultSupplyExPrice_)) / 1e18;
// (rates_ >> 192) -> last stored vault's borrow token exchange price (no need to mask with & X64 as it is anyway max 64 bits)
vaultBorrowExPrice_ = ((rates_ >> 192) * (1e18 + vaultBorrowExPrice_)) / 1e18;
}
}
/// note admin module is also calling this function self call
/// @dev updating exchange price on storage. Only need to update on storage when changing supply or borrow magnifier
function updateExchangePricesOnStorage()
public
returns (
uint256 liqSupplyExPrice_,
uint256 liqBorrowExPrice_,
uint256 vaultSupplyExPrice_,
uint256 vaultBorrowExPrice_
)
{
(liqSupplyExPrice_, liqBorrowExPrice_, vaultSupplyExPrice_, vaultBorrowExPrice_) = updateExchangePrices(
vaultVariables2
);
if (
liqSupplyExPrice_ > X64 || liqBorrowExPrice_ > X64 || vaultSupplyExPrice_ > X64 || vaultBorrowExPrice_ > X64
) {
revert FluidVaultError(ErrorTypes.VaultT1__ExchangePriceOverFlow);
}
// Updating in storage
rates =
liqSupplyExPrice_ |
(liqBorrowExPrice_ << 64) |
(vaultSupplyExPrice_ << 128) |
(vaultBorrowExPrice_ << 192);
emit LogUpdateExchangePrice(vaultSupplyExPrice_, vaultBorrowExPrice_);
}
/// @dev fetches new user's position after liquidation. The new liquidated position's debt is decreased by 0.01%
/// to make sure that branch's liquidity never becomes 0 as if it would have gotten 0 then there will be multiple cases that we would need to tackle.
/// @param positionTick_ position's tick when it was last updated through operate
/// @param positionTickId_ position's tick Id. This stores the debt factor and branch to make the first connection
/// @param positionRawDebt_ position's raw debt when it was last updated through operate
/// @param tickData_ position's tick's tickData just for minor comparison to know if data is moved to tick Id or is still in tick data
/// @return final tick position after all the liquidation
/// @return final debt of position after all the liquidation
/// @return positionRawCol_ final collateral of position after all the liquidation
/// @return branchId_ final branch's ID where the position is at currently
/// @return branchData_ final branch's data where the position is at currently
function fetchLatestPosition(
int256 positionTick_,
uint256 positionTickId_,
uint256 positionRawDebt_,
uint256 tickData_
)
public
view
returns (
int256, // positionTick_
uint256, // positionRawDebt_
uint256 positionRawCol_,
uint256 branchId_,
uint256 branchData_
)
{
uint256 initialPositionRawDebt_ = positionRawDebt_;
uint256 connectionFactor_;
bool isFullyLiquidated_;
// Checking if tick's total ID = user's tick ID
if (((tickData_ >> 1) & X24) == positionTickId_) {
// fetching from tick data itself
isFullyLiquidated_ = ((tickData_ >> 25) & 1) == 1;
branchId_ = (tickData_ >> 26) & X30;
connectionFactor_ = (tickData_ >> 56) & X50;
} else {
{
uint256 tickLiquidationData_;
unchecked {
// Fetching tick's liquidation data. One variable contains data of 3 IDs. Tick Id mapping is starting from 1.
tickLiquidationData_ =
tickId[positionTick_][(positionTickId_ + 2) / 3] >>
(((positionTickId_ + 2) % 3) * 85);
}
isFullyLiquidated_ = (tickLiquidationData_ & 1) == 1;
branchId_ = (tickLiquidationData_ >> 1) & X30;
connectionFactor_ = (tickLiquidationData_ >> 31) & X50;
}
}
// data of branch
branchData_ = branchData[branchId_];
if (isFullyLiquidated_) {
positionTick_ = type(int).min;
positionRawDebt_ = 0;
} else {
// Below information about connection debt factor
// If branch is merged, Connection debt factor is used to multiply in order to get perfect liquidation of user
// For example: Considering user was at the top.
// In first branch, the user liquidated to debt factor 0.5 and then branch got merged (branching starting from 1)
// In second branch, it got liquidated to 0.4 but when the above branch merged the debt factor on this branch was 0.6
// Meaning on 1st branch, user got liquidated by 50% & on 2nd by 33.33%. So a total of 66.6%.
// What we will set a connection factor will be 0.6/0.5 = 1.2
// So now to get user's position, this is what we'll do:
// finalDebt = (0.4 / (1 * 1.2)) * debtBeforeLiquidation
// 0.4 is current active branch's minima debt factor
// 1 is debt factor from where user started
// 1.2 is connection factor which we found out through 0.6 / 0.5
while ((branchData_ & 3) == 2) {
// If true then the branch is merged
// userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor aka adjustmentDebtFactor
connectionFactor_ = connectionFactor_.mulBigNumber(((branchData_ >> 116) & X50));
if (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR) break; // user ~100% liquidated
// Note we don't need updated branch data in case of 100% liquidated so saving gas for fetching it
// Fetching new branch data
branchId_ = (branchData_ >> 166) & X30; // Link to base branch of current branch
branchData_ = branchData[branchId_];
}
// When the while loop breaks meaning the branch now has minima Debt Factor or is a closed branch;
if (((branchData_ & 3) == 3) || (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR)) {
// Branch got closed (or user liquidated ~100%). Hence make the user's position 0
// Rare cases to get into this situation
// Branch can get close often but once closed it's tricky that some user might come iterating through there
// If a user comes then that user will be very mini user like some cents probably
positionTick_ = type(int).min;
positionRawDebt_ = 0;
} else {
// If branch is not merged, the main branch it's connected to then it'll have minima debt factor
// position debt = debt * base branch minimaDebtFactor / connectionFactor
positionRawDebt_ = positionRawDebt_.mulDivNormal(
(branchData_ >> 116) & X50, // minimaDebtFactor
connectionFactor_
);
unchecked {
// Reducing user's liquidity by 0.01% if user got liquidated.
// As this will make sure that the branch always have some debt even if all liquidated user left
// This saves a lot more logics & consideration on Operate function
// if we don't do this then we have to add logics related to closing the branch and factor connections accordingly.
if (positionRawDebt_ > (initialPositionRawDebt_ / 100)) {
positionRawDebt_ = (positionRawDebt_ * 9999) / 10000;
} else {
// if user debt reduced by more than 99% in liquidation then making user as fully liquidated
positionRawDebt_ = 0;
}
}
{
if (positionRawDebt_ > 0) {
// positionTick_ -> read minima tick of branch
unchecked {
positionTick_ = branchData_ & 4 == 4
? int((branchData_ >> 3) & X19)
: -int((branchData_ >> 3) & X19);
}
// Calculating user's collateral
uint256 ratioAtTick_ = TickMath.getRatioAtTick(int24(positionTick_));
uint256 ratioOneLess_;
unchecked {
ratioOneLess_ = (ratioAtTick_ * 10000) / 10015;
}
// formula below for better readability:
// length = ratioAtTick_ - ratioOneLess_
// ratio = ratioOneLess_ + (length * positionPartials_) / X30
// positionRawCol_ = (positionRawDebt_ * (1 << 96)) / ratio_
positionRawCol_ =
(positionRawDebt_ * TickMath.ZERO_TICK_SCALED_RATIO) /
(ratioOneLess_ + ((ratioAtTick_ - ratioOneLess_) * ((branchData_ >> 22) & X30)) / X30);
} else {
positionTick_ = type(int).min;
}
}
}
}
return (positionTick_, positionRawDebt_, positionRawCol_, branchId_, branchData_);
}
/// @dev sets `tick_` as having debt or no debt in storage `tickHasDebt` depending on `addOrRemove_`
/// @param tick_ tick to add or remove from tickHasDebt
/// @param addOrRemove_ if true then add else remove
function _updateTickHasDebt(int tick_, bool addOrRemove_) internal {
// Positive mapID_ starts from 0 & above and negative starts below 0.
// tick 0 to 255 will have mapId_ as 0 while tick -256 to -1 will have mapId_ as -1.
unchecked {
int mapId_ = tick_ < 0 ? ((tick_ + 1) / 256) - 1 : tick_ / 256;
// in case of removing:
// (tick == 255) tickHasDebt[mapId_] - 1 << 255
// (tick == 0) tickHasDebt[mapId_] - 1 << 0
// (tick == -1) tickHasDebt[mapId_] - 1 << 255
// (tick == -256) tickHasDebt[mapId_] - 1 << 0
// in case of adding:
// (tick == 255) tickHasDebt[mapId_] - 1 << 255
// (tick == 0) tickHasDebt[mapId_] - 1 << 0
// (tick == -1) tickHasDebt[mapId_] - 1 << 255
// (tick == -256) tickHasDebt[mapId_] - 1 << 0
uint position_ = uint(tick_ - (mapId_ * 256));
tickHasDebt[mapId_] = addOrRemove_
? tickHasDebt[mapId_] | (1 << position_)
: tickHasDebt[mapId_] & ~(1 << position_);
}
}
/// @dev gets next perfect top tick (tick which is not liquidated)
/// @param topTick_ current top tick which will no longer be top tick
/// @return nextTick_ next top tick which will become the new top tick
function _fetchNextTopTick(int topTick_) internal view returns (int nextTick_) {
int mapId_;
uint tickHasDebt_;
unchecked {
mapId_ = topTick_ < 0 ? ((topTick_ + 1) / 256) - 1 : topTick_ / 256;
uint bitsToRemove_ = uint(-topTick_ + (mapId_ * 256 + 256));
// Removing current top tick from tickHasDebt
tickHasDebt_ = (tickHasDebt[mapId_] << bitsToRemove_) >> bitsToRemove_;
// For last user remaining in vault there could be a lot of iterations in the while loop.
// Chances of this to happen is extremely low (like ~0%)
while (true) {
if (tickHasDebt_ > 0) {
nextTick_ = mapId_ * 256 + int(tickHasDebt_.mostSignificantBit()) - 1;
break;
}
// Reducing mapId_ by 1 in every loop; if it reaches to -129 then no filled tick exist, meaning it's the last tick
if (--mapId_ == -129) {
nextTick_ = type(int).min;
break;
}
tickHasDebt_ = tickHasDebt[mapId_];
}
}
}
/// @dev adding debt to a particular tick
/// @param totalColRaw_ total raw collateral of position
/// @param netDebtRaw_ net raw debt (total debt - dust debt)
/// @return tick_ tick where the debt is being added
/// @return tickId_ tick current id
/// @return userRawDebt_ user's total raw debt
/// @return rawDust_ dust debt used for adjustment
function _addDebtToTickWrite(
uint256 totalColRaw_,
uint256 netDebtRaw_ // debtRaw - dust
) internal returns (int256 tick_, uint256 tickId_, uint256 userRawDebt_, uint256 rawDust_) {
if (netDebtRaw_ < 10000) {
// thrown if user's debt is too low
revert FluidVaultError(ErrorTypes.VaultT1__UserDebtTooLow);
}
// tick_ & ratio_ returned from library is round down. Hence increasing it by 1 and increasing ratio by 1 tick.
uint ratio_ = (netDebtRaw_ * TickMath.ZERO_TICK_SCALED_RATIO) / totalColRaw_;
(tick_, ratio_) = TickMath.getTickAtRatio(ratio_);
unchecked {
++tick_;
ratio_ = (ratio_ * 10015) / 10000;
}
userRawDebt_ = (ratio_ * totalColRaw_) >> 96;
rawDust_ = userRawDebt_ - netDebtRaw_;
// Current state of tick
uint256 tickData_ = tickData[tick_];
tickId_ = (tickData_ >> 1) & X24;
uint tickNewDebt_;
if (tickId_ > 0 && tickData_ & 1 == 0) {
// Current debt in the tick
uint256 tickExistingRawDebt_ = (tickData_ >> 25) & X64;
tickExistingRawDebt_ = (tickExistingRawDebt_ >> 8) << (tickExistingRawDebt_ & X8);
// Tick's already initialized and not liquidated. Hence simply add the debt
tickNewDebt_ = tickExistingRawDebt_ + userRawDebt_;
if (tickExistingRawDebt_ == 0) {
// Adding tick into tickHasDebt
_updateTickHasDebt(tick_, true);
}
} else {
// Liquidation happened or tick getting initialized for the very first time.
if (tickId_ > 0) {
// Meaning a liquidation happened. Hence move the data to tickID
unchecked {
uint tickMap_ = (tickId_ + 2) / 3;
// Adding 2 in ID so we can get right mapping ID. For example for ID 1, 2 & 3 mapping should be 1 and so on..
// For example shift for id 1 should be 0, for id 2 should be 85, for id 3 it should be 170 and so on..
tickId[tick_][tickMap_] =
tickId[tick_][tickMap_] |
((tickData_ >> 25) << (((tickId_ + 2) % 3) * 85));
}
}
// Increasing total ID by one
unchecked {
++tickId_;
}
tickNewDebt_ = userRawDebt_;
// Adding tick into tickHasDebt
_updateTickHasDebt(tick_, true);
}
if (tickNewDebt_ < 10000) {
// thrown if tick's debt/liquidity is too low
revert FluidVaultError(ErrorTypes.VaultT1__TickDebtTooLow);
}
tickData[tick_] = (tickId_ << 1) | (tickNewDebt_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 25);
}
/// @dev sets new top tick. If it comes to this function then that means current top tick is perfect tick.
/// if next top tick is liquidated then unitializes the current non liquidated branch and make the liquidated branch as current branch
/// @param topTick_ current top tick
/// @param vaultVariables_ vaultVariables of storage but with newer updates
/// @return newVaultVariables_ newVaultVariables_ updated vault variable internally to this function
/// @return newTopTick_ new top tick
function _setNewTopTick(
int topTick_,
uint vaultVariables_
) internal returns (uint newVaultVariables_, int newTopTick_) {
// This function considers that the current top tick was not liquidated
// Overall flow of function:
// if new top tick liquidated (aka base branch's minima tick) -> Close the current branch and make base branch as current branch
// if new top tick not liquidated -> update things in current branch.
// if new top tick is not liquidated and same tick exist in base branch then tick is considered as not liquidated.
uint branchId_ = (vaultVariables_ >> 22) & X30; // branch id of current branch
uint256 branchData_ = branchData[branchId_];
int256 baseBranchMinimaTick_;
if ((branchData_ >> 196) & 1 == 1) {
baseBranchMinimaTick_ = int((branchData_ >> 197) & X19);
} else {
unchecked {
baseBranchMinimaTick_ = -int((branchData_ >> 197) & X19);
}
if (baseBranchMinimaTick_ == 0) {
// meaning the current branch is the master branch
baseBranchMinimaTick_ = type(int).min;
}
}
// Returns type(int).min if no top tick exist
int nextTopTickNotLiquidated_ = _fetchNextTopTick(topTick_);
newTopTick_ = baseBranchMinimaTick_ > nextTopTickNotLiquidated_
? baseBranchMinimaTick_
: nextTopTickNotLiquidated_;
if (newTopTick_ == type(int).min) {
// if this happens that means this was the last user of the vault :(
vaultVariables_ = vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001;
} else if (newTopTick_ == nextTopTickNotLiquidated_) {
// New top tick exist in current non liquidated branch
if (newTopTick_ < 0) {
unchecked {
vaultVariables_ =
(vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001) |
(uint(-newTopTick_) << 3);
}
} else {
vaultVariables_ =
(vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001) |
4 | // setting top tick as positive
(uint(newTopTick_) << 3);
}
} else {
// if this happens that means base branch exists & is the next top tick
// Remove current non liquidated branch as active.
// Not deleting here as it's going to get initialize again whenever a new top tick comes
branchData[branchId_] = 0;
// Inserting liquidated branch's minima tick
unchecked {
vaultVariables_ =
(vaultVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00000000000000000001) |
2 | // Setting top tick as liquidated
(((branchData_ >> 196) & X20) << 2) | // new current top tick = base branch minima tick
(((branchData_ >> 166) & X30) << 22) | // new current branch id = base branch id
((branchId_ - 1) << 52); // reduce total branch id by 1
}
}
newVaultVariables_ = vaultVariables_;
}
constructor(ConstantViews memory constants_) ConstantVariables(constants_) {}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
contract Structs {
// structs are used to mitigate Stack too deep errors
struct OperateMemoryVars {
// ## User's position before update ##
uint oldColRaw;
uint oldNetDebtRaw; // total debt - dust debt
int oldTick;
// ## User's position after update ##
uint colRaw;
uint debtRaw; // total debt - dust debt
uint dustDebtRaw;
int tick;
uint tickId;
// others
uint256 vaultVariables2;
uint256 branchId;
int256 topTick;
uint liquidityExPrice;
uint supplyExPrice;
uint borrowExPrice;
uint branchData;
// user's supply slot data in liquidity
uint userSupplyLiquidityData;
}
struct BranchData {
uint id;
uint data;
uint ratio;
uint debtFactor;
int minimaTick;
uint baseBranchData;
}
struct TickData {
int tick;
uint data;
uint ratio;
uint ratioOneLess;
uint length;
uint currentRatio; // current tick is ratio with partials.
uint partials;
}
// note: All the below token amounts are in raw form.
struct CurrentLiquidity {
uint256 debtRemaining; // Debt remaining to liquidate
uint256 debt; // Current liquidatable debt before reaching next check point
uint256 col; // Calculate using debt & ratioCurrent
uint256 colPerDebt; // How much collateral to liquidate per unit of Debt
uint256 totalDebtLiq; // Total debt liquidated till now
uint256 totalColLiq; // Total collateral liquidated till now
int tick; // Current tick to liquidate
uint ratio; // Current ratio to liquidate
uint tickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick
int refTick; // ref tick to liquidate
uint refRatio; // ratio at ref tick
uint refTickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick, if 3 that means it's a liquidation threshold
}
struct TickHasDebt {
int tick; // current tick
int nextTick; // next tick with liquidity
int mapId; // mapping ID of tickHasDebt
uint bitsToRemove; // liquidity to remove till tick_ so we can search for next tick
uint tickHasDebt; // getting tickHasDebt_ from tickHasDebt[mapId_]
uint mostSigBit; // most significant bit in tickHasDebt_ to get the next tick
}
struct LiquidateMemoryVars {
uint256 vaultVariables2;
int liquidationTick;
int maxTick;
uint256 supplyExPrice;
uint256 borrowExPrice;
}
struct AbsorbMemoryVariables {
uint256 supplyExPrice;
uint256 borrowExPrice;
uint256 debtAbsorbed;
uint256 colAbsorbed;
uint256 vaultVariables2;
int256 startingTick;
uint256 mostSigBit;
}
struct ConstantViews {
address liquidity;
address factory;
address adminImplementation;
address secondaryImplementation;
address supplyToken;
address borrowToken;
uint8 supplyDecimals;
uint8 borrowDecimals;
uint vaultId;
bytes32 liquiditySupplyExchangePriceSlot;
bytes32 liquidityBorrowExchangePriceSlot;
bytes32 liquidityUserSupplySlot;
bytes32 liquidityUserBorrowSlot;
}
struct RebalanceMemoryVariables {
uint256 liqSupplyExPrice;
uint256 liqBorrowExPrice;
uint256 vaultSupplyExPrice;
uint256 vaultBorrowExPrice;
}
}{
"optimizer": {
"enabled": true,
"runs": 10000000
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"address","name":"liquidity","type":"address"},{"internalType":"address","name":"factory","type":"address"},{"internalType":"address","name":"adminImplementation","type":"address"},{"internalType":"address","name":"secondaryImplementation","type":"address"},{"internalType":"address","name":"supplyToken","type":"address"},{"internalType":"address","name":"borrowToken","type":"address"},{"internalType":"uint8","name":"supplyDecimals","type":"uint8"},{"internalType":"uint8","name":"borrowDecimals","type":"uint8"},{"internalType":"uint256","name":"vaultId","type":"uint256"},{"internalType":"bytes32","name":"liquiditySupplyExchangePriceSlot","type":"bytes32"},{"internalType":"bytes32","name":"liquidityBorrowExchangePriceSlot","type":"bytes32"},{"internalType":"bytes32","name":"liquidityUserSupplySlot","type":"bytes32"},{"internalType":"bytes32","name":"liquidityUserBorrowSlot","type":"bytes32"}],"internalType":"struct Structs.ConstantViews","name":"constants_","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"colLiquidated","type":"uint256"},{"internalType":"uint256","name":"debtLiquidated","type":"uint256"}],"name":"FluidLiquidateResult","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidLiquidityCalcsError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSafeTransferError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidVaultError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"colAbsorbedRaw_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtAbsorbedRaw_","type":"uint256"}],"name":"LogAbsorb","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator_","type":"address"},{"indexed":false,"internalType":"uint256","name":"colAmt_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtAmt_","type":"uint256"},{"indexed":false,"internalType":"address","name":"to_","type":"address"}],"name":"LogLiquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user_","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftId_","type":"uint256"},{"indexed":false,"internalType":"int256","name":"colAmt_","type":"int256"},{"indexed":false,"internalType":"int256","name":"debtAmt_","type":"int256"},{"indexed":false,"internalType":"address","name":"to_","type":"address"}],"name":"LogOperate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"colAmt_","type":"int256"},{"indexed":false,"internalType":"int256","name":"debtAmt_","type":"int256"}],"name":"LogRebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"supplyExPrice_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowExPrice_","type":"uint256"}],"name":"LogUpdateExchangePrice","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"LIQUIDITY","outputs":[{"internalType":"contract IFluidLiquidity","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_FACTORY","outputs":[{"internalType":"contract IFluidVaultFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"absorb","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"constantsView","outputs":[{"components":[{"internalType":"address","name":"liquidity","type":"address"},{"internalType":"address","name":"factory","type":"address"},{"internalType":"address","name":"adminImplementation","type":"address"},{"internalType":"address","name":"secondaryImplementation","type":"address"},{"internalType":"address","name":"supplyToken","type":"address"},{"internalType":"address","name":"borrowToken","type":"address"},{"internalType":"uint8","name":"supplyDecimals","type":"uint8"},{"internalType":"uint8","name":"borrowDecimals","type":"uint8"},{"internalType":"uint256","name":"vaultId","type":"uint256"},{"internalType":"bytes32","name":"liquiditySupplyExchangePriceSlot","type":"bytes32"},{"internalType":"bytes32","name":"liquidityBorrowExchangePriceSlot","type":"bytes32"},{"internalType":"bytes32","name":"liquidityUserSupplySlot","type":"bytes32"},{"internalType":"bytes32","name":"liquidityUserBorrowSlot","type":"bytes32"}],"internalType":"struct Structs.ConstantViews","name":"constantsView_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"positionTick_","type":"int256"},{"internalType":"uint256","name":"positionTickId_","type":"uint256"},{"internalType":"uint256","name":"positionRawDebt_","type":"uint256"},{"internalType":"uint256","name":"tickData_","type":"uint256"}],"name":"fetchLatestPosition","outputs":[{"internalType":"int256","name":"","type":"int256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"positionRawCol_","type":"uint256"},{"internalType":"uint256","name":"branchId_","type":"uint256"},{"internalType":"uint256","name":"branchData_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"debtAmt_","type":"uint256"},{"internalType":"uint256","name":"colPerUnitDebt_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"bool","name":"absorb_","type":"bool"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"actualDebtAmt_","type":"uint256"},{"internalType":"uint256","name":"actualColAmt_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"liquidityCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId_","type":"uint256"},{"internalType":"int256","name":"newCol_","type":"int256"},{"internalType":"int256","name":"newDebt_","type":"int256"},{"internalType":"address","name":"to_","type":"address"}],"name":"operate","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"int256","name":"","type":"int256"},{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"slot_","type":"bytes32"}],"name":"readFromStorage","outputs":[{"internalType":"uint256","name":"result_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalance","outputs":[{"internalType":"int256","name":"supplyAmt_","type":"int256"},{"internalType":"int256","name":"borrowAmt_","type":"int256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultVariables2_","type":"uint256"}],"name":"updateExchangePrices","outputs":[{"internalType":"uint256","name":"liqSupplyExPrice_","type":"uint256"},{"internalType":"uint256","name":"liqBorrowExPrice_","type":"uint256"},{"internalType":"uint256","name":"vaultSupplyExPrice_","type":"uint256"},{"internalType":"uint256","name":"vaultBorrowExPrice_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateExchangePricesOnStorage","outputs":[{"internalType":"uint256","name":"liqSupplyExPrice_","type":"uint256"},{"internalType":"uint256","name":"liqBorrowExPrice_","type":"uint256"},{"internalType":"uint256","name":"vaultSupplyExPrice_","type":"uint256"},{"internalType":"uint256","name":"vaultBorrowExPrice_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6102206040523480156200001257600080fd5b5060405162005fa238038062005fa28339810160408190526200003591620002a6565b80516001600160a01b0390811661014081815260208401518316610160908152610100808601516101809081526080808801518716905260a0808801518716905260c08088015160ff90811690915260e0808901519091169052610120808801516101a0819052948801516101c052928701516101e052860151610200526040808701518616909152606086015190941690526000805466100000004000001781559251632d71cdb960e21b815260048101919091526001600160401b0391605b9163b5c736e490602401602060405180830381865afa1580156200011e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000144919062000396565b901c16905060006001600160401b03609b610140516001600160a01b031663b5c736e46101c0516040518263ffffffff1660e01b81526004016200018a91815260200190565b602060405180830381865afa158015620001a8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ce919062000396565b901c16905064e8d4a51000821080620001eb575064e8d4a5100081105b1562000212576040516330090e6560e11b8152617924600482015260240160405180910390fd5b60401b177ce8d4a51000000000e8d4a51000000000000000000000000000000000001760085550620003b0565b6040516101a081016001600160401b03811182821017156200027157634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200028f57600080fd5b919050565b805160ff811681146200028f57600080fd5b60006101a08284031215620002ba57600080fd5b620002c46200023f565b620002cf8362000277565b8152620002df6020840162000277565b6020820152620002f26040840162000277565b6040820152620003056060840162000277565b6060820152620003186080840162000277565b60808201526200032b60a0840162000277565b60a08201526200033e60c0840162000294565b60c08201526200035160e0840162000294565b60e08201526101008381015190820152610120808401519082015261014080840151908201526101608084015190820152610180928301519281019290925250919050565b600060208284031215620003a957600080fd5b5051919050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051615a526200055060003960006106fe0152600081816106d70152611c010152600081816106b001526124b301526000818161068901526123d1015260008181610458015281816106620152610c1f01526000818161010d015281816101c7015281816103830152818161054b01528181610c4b0152610d3b0152600081816104240152818161052601528181611c2701528181611d5101528181611f8e015281816120ad0152818161219201528181612419015281816124e001528181613c5601528181613d7301528181613f7f015261402f01526000818161059a01528181612906015261405f01526000818161028b01526105730152600061063b015260006106130152600081816105ea01528181610a4701528181611ef001528181611fcb015281816121d001528181612a4601528181613bf90152613c930152600081816105c2015281816109a101528181611db801528181611dfd015281816120eb0152613d9a0152615a526000f3fe6080604052600436106100d25760003560e01c8063540acabc1161007f578063ad20750111610059578063ad207501146104b8578063b5c736e4146104d8578063b7791bf2146104f7578063ebd93ac61461072f576100d2565b8063540acabc146104465780637d7c2a1c146104885780638433ea22146104a5576100d2565b8063103f2907116100b0578063103f29071461037157806322348cc7146103ca5780632861c7d114610412576100d2565b806302161887146102e9578063032d22761461032357806309f0d8cb14610351575b3480156100de57600080fd5b506040517f4502d0630000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690634502d06390602401602060405180830381865afa158015610169573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061018d9190615424565b8061024757506040517fe04c8e5d0000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e04c8e5d90604401602060405180830381865afa158015610223573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102479190615424565b610286576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792560048201526024015b60405180910390fd5b6102e77f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061074492505050565b005b3480156102f557600080fd5b506102fe610790565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b610336610331366004615463565b610884565b6040805193845260208401929092529082015260600161031a565b34801561035d57600080fd5b506102fe61036c3660046154a4565b6123a6565b34801561037d57600080fd5b506103a57f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161031a565b3480156103d657600080fd5b506103ea6103e53660046154bd565b61263e565b604080519586526020860194909452928401919091526060830152608082015260a00161031a565b34801561041e57600080fd5b506103a57f000000000000000000000000000000000000000000000000000000000000000081565b34801561045257600080fd5b5061047a7f000000000000000000000000000000000000000000000000000000000000000081565b60405190815260200161031a565b6104906128fe565b6040805192835260208301919091520161031a565b6104906104b33660046154ef565b61297e565b3480156104c457600080fd5b506102e76104d336600461552e565b613f67565b3480156104e457600080fd5b5061047a6104f33660046154a4565b5490565b34801561050357600080fd5b50604080516101a08101825273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000008116828401527f0000000000000000000000000000000000000000000000000000000000000000811660608301527f0000000000000000000000000000000000000000000000000000000000000000811660808301527f00000000000000000000000000000000000000000000000000000000000000001660a082015260ff7f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f00000000000000000000000000000000000000000000000000000000000000001660e08201527f00000000000000000000000000000000000000000000000000000000000000006101008201527f00000000000000000000000000000000000000000000000000000000000000006101208201527f00000000000000000000000000000000000000000000000000000000000000006101408201527f00000000000000000000000000000000000000000000000000000000000000006101608201527f0000000000000000000000000000000000000000000000000000000000000000610180820152905161031a91906155b7565b34801561073b57600080fd5b506102e761405a565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e81156001810361078757816000803e816000fd5b50505092915050565b6000806000806107a16001546123a6565b9296509094509250905067ffffffffffffffff8411806107c8575067ffffffffffffffff83115b806107da575067ffffffffffffffff82115b806107ec575067ffffffffffffffff81115b15610827576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792d600482015260240161027d565b604083811b8517608084901b1760c083901b17600855517fcde545703e0372175cadfff811d67c32910c3dcb33199679b3271c4106afdf9a906108769084908490918252602082015260400190565b60405180910390a190919293565b60008054819081906001811682036108a257600181176000556108d8565b6040517f60121cca000000000000000000000000000000000000000000000000000000008152617919600482015260240161027d565b861580156108e4575085155b806109245750861580159061092457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f087138015610924575061271087125b806109645750851580159061096457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f086138015610964575061271086125b1561099f576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791a600482015260240161027d565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480156109f85750600087135b15610a3f57348714610a3a576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791b600482015260240161027d565b610ad8565b3415610ad8577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015610a9e5750600086125b610ad8576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791b600482015260240161027d565b610b58604051806102000160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600154610100820152600282901c620fffff166000808215610ba05782600116600114610b91576207ffff600184901c16600003610bc2565b6207ffff600184901c16610bc2565b7f80000000000000000000000000000000000000000000000000000000000000005b61014085015260008c9003610cf4577f800000000000000000000000000000000000000000000000000000000000000060c08501526040517f94bf804d0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906394bf804d906044016020604051808303816000875af1158015610ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccd9190615706565b9b50847a040000000000000000000000000000000000000000000000000000019450610f13565b60008b1280610d03575060008a135b8015610dd457506040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018d905233907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690636352211e90602401602060405180830381865afa158015610d97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dbb919061571f565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610e0f576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791c600482015260240161027d565b60008c8152600360205260408120549350839003610e5d576040517f60121cca000000000000000000000000000000000000000000000000000000008152617923600482015260240161027d565b66ffffffffffffff603584901c811660ff602d86901c81169190911b6060870152607585901c909116606d85901c9182161b60a086015267ffffffffffffffff16915060018084169003610ed6577f800000000000000000000000000000000000000000000000000000000000000060c0850152610f13565b82600216600214610ef3576207ffff600284901c16600003610efe565b6207ffff600284901c165b60c0850152601583901c62ffffff1660e08501525b7f80000000000000000000000000000000000000000000000000000000000000008460c00151131561120157602d9290921c60ff16918215610f5e5760608401516001841b01610f67565b83606001516001015b9250606083610f7c8660c0015160020b6140be565b610f86919061576b565b610f92911c6001615782565b608085015260c0840151600090815260056020526040902054925060018084161480610fca57508360e0015162ffffff600185901c16115b156110f257610fe78460c001518560e0015186608001518661263e565b6101c089015260608801919091526080870182905260c087019290925260a086015191935011156110bb5767ffffffffffffffff6034856101c00151901c16925060ff8316600884901c901b92508360800151836110459190615795565b9250606483101561105557606492505b603461106684603860086001614320565b6101c086015160008581526007602052604090207ffffffffffffffffffffffffffffffffffff0000000000000000fffffffffffff9091169190921b17905560a08401516080850180519190910390526111f9565b83608001518460a00151600a546110d29190615782565b6110dc9190615795565b600a5560006080850181905260608501526111f9565b67ffffffffffffffff601984901c16915081600003611141576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791d600482015260240161027d565b60ff8216600883901c901b915081846080015110611160576000611168565b836080015182035b91506127108210156111af57600091508361014001518460c00151036111a05761119784610140015186614449565b61014086015294505b6111af8460c00151600061462b565b60196111c083603860086000614320565b60c08601516000908152600560205260409020911b6301ffffff851617905560a08401516080850180516111f5908390615795565b9052505b600060a08501525b60c08401516040850152606084015184526080840151602085015261010084015161122b906123a6565b6101a08801526101808701525061016085015260008b13156112d15761018084015161125c64e8d4a510008d61576b565b61126691906157d7565b846060018181516112779190615782565b90525060608401516fffffffffffffffffffffffffffffffff10156112cc576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792f600482015260240161027d565b611428565b60008b1215611428577fffffffffffffffffffffffffffffffff800000000000000000000000000000008b13156113845761018084015160019061131a64e8d4a510008e6157eb565b6113249190615837565b61132e919061589f565b90508360600151816000031115611375576040517f60121cca000000000000000000000000000000000000000000000000000000008152617926600482015260240161027d565b60608401805182019052611428565b7f80000000000000000000000000000000000000000000000000000000000000008b036113f25764e8d4a5100084610180015185606001516113c6919061576b565b6113d091906157d7565b6113d9906158c6565b6113e49060016158fe565b600060608601529a50611428565b6040517f60121cca00000000000000000000000000000000000000000000000000000000815261792f600482015260240161027d565b60008a13156114f8576101a084015161144664e8d4a510008c61576b565b61145091906157d7565b61145b906001615782565b92506127106103ff6052866101000151901c1684611479919061576b565b61148391906157d7565b61148d9084615782565b8460800181815161149e9190615782565b90525060808401516fffffffffffffffffffffffffffffffff10156114f3576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792f600482015260240161027d565b611618565b60008a1215611618577fffffffffffffffffffffffffffffffff800000000000000000000000000000008a13156115ad576101a084015161153e64e8d4a510008c6157eb565b6115489190615837565b6115539060016158fe565b9050806000039050836080015181111561159d576040517f60121cca000000000000000000000000000000000000000000000000000000008152617927600482015260240161027d565b6080840180518290039052611618565b7f80000000000000000000000000000000000000000000000000000000000000008a036113f257600164e8d4a51000856101a0015186608001516115f1919061576b565b6115fb91906157d7565b611604906158c6565b61160e919061589f565b6000608086015299505b8a158015611624575089155b1561165f576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791a600482015260240161027d565b6080840151156118d4576116978460600151633b9aca008660800151633b9aca01028161168e5761168e6157a8565b0460010161469e565b60a0880152608087015260e086015260c085015260008a12156117095783602001518460a0015185608001516116cd9190615795565b1115611709576040517f60121cca000000000000000000000000000000000000000000000000000000008152617934600482015260240161027d565b60008b138015611717575089155b156117bc578351602085015161173b906c010000000000000000000000009061576b565b61174591906157d7565b84606001516c010000000000000000000000008660a00151876080015161176c9190615795565b611776919061576b565b61178091906157d7565b11156117bc576040517f60121cca000000000000000000000000000000000000000000000000000000008152617934600482015260240161027d565b8361014001518460c00151126118cf5760008460c00151126117e95760018460c00151901b6001176117f5565b60c084015160000360011b5b92508460021660000361183257600283901b857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000161794506118fb565b6001603486811c633fffffff16919091016000818152600760205260409020780fffffffc00000000000000000000000000000000000000000609089901b167afffff000000000000000000000000000000000000000000000000060c28a901b161790557ffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000909616600285901b17601687901b179086901b179491505b6118fb565b7f800000000000000000000000000000000000000000000000000000000000000060c08501525b60008b128061190a575060008a135b15611aeb5773ffffffffffffffffffffffffffffffffffffffff891661192e573398505b608084015115611aeb576060846101000151901c73ffffffffffffffffffffffffffffffffffffffff1663e6aa216c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561198c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b09190615706565b9250836101a00151846101800151846119c9919061576b565b6119d391906157d7565b92506103e86103ff6020866101000151901c168402816119f5576119f56157a8565b049150722cd76fe086b93ce2f768a00b22a000000000008210611a35576c010000000000000000000000006b033b2e3c9fd0803ce8000000830402611a54565b6b033b2e3c9fd0803ce80000006c010000000000000000000000008302045b9150611a5f8261487b565b5080915050808460c001511315611aeb578360c001518460400151131580611ab05750633b9aca008460200151633b9aca010281611a9f57611a9f6157a8565b046001018460a00151856080015103115b15611aeb576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791e600482015260240161027d565b7f80000000000000000000000000000000000000000000000000000000000000008460c001511315611b475760008460c0015112611b345760018460c00151901b600117611b40565b60c084015160000360011b5b9250611b4c565b600092505b60a0840151606d90611b6390603860086000614320565b6060860151911b90602d90611b7d90603860086000614320565b60e0870151911b9060151b600186901b8615611b9a576000611b9d565b60015b60ff1617171717600360008e81526020019081526020016000208190555060008b1215611d4657610100840151603e1c6103ff1692508215611d46576040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401602060405180830381865afa158015611c83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ca79190615706565b6101e0850181905266ffffffffffffff600982901c1660ff600183901c161b9250611cd29083614c83565b9050600081138015611d0b575083610160015164e8d4a510008c6000030281611cfd57611cfd6157a8565b05816103e885810385020503125b15611d46576040517f60121cca000000000000000000000000000000000000000000000000000000008152617928600482015260240161027d565b60008b1315611ec4577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad967e1573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1614611df9576000611dfb565b345b7f00000000000000000000000000000000000000000000000000000000000000008e600080600033604051602001611e4f919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401611e7f96959493929190615982565b604080518083038185885af1158015611e9c573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611ec191906159db565b50505b60008a1215612092577fffffffffffffffffffffffff11111111111111111111111111111111111111127f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1601611f875789600003925082341115611f4957611f4433843403614d15565b611f8c565b82341015611f44576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791b600482015260240161027d565b600092505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad967e15847f000000000000000000000000000000000000000000000000000000000000000060008e6000803360405160200161201d919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6040516020818303038152906040526040518863ffffffff1660e01b815260040161204d96959493929190615982565b604080518083038185885af115801561206a573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061208f91906159db565b50505b60008b121561217757604080516000808252602082019092527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169163ad967e15917f0000000000000000000000000000000000000000000000000000000000000000918f918e9082906040518763ffffffff1660e01b815260040161213296959493929190615982565b60408051808303816000875af1158015612150573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061217491906159db565b50505b60008a131561225d57604080516000808252602082019092527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169163ad967e15917f000000000000000000000000000000000000000000000000000000000000000091908e9082908f906040518763ffffffff1660e01b815260040161221896959493929190615982565b60408051808303816000875af1158015612236573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225a91906159db565b50505b67ffffffffffffffff605286901c1692508360000151846060015160ff8516600886901c901b61228d9190615782565b6122979190615795565b925067ffffffffffffffff609286901c16915083602001518460a0015185608001516122c39190615795565b6122d690600885901c60ff86161b615782565b6122e09190615795565b915060926122f383603860086001614320565b901b605261230685603860086000614320565b901b7ffffffffffffc00000000000000000000000000000003ffffffffffffffffffff8716171760005560408051338152602081018e90529081018c9052606081018b905273ffffffffffffffffffffffffffffffffffffffff8a1660808201527ffef64760e30a41b9d5ba7dd65ff7236a61d89ed8b44c67a29e84db1a67513a1c9060a00160405180910390a150999a98995096979650505050505050565b6008546040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f0000000000000000000000000000000000000000000000000000000000000000600482015260009182918291829161248a9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b5c736e4906024015b602060405180830381865afa158015612461573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124859190615706565b614d64565b506040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006004820152909550612524907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401612444565b94505067ffffffffffffffff81811690604083901c168187108061254757508086105b15612582576040517f60121cca000000000000000000000000000000000000000000000000000000008152617932600482015260240161027d565b61271061ffff8916670de0b6b3a7640000848a670de0b6b3a764000002816125ac576125ac6157a8565b040302816125bc576125bc6157a8565b04945061271061ffff60108a901c16670de0b6b3a76400008389670de0b6b3a764000002816125ed576125ed6157a8565b040302816125fd576125fd6157a8565b049350670de0b6b3a7640000608084901c67ffffffffffffffff1686820102049450670de0b6b3a764000060c084901c858201020493505050509193509193565b600080808080868180600189901c62ffffff168b9003612684575050601a87901c633fffffff169250603887901c6603ffffffffffff166001601989901c8116146126db565b60008c8152600660205260408120600360028e0181810660550292918491048152602081019190915260400160002054901c600181811c633fffffff169750601f82901c6603ffffffffffff169450908116149150505b6000858152600760205260409020549350801561271e577f80000000000000000000000000000000000000000000000000000000000000009b50600099506128ea565b836003166002036127885761274082607486901c6603ffffffffffff16614f72565b91506001612750600f6023615782565b6001901b61275e9190615795565b82146127885760a69390931c633fffffff166000818152600760205260409020549094509261271e565b83600316600314806127b4575060016127a3600f6023615782565b6001901b6127b19190615795565b82145b156127e5577f80000000000000000000000000000000000000000000000000000000000000009b50600099506128ea565b6127fd8a607486901c6603ffffffffffff168461500a565b9950606483048a111561281a5761271061270f8b0204995061281f565b600099505b89156128c65783600416600414612842576207ffff600385901c1660000361284d565b6207ffff600385901c165b9b50600061285d8d60020b6140be565b905061271f612710820204633fffffff601687901c811661287e8385615795565b612888919061576b565b61289291906157d7565b61289c9082615782565b6128b36c010000000000000000000000008e61576b565b6128bd91906157d7565b975050506128ea565b7f80000000000000000000000000000000000000000000000000000000000000009b505b8b8a97509750505050945094509450945094565b6000806129627f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061074492505050565b80602001905181019061297591906159db565b90939092509050565b6000806129b36040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60008054906001821690036108a257600181176000556127108810806129e857506fffffffffffffffffffffffffffffffff88115b15612a23576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792e600482015260240161027d565b7fffffffffffffffffffffffff11111111111111111111111111111111111111127f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1601612ae857873414158015612aa8575061dead73ffffffffffffffffffffffffffffffffffffffff871614155b15612ae3576040517f60121cca000000000000000000000000000000000000000000000000000000008152617920600482015260240161027d565b612b24565b3415612b24576040517f60121cca000000000000000000000000000000000000000000000000000000008152617920600482015260240161027d565b6001548252600281901c620fffff16600003612b70576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791f600482015260240161027d565b8151612b7b906123a6565b90919250909150836060018460800182815250828152505050612bf86040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b612c316040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b601683901c633fffffff16808252600090815260076020908152604082205490830181905260741c6603ffffffffffff166060830181905281908103612c7f576603ffffffffc00060608401525b602083015160c41c620fffff1691508115612cc65781600116600114612cb1576207ffff600183901c16600003612cbc565b6207ffff600183901c165b6080840152612ced565b7f800000000000000000000000000000000000000000000000000000000000000060808401525b84600416600414612d0a576207ffff600386901c16600003612d15565b6207ffff600386901c165b60c08501526002851615612d2a576002612d2d565b60015b60ff1684610100018181525050612d7a6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60c085015181528651604080517fe6aa216c000000000000000000000000000000000000000000000000000000008152905160609290921c9163e6aa216c916004808201926020929091908290030181865afa158015612dde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e029190615706565b92508660800151876060015184612e19919061576b565b612e2391906157d7565b9250612e4683760a70c3c40a64e6c51999090b65f67d92400000000000006157d7565b87519092506127109060481c6103ff1681018302046060860152722cd76fe086b93ce2f768a00b22a000000000008310612e9d576c010000000000000000000000006b033b2e3c9fd0803ce8000000840402612ebc565b6b033b2e3c9fd0803ce80000006c010000000000000000000000008402045b87519093506103e890602a1c6103ff168402049150612eda8261487b565b50602088015286516103e89060341c6103ff168402049150612efb8261487b565b506040880152608087015164e8d4a510008e0281612f1b57612f1b6157a8565b0485528915612fb15760025485516fffffffffffffffffffffffffffffffff8216945060809190911c9250831115612f965784518390612f5b908461576b565b612f6591906157d7565b60a08601819052612f769083615795565b855160808088018290526000885282901b94039384176002559150612fb1565b6000600255845183900385526080850183905260a085018290525b86602001518560c00151138015612fd0575086604001518560c0015113155b15613ab357845115613ab3576040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081018290528190680100000000000000009060008960c0015112613041576101008960c001518161303b5761303b6157a8565b0561305e565b60016101008a60c001516001018161305b5761305b6157a8565b05035b6040820152845161306e906140be565b604086015261010089015160010361309957604085015160e08a015260c089015160208201526131a0565b61271f856040015161271002816130b2576130b26157a8565b046060860181905260408601510360808601819052602089015160161c633fffffff90811660c0880181905290910260608701519190040160e08a0152845160208c015160010114801561310a57508460c001516001145b156131a05773ffffffffffffffffffffffffffffffffffffffff8f1661dead0361316a576040517fcc36aa44000000000000000000000000000000000000000000000000000000008152600060048201819052602482015260440161027d565b6040517f60121cca000000000000000000000000000000000000000000000000000000008152617935600482015260240161027d565b8861010001516001036132105760c0890180516000908152600560205260408082205460608c01518c5194518452919092206301fffffe8316601a9490941b9390931760389190911b1760011790915566ffffffffffffff602182901c1660ff601983901c161b97509550613233565b67ffffffffffffffff60348960200151901c16965060ff8716600888901c901b96505b86896020018181516132459190615782565b90525060e08901516132646c010000000000000000000000008961576b565b61326e91906157d7565b8960400181815161327f9190615782565b90525060c0890151602082015114801561329e57508861010001516001145b806132ab57506080810151155b156133db5780608001516000036132d45760408082015160009081526004602052205460808201525b88610100015160010361332b57604081015160c08a015160009081036101009283020190910160608301819052608083018051821b90911c90819052900361332b5760408082015160009081526004602052908120555b608081015115613359576001613344826080015161504f565b604083015161010002010360208201526133db565b8a602001518160400151610100021215613398577f800000000000000000000000000000000000000000000000000000000000000060208201526133db565b604080820180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190819052600090815260046020522054608082015261332b565b806020015188608001511380156133f957508a602001518860800151135b156134165760808801516101208a015260026101608a0152613452565b8a602001518160200151131561343e5760208101516101208a015260016101608a0152613452565b60208b01516101208a015260036101608a01525b61346389610120015160020b6140be565b60408601526101608901516002036134f75761271f8560400151612710028161348e5761348e6157a8565b046060860181905260408087015191909103608087019081526020808b015160a61c633fffffff908116600090815260079092529290205460a08b0181905260161c821660c0880181905290510260608701519190040160a086018190526101408a0152613514565b604085015160a086018190526101408a0152633fffffff60c08601525b6c010000000000000000000000008961014001518a60600151613537919061576b565b61354191906157d7565b613557906b033b2e3c9fd0803ce8000000615795565b8960e001518a602001518b6101400151613571919061576b565b61357b91906157d7565b8a6020015161358a9190615795565b6135a0906b033b2e3c9fd0803ce800000061576b565b6135aa91906157d7565b93506b033b2e3c9fd0803ce80000008960600151856135c9919061576b565b6135d391906157d7565b9250838960200151036135ee576135eb600185615795565b93505b88518410158061360357508861016001516003145b156138cd5760808101516040808301516000908152600460205220558851841061374257885160608a01519094506b033b2e3c9fd0803ce800000090613649908661576b565b61365391906157d7565b92508289604001516136659190615795565b6c01000000000000000000000000858b602001516136839190615795565b61368d919061576b565b61369791906157d7565b96506136a28761487b565b606087015280865260208c015113156136c85784516002018552600160c0860152613755565b84516001018552606085015161271061271f8202046040870181905281900360808701819052908803633fffffff0281613704576137046157a8565b0460c086018190521561373557633fffffff8560c00151101561372b578460c00151613738565b633ffffffe613738565b60015b60c0860152613755565b6101208901516001908101865260c08601525b60208901516137648582615795565b61376e908461576b565b61377891906157d7565b9150838960800181815161378c9190615782565b9052506020890180518591906137a3908390615795565b90525060a0890180518491906137ba908390615782565b9052506040890180518491906137d1908390615795565b90525060608801516137e39083615105565b606089015260208901516064111561382b576040517f60121cca000000000000000000000000000000000000000000000000000000008152617930600482015260240161027d565b8451600013613841578451600190811b1761384a565b845160000360011b5b606089015160208b015191975060741b9060349061386d90603860086001614320565b901b60168760c00151901b600289901b60a6808d60200151901c901b60011717171717600760008a6000015181526020019081526020016000208190555060168860000151901b600287901b6034808d901c901b60021717179950613aae565b8851849003895260208901516138e38582615795565b6138ed908461576b565b6138f791906157d7565b9150838960800181815161390b9190615782565b905250602089018051859190613922908390615795565b90525060a089018051849190613939908390615782565b905250604089018051849190613950908390615795565b90525060608801516139629083615105565b6060890152610160890151680100000000000000009250600203613a8757633fffffff60a68960200151901c1696508760a00151955060006603ffffffffffff607488901c16905060006139c38a60600151836151a990919063ffffffff16565b6020808c0180518d516000908152600790935260409092207fffffffffffffffffffffffc00000000000000000000000000000000000000000909216607484901b17600217909155998b5298889052506060890181905260c487901c620fffff16978815613a5d5788600116600114613a48576207ffff60018a901c16600003613a53565b6207ffff60018a901c165b60808b0152613a84565b7f800000000000000000000000000000000000000000000000000000000000000060808b01525b50505b61012089015160c08a01526101608901516101008a015261014089015160e08a01526131a0565b505050505b64e8d4a5100087608001518660800151613acd919061576b565b613ad791906157d7565b985064e8d4a5100087606001518660a00151613af3919061576b565b613afd91906157d7565b97508c891115613b2157613b11898e6157d7565b613b1b908961576b565b97508c98505b8b89613b358a670de0b6b3a764000061576b565b613b3f91906157d7565b1015613b7b576040517f60121cca000000000000000000000000000000000000000000000000000000008152617921600482015260240161027d565b73ffffffffffffffffffffffffffffffffffffffff8b1661dead03613bd6576040517fcc36aa4400000000000000000000000000000000000000000000000000000000815260048101899052602481018a905260440161027d565b7fffffffffffffffffffffffff11111111111111111111111111111111111111127f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1601613c4f5788925034891015613c4a57613c4a338a3403614d15565b613c54565b600092505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad967e15847f000000000000000000000000000000000000000000000000000000000000000060008d60000360008033604051602001613ce8919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401613d1896959493929190615982565b604080518083038185885af1158015613d35573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613d5a91906159db565b505073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663ad967e157f000000000000000000000000000000000000000000000000000000000000000060008b8103908f81806040519080825280601f01601f191660200182016040528015613dec576020820181803683370190505b506040518763ffffffff1660e01b8152600401613e0e96959493929190615982565b60408051808303816000875af1158015613e2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e5091906159db565b505067ffffffffffffffff605287901c1692508460a0015160ff8416600885901c901b613e7d9190615795565b925067ffffffffffffffff609287901c169150846080015160ff8316600884901c901b613eaa9190615795565b91506092613ebd83603860086001614320565b901b6052613ed085603860086000614320565b7ffffffffffffc00000000000000000000000000000003ffffffffffffffffffff8916911b171760005560408051338152602081018a90529081018a905273ffffffffffffffffffffffffffffffffffffffff8c1660608201527f80fd9cc6b1821f4a510e45ffce6852ea3404807b5d3d833ffa85664408afcb669060800160405180910390a15050505050505094509492505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614613fda576040517f60121cca000000000000000000000000000000000000000000000000000000008152617929600482015260240161027d565b60005460011660000361401d576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792a600482015260240161027d565b6140548461402d838501856159ff565b7f000000000000000000000000000000000000000000000000000000000000000086615254565b50505050565b6140bb7f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061074492505050565b50565b600060ff82901d80831803617fff8111156140d857600080fd5b700100000000000000000000000000000000600182161561410657506fff9dd7de423466c20352b1246ce4856f5b6002821615614125576fff3bd55f4488ad277531fa1c725a66d00260801c5b6004821615614144576ffe78410fd6498b73cb96a6917f8532590260801c5b6008821615614163576ffcf2d9987c9be178ad5bfeffaa1232730260801c5b6010821615614182576ff9ef02c4529258b057769680fc6601b30260801c5b60208216156141a1576ff402d288133a85a17784a411f7aba0820260801c5b60408216156141c0576fe895615b5beb6386553757b0352bda900260801c5b60808216156141df576fd34f17a00ffa00a8309940a15930391a0260801c5b6101008216156141ff576fae6b7961714e20548d88ea5123f9a0ff0260801c5b61020082161561421f576f76d6461f27082d74e0feed3b388c0ca10260801c5b61040082161561423f576f372a3bfe0745d8b6b19d985d9a8b85bb0260801c5b61080082161561425f576f0be32cbee48979763cf7247dd7bb539d0260801c5b61100082161561427e576e8d4f70c9ff4924dac37612d1e2921e0260801c5b61200082161561429c576d4e009ae5519380809a02ca7aec770260801c5b6140008216156142b8576b17c45e641b6e95dee056ff100260801c5b600091507f80000000000000000000000000000000000000000000000000000000000000008416614316577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0463ffffffff81161561431657600191505b60201c0192915050565b600080856fffffffffffffffffffffffffffffffff8111156143435760809150811c5b67ffffffffffffffff81111561435b576040918201911c5b63ffffffff81111561436f576020918201911c5b61ffff811115614381576010918201911c5b60ff811115614392576008918201911c5b600f8111156143a3576004918201911c5b60038111156143b4576002918201911c5b60018111156143c4576001820191505b80156143d1576001820191505b50848110156143dd5750835b848103905085811c6000821184161561442c57600181019050806001871b0361442c57506001908101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86011b5b6001851b821061443b57600080fd5b90931b909201949350505050565b601681901c633fffffff166000818152600760205260408120549091829182600160c483901c81169003614487575060c581901c6207ffff166144bf565b5060c581901c6207ffff166000819003906144bf57507f80000000000000000000000000000000000000000000000000000000000000005b60006144ca8861531b565b90508082136144d957806144db565b815b94507f8000000000000000000000000000000000000000000000000000000000000000850361452e57867fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0000116965061461d565b8085036145a457600085121561457157600385600003901b877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc000011617965061461d565b600385901b877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc000011660041717965061461d565b6000848152600760205260408120557ffffffffffffffffffffffffffffffffffffffffffffc00000000000000000001909616623ffffc60c284901c1617660fffffffc00000609084901c16177fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff840160341b17600217955b869550505050509250929050565b600080831261463e576101008305614649565b600161010084820105035b90506101008102830382614671576000828152600460205260409020546001821b1916614686565b6000828152600460205260409020546001821b175b60009283526004602052604090922091909155505050565b6000806000806127108510156146e4576040517f60121cca000000000000000000000000000000000000000000000000000000008152617933600482015260240161027d565b6000866146fe6c010000000000000000000000008861576b565b61470891906157d7565b90506147138161487b565b60019091019550905061271061271f82020490506060614733888361576b565b901c92506147418684615795565b600086815260056020526040812054600181901c62ffffff169650919350851580159061476f575060018216155b156147b05766ffffffffffffff602183901c1660ff601984901c161b6147958682615782565b9150806000036147aa576147aa88600161462b565b50614804565b85156147f0576000878152600660209081526040808320600360028b0181810486529190935292208054601986901c929093066055029190911b90911790555b85600101955084905061480487600161462b565b612710811015614844576040517f60121cca000000000000000000000000000000000000000000000000000000008152617931600482015260240161027d565b601961485582603860086000614320565b6000898152600560205260409020911b600188901b179055509497939650919450925050565b600080630235b88083107473d85bca016a2338b31715f8e13054c005f8b995d3841117156148a857600080fd5b6c0100000000000000000000000083106000816148de57506c010000000000000000000000006a52b7d2dcc80cd2e40000008502045b811561490157507652b7d2dcc80cd2e40000000000000000000000000000008490045b6f037af932b2affa9738cc6c38ca527831811061494257614000841793506f037af932b2affa9738cc6c38ca5278316a52b7d2dcc80cd2e400000082020490505b6d010f7a088a76f267264caa114f0a811061497f57612000841793506d010f7a088a76f267264caa114f0a6a52b7d2dcc80cd2e400000082020490505b6b95da74f87f839fc2e0dc5bd981106149b857611000841793506b95da74f87f839fc2e0dc5bd96a52b7d2dcc80cd2e400000082020490505b6b06f55dedafd8491caed5a1b881106149f157610800841793506b06f55dedafd8491caed5a1b86a52b7d2dcc80cd2e400000082020490505b6b017fdd10ee11e624491b4cc18110614a2a57610400841793506b017fdd10ee11e624491b4cc16a52b7d2dcc80cd2e400000082020490505b6ab23131bf0c30217b0a2c698110614a6157610200841793506ab23131bf0c30217b0a2c696a52b7d2dcc80cd2e400000082020490505b6a79683edcb9280d797aded78110614a9857610100841793506a79683edcb9280d797aded76a52b7d2dcc80cd2e400000082020490505b6a64366e2f9919f0d9b0dc908110614ace576080841793506a64366e2f9919f0d9b0dc906a52b7d2dcc80cd2e400000082020490505b6a5b0bcda5a78850646b0a818110614b04576040841793506a5b0bcda5a78850646b0a816a52b7d2dcc80cd2e400000082020490505b6a56c840f992c70f959ae8108110614b3a576020841793506a56c840f992c70f959ae8106a52b7d2dcc80cd2e400000082020490505b6a54b9cd178695194f9be0a08110614b70576010841793506a54b9cd178695194f9be0a06a52b7d2dcc80cd2e400000082020490505b6a53b7458aff204b5e65d6818110614ba6576008841793506a53b7458aff204b5e65d6816a52b7d2dcc80cd2e400000082020490505b6a53372a2f38c240d689e4008110614bdc576004841793506a53372a2f38c240d689e4006a52b7d2dcc80cd2e400000082020490505b6a52f76617a04499e66400008110614c12576002841793506a52f76617a04499e66400006a52b7d2dcc80cd2e400000082020490505b6a52d79660f3dec355c000008110614c48576001841793506a52d79660f3dec355c000006a52b7d2dcc80cd2e400000082020490505b81614c6057806a52b7d2dcc80cd2e400000086020492505b8115614c7c579219926a52d79660f3dec355c000008582020492505b5050915091565b600066ffffffffffffff604984901c1660ff604185901c161b808203614cad576000915050614d0f565b612710613fff60a286901c168402046401ffffffff608186901c16420362ffffff60b087901c16614cde828461576b565b614ce891906157d7565b9050808311614cf8576000614cfc565b8083035b9350508084038381111561078757925050505b92915050565b600080600080600085875af1905080614d5f576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a600482015260240161027d565b505050565b67ffffffffffffffff605b82901c811690609b83901c16811580614d86575080155b15614dc2576040517fd50d751200000000000000000000000000000000000000000000000000000000815262011171600482015260240161027d565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480614dec575082155b80614df75750806001145b15614e0457505050915091565b64496cebb80084840283020484019350617fff60db87901c16925082600103614e2f57505050915091565b82600116600103614e845760019290921c91826c7e37be2022c0914b268000000081614e5d57614e5d6157a8565b049250612710601e87901c613fff166b033b2e3c9fd0803ce8000000850102049250614eb1565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b80600116600103614ee85760011c61271081016b033b2e3c9fd0803ce8000000820281614ee057614ee06157a8565b049050614f1e565b60011c61271081016b033b2e3c9fd0803ce8000000820281614f0c57614f0c6157a8565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b6000600f83811c9083901c0281681fffffffffffffffff8211614f96576022614f99565b60235b91821c919050617fff858116908516018101614000811015614fba57600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001617fff811115614ff957506603ffffffffffff9250614d0f915050565b600f9290921b909117949350505050565b6000617fff83811690831603608181101561504257600f83811c821b9085901c860281615039576150396157a8565b04915050615048565b60009150505b9392505050565b6000816fffffffffffffffffffffffffffffffff8311156150715760809150811c5b67ffffffffffffffff811115615089576040918201911c5b63ffffffff81111561509d576020918201911c5b61ffff8111156150af576010918201911c5b60ff8111156150c0576008918201911c5b600f8111156150d1576004918201911c5b60038111156150e2576002918201911c5b60018111156150f2576001820191505b80156150ff576001820191505b50919050565b6000600f83901c8202816c03ffffffffffffffffffffffff821161514c576c01ffffffffffffffffffffffff8211615145576151408261504f565b61514f565b606261514f565b60635b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd617fff87168201810194500191821c919050604083111561519c57604083600f84901b010392506151a1565b600080fd5b505092915050565b600080600f83901c7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000603186901b16816151e5576151e56157a8565b0490506000604082901c6001146151fd576040615200565b60415b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd81019290921c91617fff8581166040019250613fdd908716909101018181111561519c5703600f9190911b179050614d0f565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080615314576040517fdee51a8a00000000000000000000000000000000000000000000000000000000815262011559600482015260240161027d565b5050505050565b600080600080841261533157610100840561533c565b600161010085820105035b600081815260046020526040812054919350859003610100808502919091010190811b811c91505b81156153835760016153758361504f565b84610100020103935061540e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8301927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f036001016153f8577f8000000000000000000000000000000000000000000000000000000000000000935061540e565b6000838152600460205260409020549150615364565b505050919050565b80151581146140bb57600080fd5b60006020828403121561543657600080fd5b815161504881615416565b73ffffffffffffffffffffffffffffffffffffffff811681146140bb57600080fd5b6000806000806080858703121561547957600080fd5b843593506020850135925060408501359150606085013561549981615441565b939692955090935050565b6000602082840312156154b657600080fd5b5035919050565b600080600080608085870312156154d357600080fd5b5050823594602084013594506040840135936060013592509050565b6000806000806080858703121561550557600080fd5b8435935060208501359250604085013561551e81615441565b9150606085013561549981615416565b6000806000806060858703121561554457600080fd5b843561554f81615441565b935060208501359250604085013567ffffffffffffffff8082111561557357600080fd5b818701915087601f83011261558757600080fd5b81358181111561559657600080fd5b8860208285010111156155a857600080fd5b95989497505060200194505050565b815173ffffffffffffffffffffffffffffffffffffffff1681526101a0810160208301516155fd602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151615625604084018273ffffffffffffffffffffffffffffffffffffffff169052565b50606083015161564d606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080830151615675608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a083015161569d60a084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c08301516156b260c084018260ff169052565b5060e08301516156c760e084018260ff169052565b50610100838101519083015261012080840151908301526101408084015190830152610160808401519083015261018092830151929091019190915290565b60006020828403121561571857600080fd5b5051919050565b60006020828403121561573157600080fd5b815161504881615441565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417614d0f57614d0f61573c565b80820180821115614d0f57614d0f61573c565b81810381811115614d0f57614d0f61573c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826157e6576157e66157a8565b500490565b808202600082127f8000000000000000000000000000000000000000000000000000000000000000841416156158235761582361573c565b8181058314821517614d0f57614d0f61573c565b600082615846576158466157a8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561589a5761589a61573c565b500590565b81810360008312801583831316838312821617156158bf576158bf61573c565b5092915050565b60007f800000000000000000000000000000000000000000000000000000000000000082036158f7576158f761573c565b5060000390565b80820182811260008312801582168215821617156151a1576151a161573c565b6000815180845260005b8181101561594457602081850181015186830182015201615928565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352876020840152866040840152808616606084015280851660808401525060c060a08301526159cf60c083018461591e565b98975050505050505050565b600080604083850312156159ee57600080fd5b505080516020909101519092909150565b600060208284031215615a1157600080fd5b81356150488161544156fea264697066735822122087df04e04950c10d1518836e4d6b3d1a189dc7708362a0a8cb74ff0277b71c5464736f6c6343000815003300000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d0000000000000000000000008730d7a5af435678b2686289eeb6a74a042081e6000000000000000000000000836580dc0b1ff9351c58c173cd1da8b31a536733000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001a1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7a8e1248eddf82e10c0adc6c737b6d8da17674abf51801ea5a4549f41c2dfdf2156a30cfbf6c7293102fb2f2bbcabd282241e454a9db358e4e2d6a9e6252ba4ee34808091dd0ff71c11f548a300df0a835ff3a6176689d16be165ba67210a7254
Deployed Bytecode
0x6080604052600436106100d25760003560e01c8063540acabc1161007f578063ad20750111610059578063ad207501146104b8578063b5c736e4146104d8578063b7791bf2146104f7578063ebd93ac61461072f576100d2565b8063540acabc146104465780637d7c2a1c146104885780638433ea22146104a5576100d2565b8063103f2907116100b0578063103f29071461037157806322348cc7146103ca5780632861c7d114610412576100d2565b806302161887146102e9578063032d22761461032357806309f0d8cb14610351575b3480156100de57600080fd5b506040517f4502d0630000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d73ffffffffffffffffffffffffffffffffffffffff1690634502d06390602401602060405180830381865afa158015610169573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061018d9190615424565b8061024757506040517fe04c8e5d0000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d73ffffffffffffffffffffffffffffffffffffffff169063e04c8e5d90604401602060405180830381865afa158015610223573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102479190615424565b610286576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792560048201526024015b60405180910390fd5b6102e77f0000000000000000000000008730d7a5af435678b2686289eeb6a74a042081e66000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061074492505050565b005b3480156102f557600080fd5b506102fe610790565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b610336610331366004615463565b610884565b6040805193845260208401929092529082015260600161031a565b34801561035d57600080fd5b506102fe61036c3660046154a4565b6123a6565b34801561037d57600080fd5b506103a57f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d81565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161031a565b3480156103d657600080fd5b506103ea6103e53660046154bd565b61263e565b604080519586526020860194909452928401919091526060830152608082015260a00161031a565b34801561041e57600080fd5b506103a57f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49781565b34801561045257600080fd5b5061047a7f000000000000000000000000000000000000000000000000000000000000000181565b60405190815260200161031a565b6104906128fe565b6040805192835260208301919091520161031a565b6104906104b33660046154ef565b61297e565b3480156104c457600080fd5b506102e76104d336600461552e565b613f67565b3480156104e457600080fd5b5061047a6104f33660046154a4565b5490565b34801561050357600080fd5b50604080516101a08101825273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497811682527f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d811660208301527f0000000000000000000000008730d7a5af435678b2686289eeb6a74a042081e68116828401527f000000000000000000000000836580dc0b1ff9351c58c173cd1da8b31a536733811660608301527f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee811660808301527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481660a082015260ff7f0000000000000000000000000000000000000000000000000000000000000012811660c08301527f00000000000000000000000000000000000000000000000000000000000000061660e08201527f00000000000000000000000000000000000000000000000000000000000000016101008201527fa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d76101208201527fa8e1248eddf82e10c0adc6c737b6d8da17674abf51801ea5a4549f41c2dfdf216101408201527f56a30cfbf6c7293102fb2f2bbcabd282241e454a9db358e4e2d6a9e6252ba4ee6101608201527f34808091dd0ff71c11f548a300df0a835ff3a6176689d16be165ba67210a7254610180820152905161031a91906155b7565b34801561073b57600080fd5b506102e761405a565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e81156001810361078757816000803e816000fd5b50505092915050565b6000806000806107a16001546123a6565b9296509094509250905067ffffffffffffffff8411806107c8575067ffffffffffffffff83115b806107da575067ffffffffffffffff82115b806107ec575067ffffffffffffffff81115b15610827576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792d600482015260240161027d565b604083811b8517608084901b1760c083901b17600855517fcde545703e0372175cadfff811d67c32910c3dcb33199679b3271c4106afdf9a906108769084908490918252602082015260400190565b60405180910390a190919293565b60008054819081906001811682036108a257600181176000556108d8565b6040517f60121cca000000000000000000000000000000000000000000000000000000008152617919600482015260240161027d565b861580156108e4575085155b806109245750861580159061092457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f087138015610924575061271087125b806109645750851580159061096457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f086138015610964575061271086125b1561099f576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791a600482015260240161027d565b7f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480156109f85750600087135b15610a3f57348714610a3a576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791b600482015260240161027d565b610ad8565b3415610ad8577f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015610a9e5750600086125b610ad8576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791b600482015260240161027d565b610b58604051806102000160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600154610100820152600282901c620fffff166000808215610ba05782600116600114610b91576207ffff600184901c16600003610bc2565b6207ffff600184901c16610bc2565b7f80000000000000000000000000000000000000000000000000000000000000005b61014085015260008c9003610cf4577f800000000000000000000000000000000000000000000000000000000000000060c08501526040517f94bf804d0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000160048201523360248201527f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d73ffffffffffffffffffffffffffffffffffffffff16906394bf804d906044016020604051808303816000875af1158015610ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccd9190615706565b9b50847a040000000000000000000000000000000000000000000000000000019450610f13565b60008b1280610d03575060008a135b8015610dd457506040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018d905233907f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d73ffffffffffffffffffffffffffffffffffffffff1690636352211e90602401602060405180830381865afa158015610d97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dbb919061571f565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610e0f576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791c600482015260240161027d565b60008c8152600360205260408120549350839003610e5d576040517f60121cca000000000000000000000000000000000000000000000000000000008152617923600482015260240161027d565b66ffffffffffffff603584901c811660ff602d86901c81169190911b6060870152607585901c909116606d85901c9182161b60a086015267ffffffffffffffff16915060018084169003610ed6577f800000000000000000000000000000000000000000000000000000000000000060c0850152610f13565b82600216600214610ef3576207ffff600284901c16600003610efe565b6207ffff600284901c165b60c0850152601583901c62ffffff1660e08501525b7f80000000000000000000000000000000000000000000000000000000000000008460c00151131561120157602d9290921c60ff16918215610f5e5760608401516001841b01610f67565b83606001516001015b9250606083610f7c8660c0015160020b6140be565b610f86919061576b565b610f92911c6001615782565b608085015260c0840151600090815260056020526040902054925060018084161480610fca57508360e0015162ffffff600185901c16115b156110f257610fe78460c001518560e0015186608001518661263e565b6101c089015260608801919091526080870182905260c087019290925260a086015191935011156110bb5767ffffffffffffffff6034856101c00151901c16925060ff8316600884901c901b92508360800151836110459190615795565b9250606483101561105557606492505b603461106684603860086001614320565b6101c086015160008581526007602052604090207ffffffffffffffffffffffffffffffffffff0000000000000000fffffffffffff9091169190921b17905560a08401516080850180519190910390526111f9565b83608001518460a00151600a546110d29190615782565b6110dc9190615795565b600a5560006080850181905260608501526111f9565b67ffffffffffffffff601984901c16915081600003611141576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791d600482015260240161027d565b60ff8216600883901c901b915081846080015110611160576000611168565b836080015182035b91506127108210156111af57600091508361014001518460c00151036111a05761119784610140015186614449565b61014086015294505b6111af8460c00151600061462b565b60196111c083603860086000614320565b60c08601516000908152600560205260409020911b6301ffffff851617905560a08401516080850180516111f5908390615795565b9052505b600060a08501525b60c08401516040850152606084015184526080840151602085015261010084015161122b906123a6565b6101a08801526101808701525061016085015260008b13156112d15761018084015161125c64e8d4a510008d61576b565b61126691906157d7565b846060018181516112779190615782565b90525060608401516fffffffffffffffffffffffffffffffff10156112cc576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792f600482015260240161027d565b611428565b60008b1215611428577fffffffffffffffffffffffffffffffff800000000000000000000000000000008b13156113845761018084015160019061131a64e8d4a510008e6157eb565b6113249190615837565b61132e919061589f565b90508360600151816000031115611375576040517f60121cca000000000000000000000000000000000000000000000000000000008152617926600482015260240161027d565b60608401805182019052611428565b7f80000000000000000000000000000000000000000000000000000000000000008b036113f25764e8d4a5100084610180015185606001516113c6919061576b565b6113d091906157d7565b6113d9906158c6565b6113e49060016158fe565b600060608601529a50611428565b6040517f60121cca00000000000000000000000000000000000000000000000000000000815261792f600482015260240161027d565b60008a13156114f8576101a084015161144664e8d4a510008c61576b565b61145091906157d7565b61145b906001615782565b92506127106103ff6052866101000151901c1684611479919061576b565b61148391906157d7565b61148d9084615782565b8460800181815161149e9190615782565b90525060808401516fffffffffffffffffffffffffffffffff10156114f3576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792f600482015260240161027d565b611618565b60008a1215611618577fffffffffffffffffffffffffffffffff800000000000000000000000000000008a13156115ad576101a084015161153e64e8d4a510008c6157eb565b6115489190615837565b6115539060016158fe565b9050806000039050836080015181111561159d576040517f60121cca000000000000000000000000000000000000000000000000000000008152617927600482015260240161027d565b6080840180518290039052611618565b7f80000000000000000000000000000000000000000000000000000000000000008a036113f257600164e8d4a51000856101a0015186608001516115f1919061576b565b6115fb91906157d7565b611604906158c6565b61160e919061589f565b6000608086015299505b8a158015611624575089155b1561165f576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791a600482015260240161027d565b6080840151156118d4576116978460600151633b9aca008660800151633b9aca01028161168e5761168e6157a8565b0460010161469e565b60a0880152608087015260e086015260c085015260008a12156117095783602001518460a0015185608001516116cd9190615795565b1115611709576040517f60121cca000000000000000000000000000000000000000000000000000000008152617934600482015260240161027d565b60008b138015611717575089155b156117bc578351602085015161173b906c010000000000000000000000009061576b565b61174591906157d7565b84606001516c010000000000000000000000008660a00151876080015161176c9190615795565b611776919061576b565b61178091906157d7565b11156117bc576040517f60121cca000000000000000000000000000000000000000000000000000000008152617934600482015260240161027d565b8361014001518460c00151126118cf5760008460c00151126117e95760018460c00151901b6001176117f5565b60c084015160000360011b5b92508460021660000361183257600283901b857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000161794506118fb565b6001603486811c633fffffff16919091016000818152600760205260409020780fffffffc00000000000000000000000000000000000000000609089901b167afffff000000000000000000000000000000000000000000000000060c28a901b161790557ffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000909616600285901b17601687901b179086901b179491505b6118fb565b7f800000000000000000000000000000000000000000000000000000000000000060c08501525b60008b128061190a575060008a135b15611aeb5773ffffffffffffffffffffffffffffffffffffffff891661192e573398505b608084015115611aeb576060846101000151901c73ffffffffffffffffffffffffffffffffffffffff1663e6aa216c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561198c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b09190615706565b9250836101a00151846101800151846119c9919061576b565b6119d391906157d7565b92506103e86103ff6020866101000151901c168402816119f5576119f56157a8565b049150722cd76fe086b93ce2f768a00b22a000000000008210611a35576c010000000000000000000000006b033b2e3c9fd0803ce8000000830402611a54565b6b033b2e3c9fd0803ce80000006c010000000000000000000000008302045b9150611a5f8261487b565b5080915050808460c001511315611aeb578360c001518460400151131580611ab05750633b9aca008460200151633b9aca010281611a9f57611a9f6157a8565b046001018460a00151856080015103115b15611aeb576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791e600482015260240161027d565b7f80000000000000000000000000000000000000000000000000000000000000008460c001511315611b475760008460c0015112611b345760018460c00151901b600117611b40565b60c084015160000360011b5b9250611b4c565b600092505b60a0840151606d90611b6390603860086000614320565b6060860151911b90602d90611b7d90603860086000614320565b60e0870151911b9060151b600186901b8615611b9a576000611b9d565b60015b60ff1617171717600360008e81526020019081526020016000208190555060008b1215611d4657610100840151603e1c6103ff1692508215611d46576040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f56a30cfbf6c7293102fb2f2bbcabd282241e454a9db358e4e2d6a9e6252ba4ee60048201527f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401602060405180830381865afa158015611c83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ca79190615706565b6101e0850181905266ffffffffffffff600982901c1660ff600183901c161b9250611cd29083614c83565b9050600081138015611d0b575083610160015164e8d4a510008c6000030281611cfd57611cfd6157a8565b05816103e885810385020503125b15611d46576040517f60121cca000000000000000000000000000000000000000000000000000000008152617928600482015260240161027d565b60008b1315611ec4577f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff1663ad967e1573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff1614611df9576000611dfb565b345b7f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8e600080600033604051602001611e4f919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401611e7f96959493929190615982565b604080518083038185885af1158015611e9c573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611ec191906159db565b50505b60008a1215612092577fffffffffffffffffffffffff11111111111111111111111111111111111111127f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1601611f875789600003925082341115611f4957611f4433843403614d15565b611f8c565b82341015611f44576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791b600482015260240161027d565b600092505b7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff1663ad967e15847f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4860008e6000803360405160200161201d919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6040516020818303038152906040526040518863ffffffff1660e01b815260040161204d96959493929190615982565b604080518083038185885af115801561206a573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061208f91906159db565b50505b60008b121561217757604080516000808252602082019092527f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169163ad967e15917f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee918f918e9082906040518763ffffffff1660e01b815260040161213296959493929190615982565b60408051808303816000875af1158015612150573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061217491906159db565b50505b60008a131561225d57604080516000808252602082019092527f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169163ad967e15917f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4891908e9082908f906040518763ffffffff1660e01b815260040161221896959493929190615982565b60408051808303816000875af1158015612236573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225a91906159db565b50505b67ffffffffffffffff605286901c1692508360000151846060015160ff8516600886901c901b61228d9190615782565b6122979190615795565b925067ffffffffffffffff609286901c16915083602001518460a0015185608001516122c39190615795565b6122d690600885901c60ff86161b615782565b6122e09190615795565b915060926122f383603860086001614320565b901b605261230685603860086000614320565b901b7ffffffffffffc00000000000000000000000000000003ffffffffffffffffffff8716171760005560408051338152602081018e90529081018c9052606081018b905273ffffffffffffffffffffffffffffffffffffffff8a1660808201527ffef64760e30a41b9d5ba7dd65ff7236a61d89ed8b44c67a29e84db1a67513a1c9060a00160405180910390a150999a98995096979650505050505050565b6008546040517fb5c736e40000000000000000000000000000000000000000000000000000000081527fa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7600482015260009182918291829161248a9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497169063b5c736e4906024015b602060405180830381865afa158015612461573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124859190615706565b614d64565b506040517fb5c736e40000000000000000000000000000000000000000000000000000000081527fa8e1248eddf82e10c0adc6c737b6d8da17674abf51801ea5a4549f41c2dfdf216004820152909550612524907f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401612444565b94505067ffffffffffffffff81811690604083901c168187108061254757508086105b15612582576040517f60121cca000000000000000000000000000000000000000000000000000000008152617932600482015260240161027d565b61271061ffff8916670de0b6b3a7640000848a670de0b6b3a764000002816125ac576125ac6157a8565b040302816125bc576125bc6157a8565b04945061271061ffff60108a901c16670de0b6b3a76400008389670de0b6b3a764000002816125ed576125ed6157a8565b040302816125fd576125fd6157a8565b049350670de0b6b3a7640000608084901c67ffffffffffffffff1686820102049450670de0b6b3a764000060c084901c858201020493505050509193509193565b600080808080868180600189901c62ffffff168b9003612684575050601a87901c633fffffff169250603887901c6603ffffffffffff166001601989901c8116146126db565b60008c8152600660205260408120600360028e0181810660550292918491048152602081019190915260400160002054901c600181811c633fffffff169750601f82901c6603ffffffffffff169450908116149150505b6000858152600760205260409020549350801561271e577f80000000000000000000000000000000000000000000000000000000000000009b50600099506128ea565b836003166002036127885761274082607486901c6603ffffffffffff16614f72565b91506001612750600f6023615782565b6001901b61275e9190615795565b82146127885760a69390931c633fffffff166000818152600760205260409020549094509261271e565b83600316600314806127b4575060016127a3600f6023615782565b6001901b6127b19190615795565b82145b156127e5577f80000000000000000000000000000000000000000000000000000000000000009b50600099506128ea565b6127fd8a607486901c6603ffffffffffff168461500a565b9950606483048a111561281a5761271061270f8b0204995061281f565b600099505b89156128c65783600416600414612842576207ffff600385901c1660000361284d565b6207ffff600385901c165b9b50600061285d8d60020b6140be565b905061271f612710820204633fffffff601687901c811661287e8385615795565b612888919061576b565b61289291906157d7565b61289c9082615782565b6128b36c010000000000000000000000008e61576b565b6128bd91906157d7565b975050506128ea565b7f80000000000000000000000000000000000000000000000000000000000000009b505b8b8a97509750505050945094509450945094565b6000806129627f000000000000000000000000836580dc0b1ff9351c58c173cd1da8b31a5367336000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061074492505050565b80602001905181019061297591906159db565b90939092509050565b6000806129b36040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60008054906001821690036108a257600181176000556127108810806129e857506fffffffffffffffffffffffffffffffff88115b15612a23576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792e600482015260240161027d565b7fffffffffffffffffffffffff11111111111111111111111111111111111111127f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1601612ae857873414158015612aa8575061dead73ffffffffffffffffffffffffffffffffffffffff871614155b15612ae3576040517f60121cca000000000000000000000000000000000000000000000000000000008152617920600482015260240161027d565b612b24565b3415612b24576040517f60121cca000000000000000000000000000000000000000000000000000000008152617920600482015260240161027d565b6001548252600281901c620fffff16600003612b70576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791f600482015260240161027d565b8151612b7b906123a6565b90919250909150836060018460800182815250828152505050612bf86040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b612c316040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b601683901c633fffffff16808252600090815260076020908152604082205490830181905260741c6603ffffffffffff166060830181905281908103612c7f576603ffffffffc00060608401525b602083015160c41c620fffff1691508115612cc65781600116600114612cb1576207ffff600183901c16600003612cbc565b6207ffff600183901c165b6080840152612ced565b7f800000000000000000000000000000000000000000000000000000000000000060808401525b84600416600414612d0a576207ffff600386901c16600003612d15565b6207ffff600386901c165b60c08501526002851615612d2a576002612d2d565b60015b60ff1684610100018181525050612d7a6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60c085015181528651604080517fe6aa216c000000000000000000000000000000000000000000000000000000008152905160609290921c9163e6aa216c916004808201926020929091908290030181865afa158015612dde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e029190615706565b92508660800151876060015184612e19919061576b565b612e2391906157d7565b9250612e4683760a70c3c40a64e6c51999090b65f67d92400000000000006157d7565b87519092506127109060481c6103ff1681018302046060860152722cd76fe086b93ce2f768a00b22a000000000008310612e9d576c010000000000000000000000006b033b2e3c9fd0803ce8000000840402612ebc565b6b033b2e3c9fd0803ce80000006c010000000000000000000000008402045b87519093506103e890602a1c6103ff168402049150612eda8261487b565b50602088015286516103e89060341c6103ff168402049150612efb8261487b565b506040880152608087015164e8d4a510008e0281612f1b57612f1b6157a8565b0485528915612fb15760025485516fffffffffffffffffffffffffffffffff8216945060809190911c9250831115612f965784518390612f5b908461576b565b612f6591906157d7565b60a08601819052612f769083615795565b855160808088018290526000885282901b94039384176002559150612fb1565b6000600255845183900385526080850183905260a085018290525b86602001518560c00151138015612fd0575086604001518560c0015113155b15613ab357845115613ab3576040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081018290528190680100000000000000009060008960c0015112613041576101008960c001518161303b5761303b6157a8565b0561305e565b60016101008a60c001516001018161305b5761305b6157a8565b05035b6040820152845161306e906140be565b604086015261010089015160010361309957604085015160e08a015260c089015160208201526131a0565b61271f856040015161271002816130b2576130b26157a8565b046060860181905260408601510360808601819052602089015160161c633fffffff90811660c0880181905290910260608701519190040160e08a0152845160208c015160010114801561310a57508460c001516001145b156131a05773ffffffffffffffffffffffffffffffffffffffff8f1661dead0361316a576040517fcc36aa44000000000000000000000000000000000000000000000000000000008152600060048201819052602482015260440161027d565b6040517f60121cca000000000000000000000000000000000000000000000000000000008152617935600482015260240161027d565b8861010001516001036132105760c0890180516000908152600560205260408082205460608c01518c5194518452919092206301fffffe8316601a9490941b9390931760389190911b1760011790915566ffffffffffffff602182901c1660ff601983901c161b97509550613233565b67ffffffffffffffff60348960200151901c16965060ff8716600888901c901b96505b86896020018181516132459190615782565b90525060e08901516132646c010000000000000000000000008961576b565b61326e91906157d7565b8960400181815161327f9190615782565b90525060c0890151602082015114801561329e57508861010001516001145b806132ab57506080810151155b156133db5780608001516000036132d45760408082015160009081526004602052205460808201525b88610100015160010361332b57604081015160c08a015160009081036101009283020190910160608301819052608083018051821b90911c90819052900361332b5760408082015160009081526004602052908120555b608081015115613359576001613344826080015161504f565b604083015161010002010360208201526133db565b8a602001518160400151610100021215613398577f800000000000000000000000000000000000000000000000000000000000000060208201526133db565b604080820180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190819052600090815260046020522054608082015261332b565b806020015188608001511380156133f957508a602001518860800151135b156134165760808801516101208a015260026101608a0152613452565b8a602001518160200151131561343e5760208101516101208a015260016101608a0152613452565b60208b01516101208a015260036101608a01525b61346389610120015160020b6140be565b60408601526101608901516002036134f75761271f8560400151612710028161348e5761348e6157a8565b046060860181905260408087015191909103608087019081526020808b015160a61c633fffffff908116600090815260079092529290205460a08b0181905260161c821660c0880181905290510260608701519190040160a086018190526101408a0152613514565b604085015160a086018190526101408a0152633fffffff60c08601525b6c010000000000000000000000008961014001518a60600151613537919061576b565b61354191906157d7565b613557906b033b2e3c9fd0803ce8000000615795565b8960e001518a602001518b6101400151613571919061576b565b61357b91906157d7565b8a6020015161358a9190615795565b6135a0906b033b2e3c9fd0803ce800000061576b565b6135aa91906157d7565b93506b033b2e3c9fd0803ce80000008960600151856135c9919061576b565b6135d391906157d7565b9250838960200151036135ee576135eb600185615795565b93505b88518410158061360357508861016001516003145b156138cd5760808101516040808301516000908152600460205220558851841061374257885160608a01519094506b033b2e3c9fd0803ce800000090613649908661576b565b61365391906157d7565b92508289604001516136659190615795565b6c01000000000000000000000000858b602001516136839190615795565b61368d919061576b565b61369791906157d7565b96506136a28761487b565b606087015280865260208c015113156136c85784516002018552600160c0860152613755565b84516001018552606085015161271061271f8202046040870181905281900360808701819052908803633fffffff0281613704576137046157a8565b0460c086018190521561373557633fffffff8560c00151101561372b578460c00151613738565b633ffffffe613738565b60015b60c0860152613755565b6101208901516001908101865260c08601525b60208901516137648582615795565b61376e908461576b565b61377891906157d7565b9150838960800181815161378c9190615782565b9052506020890180518591906137a3908390615795565b90525060a0890180518491906137ba908390615782565b9052506040890180518491906137d1908390615795565b90525060608801516137e39083615105565b606089015260208901516064111561382b576040517f60121cca000000000000000000000000000000000000000000000000000000008152617930600482015260240161027d565b8451600013613841578451600190811b1761384a565b845160000360011b5b606089015160208b015191975060741b9060349061386d90603860086001614320565b901b60168760c00151901b600289901b60a6808d60200151901c901b60011717171717600760008a6000015181526020019081526020016000208190555060168860000151901b600287901b6034808d901c901b60021717179950613aae565b8851849003895260208901516138e38582615795565b6138ed908461576b565b6138f791906157d7565b9150838960800181815161390b9190615782565b905250602089018051859190613922908390615795565b90525060a089018051849190613939908390615782565b905250604089018051849190613950908390615795565b90525060608801516139629083615105565b6060890152610160890151680100000000000000009250600203613a8757633fffffff60a68960200151901c1696508760a00151955060006603ffffffffffff607488901c16905060006139c38a60600151836151a990919063ffffffff16565b6020808c0180518d516000908152600790935260409092207fffffffffffffffffffffffc00000000000000000000000000000000000000000909216607484901b17600217909155998b5298889052506060890181905260c487901c620fffff16978815613a5d5788600116600114613a48576207ffff60018a901c16600003613a53565b6207ffff60018a901c165b60808b0152613a84565b7f800000000000000000000000000000000000000000000000000000000000000060808b01525b50505b61012089015160c08a01526101608901516101008a015261014089015160e08a01526131a0565b505050505b64e8d4a5100087608001518660800151613acd919061576b565b613ad791906157d7565b985064e8d4a5100087606001518660a00151613af3919061576b565b613afd91906157d7565b97508c891115613b2157613b11898e6157d7565b613b1b908961576b565b97508c98505b8b89613b358a670de0b6b3a764000061576b565b613b3f91906157d7565b1015613b7b576040517f60121cca000000000000000000000000000000000000000000000000000000008152617921600482015260240161027d565b73ffffffffffffffffffffffffffffffffffffffff8b1661dead03613bd6576040517fcc36aa4400000000000000000000000000000000000000000000000000000000815260048101899052602481018a905260440161027d565b7fffffffffffffffffffffffff11111111111111111111111111111111111111127f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1601613c4f5788925034891015613c4a57613c4a338a3403614d15565b613c54565b600092505b7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff1663ad967e15847f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4860008d60000360008033604051602001613ce8919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401613d1896959493929190615982565b604080518083038185885af1158015613d35573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613d5a91906159db565b505073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e4971663ad967e157f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60008b8103908f81806040519080825280601f01601f191660200182016040528015613dec576020820181803683370190505b506040518763ffffffff1660e01b8152600401613e0e96959493929190615982565b60408051808303816000875af1158015613e2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e5091906159db565b505067ffffffffffffffff605287901c1692508460a0015160ff8416600885901c901b613e7d9190615795565b925067ffffffffffffffff609287901c169150846080015160ff8316600884901c901b613eaa9190615795565b91506092613ebd83603860086001614320565b901b6052613ed085603860086000614320565b7ffffffffffffc00000000000000000000000000000003ffffffffffffffffffff8916911b171760005560408051338152602081018a90529081018a905273ffffffffffffffffffffffffffffffffffffffff8c1660608201527f80fd9cc6b1821f4a510e45ffce6852ea3404807b5d3d833ffa85664408afcb669060800160405180910390a15050505050505094509492505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e4971614613fda576040517f60121cca000000000000000000000000000000000000000000000000000000008152617929600482015260240161027d565b60005460011660000361401d576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792a600482015260240161027d565b6140548461402d838501856159ff565b7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49786615254565b50505050565b6140bb7f000000000000000000000000836580dc0b1ff9351c58c173cd1da8b31a5367336000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061074492505050565b50565b600060ff82901d80831803617fff8111156140d857600080fd5b700100000000000000000000000000000000600182161561410657506fff9dd7de423466c20352b1246ce4856f5b6002821615614125576fff3bd55f4488ad277531fa1c725a66d00260801c5b6004821615614144576ffe78410fd6498b73cb96a6917f8532590260801c5b6008821615614163576ffcf2d9987c9be178ad5bfeffaa1232730260801c5b6010821615614182576ff9ef02c4529258b057769680fc6601b30260801c5b60208216156141a1576ff402d288133a85a17784a411f7aba0820260801c5b60408216156141c0576fe895615b5beb6386553757b0352bda900260801c5b60808216156141df576fd34f17a00ffa00a8309940a15930391a0260801c5b6101008216156141ff576fae6b7961714e20548d88ea5123f9a0ff0260801c5b61020082161561421f576f76d6461f27082d74e0feed3b388c0ca10260801c5b61040082161561423f576f372a3bfe0745d8b6b19d985d9a8b85bb0260801c5b61080082161561425f576f0be32cbee48979763cf7247dd7bb539d0260801c5b61100082161561427e576e8d4f70c9ff4924dac37612d1e2921e0260801c5b61200082161561429c576d4e009ae5519380809a02ca7aec770260801c5b6140008216156142b8576b17c45e641b6e95dee056ff100260801c5b600091507f80000000000000000000000000000000000000000000000000000000000000008416614316577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0463ffffffff81161561431657600191505b60201c0192915050565b600080856fffffffffffffffffffffffffffffffff8111156143435760809150811c5b67ffffffffffffffff81111561435b576040918201911c5b63ffffffff81111561436f576020918201911c5b61ffff811115614381576010918201911c5b60ff811115614392576008918201911c5b600f8111156143a3576004918201911c5b60038111156143b4576002918201911c5b60018111156143c4576001820191505b80156143d1576001820191505b50848110156143dd5750835b848103905085811c6000821184161561442c57600181019050806001871b0361442c57506001908101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86011b5b6001851b821061443b57600080fd5b90931b909201949350505050565b601681901c633fffffff166000818152600760205260408120549091829182600160c483901c81169003614487575060c581901c6207ffff166144bf565b5060c581901c6207ffff166000819003906144bf57507f80000000000000000000000000000000000000000000000000000000000000005b60006144ca8861531b565b90508082136144d957806144db565b815b94507f8000000000000000000000000000000000000000000000000000000000000000850361452e57867fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0000116965061461d565b8085036145a457600085121561457157600385600003901b877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc000011617965061461d565b600385901b877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc000011660041717965061461d565b6000848152600760205260408120557ffffffffffffffffffffffffffffffffffffffffffffc00000000000000000001909616623ffffc60c284901c1617660fffffffc00000609084901c16177fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff840160341b17600217955b869550505050509250929050565b600080831261463e576101008305614649565b600161010084820105035b90506101008102830382614671576000828152600460205260409020546001821b1916614686565b6000828152600460205260409020546001821b175b60009283526004602052604090922091909155505050565b6000806000806127108510156146e4576040517f60121cca000000000000000000000000000000000000000000000000000000008152617933600482015260240161027d565b6000866146fe6c010000000000000000000000008861576b565b61470891906157d7565b90506147138161487b565b60019091019550905061271061271f82020490506060614733888361576b565b901c92506147418684615795565b600086815260056020526040812054600181901c62ffffff169650919350851580159061476f575060018216155b156147b05766ffffffffffffff602183901c1660ff601984901c161b6147958682615782565b9150806000036147aa576147aa88600161462b565b50614804565b85156147f0576000878152600660209081526040808320600360028b0181810486529190935292208054601986901c929093066055029190911b90911790555b85600101955084905061480487600161462b565b612710811015614844576040517f60121cca000000000000000000000000000000000000000000000000000000008152617931600482015260240161027d565b601961485582603860086000614320565b6000898152600560205260409020911b600188901b179055509497939650919450925050565b600080630235b88083107473d85bca016a2338b31715f8e13054c005f8b995d3841117156148a857600080fd5b6c0100000000000000000000000083106000816148de57506c010000000000000000000000006a52b7d2dcc80cd2e40000008502045b811561490157507652b7d2dcc80cd2e40000000000000000000000000000008490045b6f037af932b2affa9738cc6c38ca527831811061494257614000841793506f037af932b2affa9738cc6c38ca5278316a52b7d2dcc80cd2e400000082020490505b6d010f7a088a76f267264caa114f0a811061497f57612000841793506d010f7a088a76f267264caa114f0a6a52b7d2dcc80cd2e400000082020490505b6b95da74f87f839fc2e0dc5bd981106149b857611000841793506b95da74f87f839fc2e0dc5bd96a52b7d2dcc80cd2e400000082020490505b6b06f55dedafd8491caed5a1b881106149f157610800841793506b06f55dedafd8491caed5a1b86a52b7d2dcc80cd2e400000082020490505b6b017fdd10ee11e624491b4cc18110614a2a57610400841793506b017fdd10ee11e624491b4cc16a52b7d2dcc80cd2e400000082020490505b6ab23131bf0c30217b0a2c698110614a6157610200841793506ab23131bf0c30217b0a2c696a52b7d2dcc80cd2e400000082020490505b6a79683edcb9280d797aded78110614a9857610100841793506a79683edcb9280d797aded76a52b7d2dcc80cd2e400000082020490505b6a64366e2f9919f0d9b0dc908110614ace576080841793506a64366e2f9919f0d9b0dc906a52b7d2dcc80cd2e400000082020490505b6a5b0bcda5a78850646b0a818110614b04576040841793506a5b0bcda5a78850646b0a816a52b7d2dcc80cd2e400000082020490505b6a56c840f992c70f959ae8108110614b3a576020841793506a56c840f992c70f959ae8106a52b7d2dcc80cd2e400000082020490505b6a54b9cd178695194f9be0a08110614b70576010841793506a54b9cd178695194f9be0a06a52b7d2dcc80cd2e400000082020490505b6a53b7458aff204b5e65d6818110614ba6576008841793506a53b7458aff204b5e65d6816a52b7d2dcc80cd2e400000082020490505b6a53372a2f38c240d689e4008110614bdc576004841793506a53372a2f38c240d689e4006a52b7d2dcc80cd2e400000082020490505b6a52f76617a04499e66400008110614c12576002841793506a52f76617a04499e66400006a52b7d2dcc80cd2e400000082020490505b6a52d79660f3dec355c000008110614c48576001841793506a52d79660f3dec355c000006a52b7d2dcc80cd2e400000082020490505b81614c6057806a52b7d2dcc80cd2e400000086020492505b8115614c7c579219926a52d79660f3dec355c000008582020492505b5050915091565b600066ffffffffffffff604984901c1660ff604185901c161b808203614cad576000915050614d0f565b612710613fff60a286901c168402046401ffffffff608186901c16420362ffffff60b087901c16614cde828461576b565b614ce891906157d7565b9050808311614cf8576000614cfc565b8083035b9350508084038381111561078757925050505b92915050565b600080600080600085875af1905080614d5f576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a600482015260240161027d565b505050565b67ffffffffffffffff605b82901c811690609b83901c16811580614d86575080155b15614dc2576040517fd50d751200000000000000000000000000000000000000000000000000000000815262011171600482015260240161027d565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480614dec575082155b80614df75750806001145b15614e0457505050915091565b64496cebb80084840283020484019350617fff60db87901c16925082600103614e2f57505050915091565b82600116600103614e845760019290921c91826c7e37be2022c0914b268000000081614e5d57614e5d6157a8565b049250612710601e87901c613fff166b033b2e3c9fd0803ce8000000850102049250614eb1565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b80600116600103614ee85760011c61271081016b033b2e3c9fd0803ce8000000820281614ee057614ee06157a8565b049050614f1e565b60011c61271081016b033b2e3c9fd0803ce8000000820281614f0c57614f0c6157a8565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b6000600f83811c9083901c0281681fffffffffffffffff8211614f96576022614f99565b60235b91821c919050617fff858116908516018101614000811015614fba57600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001617fff811115614ff957506603ffffffffffff9250614d0f915050565b600f9290921b909117949350505050565b6000617fff83811690831603608181101561504257600f83811c821b9085901c860281615039576150396157a8565b04915050615048565b60009150505b9392505050565b6000816fffffffffffffffffffffffffffffffff8311156150715760809150811c5b67ffffffffffffffff811115615089576040918201911c5b63ffffffff81111561509d576020918201911c5b61ffff8111156150af576010918201911c5b60ff8111156150c0576008918201911c5b600f8111156150d1576004918201911c5b60038111156150e2576002918201911c5b60018111156150f2576001820191505b80156150ff576001820191505b50919050565b6000600f83901c8202816c03ffffffffffffffffffffffff821161514c576c01ffffffffffffffffffffffff8211615145576151408261504f565b61514f565b606261514f565b60635b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd617fff87168201810194500191821c919050604083111561519c57604083600f84901b010392506151a1565b600080fd5b505092915050565b600080600f83901c7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000603186901b16816151e5576151e56157a8565b0490506000604082901c6001146151fd576040615200565b60415b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd81019290921c91617fff8581166040019250613fdd908716909101018181111561519c5703600f9190911b179050614d0f565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080615314576040517fdee51a8a00000000000000000000000000000000000000000000000000000000815262011559600482015260240161027d565b5050505050565b600080600080841261533157610100840561533c565b600161010085820105035b600081815260046020526040812054919350859003610100808502919091010190811b811c91505b81156153835760016153758361504f565b84610100020103935061540e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8301927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f036001016153f8577f8000000000000000000000000000000000000000000000000000000000000000935061540e565b6000838152600460205260409020549150615364565b505050919050565b80151581146140bb57600080fd5b60006020828403121561543657600080fd5b815161504881615416565b73ffffffffffffffffffffffffffffffffffffffff811681146140bb57600080fd5b6000806000806080858703121561547957600080fd5b843593506020850135925060408501359150606085013561549981615441565b939692955090935050565b6000602082840312156154b657600080fd5b5035919050565b600080600080608085870312156154d357600080fd5b5050823594602084013594506040840135936060013592509050565b6000806000806080858703121561550557600080fd5b8435935060208501359250604085013561551e81615441565b9150606085013561549981615416565b6000806000806060858703121561554457600080fd5b843561554f81615441565b935060208501359250604085013567ffffffffffffffff8082111561557357600080fd5b818701915087601f83011261558757600080fd5b81358181111561559657600080fd5b8860208285010111156155a857600080fd5b95989497505060200194505050565b815173ffffffffffffffffffffffffffffffffffffffff1681526101a0810160208301516155fd602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151615625604084018273ffffffffffffffffffffffffffffffffffffffff169052565b50606083015161564d606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080830151615675608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a083015161569d60a084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c08301516156b260c084018260ff169052565b5060e08301516156c760e084018260ff169052565b50610100838101519083015261012080840151908301526101408084015190830152610160808401519083015261018092830151929091019190915290565b60006020828403121561571857600080fd5b5051919050565b60006020828403121561573157600080fd5b815161504881615441565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417614d0f57614d0f61573c565b80820180821115614d0f57614d0f61573c565b81810381811115614d0f57614d0f61573c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826157e6576157e66157a8565b500490565b808202600082127f8000000000000000000000000000000000000000000000000000000000000000841416156158235761582361573c565b8181058314821517614d0f57614d0f61573c565b600082615846576158466157a8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561589a5761589a61573c565b500590565b81810360008312801583831316838312821617156158bf576158bf61573c565b5092915050565b60007f800000000000000000000000000000000000000000000000000000000000000082036158f7576158f761573c565b5060000390565b80820182811260008312801582168215821617156151a1576151a161573c565b6000815180845260005b8181101561594457602081850181015186830182015201615928565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352876020840152866040840152808616606084015280851660808401525060c060a08301526159cf60c083018461591e565b98975050505050505050565b600080604083850312156159ee57600080fd5b505080516020909101519092909150565b600060208284031215615a1157600080fd5b81356150488161544156fea264697066735822122087df04e04950c10d1518836e4d6b3d1a189dc7708362a0a8cb74ff0277b71c5464736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d0000000000000000000000008730d7a5af435678b2686289eeb6a74a042081e6000000000000000000000000836580dc0b1ff9351c58c173cd1da8b31a536733000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001a1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7a8e1248eddf82e10c0adc6c737b6d8da17674abf51801ea5a4549f41c2dfdf2156a30cfbf6c7293102fb2f2bbcabd282241e454a9db358e4e2d6a9e6252ba4ee34808091dd0ff71c11f548a300df0a835ff3a6176689d16be165ba67210a7254
-----Decoded View---------------
Arg [0] : constants_ (tuple):
Arg [1] : liquidity (address): 0x52Aa899454998Be5b000Ad077a46Bbe360F4e497
Arg [2] : factory (address): 0x324c5Dc1fC42c7a4D43d92df1eBA58a54d13Bf2d
Arg [3] : adminImplementation (address): 0x8730d7a5aF435678b2686289eEB6A74A042081e6
Arg [4] : secondaryImplementation (address): 0x836580Dc0B1FF9351c58c173CD1dA8b31A536733
Arg [5] : supplyToken (address): 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
Arg [6] : borrowToken (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [7] : supplyDecimals (uint8): 18
Arg [8] : borrowDecimals (uint8): 6
Arg [9] : vaultId (uint256): 1
Arg [10] : liquiditySupplyExchangePriceSlot (bytes32): 0xa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7
Arg [11] : liquidityBorrowExchangePriceSlot (bytes32): 0xa8e1248eddf82e10c0adc6c737b6d8da17674abf51801ea5a4549f41c2dfdf21
Arg [12] : liquidityUserSupplySlot (bytes32): 0x56a30cfbf6c7293102fb2f2bbcabd282241e454a9db358e4e2d6a9e6252ba4ee
Arg [13] : liquidityUserBorrowSlot (bytes32): 0x34808091dd0ff71c11f548a300df0a835ff3a6176689d16be165ba67210a7254
-----Encoded View---------------
13 Constructor Arguments found :
Arg [0] : 00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497
Arg [1] : 000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d
Arg [2] : 0000000000000000000000008730d7a5af435678b2686289eeb6a74a042081e6
Arg [3] : 000000000000000000000000836580dc0b1ff9351c58c173cd1da8b31a536733
Arg [4] : 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
Arg [5] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : a1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7
Arg [10] : a8e1248eddf82e10c0adc6c737b6d8da17674abf51801ea5a4549f41c2dfdf21
Arg [11] : 56a30cfbf6c7293102fb2f2bbcabd282241e454a9db358e4e2d6a9e6252ba4ee
Arg [12] : 34808091dd0ff71c11f548a300df0a835ff3a6176689d16be165ba67210a7254
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.