Contents
- libraries
- ILiquidator
- IManager
- Borrower
- Factory
- BorrowerDeployer
- Ledger
- Lender
- IRateModel
- RateModel
- SafeRateLib
- VolatilityOracle
Contents
- constants
- AuctionAmounts
- Assets
- Prices
- BalanceSheet
- exp1e12
- LiquidityAmounts
- mulDiv96
- square
- mulDiv128
- mulDiv224
- mulDiv128Up
- Oracle
- zip
- extract
- Rewards
- TickMath
- Volatility
Contents
Constants
ONE
The initial value of Lender
's borrowIndex
uint256 constant ONE = 1e12;
BORROWS_SCALER
An additional scaling factor applied to borrowed amounts before dividing by borrowIndex
and storing.
72 matches the type of borrowIndex
in Ledger
to guarantee that the stored borrow units fit in uint256.
uint256 constant BORROWS_SCALER = ONE << 72;
MAX_RATE
The maximum percentage yield per second, scaled up by 1e12. The current value is equivalent to
((1 + 706354 / 1e12) ** (24 * 60 * 60)) - 1
β +6.3% per day or +53% per week. If the rate is consistently at
this maximum value, the Lender
will function for 1 year before borrowIndex
overflows.
uint256 constant MAX_RATE = 706354;
DEFAULT_ANTE
The default amount of Ether required to take on debt in a Borrower
. The Factory
can override this value
on a per-market basis. Incentivizes calls to Borrower.warn
.
uint208 constant DEFAULT_ANTE = 0.01 ether;
DEFAULT_N_SIGMA
The default number of standard deviations of price movement used to determine probe prices for Borrower
solvency. The Factory
can override this value on a per-market basis. Expressed x10, e.g. 50 β 5Ο
uint8 constant DEFAULT_N_SIGMA = 50;
DEFAULT_MANIPULATION_THRESHOLD_DIVISOR
Assume someone is manipulating the Uniswap TWAP oracle. To steal money from the protocol and create bad debt, they would need to change the TWAP by a factor of (1 / LTV), where the LTV is a function of volatility. We have a manipulation metric that increases as an attacker tries to change the TWAP. If this metric rises above a certain threshold, certain functionality will be paused, e.g. no new debt can be created. The threshold is calculated as follows: \( \text{manipulationThreshold} = \frac{log_{1.0001}\left( \frac{1}{\text{LTV}} \right)}{\text{MANIPULATION_THRESHOLD_DIVISOR}} \)
uint8 constant DEFAULT_MANIPULATION_THRESHOLD_DIVISOR = 12;
DEFAULT_RESERVE_FACTOR
The default portion of interest that will accrue to a Lender
's RESERVE
address.
Expressed as a reciprocal, e.g. 16 β 6.25%
uint8 constant DEFAULT_RESERVE_FACTOR = 16;
CONSTRAINT_N_SIGMA_MIN
The lowest number of standard deviations of price movement allowed for determining Borrower
probe prices.
Expressed x10, e.g. 40 β 4Ο
uint8 constant CONSTRAINT_N_SIGMA_MIN = 40;
CONSTRAINT_N_SIGMA_MAX
The highest number of standard deviations of price movement allowed for determining Borrower
probe prices.
Expressed x10, e.g. 80 β 8Ο
uint8 constant CONSTRAINT_N_SIGMA_MAX = 80;
CONSTRAINT_MANIPULATION_THRESHOLD_DIVISOR_MIN
The minimum value of the manipulationThresholdDivisor
, described above
uint8 constant CONSTRAINT_MANIPULATION_THRESHOLD_DIVISOR_MIN = 10;
CONSTRAINT_MANIPULATION_THRESHOLD_DIVISOR_MAX
The maximum value of the manipulationThresholdDivisor
, described above
uint8 constant CONSTRAINT_MANIPULATION_THRESHOLD_DIVISOR_MAX = 16;
CONSTRAINT_RESERVE_FACTOR_MIN
The lower bound on what any Lender
's reserve factor can be. Expressed as reciprocal, e.g. 4 β 25%
uint8 constant CONSTRAINT_RESERVE_FACTOR_MIN = 4;
CONSTRAINT_RESERVE_FACTOR_MAX
The upper bound on what any Lender
's reserve factor can be. Expressed as reciprocal, e.g. 20 β 5%
uint8 constant CONSTRAINT_RESERVE_FACTOR_MAX = 20;
CONSTRAINT_ANTE_MAX
The maximum amount of Ether that Borrower
s can be required to post before taking on debt
uint216 constant CONSTRAINT_ANTE_MAX = 0.5 ether;
MAX_LEVERAGE
\( 1 + \frac{1}{\text{MAX_LEVERAGE}} \) should be greater than the maximum feasible single-block
accrualFactor
so that liquidators have time to respond to interest updates
uint256 constant MAX_LEVERAGE = 200;
LIQUIDATION_INCENTIVE
The minimum discount that a healthy Borrower
should be able to offer a liquidator when swapping
assets. Expressed as reciprocal, e.g. 20 β 5%
uint256 constant LIQUIDATION_INCENTIVE = 20;
LIQUIDATION_GRACE_PERIOD
The minimum time that must pass between calls to Borrower.warn
and Borrower.liquidate
.
uint256 constant LIQUIDATION_GRACE_PERIOD = 5 minutes;
TERMINATING_CLOSE_FACTOR
The minimum closeFactor
necessary to conclude a liquidation auction. To actually conclude the auction,
Borrower.liquidate
must result in a healthy balance sheet (in addition to this closeFactor
requirement).
Expressed in basis points.
NOTE: The ante is depleted after just 4 Borrower.warn
ings. By requiring that each auction repay at least
68%, we ensure that after 4 auctions, no more than 1% of debt remains ((1 - 0.6838)^4). Increasing the threshold
would reduce that further, but we don't want to prolong individual auctions unnecessarily since the incentive
(and loss to Borrower
s) increases with time.
uint256 constant TERMINATING_CLOSE_FACTOR = 6837;
PROBE_SQRT_SCALER_MIN
The minimum scaling factor by which sqrtMeanPriceX96
is multiplied or divided to get probe prices
uint256 constant PROBE_SQRT_SCALER_MIN = 1.026248453011e12;
PROBE_SQRT_SCALER_MAX
The maximum scaling factor by which sqrtMeanPriceX96
is multiplied or divided to get probe prices
uint256 constant PROBE_SQRT_SCALER_MAX = 3.078745359035e12;
LTV_NUMERATOR
Equivalent to \( \frac{10^{36}}{1 + \frac{1}{liquidationIncentive} + \frac{1}{maxLeverage}} \)
uint256 constant LTV_NUMERATOR = uint256(LIQUIDATION_INCENTIVE * MAX_LEVERAGE * 1e36)
/ (LIQUIDATION_INCENTIVE * MAX_LEVERAGE + LIQUIDATION_INCENTIVE + MAX_LEVERAGE);
LTV_MIN
The minimum loan-to-value ratio. Actual ratio is based on implied volatility; this is just a lower bound.
Expressed as a 1e12 percentage, e.g. 0.10e12 β 10%. Must be greater than TickMath.MIN_SQRT_RATIO
because
we reuse a base 1.0001 logarithm in BalanceSheet
uint256 constant LTV_MIN = LTV_NUMERATOR / (PROBE_SQRT_SCALER_MAX * PROBE_SQRT_SCALER_MAX);
LTV_MAX
The maximum loan-to-value ratio. Actual ratio is based on implied volatility; this is just a upper bound. Expressed as a 1e12 percentage, e.g. 0.90e12 β 90%
uint256 constant LTV_MAX = LTV_NUMERATOR / (PROBE_SQRT_SCALER_MIN * PROBE_SQRT_SCALER_MIN);
IV_SCALE
The timescale of implied volatility, applied to measurements and calculations. When BalanceSheet
detects
that an nSigma
event would cause insolvency in this time period, it enables liquidations. So if you squint your
eyes and wave your hands enough, this is (in expectation) the time liquidators have to act before the protocol
accrues bad debt.
uint32 constant IV_SCALE = 24 hours;
IV_COLD_START
The initial value of implied volatility, used when VolatilityOracle.prepare
is called for a new pool.
Expressed as a 1e12 percentage at IV_SCALE
, e.g. {0.12e12, 24 hours} β 12% daily β 229% annual. Error on the
side of making this too large (resulting in low LTV).
uint104 constant IV_COLD_START = 0.127921282726e12;
IV_CHANGE_PER_SECOND
The maximum rate at which (reported) implied volatility can change. Raw samples in VolatilityOracle.update
are clamped (before being stored) so as not to exceed this rate.
Expressed in 1e12 percentage points at IV_SCALE
per second, e.g. {115740, 24 hours} means daily IV can
change by 0.0000116 percentage points per second β 1 percentage point per day.
uint256 constant IV_CHANGE_PER_SECOND = 115740;
IV_CHANGE_PER_UPDATE
The maximum amount by which (reported) implied volatility can change with a single VolatilityOracle.update
call. If updates happen as frequently as possible (every FEE_GROWTH_SAMPLE_PERIOD
), this cap is no different
from IV_CHANGE_PER_SECOND
alone.
uint104 constant IV_CHANGE_PER_UPDATE = uint104(IV_CHANGE_PER_SECOND * FEE_GROWTH_SAMPLE_PERIOD);
IV_EMA_GAIN_POS
The gain on the EMA update when IV is increasing. Expressed as reciprocal, e.g. 20 β 0.05
int256 constant IV_EMA_GAIN_POS = 20;
IV_EMA_GAIN_NEG
The gain on the EMA update when IV is decreasing. Expressed as reciprocal, e.g. 100 β 0.01
int256 constant IV_EMA_GAIN_NEG = 100;
FEE_GROWTH_AVG_WINDOW
To estimate volume, we need 2 samples. One is always at the current block, the other is from
FEE_GROWTH_AVG_WINDOW
seconds ago, +/- FEE_GROWTH_SAMPLE_PERIOD / 2
. Larger values make the resulting volume
estimate more robust, but may cause the oracle to miss brief spikes in activity.
uint256 constant FEE_GROWTH_AVG_WINDOW = 72 hours;
FEE_GROWTH_ARRAY_LENGTH
The length of the circular buffer that stores feeGrowthGlobals samples. Must be in interval \( \left[ \frac{\text{FEE_GROWTH_AVG_WINDOW}}{\text{FEE_GROWTH_SAMPLE_PERIOD}}, 256 \right) \)
uint8 constant FEE_GROWTH_ARRAY_LENGTH = 32;
FEE_GROWTH_SAMPLE_PERIOD
The minimum number of seconds that must elapse before a new feeGrowthGlobals sample will be stored. This controls how often the oracle can update IV.
uint256 constant FEE_GROWTH_SAMPLE_PERIOD = 4 hours;
UNISWAP_AVG_WINDOW
To compute Uniswap mean price & liquidity, we need 2 samples. One is always at the current block, the other is
from UNISWAP_AVG_WINDOW
seconds ago. Larger values make the resulting price/liquidity values harder to
manipulate, but also make the oracle slower to respond to changes.
uint32 constant UNISWAP_AVG_WINDOW = 30 minutes;
Constants
Q8
uint256 constant Q8 = 1 << 8;
Q16
uint256 constant Q16 = 1 << 16;
Q24
uint256 constant Q24 = 1 << 24;
Q32
uint256 constant Q32 = 1 << 32;
Q40
uint256 constant Q40 = 1 << 40;
Q48
uint256 constant Q48 = 1 << 48;
Q56
uint256 constant Q56 = 1 << 56;
Q64
uint256 constant Q64 = 1 << 64;
Q72
uint256 constant Q72 = 1 << 72;
Q80
uint256 constant Q80 = 1 << 80;
Q88
uint256 constant Q88 = 1 << 88;
Q96
uint256 constant Q96 = 1 << 96;
Q104
uint256 constant Q104 = 1 << 104;
Q112
uint256 constant Q112 = 1 << 112;
Q120
uint256 constant Q120 = 1 << 120;
Q128
uint256 constant Q128 = 1 << 128;
BalanceSheet
Author: Aloe Labs, Inc.
Provides functions for computing a Borrower
's health
State Variables
_Q
uint256 private constant _Q = 22.8811827075e18;
_R
uint256 private constant _R = 103567.889099532e12;
_S
uint256 private constant _S = 0.95e12;
_M
uint256 private constant _M = 20.405429e6;
_N
uint256 private constant _N = 7 days - LIQUIDATION_GRACE_PERIOD;
Functions
auctionTime
function auctionTime(uint256 warnTime) internal view returns (uint256);
auctionCurve
function auctionCurve(uint256 t) internal pure returns (uint256);
computeAuctionAmounts
function computeAuctionAmounts(
uint160 sqrtPriceX96,
uint256 assets0,
uint256 assets1,
uint256 liabilities0,
uint256 liabilities1,
uint256 t,
uint256 closeFactor
) internal pure returns (AuctionAmounts memory amounts);
isHealthy
Checks whether a Borrower
is healthy given the probe prices and its current assets and liabilities.
Should be used when assets
at prices.a
differ from those at prices.b
(due to Uniswap positions).
function isHealthy(
Prices memory prices,
Assets memory assets,
uint256 liabilities0,
uint256 liabilities1
) internal pure returns (bool);
isHealthy
Checks whether a Borrower
is healthy given the probe prices and its current assets and liabilities.
Can be used when assets
at prices.a
are the same as those at prices.b
(no Uniswap positions).
function isHealthy(
Prices memory prices,
uint256 assets0,
uint256 assets1,
uint256 liabilities0,
uint256 liabilities1
) internal pure returns (bool);
isSolvent
function isSolvent(
uint160 sqrtPriceX96,
uint256 assets0,
uint256 assets1,
uint256 liabilities0,
uint256 liabilities1
) internal pure returns (bool);
computeProbePrices
Given data from the ORACLE
(first 3 args) and parameters from the FACTORY
(last 2 args), computes
the probe prices at which to check the account's health
function computeProbePrices(
uint56 metric,
uint256 sqrtMeanPriceX96,
uint256 iv,
uint8 nSigma,
uint8 manipulationThresholdDivisor
) internal pure returns (uint160 a, uint160 b, bool seemsLegit);
Parameters
Name | Type | Description |
---|---|---|
metric | uint56 | The manipulation metric (from oracle) |
sqrtMeanPriceX96 | uint256 | The current TWAP, expressed as a sqrtPriceX96 (from oracle) |
iv | uint256 | The estimated implied volatility, expressed as a 1e12 percentage (from oracle) |
nSigma | uint8 | The number of standard deviations of price movement to account for (from factory) |
manipulationThresholdDivisor | uint8 | Helps compute the manipulation threshold (from factory). See Constants.sol |
Returns
Name | Type | Description |
---|---|---|
a | uint160 | \( \text{TWAP} \cdot e^{-n \cdot \sigma} \) expressed as a sqrtPriceX96 |
b | uint160 | \( \text{TWAP} \cdot e^{+n \cdot \sigma} \) expressed as a sqrtPriceX96 |
seemsLegit | bool | Whether the Uniswap TWAP has been manipulated enough to create bad debt at the effective LTV |
_manipulationThreshold
Equivalent to \( \frac{log_{1.0001} \left( \frac{10^{12}}{ltv} \right)}{\text{MANIPULATION_THRESHOLD_DIVISOR}} \)
function _manipulationThreshold(uint160 ltv, uint8 manipulationThresholdDivisor) private pure returns (uint24);
_ltv
The effective LTV implied by sqrtScaler
. This LTV is accurate for fixed assets and out-of-range
Uniswap positions, but not for in-range Uniswap positions (impermanent losses make their effective LTV
slightly smaller).
function _ltv(uint256 sqrtScaler) private pure returns (uint160 ltv);
LiquidityAmounts
Authors: Aloe Labs, Inc., Modified from Uniswap
Provides functions for computing liquidity amounts from token amounts and prices
Functions
getAmountsForLiquidity
Computes the token0 and token1 value for a given amount of liquidity, the current pool prices and the prices at the tick boundaries
function getAmountsForLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0, uint256 amount1);
Parameters
Name | Type | Description |
---|---|---|
sqrtRatioX96 | uint160 | A sqrt price representing the current pool prices |
sqrtRatioAX96 | uint160 | A sqrt price representing the first tick boundary |
sqrtRatioBX96 | uint160 | A sqrt price representing the second tick boundary |
liquidity | uint128 | The liquidity being valued |
Returns
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of token0 |
amount1 | uint256 | The amount of token1 |
getValuesOfLiquidity
Computes the value of each portion of the liquidity in terms of token1
Each return value can fit in a uint192 if necessary
function getValuesOfLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 value0, uint256 value1);
Parameters
Name | Type | Description |
---|---|---|
sqrtRatioX96 | uint160 | A sqrt price representing the current pool prices |
sqrtRatioAX96 | uint160 | A sqrt price representing the lower tick boundary |
sqrtRatioBX96 | uint160 | A sqrt price representing the upper tick boundary |
liquidity | uint128 | The liquidity being valued |
Returns
Name | Type | Description |
---|---|---|
value0 | uint256 | The value of amount0 underlying liquidity , in terms of token1 |
value1 | uint256 | The amount of token1 |
getValueOfLiquidity
Computes the value of the liquidity in terms of token1
The return value can fit in a uint192 if necessary
function getValueOfLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
sqrtRatioX96 | uint160 | A sqrt price representing the current pool prices |
sqrtRatioAX96 | uint160 | A sqrt price representing the lower tick boundary |
sqrtRatioBX96 | uint160 | A sqrt price representing the upper tick boundary |
liquidity | uint128 | The liquidity being valued |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | The value of the underlying liquidity , in terms of token1 |
_getAmount0ForLiquidity
Computes the amount of token0 for a given amount of liquidity and a price range
function _getAmount0ForLiquidity(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) private pure returns (uint256 amount0);
Parameters
Name | Type | Description |
---|---|---|
sqrtRatioAX96 | uint160 | A sqrt price representing the first tick boundary |
sqrtRatioBX96 | uint160 | A sqrt price representing the second tick boundary |
liquidity | uint128 | The liquidity being valued |
Returns
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of token0. Will fit in a uint224 if you need it to |
_getAmount1ForLiquidity
Computes the amount of token1 for a given amount of liquidity and a price range
function _getAmount1ForLiquidity(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) private pure returns (uint256 amount1);
Parameters
Name | Type | Description |
---|---|---|
sqrtRatioAX96 | uint160 | A sqrt price representing the first tick boundary |
sqrtRatioBX96 | uint160 | A sqrt price representing the second tick boundary |
liquidity | uint128 | The liquidity being valued |
Returns
Name | Type | Description |
---|---|---|
amount1 | uint256 | The amount of token1. Will fit in a uint192 if you need it to |
square
Equivalent to fullMulDiv(x, x, 1 << 64)
function square(uint160 x) pure returns (uint256 result);
mulDiv96
Equivalent to fullMulDiv(x, y, 1 << 96)
.
NOTE: Does not check for overflow, so choose x
and y
carefully.
function mulDiv96(uint256 x, uint256 y) pure returns (uint256 result);
mulDiv128
Equivalent to fullMulDiv(x, x, 1 << 128)
function mulDiv128(uint256 x, uint256 y) pure returns (uint256 result);
mulDiv128Up
Equivalent to fullMulDivUp(x, x, 1 << 128)
function mulDiv128Up(uint256 x, uint256 y) pure returns (uint256 result);
mulDiv224
Equivalent to fullMulDiv(x, y, 1 << 224)
.
NOTE: Does not check for overflow, so choose x
and y
carefully.
function mulDiv224(uint256 x, uint256 y) pure returns (uint256 result);
Oracle
Authors: Aloe Labs, Inc., Modified from Uniswap
Provides functions to integrate with V3 pool oracle
Functions
consult
Calculates time-weighted means of tick and liquidity for a given Uniswap V3 pool
function consult(IUniswapV3Pool pool, uint40 seed) internal view returns (uint56 metric, uint160 sqrtMeanPriceX96);
Parameters
Name | Type | Description |
---|---|---|
pool | IUniswapV3Pool | Address of the pool that we want to observe |
seed | uint40 | The indices of pool.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations. Determine these off-chain to make this method more efficient than Uniswap's binary search. If any of the highest 8 bits are set, we fallback to onchain binary search. |
Returns
Name | Type | Description |
---|---|---|
metric | uint56 | If the price was manipulated at any point in the past UNISWAP_AVG_WINDOW seconds, then at some point in that period, this value will spike. It may still be high now, or (if the attacker is smart and well-financed) it may have returned to nominal. |
sqrtMeanPriceX96 | uint160 | sqrt(TWAP) over the past UNISWAP_AVG_WINDOW seconds |
observe
Searches for oracle observations nearest to the target
time. If target
lies between two existing
observations, linearly interpolate between them. If target
is newer than the most recent observation,
we interpolate between the most recent one and a hypothetical one taken at the current block.
As long as target <= block.timestamp
, return values should match what you'd get from Uniswap.
function observe(
IUniswapV3Pool pool,
uint32 target,
uint256 seed,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality
) internal view returns (int56, uint160);
Parameters
Name | Type | Description |
---|---|---|
pool | IUniswapV3Pool | The Uniswap pool to examine |
target | uint32 | The timestamp of the desired observation |
seed | uint256 | The index of pool.observations where we start our search. Can be determined off-chain to make this method more efficient than Uniswap's binary search. |
tick | int24 | The current tick (from pool.slot0() ) |
observationIndex | uint16 | The current observation index (from pool.slot0() ) |
observationCardinality | uint16 | The current observation cardinality. Should be determined as follows: solidity (, , uint16 observationIndex, uint16 observationCardinality, , , ) = pool.slot0(); (, , , bool initialized) = pool.observations((observationIndex + 1) % observationCardinality); if (!initialized) observationCardinality = observationIndex + 1; NOTE: If you fail to account for the !initialized case, and target comes before the oldest observation, this may return incorrect data instead of reverting with "OLD". |
Returns
Name | Type | Description |
---|---|---|
<none> | int56 | The tick * time elapsed since pool was first initialized |
<none> | uint160 | The time elapsed / max(1, liquidity) since pool was first initialized |
getMaxSecondsAgo
Given a pool, returns the number of seconds ago of the oldest stored observation
(, , uint16 observationIndex, uint16 observationCardinality, , , ) = pool.slot0();
function getMaxSecondsAgo(
IUniswapV3Pool pool,
uint16 observationIndex,
uint16 observationCardinality
) internal view returns (uint32 secondsAgo);
Parameters
Name | Type | Description |
---|---|---|
pool | IUniswapV3Pool | Address of Uniswap V3 pool that we want to observe |
observationIndex | uint16 | The observation index from pool.slot0() |
observationCardinality | uint16 | The observationCardinality from pool.slot0() |
Returns
Name | Type | Description |
---|---|---|
secondsAgo | uint32 | The number of seconds ago that the oldest observation was stored |
ποΈ Positions
extract
Extracts up to three Uniswap positions from zipped
. Each position consists of an int24 lower
and
int24 upper
, and will be included in the output array iff lower != upper
. The output array is flattened
such that lower and upper ticks are next to each other, e.g. one position may be at indices 0 & 1, and another
at indices 2 & 3.
The output array's length will be one of {0, 2, 4, 6}. We do not validate that lower < upper
, nor do
we check whether positions actually hold liquidity. Also note that this function will revert if zipped
contains duplicate positions like [-100, 100, -100, 100].
function extract(uint256 zipped) pure returns (int24[] memory positionsOfNonZeroWidth);
Parameters
Name | Type | Description |
---|---|---|
zipped | uint256 | Encoded Uniswap positions. Equivalent to the layout of int24[6] storage yourPositions |
Returns
Name | Type | Description |
---|---|---|
positionsOfNonZeroWidth | int24[] | Flattened array of Uniswap positions that may or may not hold liquidity |
zip
Compresses positions
into zipped
. Useful for creating the return value of IManager.callback
function zip(int24[6] memory positions) pure returns (uint144 zipped);
Parameters
Name | Type | Description |
---|---|---|
positions | int24[6] | A flattened array of ticks, each consecutive pair of indices representing one Uniswap position |
Rewards
Authors: Aloe Labs, Inc., Inspired by Yield Protocol
Implements logic for staking rewards
State Variables
_REWARDS_SLOT
bytes32 private constant _REWARDS_SLOT = keccak256("aloe.ii.rewards");
Functions
setRate
Sets the pool's rewards rate. May be 0.
function setRate(Storage storage store, uint160 accumulated, uint64 rate) internal;
Parameters
Name | Type | Description |
---|---|---|
store | Storage | The rewards storage pointer |
accumulated | uint160 | Up-to-date poolState.accumulated , i.e. the output of _accumulate |
rate | uint64 | The rewards rate, specified as [token units per second]. Keep between 10^17 and 10^28 token units per year for smooth operation -- between 0.1 and 10 billion tokens, assuming 18 decimals. |
claim
function claim(
Storage storage store,
uint160 accumulated,
address user,
uint256 balance
) internal returns (uint96 earned);
updatePoolState
Ensures that changes in the pool's totalSupply
don't mess up rewards accounting. Should
be called anytime totalSupply
changes.
Use Rewards.load()
to easily obtain the first two arguments
function updatePoolState(Storage storage store, uint160 accumulated) internal;
Parameters
Name | Type | Description |
---|---|---|
store | Storage | The rewards storage pointer |
accumulated | uint160 | Up-to-date poolState.accumulated , i.e. the output of _accumulate |
updateUserState
Tracks how much reward a user
earned while holding a particular balance
. Should be
called anytime their balance changes.
Use Rewards.load()
to easily obtain the first two arguments
function updateUserState(Storage storage store, uint160 accumulated, address user, uint256 balance) internal;
Parameters
Name | Type | Description |
---|---|---|
store | Storage | The rewards storage pointer |
accumulated | uint160 | Up-to-date poolState.accumulated , i.e. the output of _accumulate |
user | address | The user whose balance (# of shares) is about to change |
balance | uint256 | The user's balance (# of shares) -- before it changes |
previewUserState
function previewUserState(
Storage storage store,
uint160 accumulated,
address user,
uint256 balance
) internal view returns (UserState memory userState);
getRate
function getRate() internal view returns (uint64);
load
Returns arguments to be used in updatePoolState
and updateUserState
. No good semantic
meaning here, just a coincidence that both functions need this information.
function load(uint256 totalSupply) internal view returns (Storage storage store, uint160 accumulator);
_accumulate
Accumulates rewards based on the current rate
and time elapsed since last update
function _accumulate(PoolState memory poolState, uint256 totalSupply) private view returns (uint160);
_storage
Diamond-pattern-style storage getter
function _storage() private pure returns (Storage storage store);
Events
RewardsRateSet
event RewardsRateSet(uint64 rate);
RewardsClaimed
event RewardsClaimed(address indexed user, uint96 amount);
Structs
PoolState
struct PoolState {
uint160 accumulated;
uint32 lastUpdated;
uint64 rate;
}
UserState
struct UserState {
uint96 earned;
uint160 checkpoint;
}
Storage
struct Storage {
PoolState poolState;
mapping(address => UserState) userStates;
}
TickMath
Authors: Aloe Labs, Inc., Modified from Uniswap and Aperture Finance
Computes sqrt price for ticks of size 1.0001, i.e. \(\sqrt{1.0001^{tick}}\) as fixed point Q64.96 numbers. Supports prices between \(2^{-128}\) and \(2^{128}\)
State Variables
MIN_TICK
The minimum tick that may be passed to getSqrtRatioAtTick
computed from \( log_{1.0001}2^{-128} \)
int24 internal constant MIN_TICK = -887272;
MAX_TICK
The maximum tick that may be passed to getSqrtRatioAtTick
computed from \( log_{1.0001}2^{128} \)
int24 internal constant MAX_TICK = 887272;
MIN_SQRT_RATIO
The minimum value that can be returned from getSqrtRatioAtTick
. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
MAX_SQRT_RATIO
The maximum value that can be returned from getSqrtRatioAtTick
. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
MAX_SQRT_RATIO_MINUS_MIN_SQRT_RATIO_MINUS_ONE
A threshold used for optimized bounds check, equals MAX_SQRT_RATIO - MIN_SQRT_RATIO - 1
uint160 private constant MAX_SQRT_RATIO_MINUS_MIN_SQRT_RATIO_MINUS_ONE =
1461446703485210103287273052203988822378723970342 - 4295128739 - 1;
Functions
getSqrtRatioAtTick
Calculates \( \sqrt{1.0001^{tick}} * 2^{96} \)
Throws if |tick| > max tick
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96);
Parameters
Name | Type | Description |
---|---|---|
tick | int24 | The input tick for the above formula |
Returns
Name | Type | Description |
---|---|---|
sqrtPriceX96 | uint160 | A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) at the given tick |
getTickAtSqrtRatio
Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may ever return.
function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick);
Parameters
Name | Type | Description |
---|---|---|
sqrtPriceX96 | uint160 | The sqrt ratio for which to compute the tick as a Q64.96 |
Returns
Name | Type | Description |
---|---|---|
tick | int24 | The greatest tick for which the ratio is less than or equal to the input ratio |
floor
Rounds down to the nearest tick where tick % tickSpacing == 0
Ensure tick +/- tickSpacing does not overflow or underflow int24
function floor(int24 tick, int24 tickSpacing) internal pure returns (int24);
Parameters
Name | Type | Description |
---|---|---|
tick | int24 | The tick to round |
tickSpacing | int24 | The tick spacing to round to |
Returns
Name | Type | Description |
---|---|---|
<none> | int24 | the floored tick |
ceil
Rounds up to the nearest tick where tick % tickSpacing == 0
Ensure tick +/- tickSpacing does not overflow or underflow int24
function ceil(int24 tick, int24 tickSpacing) internal pure returns (int24);
Parameters
Name | Type | Description |
---|---|---|
tick | int24 | The tick to round |
tickSpacing | int24 | The tick spacing to round to |
Returns
Name | Type | Description |
---|---|---|
<none> | int24 | the ceiled tick |
Volatility
Author: Aloe Labs, Inc.
Provides functions that use Uniswap v3 to compute price volatility
State Variables
_Q224Div1e18
uint256 private constant _Q224Div1e18 = (uint256(1 << 224) * 1e6) / 1e24;
_Q128Div1e18
uint256 private constant _Q128Div1e18 = (uint256(1 << 128) * 1e6) / 1e24;
Functions
estimate
Estimates implied volatility using this math.
The return value can fit in uint128 if necessary
function estimate(
PoolMetadata memory metadata,
uint160 sqrtMeanPriceX96,
FeeGrowthGlobals memory a,
FeeGrowthGlobals memory b,
uint32 scale
) internal pure returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
metadata | PoolMetadata | The pool's metadata (may be cached) |
sqrtMeanPriceX96 | uint160 | sqrt(TWAP) over some period. Likely from Oracle.consult |
a | FeeGrowthGlobals | The pool's cumulative feeGrowthGlobals some time in the past |
b | FeeGrowthGlobals | The pool's cumulative feeGrowthGlobals as of the current block |
scale | uint32 | The timescale (in seconds) in which IV should be reported, e.g. hourly, daily, annualized |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | An estimate of the implied volatility scaled by 1e12 |
Structs
PoolMetadata
struct PoolMetadata {
uint24 gamma0;
uint24 gamma1;
int24 tickSpacing;
}
FeeGrowthGlobals
struct FeeGrowthGlobals {
uint256 feeGrowthGlobal0X128;
uint256 feeGrowthGlobal1X128;
uint32 timestamp;
}
Borrower
Inherits: IUniswapV3MintCallback
Author: Aloe Labs, Inc.
"Test everything; hold fast what is good." - 1 Thessalonians 5:21
State Variables
SLOT0_MASK_POSITIONS
uint256 private constant SLOT0_MASK_POSITIONS = 0x000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff;
SLOT0_MASK_USERSPACE
uint256 private constant SLOT0_MASK_USERSPACE = 0x000000000000ffffffffffffffff000000000000000000000000000000000000;
SLOT0_MASK_AUCTION
uint256 private constant SLOT0_MASK_AUCTION = 0x00ffffffffff0000000000000000000000000000000000000000000000000000;
SLOT0_MASK_STATE
uint256 private constant SLOT0_MASK_STATE = 0x7f00000000000000000000000000000000000000000000000000000000000000;
SLOT0_DIRT
uint256 private constant SLOT0_DIRT = 0x8000000000000000000000000000000000000000000000000000000000000000;
FACTORY
The factory that created this contract
Factory public immutable FACTORY;
ORACLE
The oracle to use for prices and implied volatility
VolatilityOracle public immutable ORACLE;
UNISWAP_POOL
The Uniswap pair in which this Borrower
can manage positions
IUniswapV3Pool public immutable UNISWAP_POOL;
TOKEN0
The first token of the Uniswap pair
ERC20 public immutable TOKEN0;
TOKEN1
The second token of the Uniswap pair
ERC20 public immutable TOKEN1;
LENDER0
The lender of TOKEN0
Lender public immutable LENDER0;
LENDER1
The lender of TOKEN1
Lender public immutable LENDER1;
slot0
The Borrower
's only mutable storage. Lowest 144 bits store the lower/upper bounds of up to 3 Uniswap
positions, encoded by Positions.zip
. Next 64 bits are unused within the Borrower
and available to users as
"free" storage οΌ no additional sstore's. These 208 bits (144 + 64) are passed to IManager.callback
, and get
updated when the callback returns a non-zero value. The next 40 bits are either 0 or the warning time. The
highest 8 bits represent the current State
enum, plus 128. We add 128 (i.e. set the highest bit to 1) so that
the slot is always non-zero, even in the absence of Uniswap positions οΌ this saves gas.
uint256 public slot0;
Functions
onlyInModifyCallback
modifier onlyInModifyCallback();
constructor
constructor(VolatilityOracle oracle, IUniswapV3Pool pool, Lender lender0, Lender lender1);
receive
receive() external payable;
owner
function owner() public pure returns (address);
warn
Warns the borrower that they're about to be liquidated
function warn(uint40 oracleSeed) external;
Parameters
Name | Type | Description |
---|---|---|
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
clear
Clears the warning state if the account is healthy and has a full ante
If you bring the account back to health via a modify
call, the warning state is cleared
automatically. However, if borrowing is paused and modify
is restricted, you may want to repay
the Lender
(s) directly and use this to clear the warning.
function clear(uint40 oracleSeed) external payable;
Parameters
Name | Type | Description |
---|---|---|
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
liquidate
Liquidates the borrower, using all available assets to pay down liabilities. callee
must
transfer at least amounts.repay0
and amounts.repay1
to LENDER0
and LENDER1
, respectively.
amounts.out0
and amounts.out1
start at 0 and increase over time. Once their value exceeds what
must be repaid, the excess acts as a liquidation incentive.
The amounts out are 0 for the entirety of the LIQUIDATION_GRACE_PERIOD
. They start rising
afterwards, reaching 105% of the repay value after 5 minutes and 112% after 55 minutes.
function liquidate(ILiquidator callee, bytes calldata data, uint256 closeFactor, uint40 oracleSeed) external;
Parameters
Name | Type | Description |
---|---|---|
callee | ILiquidator | The smart contract responsible for swapping and repaying |
data | bytes | Encoded parameters that get forwarded to callee |
closeFactor | uint256 | The fraction of liabilities to repay, expressed in basis points |
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
modify
Allows the owner to manage their account by handing control to some callee
. Inside the
callback callee
has access to all sub-commands (uniswapDeposit
, uniswapWithdraw
, transfer
,
borrow
, repay
, and withdrawAnte
). Whatever callee
does, the account MUST be healthy
after the callback.
function modify(IManager callee, bytes calldata data, uint40 oracleSeed) external payable;
Parameters
Name | Type | Description |
---|---|---|
callee | IManager | The smart contract that will get temporary control of this account |
data | bytes | Encoded parameters that get forwarded to callee |
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
uniswapV3MintCallback
Callback for Uniswap V3 pool; necessary for uniswapDeposit
to work
function uniswapV3MintCallback(uint256 amount0, uint256 amount1, bytes calldata) external;
Parameters
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of TOKEN0 owed to the UNISWAP_POOL |
amount1 | uint256 | The amount of TOKEN1 owed to the UNISWAP_POOL |
<none> | bytes |
uniswapDeposit
Allows the owner()
to add liquidity to a Uniswap position (or create a new one). Only works
within the modify
callback.
The LiquidityAmounts
library can help convert underlying amounts to units of liquidity
.
NOTE: Depending on your use-case, it may be more gas-efficient to call UNISWAP_POOL.mint
in your
own contract, instead of doing uniswapDeposit
inside of modify
's callback. As long as you set
this Borrower
as the recipient in UNISWAP_POOL.mint
, the result is the same.
function uniswapDeposit(
int24 lower,
int24 upper,
uint128 liquidity
) external onlyInModifyCallback returns (uint256 amount0, uint256 amount1);
Parameters
Name | Type | Description |
---|---|---|
lower | int24 | The tick at the position's lower bound |
upper | int24 | The tick at the position's upper bound |
liquidity | uint128 | The amount of liquidity to add, in Uniswap's internal units |
Returns
Name | Type | Description |
---|---|---|
amount0 | uint256 | The precise amount of TOKEN0 that went into the Uniswap position |
amount1 | uint256 | The precise amount of TOKEN1 that went into the Uniswap position |
uniswapWithdraw
Allows the owner()
to withdraw liquidity from one of their Uniswap positions. Only works within
the modify
callback.
The LiquidityAmounts
library can help convert underlying amounts to units of liquidity
function uniswapWithdraw(
int24 lower,
int24 upper,
uint128 liquidity,
address recipient
) external onlyInModifyCallback returns (uint256 burned0, uint256 burned1, uint256 collected0, uint256 collected1);
Parameters
Name | Type | Description |
---|---|---|
lower | int24 | The tick at the position's lower bound |
upper | int24 | The tick at the position's upper bound |
liquidity | uint128 | The amount of liquidity to remove, in Uniswap's internal units. Pass 0 to collect fees without burning any liquidity. |
recipient | address | Receives the tokens from Uniswap. Usually the address of this Borrower account. |
Returns
Name | Type | Description |
---|---|---|
burned0 | uint256 | The amount of TOKEN0 that was removed from the Uniswap position |
burned1 | uint256 | The amount of TOKEN1 that was removed from the Uniswap position |
collected0 | uint256 | Equal to burned0 plus any earned TOKEN0 fees that hadn't yet been claimed |
collected1 | uint256 | Equal to burned1 plus any earned TOKEN1 fees that hadn't yet been claimed |
transfer
The most flexible sub-command. Allows the owner()
to transfer amounts of TOKEN0
and TOKEN1
to any recipient
they want. Only works within the modify
callback.
function transfer(uint256 amount0, uint256 amount1, address recipient) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of TOKEN0 to transfer |
amount1 | uint256 | The amount of TOKEN1 to transfer |
recipient | address | Receives the transferred tokens |
transferEth
Allows the owner()
to transfer an amount
of ETH to any recipient
they want. Only works within
the modify
callback.
function transferEth(uint256 amount, address payable recipient) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
amount | uint256 | The amount of ETH to transfer |
recipient | address payable | Receives the ETH |
borrow
Allows the owner()
to borrow funds from LENDER0
and LENDER1
. Only works within the modify
callback.
If amount0 > 0
and interest hasn't yet accrued in this block for LENDER0
, it will accrue
prior to processing your new borrow. Same goes for amount1 > 0
and LENDER1
.
function borrow(uint256 amount0, uint256 amount1, address recipient) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of TOKEN0 to borrow |
amount1 | uint256 | The amount of TOKEN1 to borrow |
recipient | address | Receives the borrowed tokens. Usually the address of this Borrower account. |
repay
Allows the owner()
to repay debts to LENDER0
and LENDER1
. Only works within the modify
callback.
This is technically unnecessary since you could call Lender.repay
directly, specifying this
contract as the beneficiary
and using the transfer
sub-command to make payments. We include it
because it's convenient and gas-efficient for common use-cases.
function repay(uint256 amount0, uint256 amount1) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of TOKEN0 to repay |
amount1 | uint256 | The amount of TOKEN1 to repay |
rescue
Allows the owner()
to perform arbitrary transfers. Useful for rescuing misplaced funds. Only
works within the modify
callback.
function rescue(ERC20 token, uint256 amount, address recipient) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
token | ERC20 | The ERC20 token to transfer |
amount | uint256 | The amount to transfer |
recipient | address | Receives the transferred tokens |
getUniswapPositions
function getUniswapPositions() external view returns (int24[] memory);
getAssets
function getAssets() external view returns (Assets memory);
getLiabilities
function getLiabilities() public view returns (uint256 amount0, uint256 amount1);
getPrices
Summarizes all oracle data pertinent to account health
If seemsLegit == false
, you can call Factory.pause
to temporarily disable borrows
function getPrices(uint40 oracleSeed) public view returns (Prices memory, bool, bool, uint208);
Parameters
Name | Type | Description |
---|---|---|
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
Returns
Name | Type | Description |
---|---|---|
<none> | Prices | The probe prices currently being used to evaluate account health |
<none> | bool | Whether the Uniswap TWAP seems to have been manipulated or not |
<none> | bool | Whether the factory has paused this market |
<none> | uint208 | The current ante that must be posted before borrowing |
_getAssets
function _getAssets(uint256 slot0_, Prices memory prices) private view returns (Assets memory assets);
_uniswapWithdraw
function _uniswapWithdraw(uint256 slot0_) private;
_uniswapWithdraw
function _uniswapWithdraw(
int24 lower,
int24 upper,
uint128 liquidity,
address recipient
) private returns (uint256 burned0, uint256 burned1, uint256 collected0, uint256 collected1);
Events
Warn
Emitted when the account gets warn
ed. The liquidation incentive will be 0 for 5 minutes,
giving the account owner time to regain health on their own. After this LIQUIDATION_GRACE_PERIOD
,
the incentive starts increasing (following the Dutch Auction curve in BalanceSheet
).
Simply regaining health is not enough. To clear the warning, you must replenish the ante
and
call modify
.
event Warn();
Liquidate
Emitted when the account gets liquidate
d
event Liquidate();
Enums
State
enum State {
Ready,
Locked,
InModifyCallback
}
Borrower
Inherits: IUniswapV3MintCallback
Author: Aloe Labs, Inc.
"Test everything; hold fast what is good." - 1 Thessalonians 5:21
State Variables
SLOT0_MASK_POSITIONS
uint256 private constant SLOT0_MASK_POSITIONS = 0x000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff;
SLOT0_MASK_USERSPACE
uint256 private constant SLOT0_MASK_USERSPACE = 0x000000000000ffffffffffffffff000000000000000000000000000000000000;
SLOT0_MASK_AUCTION
uint256 private constant SLOT0_MASK_AUCTION = 0x00ffffffffff0000000000000000000000000000000000000000000000000000;
SLOT0_MASK_STATE
uint256 private constant SLOT0_MASK_STATE = 0x7f00000000000000000000000000000000000000000000000000000000000000;
SLOT0_DIRT
uint256 private constant SLOT0_DIRT = 0x8000000000000000000000000000000000000000000000000000000000000000;
FACTORY
The factory that created this contract
Factory public immutable FACTORY;
ORACLE
The oracle to use for prices and implied volatility
VolatilityOracle public immutable ORACLE;
UNISWAP_POOL
The Uniswap pair in which this Borrower
can manage positions
IUniswapV3Pool public immutable UNISWAP_POOL;
TOKEN0
The first token of the Uniswap pair
ERC20 public immutable TOKEN0;
TOKEN1
The second token of the Uniswap pair
ERC20 public immutable TOKEN1;
LENDER0
The lender of TOKEN0
Lender public immutable LENDER0;
LENDER1
The lender of TOKEN1
Lender public immutable LENDER1;
slot0
The Borrower
's only mutable storage. Lowest 144 bits store the lower/upper bounds of up to 3 Uniswap
positions, encoded by Positions.zip
. Next 64 bits are unused within the Borrower
and available to users as
"free" storage οΌ no additional sstore's. These 208 bits (144 + 64) are passed to IManager.callback
, and get
updated when the callback returns a non-zero value. The next 40 bits are either 0 or the warning time. The
highest 8 bits represent the current State
enum, plus 128. We add 128 (i.e. set the highest bit to 1) so that
the slot is always non-zero, even in the absence of Uniswap positions οΌ this saves gas.
uint256 public slot0;
Functions
onlyInModifyCallback
modifier onlyInModifyCallback();
constructor
constructor(VolatilityOracle oracle, IUniswapV3Pool pool, Lender lender0, Lender lender1);
receive
receive() external payable;
owner
function owner() public pure returns (address);
warn
Warns the borrower that they're about to be liquidated
function warn(uint40 oracleSeed) external;
Parameters
Name | Type | Description |
---|---|---|
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
clear
Clears the warning state if the account is healthy and has a full ante
If you bring the account back to health via a modify
call, the warning state is cleared
automatically. However, if borrowing is paused and modify
is restricted, you may want to repay
the Lender
(s) directly and use this to clear the warning.
function clear(uint40 oracleSeed) external payable;
Parameters
Name | Type | Description |
---|---|---|
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
liquidate
Liquidates the borrower, using all available assets to pay down liabilities. callee
must
transfer at least amounts.repay0
and amounts.repay1
to LENDER0
and LENDER1
, respectively.
amounts.out0
and amounts.out1
start at 0 and increase over time. Once their value exceeds what
must be repaid, the excess acts as a liquidation incentive.
The amounts out are 0 for the entirety of the LIQUIDATION_GRACE_PERIOD
. They start rising
afterwards, reaching 105% of the repay value after 5 minutes and 112% after 55 minutes.
function liquidate(ILiquidator callee, bytes calldata data, uint256 closeFactor, uint40 oracleSeed) external;
Parameters
Name | Type | Description |
---|---|---|
callee | ILiquidator | The smart contract responsible for swapping and repaying |
data | bytes | Encoded parameters that get forwarded to callee |
closeFactor | uint256 | The fraction of liabilities to repay, expressed in basis points |
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
modify
Allows the owner to manage their account by handing control to some callee
. Inside the
callback callee
has access to all sub-commands (uniswapDeposit
, uniswapWithdraw
, transfer
,
borrow
, repay
, and withdrawAnte
). Whatever callee
does, the account MUST be healthy
after the callback.
function modify(IManager callee, bytes calldata data, uint40 oracleSeed) external payable;
Parameters
Name | Type | Description |
---|---|---|
callee | IManager | The smart contract that will get temporary control of this account |
data | bytes | Encoded parameters that get forwarded to callee |
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
uniswapV3MintCallback
Callback for Uniswap V3 pool; necessary for uniswapDeposit
to work
function uniswapV3MintCallback(uint256 amount0, uint256 amount1, bytes calldata) external;
Parameters
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of TOKEN0 owed to the UNISWAP_POOL |
amount1 | uint256 | The amount of TOKEN1 owed to the UNISWAP_POOL |
<none> | bytes |
uniswapDeposit
Allows the owner()
to add liquidity to a Uniswap position (or create a new one). Only works
within the modify
callback.
The LiquidityAmounts
library can help convert underlying amounts to units of liquidity
.
NOTE: Depending on your use-case, it may be more gas-efficient to call UNISWAP_POOL.mint
in your
own contract, instead of doing uniswapDeposit
inside of modify
's callback. As long as you set
this Borrower
as the recipient in UNISWAP_POOL.mint
, the result is the same.
function uniswapDeposit(
int24 lower,
int24 upper,
uint128 liquidity
) external onlyInModifyCallback returns (uint256 amount0, uint256 amount1);
Parameters
Name | Type | Description |
---|---|---|
lower | int24 | The tick at the position's lower bound |
upper | int24 | The tick at the position's upper bound |
liquidity | uint128 | The amount of liquidity to add, in Uniswap's internal units |
Returns
Name | Type | Description |
---|---|---|
amount0 | uint256 | The precise amount of TOKEN0 that went into the Uniswap position |
amount1 | uint256 | The precise amount of TOKEN1 that went into the Uniswap position |
uniswapWithdraw
Allows the owner()
to withdraw liquidity from one of their Uniswap positions. Only works within
the modify
callback.
The LiquidityAmounts
library can help convert underlying amounts to units of liquidity
function uniswapWithdraw(
int24 lower,
int24 upper,
uint128 liquidity,
address recipient
) external onlyInModifyCallback returns (uint256 burned0, uint256 burned1, uint256 collected0, uint256 collected1);
Parameters
Name | Type | Description |
---|---|---|
lower | int24 | The tick at the position's lower bound |
upper | int24 | The tick at the position's upper bound |
liquidity | uint128 | The amount of liquidity to remove, in Uniswap's internal units. Pass 0 to collect fees without burning any liquidity. |
recipient | address | Receives the tokens from Uniswap. Usually the address of this Borrower account. |
Returns
Name | Type | Description |
---|---|---|
burned0 | uint256 | The amount of TOKEN0 that was removed from the Uniswap position |
burned1 | uint256 | The amount of TOKEN1 that was removed from the Uniswap position |
collected0 | uint256 | Equal to burned0 plus any earned TOKEN0 fees that hadn't yet been claimed |
collected1 | uint256 | Equal to burned1 plus any earned TOKEN1 fees that hadn't yet been claimed |
transfer
The most flexible sub-command. Allows the owner()
to transfer amounts of TOKEN0
and TOKEN1
to any recipient
they want. Only works within the modify
callback.
function transfer(uint256 amount0, uint256 amount1, address recipient) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of TOKEN0 to transfer |
amount1 | uint256 | The amount of TOKEN1 to transfer |
recipient | address | Receives the transferred tokens |
transferEth
Allows the owner()
to transfer an amount
of ETH to any recipient
they want. Only works within
the modify
callback.
function transferEth(uint256 amount, address payable recipient) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
amount | uint256 | The amount of ETH to transfer |
recipient | address payable | Receives the ETH |
borrow
Allows the owner()
to borrow funds from LENDER0
and LENDER1
. Only works within the modify
callback.
If amount0 > 0
and interest hasn't yet accrued in this block for LENDER0
, it will accrue
prior to processing your new borrow. Same goes for amount1 > 0
and LENDER1
.
function borrow(uint256 amount0, uint256 amount1, address recipient) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of TOKEN0 to borrow |
amount1 | uint256 | The amount of TOKEN1 to borrow |
recipient | address | Receives the borrowed tokens. Usually the address of this Borrower account. |
repay
Allows the owner()
to repay debts to LENDER0
and LENDER1
. Only works within the modify
callback.
This is technically unnecessary since you could call Lender.repay
directly, specifying this
contract as the beneficiary
and using the transfer
sub-command to make payments. We include it
because it's convenient and gas-efficient for common use-cases.
function repay(uint256 amount0, uint256 amount1) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
amount0 | uint256 | The amount of TOKEN0 to repay |
amount1 | uint256 | The amount of TOKEN1 to repay |
rescue
Allows the owner()
to perform arbitrary transfers. Useful for rescuing misplaced funds. Only
works within the modify
callback.
function rescue(ERC20 token, uint256 amount, address recipient) external onlyInModifyCallback;
Parameters
Name | Type | Description |
---|---|---|
token | ERC20 | The ERC20 token to transfer |
amount | uint256 | The amount to transfer |
recipient | address | Receives the transferred tokens |
getUniswapPositions
function getUniswapPositions() external view returns (int24[] memory);
getAssets
function getAssets() external view returns (Assets memory);
getLiabilities
function getLiabilities() public view returns (uint256 amount0, uint256 amount1);
getPrices
Summarizes all oracle data pertinent to account health
If seemsLegit == false
, you can call Factory.pause
to temporarily disable borrows
function getPrices(uint40 oracleSeed) public view returns (Prices memory, bool, bool, uint208);
Parameters
Name | Type | Description |
---|---|---|
oracleSeed | uint40 | The indices of UNISWAP_POOL.observations where we start our search for the 30-minute-old (lowest 16 bits) and 60-minute-old (next 16 bits) observations when getting TWAPs. If any of the highest 8 bits are set, we fallback to onchain binary search. |
Returns
Name | Type | Description |
---|---|---|
<none> | Prices | The probe prices currently being used to evaluate account health |
<none> | bool | Whether the Uniswap TWAP seems to have been manipulated or not |
<none> | bool | Whether the factory has paused this market |
<none> | uint208 | The current ante that must be posted before borrowing |
_getAssets
function _getAssets(uint256 slot0_, Prices memory prices) private view returns (Assets memory assets);
_uniswapWithdraw
function _uniswapWithdraw(uint256 slot0_) private;
_uniswapWithdraw
function _uniswapWithdraw(
int24 lower,
int24 upper,
uint128 liquidity,
address recipient
) private returns (uint256 burned0, uint256 burned1, uint256 collected0, uint256 collected1);
Events
Warn
Emitted when the account gets warn
ed. The liquidation incentive will be 0 for 5 minutes,
giving the account owner time to regain health on their own. After this LIQUIDATION_GRACE_PERIOD
,
the incentive starts increasing (following the Dutch Auction curve in BalanceSheet
).
Simply regaining health is not enough. To clear the warning, you must replenish the ante
and
call modify
.
event Warn();
Liquidate
Emitted when the account gets liquidate
d
event Liquidate();
Enums
State
enum State {
Ready,
Locked,
InModifyCallback
}
ILiquidator
Functions
receive
receive() external payable;
callback
Transfers amounts.out0
and amounts.out1
to the liquidator with the expectation that they'll
transfer amounts.repay0
and amounts.repay1
to the appropriate Lender
s, executing swaps if necessary.
The liquidator can keep leftover funds as a reward.
function callback(bytes calldata data, address caller, AuctionAmounts memory amounts) external;
Parameters
Name | Type | Description |
---|---|---|
data | bytes | Encoded parameters that were passed to Borrower.liquidate |
caller | address | The address that called Borrower.liquidate |
amounts | AuctionAmounts | The key amounts involved in the liquidation |
IManager
Functions
callback
Gives the IManager
full control of the Borrower
. Called within Borrower.modify
.
In most cases, you'll want to verify that msg.sender
is, in fact, a Borrower
using
factory.isBorrower(msg.sender)
.
function callback(bytes calldata data, address owner, uint208 positions) external returns (uint208);
Parameters
Name | Type | Description |
---|---|---|
data | bytes | Encoded parameters that were passed to Borrower.modify |
owner | address | The owner of the Borrower |
positions | uint208 | The Borrower 's current Uniswap positions. You can convert them to an array using Positions.extract |
Returns
Name | Type | Description |
---|---|---|
<none> | uint208 | Updated positions, encoded using Positions.zip . Return 0 if you don't wish to make any changes. |
Factory
Author: Aloe Labs, Inc.
"Test everything; hold fast what is good." - 1 Thessalonians 5:21
State Variables
GOVERNOR
The only address that can propose new MarketConfig
s and rewards programs
address public immutable GOVERNOR;
ORACLE
The oracle to use for prices and implied volatility
VolatilityOracle public immutable ORACLE;
LENDER_IMPLEMENTATION
The implementation to which all Lender
clones will point
address public immutable LENDER_IMPLEMENTATION;
_BORROWER_DEPLOYER
A simple contract that deploys Borrower
s to keep Factory
bytecode size down
BorrowerDeployer private immutable _BORROWER_DEPLOYER;
DEFAULT_RATE_MODEL
The rate model that Lender
s will use when first created
IRateModel public immutable DEFAULT_RATE_MODEL;
getMarket
Returns the Market
addresses associated with a Uniswap V3 pool
mapping(IUniswapV3Pool => Market) public getMarket;
getParameters
Returns the borrowing Parameters
associated with a Uniswap V3 pool
mapping(IUniswapV3Pool => Parameters) public getParameters;
peer
Returns the other Lender
in the Market
iff input is itself a Lender
, otherwise 0
mapping(address => address) public peer;
isBorrower
Returns whether the given address is a Borrower
deployed by this Factory
mapping(address => bool) public isBorrower;
rewardsToken
The token in which rewards are paid out
ERC20 public rewardsToken;
couriers
Returns the Courier
for any given ID
mapping(uint32 => Courier) public couriers;
Functions
constructor
constructor(
address governor,
address reserve,
VolatilityOracle oracle,
BorrowerDeployer borrowerDeployer,
IRateModel defaultRateModel
);
pause
function pause(IUniswapV3Pool pool, uint40 oracleSeed) external;
createMarket
function createMarket(IUniswapV3Pool pool) external;
createBorrower
function createBorrower(IUniswapV3Pool pool, address owner, bytes12 salt) external returns (Borrower borrower);
claimRewards
function claimRewards(Lender[] calldata lenders, address beneficiary) external returns (uint256 earned);
enrollCourier
Enrolls msg.sender
in the referral program. This allows frontends/wallets/apps to
credit themselves for a given user's deposit, and receive a portion of their interest. Note
that after enrolling, msg.sender
will not be eligible for REWARDS_TOKEN
rewards.
See Lender.creditCourier
function enrollCourier(uint32 id, uint16 cut) external;
Parameters
Name | Type | Description |
---|---|---|
id | uint32 | A unique identifier for the courier |
cut | uint16 | The portion of interest the courier will receive. Should be in the range [0, 10000), with 10000 being 100%. |
governRewardsToken
function governRewardsToken(ERC20 rewardsToken_) external;
governRewardsRate
function governRewardsRate(Lender lender, uint64 rate) external;
governMarketConfig
function governMarketConfig(IUniswapV3Pool pool, MarketConfig memory config) external;
_setMarketConfig
function _setMarketConfig(IUniswapV3Pool pool, MarketConfig memory config, uint32 pausedUntilTime) private;
_newBorrower
function _newBorrower(IUniswapV3Pool pool, Lender lender0, Lender lender1) private returns (Borrower);
Events
CreateMarket
event CreateMarket(IUniswapV3Pool indexed pool, Lender lender0, Lender lender1);
CreateBorrower
event CreateBorrower(IUniswapV3Pool indexed pool, address indexed owner, Borrower account);
EnrollCourier
event EnrollCourier(uint32 indexed id, address indexed wallet, uint16 cut);
SetMarketConfig
event SetMarketConfig(IUniswapV3Pool indexed pool, MarketConfig config);
Structs
Market
struct Market {
Lender lender0;
Lender lender1;
Borrower borrowerImplementation;
}
Parameters
struct Parameters {
uint208 ante;
uint8 nSigma;
uint8 manipulationThresholdDivisor;
uint32 pausedUntilTime;
}
MarketConfig
struct MarketConfig {
uint208 ante;
uint8 nSigma;
uint8 manipulationThresholdDivisor;
uint8 reserveFactor0;
uint8 reserveFactor1;
IRateModel rateModel0;
IRateModel rateModel1;
}
Courier
struct Courier {
address wallet;
uint16 cut;
}
Lender
Inherits: Ledger
Author: Aloe Labs, Inc.
"Test everything; hold fast what is good." - 1 Thessalonians 5:21
Functions
constructor
constructor(address reserve) Ledger(reserve);
initialize
function initialize() external;
setRateModelAndReserveFactor
Sets the rateModel
and reserveFactor
. Only the FACTORY
can call this.
function setRateModelAndReserveFactor(IRateModel rateModel_, uint8 reserveFactor_) external;
setRewardsRate
Sets the rewards rate. May be 0. Only the FACTORY
can call this.
function setRewardsRate(uint64 rate) external;
Parameters
Name | Type | Description |
---|---|---|
rate | uint64 | The rewards rate, specified in [token units per second]. If non-zero, keep between 10^17 and 10^28 token units per year for smooth operation. Assuming FACTORY.rewardsToken() has 18 decimals, this is between 0.1 and 10 billion tokens per year. |
whitelist
Allows borrower
to call borrow
. One the FACTORY
can call this.
function whitelist(address borrower) external;
claimRewards
function claimRewards(address owner) external returns (uint96 earned);
deposit
Mints shares
to beneficiary
by depositing exactly amount
of underlying tokens
deposit
is more efficient than mint
and is the recommended way of depositing. Also
supports the additional flow where you prepay amount
instead of relying on approve/transferFrom.
function deposit(uint256 amount, address beneficiary, uint32 courierId) public returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
amount | uint256 | The amount of underlying tokens to deposit |
beneficiary | address | The receiver of shares |
courierId | uint32 | The ID of the courier (or 0, to indicate lack thereof) that will receive a cut of beneficiary 's future interest. Only takes effect when balanceOf(beneficiary) == 0 . In all other cases, pass 0 to avoid wasting gas on courier-related checks. |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The number of shares (banknotes) minted to beneficiary |
deposit
function deposit(uint256 amount, address beneficiary) external returns (uint256 shares);
mint
function mint(uint256 shares, address beneficiary) external returns (uint256 amount);
redeem
Burns shares
from owner
and sends amount
of underlying tokens to receiver
. If
owner
has a courier, additional shares will be transferred from owner
to the courier as a fee.
redeem
is more efficient than withdraw
and is the recommended way of withdrawing
function redeem(uint256 shares, address recipient, address owner) public returns (uint256 amount);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The number of shares to burn in exchange for underlying tokens. To burn all your shares, you can pass maxRedeem(owner) . If maxRedeem(owner) is changing over time (due to a courier or high utilization) you can pass type(uint256).max and it will be computed in-place. |
recipient | address | The receiver of amount of underlying tokens |
owner | address | The user from whom shares are taken (for both the burn and possible fee transfer) |
Returns
Name | Type | Description |
---|---|---|
amount | uint256 | The number of underlying tokens transferred to recipient |
withdraw
function withdraw(uint256 amount, address recipient, address owner) external returns (uint256 shares);
borrow
Sends amount
of asset
to recipient
and increases msg.sender
's debt by units
function borrow(uint256 amount, address recipient) external returns (uint256 units);
repay
Reduces beneficiary
's debt by units
, assuming someone has pre-paid amount
of asset
. To repay
all debt for some account, call repay(borrowBalance(account), account)
.
To avoid frontrunning, amount
should be pre-paid in the same transaction as the repay
call.
function repay(uint256 amount, address beneficiary) external returns (uint256 units);
accrueInterest
function accrueInterest() public returns (uint72);
approve
function approve(address spender, uint256 shares) external returns (bool);
transfer
function transfer(address to, uint256 shares) external returns (bool);
transferFrom
function transferFrom(address from, address to, uint256 shares) external returns (bool);
permit
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
_transfer
Transfers shares
from from
to to
, iff neither of them have a courier
function _transfer(address from, address to, uint256 shares) private;
_mint
Make sure to do something with the return value, newTotalSupply
!
function _mint(
address to,
uint256 shares,
uint256 amount,
uint256 totalSupply_,
uint32 courierId
) private returns (uint256 newTotalSupply);
_burn
Make sure to do something with the return value, newTotalSupply
!
function _burn(
address from,
uint256 shares,
uint256 inventory,
uint256 totalSupply_
) private returns (uint256 newTotalSupply);
_load
function _load() private returns (Cache memory cache, uint256 inventory);
_save
function _save(Cache memory cache, bool didChangeBorrowBase) private;
Events
Approval
event Approval(address indexed owner, address indexed spender, uint256 amount);
Transfer
event Transfer(address indexed from, address indexed to, uint256 amount);
Deposit
event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
Withdraw
event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares);
Borrow
event Borrow(address indexed caller, address indexed recipient, uint256 amount, uint256 units);
Repay
event Repay(address indexed caller, address indexed beneficiary, uint256 amount, uint256 units);
CreditCourier
event CreditCourier(uint32 indexed id, address indexed account);
Lender
Inherits: Ledger
Author: Aloe Labs, Inc.
"Test everything; hold fast what is good." - 1 Thessalonians 5:21
Functions
constructor
constructor(address reserve) Ledger(reserve);
initialize
function initialize() external;
setRateModelAndReserveFactor
Sets the rateModel
and reserveFactor
. Only the FACTORY
can call this.
function setRateModelAndReserveFactor(IRateModel rateModel_, uint8 reserveFactor_) external;
setRewardsRate
Sets the rewards rate. May be 0. Only the FACTORY
can call this.
function setRewardsRate(uint64 rate) external;
Parameters
Name | Type | Description |
---|---|---|
rate | uint64 | The rewards rate, specified in [token units per second]. If non-zero, keep between 10^17 and 10^28 token units per year for smooth operation. Assuming FACTORY.rewardsToken() has 18 decimals, this is between 0.1 and 10 billion tokens per year. |
whitelist
Allows borrower
to call borrow
. One the FACTORY
can call this.
function whitelist(address borrower) external;
claimRewards
function claimRewards(address owner) external returns (uint96 earned);
deposit
Mints shares
to beneficiary
by depositing exactly amount
of underlying tokens
deposit
is more efficient than mint
and is the recommended way of depositing. Also
supports the additional flow where you prepay amount
instead of relying on approve/transferFrom.
function deposit(uint256 amount, address beneficiary, uint32 courierId) public returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
amount | uint256 | The amount of underlying tokens to deposit |
beneficiary | address | The receiver of shares |
courierId | uint32 | The ID of the courier (or 0, to indicate lack thereof) that will receive a cut of beneficiary 's future interest. Only takes effect when balanceOf(beneficiary) == 0 . In all other cases, pass 0 to avoid wasting gas on courier-related checks. |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The number of shares (banknotes) minted to beneficiary |
deposit
function deposit(uint256 amount, address beneficiary) external returns (uint256 shares);
mint
function mint(uint256 shares, address beneficiary) external returns (uint256 amount);
redeem
Burns shares
from owner
and sends amount
of underlying tokens to receiver
. If
owner
has a courier, additional shares will be transferred from owner
to the courier as a fee.
redeem
is more efficient than withdraw
and is the recommended way of withdrawing
function redeem(uint256 shares, address recipient, address owner) public returns (uint256 amount);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The number of shares to burn in exchange for underlying tokens. To burn all your shares, you can pass maxRedeem(owner) . If maxRedeem(owner) is changing over time (due to a courier or high utilization) you can pass type(uint256).max and it will be computed in-place. |
recipient | address | The receiver of amount of underlying tokens |
owner | address | The user from whom shares are taken (for both the burn and possible fee transfer) |
Returns
Name | Type | Description |
---|---|---|
amount | uint256 | The number of underlying tokens transferred to recipient |
withdraw
function withdraw(uint256 amount, address recipient, address owner) external returns (uint256 shares);
borrow
Sends amount
of asset
to recipient
and increases msg.sender
's debt by units
function borrow(uint256 amount, address recipient) external returns (uint256 units);
repay
Reduces beneficiary
's debt by units
, assuming someone has pre-paid amount
of asset
. To repay
all debt for some account, call repay(borrowBalance(account), account)
.
To avoid frontrunning, amount
should be pre-paid in the same transaction as the repay
call.
function repay(uint256 amount, address beneficiary) external returns (uint256 units);
accrueInterest
function accrueInterest() public returns (uint72);
approve
function approve(address spender, uint256 shares) external returns (bool);
transfer
function transfer(address to, uint256 shares) external returns (bool);
transferFrom
function transferFrom(address from, address to, uint256 shares) external returns (bool);
permit
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
_transfer
Transfers shares
from from
to to
, iff neither of them have a courier
function _transfer(address from, address to, uint256 shares) private;
_mint
Make sure to do something with the return value, newTotalSupply
!
function _mint(
address to,
uint256 shares,
uint256 amount,
uint256 totalSupply_,
uint32 courierId
) private returns (uint256 newTotalSupply);
_burn
Make sure to do something with the return value, newTotalSupply
!
function _burn(
address from,
uint256 shares,
uint256 inventory,
uint256 totalSupply_
) private returns (uint256 newTotalSupply);
_load
function _load() private returns (Cache memory cache, uint256 inventory);
_save
function _save(Cache memory cache, bool didChangeBorrowBase) private;
Events
Approval
event Approval(address indexed owner, address indexed spender, uint256 amount);
Transfer
event Transfer(address indexed from, address indexed to, uint256 amount);
Deposit
event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
Withdraw
event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares);
Borrow
event Borrow(address indexed caller, address indexed recipient, uint256 amount, uint256 units);
Repay
event Repay(address indexed caller, address indexed beneficiary, uint256 amount, uint256 units);
CreditCourier
event CreditCourier(uint32 indexed id, address indexed account);
Ledger
State Variables
FACTORY
Factory public immutable FACTORY;
RESERVE
address public immutable RESERVE;
totalSupply
Doesn't include reserve inflation. If you want that, use stats()
uint112 public totalSupply;
lastBalance
Used in lieu of asset.balanceOf
to prevent inflation attacks
uint112 public lastBalance;
lastAccrualTime
The last block.timestamp
at which interest accrued
uint32 public lastAccrualTime;
borrowBase
The principle of all outstanding loans as if they were taken out at borrowIndex = ONE
uint184 public borrowBase;
borrowIndex
Tracks all-time growth of borrow interest. Starts at ONE
and increases monotonically over time
uint72 public borrowIndex;
borrows
The principle of a given user's loan as if it was taken out at borrowIndex = ONE
mapping(address => uint256) public borrows;
balances
Highest 32 bits are the referral code, next 112 are the principle, lowest 112 are the shares.
mapping(address => uint256) public balances;
allowance
mapping(address => mapping(address => uint256)) public allowance;
nonces
mapping(address => uint256) public nonces;
rateModel
rateModel.getYieldPerSecond
is given 100000 gas, and the output is clamped to MAX_RATE
. If
the call reverts, it's treated the same as if it returned 0.
IRateModel public rateModel;
reserveFactor
The portion of interest that accrues to the RESERVE
. Expressed as a reciprocal, e.g. 16 β 6.25%
uint8 public reserveFactor;
Functions
constructor
constructor(address reserve);
supportsInterface
Returns true if this contract implements the interface defined by interfaceId
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
name
The name of the banknote.
function name() external view returns (string memory);
symbol
The symbol of the banknote.
function symbol() external view returns (string memory);
decimals
The number of decimals the banknote uses. Matches the underlying token.
function decimals() external view returns (uint8);
asset
The address of the underlying token.
function asset() public pure returns (ERC20);
peer
The address of the other Lender
in the market
function peer() public view returns (address);
DOMAIN_SEPARATOR
The domain separator for EIP-2612
function DOMAIN_SEPARATOR() public view returns (bytes32);
stats
Gets basic lending statistics as if accrueInterest
were just called.
function stats() external view returns (uint72, uint256, uint256, uint256);
Returns
Name | Type | Description |
---|---|---|
<none> | uint72 | The updated borrowIndex |
<none> | uint256 | The sum of all banknote balances, in underlying units (i.e. totalAssets ) |
<none> | uint256 | The sum of all outstanding debts, in underlying units |
<none> | uint256 | The sum of all banknote balances. Will differ from totalSupply due to reserves inflation |
rewardsRate
The rewards rate, specified as [token units per second]
function rewardsRate() external view returns (uint64 rate);
rewardsOf
All rewards earned by account
that have not yet been paid out
function rewardsOf(address account) external view returns (uint96);
courierOf
The ID of the referrer associated with account
's deposit. If 0, they have no courier.
function courierOf(address account) external view returns (uint32);
principleOf
The lending principle of account
. Only tracked if they have a courier.
function principleOf(address account) external view returns (uint256);
balanceOf
The number of shares held by account
function balanceOf(address account) public view returns (uint256);
underlyingBalance
The amount of asset
owed to account
after accruing the latest interest, i.e.
the value that maxWithdraw
would return if outstanding borrows weren't a constraint.
Fees owed to couriers are automatically subtracted from this value in real-time, but couriers
themselves won't receive earnings until users redeem
or withdraw
.
Because of the fees, βunderlyingBalances != totalAssets
function underlyingBalance(address account) external view returns (uint256);
underlyingBalanceStored
The amount of asset
owed to account
before accruing the latest interest.
See underlyingBalance
for details.
An underestimate; more gas efficient than underlyingBalance
function underlyingBalanceStored(address account) external view returns (uint256);
borrowBalance
The amount of asset
owed by account
after accruing the latest interest. If one calls
repay(borrowBalance(account), account)
, the account
will be left with a borrow balance of 0.
function borrowBalance(address account) external view returns (uint256);
borrowBalanceStored
The amount of asset
owed by account
before accruing the latest interest.
function borrowBalanceStored(address account) external view returns (uint256);
totalAssets
The total amount of asset
under management
convertToShares(totalAssets()) != totalSupply()
due to reserves inflation. If you need
the up-to-date supply, use stats()
function totalAssets() external view returns (uint256);
convertToShares
function convertToShares(uint256 assets) public view returns (uint256);
convertToAssets
function convertToAssets(uint256 shares) public view returns (uint256);
previewDeposit
function previewDeposit(uint256 assets) public view returns (uint256);
previewMint
function previewMint(uint256 shares) public view returns (uint256);
previewRedeem
function previewRedeem(uint256 shares) public view returns (uint256);
previewWithdraw
function previewWithdraw(uint256 assets) public view returns (uint256);
maxDeposit
Returns a conservative estimate of the maximum amount of asset()
that can be deposited into the
Vault for receiver
, through a deposit call.
Should return the precise maximum. In this case that'd be on the order of 2^112 with constraints
coming from both lastBalance
and totalSupply
, which changes during interest accrual. Instead of doing
complicated math, we provide a constant conservative estimate of 2^96.
function maxDeposit(address) external pure returns (uint256);
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | The maximum amount of asset() that can be deposited |
maxMint
Returns a conservative estimate of the maximum number of Vault shares that can be minted for receiver
,
through a mint call.
Should return the precise maximum. In this case that'd be on the order of 2^112 with constraints
coming from both lastBalance
and totalSupply
, which changes during interest accrual. Instead of doing
complicated math, we provide a constant conservative estimate of 2^96.
function maxMint(address) external pure returns (uint256);
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | The maximum number of Vault shares that can be minted |
maxRedeem
Returns the maximum number of Vault shares that can be redeemed in the Vault by owner
, through a
redeem call.
function maxRedeem(address owner) public view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
owner | address | The address that would burn Vault shares when redeeming |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | The maximum number of Vault shares that can be redeemed |
maxWithdraw
Returns the maximum amount of asset()
that can be withdrawn from the Vault by owner
, through a
withdraw call.
function maxWithdraw(address owner) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
owner | address | The address that would burn Vault shares when withdrawing |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | The maximum amount of asset() that can be withdrawn |
_previewInterest
Accrues interest up to the current block.timestamp
. Updates and returns cache
, but doesn't write
anything to storage.
function _previewInterest(Cache memory cache) internal view returns (Cache memory, uint256, uint256);
_convertToShares
function _convertToShares(
uint256 assets,
uint256 inventory,
uint256 totalSupply_,
bool roundUp
) internal pure returns (uint256);
_convertToAssets
function _convertToAssets(
uint256 shares,
uint256 inventory,
uint256 totalSupply_,
bool roundUp
) internal pure returns (uint256);
_nominalShares
The account
's balance, minus any shares earned by their courier
function _nominalShares(
address account,
uint256 inventory,
uint256 totalSupply_
) private view returns (uint256 balance);
_getCache
function _getCache() internal view returns (Cache memory);
Structs
Cache
struct Cache {
uint256 totalSupply;
uint256 lastBalance;
uint256 lastAccrualTime;
uint256 borrowBase;
uint256 borrowIndex;
}
π IFlashBorrower
RateModel
Inherits: IRateModel
Author: Aloe Labs, Inc.
"Test everything; hold fast what is good." - 1 Thessalonians 5:21
State Variables
_A
uint256 private constant _A = 6.1010463348e20;
_B
uint256 private constant _B = _A / 1e18;
Functions
getYieldPerSecond
Specifies the percentage yield per second for a lender
. Need not be a pure function
of utilization
. To convert to APY: (1 + returnValue / 1e12) ** secondsPerYear - 1
function getYieldPerSecond(uint256 utilization, address) external pure returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
utilization | uint256 | The lender 's total borrows divided by total assets, scaled up by 1e18 |
<none> | address |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | The percentage yield per second, scaled up by 1e12 |
RateModel
Inherits: IRateModel
Author: Aloe Labs, Inc.
"Test everything; hold fast what is good." - 1 Thessalonians 5:21
State Variables
_A
uint256 private constant _A = 6.1010463348e20;
_B
uint256 private constant _B = _A / 1e18;
Functions
getYieldPerSecond
Specifies the percentage yield per second for a lender
. Need not be a pure function
of utilization
. To convert to APY: (1 + returnValue / 1e12) ** secondsPerYear - 1
function getYieldPerSecond(uint256 utilization, address) external pure returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
utilization | uint256 | The lender 's total borrows divided by total assets, scaled up by 1e18 |
<none> | address |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | The percentage yield per second, scaled up by 1e12 |
SafeRateLib
Functions
getAccrualFactor
function getAccrualFactor(IRateModel rateModel, uint256 utilization, uint256 dt) internal view returns (uint256);
_computeAccrualFactor
function _computeAccrualFactor(uint256 rate, uint256 dt) private pure returns (uint256);
IRateModel
Functions
getYieldPerSecond
Specifies the percentage yield per second for a lender
. Need not be a pure function
of utilization
. To convert to APY: (1 + returnValue / 1e12) ** secondsPerYear - 1
function getYieldPerSecond(uint256 utilization, address lender) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
utilization | uint256 | The lender 's total borrows divided by total assets, scaled up by 1e18 |
lender | address | The Lender to examine |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | The percentage yield per second, scaled up by 1e12 |
VolatilityOracle
Author: Aloe Labs, Inc.
"Test everything; hold fast what is good." - 1 Thessalonians 5:21
State Variables
cachedMetadata
mapping(IUniswapV3Pool => Volatility.PoolMetadata) public cachedMetadata;
feeGrowthGlobals
mapping(IUniswapV3Pool => Volatility.FeeGrowthGlobals[FEE_GROWTH_ARRAY_LENGTH]) public feeGrowthGlobals;
lastWrites
mapping(IUniswapV3Pool => LastWrite) public lastWrites;
Functions
prepare
function prepare(IUniswapV3Pool pool) external;
update
function update(IUniswapV3Pool pool, uint40 seed) external returns (uint56, uint160, uint256);
consult
function consult(IUniswapV3Pool pool, uint40 seed) external view returns (uint56, uint160, uint256);
_ema
function _ema(int256 oldIV, int256 estimate) private pure returns (uint104);
_interpolateIV
function _interpolateIV(LastWrite memory lastWrite) private view returns (uint256);
_getPoolMetadata
function _getPoolMetadata(IUniswapV3Pool pool) private view returns (Volatility.PoolMetadata memory metadata);
_getFeeGrowthGlobalsNow
function _getFeeGrowthGlobalsNow(IUniswapV3Pool pool) private view returns (Volatility.FeeGrowthGlobals memory);
_getFeeGrowthGlobalsOld
function _getFeeGrowthGlobalsOld(
Volatility.FeeGrowthGlobals[FEE_GROWTH_ARRAY_LENGTH] storage arr,
uint256 index
) private view returns (Volatility.FeeGrowthGlobals memory);
_binarySearch
function _binarySearch(
Volatility.FeeGrowthGlobals[FEE_GROWTH_ARRAY_LENGTH] storage arr,
uint256 l,
uint256 target
) private view returns (Volatility.FeeGrowthGlobals memory);
_isInInterval
function _isInInterval(uint256 min, uint256 x, uint256 max) private pure returns (bool);
Events
Update
event Update(IUniswapV3Pool indexed pool, uint160 sqrtMeanPriceX96, uint104 iv);
Structs
LastWrite
struct LastWrite {
uint8 index;
uint40 time;
uint104 oldIV;
uint104 newIV;
}
Contents
Contents
Uniswap
State Variables
_INIT_CODE_HASH
bytes32 private constant _INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
Functions
info
Wrapper around IUniswapV3Pool.positions()
that assumes positions
is owned by this
function info(Position memory position, IUniswapV3Pool pool) internal view returns (PositionInfo memory positionInfo);
info
Wrapper around IUniswapV3Pool.positions()
.
function info(
Position memory position,
IUniswapV3Pool pool,
address owner
) internal view returns (PositionInfo memory positionInfo);
fees
function fees(
Position memory position,
IUniswapV3Pool pool,
PositionInfo memory positionInfo,
FeeComputationCache memory c
) internal view returns (uint256 amount0, uint256 amount1);
liquidityForAmount0
function liquidityForAmount0(Position memory position, uint256 amount0) internal pure returns (uint128);
liquidityForAmount0
Computes the amount of liquidity received for a given amount of token0 and price range
Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
function liquidityForAmount0(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0
) internal pure returns (uint128 liquidity);
Parameters
Name | Type | Description |
---|---|---|
sqrtRatioAX96 | uint160 | A sqrt price representing the first tick boundary |
sqrtRatioBX96 | uint160 | A sqrt price representing the second tick boundary |
amount0 | uint256 | The amount0 being sent in |
Returns
Name | Type | Description |
---|---|---|
liquidity | uint128 | The amount of returned liquidity |
liquidityForAmount1
function liquidityForAmount1(Position memory position, uint256 amount1) internal pure returns (uint128);
liquidityForAmount1
Computes the amount of liquidity received for a given amount of token1 and price range
Calculates amount1 / (sqrt(upper) - sqrt(lower)).
function liquidityForAmount1(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount1
) internal pure returns (uint128 liquidity);
Parameters
Name | Type | Description |
---|---|---|
sqrtRatioAX96 | uint160 | A sqrt price representing the first tick boundary |
sqrtRatioBX96 | uint160 | A sqrt price representing the second tick boundary |
amount1 | uint256 | The amount1 being sent in |
Returns
Name | Type | Description |
---|---|---|
liquidity | uint128 | The amount of returned liquidity |
liquidityForAmounts
function liquidityForAmounts(
Position memory position,
uint160 sqrtPriceX96,
uint256 amount0,
uint256 amount1
) internal pure returns (uint128);
liquidityForAmounts
Computes the maximum amount of liquidity received for a given amount of token0, token1, the current pool prices and the prices at the tick boundaries
function liquidityForAmounts(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0,
uint256 amount1
) internal pure returns (uint128 liquidity);
Parameters
Name | Type | Description |
---|---|---|
sqrtRatioX96 | uint160 | A sqrt price representing the current pool prices |
sqrtRatioAX96 | uint160 | A sqrt price representing the first tick boundary |
sqrtRatioBX96 | uint160 | A sqrt price representing the second tick boundary |
amount0 | uint256 | The amount of token0 being sent in |
amount1 | uint256 | The amount of token1 being sent in |
Returns
Name | Type | Description |
---|---|---|
liquidity | uint128 | The maximum amount of liquidity received |
amountsForLiquidity
Wrapper around LiquidityAmounts.getAmountsForLiquidity()
.
function amountsForLiquidity(
Position memory position,
uint160 sqrtPriceX96,
uint128 liquidity
) internal pure returns (uint256, uint256);
valueOfLiquidity
Wrapper around LiquidityAmounts.getValueOfLiquidity()
function valueOfLiquidity(
Position memory position,
uint160 sqrtPriceX96,
uint128 liquidity
) internal pure returns (uint256);
computePoolAddress
function computePoolAddress(
address factory,
address token0,
address token1,
uint24 fee
) internal pure returns (IUniswapV3Pool pool);
_fees
function _fees(
PositionInfo memory positionInfo,
uint256 feeGrowthInside0X128,
uint256 feeGrowthInside1X128
) private pure returns (uint256 amount0, uint256 amount1);
Structs
Position
struct Position {
int24 lower;
int24 upper;
}
PositionInfo
struct PositionInfo {
uint128 liquidity;
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
uint128 tokensOwed0;
uint128 tokensOwed1;
}
FeeComputationCache
struct FeeComputationCache {
int24 currentTick;
uint256 feeGrowthGlobal0X128;
uint256 feeGrowthGlobal1X128;
}
ποΈ helpers
π LenderAccrualHelper
π OracleUpdateHelper
Contents
- BoostManager
- BorrowerNFTMultiManager
- BorrowerNFTSimpleManager
- BorrowerNFTWithdrawManager
- FrontendManager
- Permit2Manager
- SimpleManager
- UniswapNFTManager
BoostManager
Inherits: IManager, IUniswapV3SwapCallback
State Variables
FACTORY
Factory public immutable FACTORY;
BORROWER_NFT
address public immutable BORROWER_NFT;
UNISWAP_NFT
IUniswapPositionNFT public immutable UNISWAP_NFT;
Functions
constructor
constructor(Factory factory, address borrowerNft, IUniswapPositionNFT uniswapNft);
uniswapV3SwapCallback
function uniswapV3SwapCallback(int256 amount0, int256 amount1, bytes calldata data) external;
callback
function callback(bytes calldata data, address owner, uint208 positions) external override returns (uint208);
_action0Mint
function _action0Mint(Borrower borrower, address owner, bytes memory args) private returns (uint208);
_action2Burn
function _action2Burn(
Borrower borrower,
address owner,
bytes memory args,
uint208 positions
) private returns (uint208);
_withdrawFromUniswapNFT
function _withdrawFromUniswapNFT(
uint256 tokenId,
uint128 liquidity,
address recipient
) private returns (uint256 burned0, uint256 burned1);
FrontendManager
Inherits: IManager, IUniswapV3SwapCallback
State Variables
FACTORY
Factory public immutable FACTORY;
Functions
constructor
constructor(Factory factory);
uniswapV3SwapCallback
function uniswapV3SwapCallback(int256 amount0, int256 amount1, bytes calldata data) external;
callback
function callback(bytes calldata data, address owner, uint208) external returns (uint208 positions);
Events
Modify
event Modify(address indexed borrower, int24 tick);
SimpleManager
Inherits: IManager
Functions
callback
function callback(bytes calldata data, address, uint208) external override returns (uint208);
UniswapNFTManager
Inherits: IManager
State Variables
FACTORY
Factory public immutable FACTORY;
BORROWER_NFT
address public immutable BORROWER_NFT;
UNISWAP_NFT
IUniswapPositionNFT public immutable UNISWAP_NFT;
Functions
constructor
constructor(Factory factory, address borrowerNft, IUniswapPositionNFT uniswapNft);
callback
function callback(bytes calldata data, address owner, uint208) external override returns (uint208 positions);
_withdrawFromNFT
function _withdrawFromNFT(uint256 tokenId, uint128 liquidity, address recipient) private;
BorrowerLens
Functions
predictBorrowerAddress
function predictBorrowerAddress(
IUniswapV3Pool pool,
address owner,
bytes12 salt,
address caller,
Factory factory
) external view returns (address borrower);
getSummary
function getSummary(Borrower account)
external
view
returns (
uint256 balanceEth,
uint256 balance0,
uint256 balance1,
uint256 liabilities0,
uint256 liabilities1,
uint256 slot0,
uint128[] memory liquidity
);
getHealth
Mirrors the logic in BalanceSheet.isHealthy
, but returns numbers instead of a boolean
function getHealth(Borrower account) external view returns (uint256 healthA, uint256 healthB);
isInUse
function isInUse(Borrower borrower) external view returns (bool, IUniswapV3Pool);
getUniswapPositions
function getUniswapPositions(Borrower account)
public
view
returns (int24[] memory positions, uint128[] memory liquidity, uint256[] memory fees);
_health
function _health(
uint160 sqrtPriceX96,
uint256 assets0,
uint256 assets1,
uint256 liabilities0,
uint256 liabilities1
) private pure returns (uint256 health);
LenderLens
Functions
readBasics
function readBasics(Lender lender)
external
view
returns (
ERC20 asset,
uint256 interestRate,
uint256 utilization,
uint256 inventory,
uint256 totalBorrows,
uint256 totalSupply,
uint8 reserveFactor,
uint64 rewardsRate
);
isMaxRedeemDynamic
Indicates whether lender.maxRedeem(owner)
is dynamic, i.e. whether it's changing over time or not
In most cases, lender.maxRedeem(owner)
returns a static number of shares, and a standard lender.redeem
call can successfully redeem everything. However, if the user has a courier or if utilization is too high, the
number of shares may change block by block. In that case, to redeem the maximum value, you can pass
type(uint256).max
to lender.redeem
.
function isMaxRedeemDynamic(Lender lender, address owner) external view returns (bool);
Router
State Variables
PERMIT2
IPermit2 public immutable PERMIT2;
Functions
constructor
constructor(IPermit2 permit2);
depositWithPermit2
Deposits amount
of lender.asset()
to lender
using {nonce
, deadline
, signature
} for Permit2,
and gives courierId
a cut of future interest earned by msg.sender
. v
, r
, and s
are used with
lender.permit
in order to (a) achieve 0 balance if necessary and (b) set the courier.
This innoculates Lender
against a potential courier frontrunning attack by redeeming all shares (if any
are present) before assigning the new courierId
. Lender
then clears the permit
ed allowance in deposit
,
meaning this contract is left with no special permissions.
function depositWithPermit2(
Lender lender,
uint256 amount,
uint16 transmittance,
uint256 nonce,
uint256 deadline,
bytes calldata signature,
uint32 courierId,
uint8 v,
bytes32 r,
bytes32 s
) external payable returns (uint256 shares);
depositWithPermit2
function depositWithPermit2(
Lender lender,
uint256 amount,
uint16 transmittance,
uint256 nonce,
uint256 deadline,
bytes calldata signature
) external payable returns (uint256 shares);
repayWithPermit2
function repayWithPermit2(
Lender lender,
bool max,
uint256 amount,
address beneficiary,
uint256 nonce,
uint256 deadline,
bytes calldata signature
) external returns (uint256 units);